diff --git a/README.md b/README.md
index c4131d3..6710311 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,8 @@ In the app, open the wallet menu and use **Friendbot** to fund your Freighter ac
## How it works
+> For detailed flowcharts, mathematical accrual proofs, and sequence diagrams, see [docs/architecture.md](docs/architecture.md).
+
### StreamRouter
Accrual is a pure function of ledger close time, so no keeper, cron, or oracle is needed:
@@ -73,6 +75,7 @@ To verify one: canonicalize `receipt` with any RFC-8785 implementation, SHA-256
## Repository layout
```
+docs/ architecture specification & Mermaid diagrams
contracts/ Soroban smart contracts (Rust)
stream_router/ per-second streams
cascade_splitter/ recursive split tables
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..c91919c
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,235 @@
+# FlowPulse Architecture Specification
+
+This document details the core architectural mechanisms, mathematical invariants, and smart contract flows of **FlowPulse** on Stellar Soroban:
+1. **StreamRouter**: Continuous per-second linear token streaming, accrual formulas, and dry-gap checkpointing.
+2. **CascadeSplitter**: Recursive dependency split routing, rounding dust conservation, and cycle termination.
+3. **Audit Receipts & Client Security**: Cryptographic verification under RFC-8785 (JCS) and Ed25519.
+
+---
+
+## 1. System Overview
+
+FlowPulse consists of two core Soroban smart contracts written in Rust (`contracts/`), an audit receipt engine, and a web application (`frontend/`).
+
+```mermaid
+flowchart TD
+ subgraph Users ["Actors & Ecosystem"]
+ Sponsor["Sponsor / Donor"]
+ Maintainer["Maintainer (Root Project)"]
+ Dependency["Upstream Dependencies (Transitive)"]
+ end
+
+ subgraph Contracts ["Soroban Smart Contracts (Testnet)"]
+ SR["StreamRouter Contract
contracts/stream_router"]
+ CS["CascadeSplitter Contract
contracts/cascade_splitter"]
+ end
+
+ subgraph Client ["Client Layer (React / Vite)"]
+ UI["Drafting Standard Interface"]
+ ReceiptEngine["RFC-8785 Audit Engine
frontend/src/lib/receipts.ts"]
+ end
+
+ Sponsor -->|"create_stream(deposit, rate)"| SR
+ Maintainer -->|"withdraw() / withdraw_all()"| SR
+ Maintainer -->|"set_splits(splits[])"| CS
+ Sponsor -->|"distribute(owner, token, amount)"| CS
+ CS -->|"Recursive cascade payouts (depth <= 3)"| Dependency
+ SR -.->|"Ledger Events"| ReceiptEngine
+ CS -.->|"Ledger Events"| ReceiptEngine
+ ReceiptEngine -->|"Canonical Digest + Signature"| UI
+```
+
+---
+
+## 2. StreamRouter Architecture & Accrual Mechanics
+
+The `StreamRouter` contract allows any account to escrow tokens and stream them to a recipient second-by-second based strictly on Stellar ledger close times (`env.ledger().timestamp()`). No keepers, cron jobs, or off-chain oracles are required.
+
+### 2.1 Mathematical Accrual Formula
+
+At any timestamp $t$, the cumulative amount streamed to the recipient $\text{streamed}(t)$ is governed by:
+
+$$\text{effective\_time}(t) = \begin{cases} \text{cancelled\_at} & \text{if } \text{cancelled\_at} \ne 0 \\ t & \text{otherwise} \end{cases}$$
+
+$$\Delta t = \max(0, \text{effective\_time}(t) - \text{checkpoint\_time})$$
+
+$$\text{accrued}(t) = \text{checkpoint\_amount} + (\Delta t \times \text{rate\_per\_sec})$$
+
+$$\text{streamed}(t) = \min(\text{accrued}(t), \text{deposit})$$
+
+The withdrawable balance available to the recipient at timestamp $t$ is:
+
+$$\text{withdrawable}(t) = \text{streamed}(t) - \text{withdrawn}$$
+
+### 2.2 Accrual Timeline & Dry-Run Checkpointing
+
+When a stream's deposit is exhausted, it enters a "dry" state. If the sender subsequently calls `top_up(amount)`, the recipient must **not** be credited for the idle gap while the stream had zero remaining runway.
+
+To handle this cleanly in $O(1)$ without loop state updates:
+1. `checkpoint_amount` is set to `deposit`.
+2. `checkpoint_time` is updated to current ledger time `now`.
+3. `deposit` is incremented by `amount`.
+4. Linear accrual resumes strictly from `checkpoint_time`.
+
+```mermaid
+flowchart LR
+ A["T0: Opened
deposit = 1000
rate = 10/s"] -->|"Accruing (100s)"| B["T100: Stream Dry
streamed = 1000
runway exhausted"]
+ B -->|"Idle Gap (30s)
No accrual credited"| C["T130: top_up(500)
checkpoint_amount = 1000
checkpoint_time = 130
deposit = 1500"]
+ C -->|"Accrual Resumes (50s)"| D["T180: Stream Active
streamed = 1500
runway exhausted"]
+```
+
+### 2.3 Stream Lifecycle Sequence
+
+```mermaid
+sequenceDiagram
+ autonumber
+ actor Sender as Stream Sender
+ participant SR as StreamRouter Contract
+ participant Token as Stellar SAC Token
+ actor Recipient as Stream Recipient
+
+ Sender->>Token: Approve / Balance check
+ Sender->>SR: create_stream(recipient, token, rate, deposit)
+ SR->>Token: transfer(sender -> contract, deposit)
+ Note over SR: Stream initialized with
checkpoint_amount=0, checkpoint_time=now
+
+ loop Continuous Linear Accrual
+ Note over SR,Recipient: Time advances via ledger close time
+ end
+
+ Recipient->>SR: withdraw(stream_id)
+ Note over SR: amount = streamed_at(now) - withdrawn
+ SR->>Token: transfer(contract -> recipient, amount)
+ SR-->>Recipient: Withdrawn event emitted
+
+ alt Top Up Stream
+ Sender->>SR: top_up(stream_id, top_up_amount)
+ Note over SR: If dry: checkpoint_time = now
+ SR->>Token: transfer(sender -> contract, top_up_amount)
+ else Cancel Stream
+ Sender->>SR: cancel(stream_id)
+ Note over SR: owed = streamed_at(now) - withdrawn
refund = deposit - streamed_at(now)
+ SR->>Token: transfer(contract -> recipient, owed)
+ SR->>Token: transfer(contract -> sender, refund)
+ SR-->>Sender: Cancelled event emitted
+ end
+```
+
+---
+
+## 3. CascadeSplitter Architecture & Recursive Dependency Routing
+
+The `CascadeSplitter` contract implements automated recursive revenue sharing for open-source dependency trees. Maintainers publish split tables specifying upstream dependencies in basis points (`1 bps = 0.01%`, `TOTAL_BPS = 10_000`).
+
+### 3.1 Data Model
+- `Split`: `{ recipient: Address, bps: u32, label: String }`
+- `Payout`: `{ recipient: Address, amount: i128, depth: u32 }`
+- Constraints per table:
+ - `splits.len() <= MAX_SPLITS` (32)
+ - `bps > 0` (zero weight prohibited)
+ - `recipient != owner` (self-splits prohibited)
+ - Recipients are strictly deduplicated
+ - $\sum \text{bps} \le 10\_000$ (unallocated basis points form the owner's reserve)
+
+### 3.2 Recursive `route` Algorithm
+
+When funds arrive via `distribute(from, owner, token, amount)` or `preview(owner, amount)`, `CascadeSplitter` evaluates splits recursively:
+
+```mermaid
+flowchart TD
+ Start(["route(owner, amount, depth)"]) --> CheckDepth{"depth < MAX_DEPTH
(depth < 3)?"}
+
+ CheckDepth -- Yes --> ReadSplits["Read owner's split table from persistent storage"]
+ CheckDepth -- No --> LeafHalt["splits = empty vector
(Halt recursion at depth 3)"]
+
+ ReadSplits --> SplitLoop{"For each split in table"}
+ LeafHalt --> CreditOwner
+
+ SplitLoop --> CalcShare["share = amount * bps / 10,000"]
+ CalcShare --> ShareZero{"share == 0?"}
+ ShareZero -- Yes --> NextSplit["Skip (Dust share)"]
+ ShareZero -- No --> SubRoute["remaining = remaining - share
route(split.recipient, share, depth + 1)"]
+ SubRoute --> NextSplit
+ NextSplit --> SplitLoop
+
+ SplitLoop -- Finished all splits --> CreditOwner["remaining > 0?"]
+ CreditOwner -- Yes --> Credit["credit(owner, remaining, depth)
Credited to owner at current depth"]
+ CreditOwner -- No --> Done(["Return accumulated payouts"])
+ Credit --> Done
+```
+
+### 3.3 Core Invariants & Proofs
+
+#### Invariant 1: Conservation of Funds ($\sum \text{payouts} = \text{amount}$)
+At each recursion level:
+$$\text{remaining} = \text{amount} - \sum_{i} \left\lfloor \frac{\text{amount} \times \text{bps}_i}{10,000} \right\rfloor$$
+Because integer truncation rounds down, $\sum \text{shares} \le \text{amount}$ and $\text{remaining} \ge 0$.
+The `remaining` balance (unallocated basis points reserve + rounding truncation dust) is explicitly credited to `owner` at the current `depth`. Hence, no tokens can ever leak or be created:
+$$\sum_{p \in \text{payouts}} p.\text{amount} \equiv \text{amount}$$
+
+#### Invariant 2: Bounded Depth ($\le 3$) and Cycle Termination
+The recursion parameter `depth` increments by $1$ on each child hop (`depth + 1`).
+When `depth == MAX_DEPTH` ($3$), `read_splits` is bypassed and the full incoming sub-share is credited to `owner` with $0$ outgoing recursive calls.
+Consequently:
+1. Every payout in the resulting vector satisfies $p.\text{depth} \le 3$.
+2. Mutual cycles (e.g. $A \leftrightarrow B$) or circular rings ($A \to B \to C \to A$) terminate deterministically within at most $3$ hops without stack overflow or infinite loops.
+
+```mermaid
+flowchart TD
+ subgraph CyclicGraph ["Example: Mutual Dependency Cycle"]
+ NodeA["Maintainer A
splits 100% to B"]
+ NodeB["Dependency B
splits 100% to A"]
+ NodeA <--> NodeB
+ end
+
+ subgraph CascadeExecution ["Execution Trace (Amount = 1000)"]
+ Hop0["Depth 0: route(A, 1000)
routes 100% to B"]
+ Hop1["Depth 1: route(B, 1000)
routes 100% to A"]
+ Hop2["Depth 2: route(A, 1000)
routes 100% to B"]
+ Hop3["Depth 3: route(B, 1000)
depth == MAX_DEPTH -> Terminate!"]
+
+ Hop0 --> Hop1 --> Hop2 --> Hop3
+ Hop3 --> Result["Payout settles: B receives 1000 at depth 3"]
+ end
+```
+
+---
+
+## 4. Cryptographic Audit Receipts
+
+Every user transaction produces an offline-verifiable audit receipt conforming to **RFC-8785 (JSON Canonicalization Scheme)**.
+
+```mermaid
+sequenceDiagram
+ participant User as Client Browser
+ participant Horizon as Stellar Horizon / RPC
+ participant Contract as Soroban Contract
+ participant Receipt as Receipt Verifier
+
+ User->>Contract: Invoke contract function (e.g. withdraw_all)
+ Contract-->>Horizon: Transaction committed to ledger block
+ Horizon-->>User: Horizon Response (envelope_xdr, result_xdr, ledger)
+
+ Note over User: Form receipt payload
{schema, contract_id, function, tx_hash, ledger, envelope_xdr}
+ User->>Receipt: RFC-8785 Canonicalize & SHA-256 Digest
+ User->>User: Store receipt + digest in local storage / export
+
+ Note over Receipt: Offline Independent Verification:
+ Receipt->>Receipt: 1. Verify SHA-256 matches canonical JCS string
+ Receipt->>Receipt: 2. Parse envelope_xdr and verify Ed25519 signature
+ Receipt->>Horizon: 3. Verify transaction exists in ledger block
+```
+
+---
+
+## 5. Summary Matrix of Invariants
+
+| Component | Invariant | Enforcement Mechanism |
+|---|---|---|
+| **StreamRouter** | Linear accrual per second | Pure arithmetic against `ledger().timestamp()` |
+| **StreamRouter** | Dry-gap fairness | Checkpointing `checkpoint_amount` & `checkpoint_time` on top-up |
+| **StreamRouter** | Payer solvency | Escrow locked on contract creation |
+| **CascadeSplitter** | Strict fund conservation | Rounding dust and reserve credited to current owner |
+| **CascadeSplitter** | Cycle termination | Hard cutoff at `MAX_DEPTH = 3` |
+| **CascadeSplitter** | Zero weight prevention | Rejection on `bps == 0` or duplicate recipients |
+| **Receipts** | Tamper detection | RFC-8785 canonical serialization + cryptographic SHA-256 digest |