Cross-Layer Communication Flows
This section illustrates how the on-chain and off-chain layers interact during typical operations. Each flow shows the sequence of method calls and data exchange between Client, Clearnode, and Smart Contracts.
Jump to a specific flow:
- Authentication Flow - Establish session with session key delegation
- Channel Creation Flow - Open payment channel on blockchain
- Off-Chain Transfer Flow - Instant transfers without gas
- App Session Lifecycle - Multi-party application flow
- Cooperative Closure - Fast channel closure
- Challenge-Response Closure - Dispute resolution
Authentication Flow
Purpose
Establish authenticated session with session key delegation.
Actors
- Client: User application or SDK
- Clearnode: Off-chain service provider
Sequence Diagram
Steps
Step 1: Client Generates Session Keypair
The session key is generated entirely off-chain and the private key never leaves the client:
session_private_key = random()
session_public_key = derive(session_private_key)
session_address = address(session_public_key)
Step 2: Client → Clearnode: auth_request (public, no signature)
The client sends a public registration request (no signature required):
Request:
{
address: user_wallet_address
session_key: session_address
allowances: [{"asset": "usdc", "amount": "100.0"}]
scope: "transfer,app.create"
expires_at: 1762417328123 // Unix ms
}
Step 3: Clearnode Validates and Generates Challenge
The clearnode performs validation:
- Validate address/session_key format, optional allowances/scope, expires_at
- Generate challenge UUID
Step 4: Clearnode → Client: auth_challenge
The clearnode responds with a challenge:
Response:
{
challenge_message: "550e8400-e29b-41d4-a716-446655440000"
}
Signature: signed by Clearnode
Step 5: Client Signs Challenge (MAIN wallet, EIP-712)
The client signs the challenge using the main wallet over the Policy typed data (includes challenge, wallet, session_key, expires_at, scope, allowances):
challenge_signature = signTypedData(policyTypedData, main_wallet_private_key)
Step 6: Client → Clearnode: auth_verify
The client submits the signed challenge (or a previously issued JWT):
Request:
{
challenge: "550e8400-e29b-41d4-a716-446655440000",
// alternatively:
// jwt: "<existing_jwt>"
}
Signature: EIP-712 signature by main wallet (required if jwt is absent)
Step 7: Clearnode Validates Challenge
The clearnode validates:
- Signature recovers the wallet used in
auth_request - Challenge matches pending authentication
- Challenge not expired or reused
Step 8: Clearnode → Client: auth_verify Response
The clearnode confirms authentication:
Response:
{
address: user_wallet_address
session_key: session_address
jwt_token: "<jwt>"
success: true
}
Step 9: Session Established
- All subsequent requests signed with
session_private_key - The clearnode enforces allowances and expiration
- No main wallet interaction required until session expires
Key Points
- Session private key NEVER leaves the client
- Main wallet only signs once (
auth_request) - All subsequent operations use session key
- Allowances prevent unlimited spending
- Challenge-response prevents replay attacks
Related Methods: auth_request, auth_challenge, auth_verify
Channel Creation Flow
Purpose
Open a payment channel with zero initial balance; fund it later via resize_channel.
Actors
- Client: User application or SDK
- Clearnode: Off-chain service provider
- Smart Contract: Custody Contract
- Blockchain: Ethereum-compatible network
Sequence Diagram
Steps
Step 1: Client → Clearnode: create_channel
Client requests channel creation:
Request:
{
chain_id: 137 // Polygon
token: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" // USDC
}
Signature: session key signature
Step 2: Clearnode Processes Request
The clearnode:
- Validates token is supported on chain
- Generates unique nonce
- Selects adjudicator (SimpleConsensus for payment channels)
- Creates Channel struct
- Computes
channelId=keccak256(abi.encode(Channel)) - Creates initial State with
intent: INITIALIZE,version: 0,state_data: "0x", zero allocations - Packs state (
abi.encode(channelId, intent, version, data, allocations)in Solidity terms) - Signs packed state with clearnode's participant key
Step 3: Clearnode → Client: Response
Response:
{
channel: {
participants: [user_address, clearnode_address]
adjudicator: 0xSimpleConsensusAddress
challenge: 86400
nonce: 1699123456789
}
state: {
intent: INITIALIZE
version: 0
data: "0x"
allocations: [
{destination: user_address, token: usdc, amount: 0},
{destination: clearnode_address, token: usdc, amount: 0}
]
}
server_signature: "0xClearnodeSig..."
channel_id: "0xChannelId..."
}
The clearnode provides its signature BEFORE the user commits funds on-chain. This ensures both parties have committed before any on-chain transaction occurs.
Steps 4-5: Client Validates and Signs
Client:
- Recomputes
channelIdand verifies it matches - Recomputes packed state and verifies clearnode signature
- Signs packed state with user's participant key
Step 6: Client → Blockchain: Custody.create()
Client submits transaction:
Custody.create(channel, state, userSig, serverSig)
Step 7: Blockchain Validates and Creates Channel
Contract:
- Verifies user's signature is valid
- Verifies clearnode's signature is valid
- Stores channel parameters and funding state (zero balances)
- Sets channel status to
OPEN - Emits
Openedevent
Step 8: Event Listener Detects Creation
The clearnode's event listener:
- Detects
Openedevent - Validates channel parameters
Steps 9-10: Notifications
The clearnode:
- Updates internal database: channel status = open (zero balance)
- Sends
channel_updatenotification to client
Step 11: Channel Active
- Channel active with zero balance
- Use
resize_channelto fund the channel
Key Points
- Off-chain preparation: Clearnode prepares and signs channel configuration
- On-chain execution: User submits transaction to lock funds
- This ensures clearnode is ready to join before user risks funds
Related Methods: create_channel
Off-Chain Transfer Flow
Purpose
Transfer funds between users instantly without blockchain transaction.
Actors
- Sender (Client A): Initiating user
- Clearnode: Off-chain service provider
- Receiver (Client B): Receiving user
Sequence Diagram
Steps
Step 1: Client A → Clearnode: transfer
Sender initiates transfer:
Request:
{
destination: "0xClientB_Address", // or destination_user_tag: "UX123D"
allocations: [{"asset": "usdc", "amount": "50.0"}]
}
Signature: Client A's session key
Step 2: Clearnode Validates
The clearnode validates:
- Client A is authenticated
- Client A has >= 50 USDC available balance
- Destination address/tag is valid (account is created if new)
- Asset "usdc" is supported
Step 3: Clearnode Creates Ledger Entries
Double-entry bookkeeping:
Entry 1 (Debit from Client A unified account):
{
account_id: Client A address
asset: "usdc"
credit: "0.0"
debit: "50.0"
}
Entry 2 (Credit to Client B unified account):
{
account_id: Client B address
asset: "usdc"
credit: "50.0"
debit: "0.0"
}
Step 4: Clearnode Creates Transaction Record
{
id: 1,
tx_type: "transfer",
from_account: Client A address,
from_account_tag: "NQKO7C",
to_account: Client B address,
to_account_tag: "UX123D",
asset: "usdc",
amount: "50.0",
created_at: "2023-05-01T12:00:00Z"
}
Step 5: Clearnode → Client A: Response
Response:
{
transactions: [
{
id: 1,
tx_type: "transfer",
from_account: "0xA...",
from_account_tag: "NQKO7C",
to_account: "0xB...",
to_account_tag: "UX123D",
asset: "usdc",
amount: "50.0",
created_at: "2023-05-01T12:00:00Z"
}
]
}
Step 6-7: Clearnode → Clients: Notifications
tr(transfer) notification to sender/receiver withtransactionsarraybu(balance update) notification reflecting new balances
Step 9: Transfer Complete
- Instant (< 1 second)
- No blockchain transaction
- Zero gas fees
- Both parties notified
Key Points
- Purely off-chain: Database transaction, no blockchain
- Instant settlement: < 1 second typical
- Zero gas fees: No on-chain transaction required
- Double-entry bookkeeping: Accounting accuracy guaranteed
- Receiver account auto-created: Destination tag/address need not have a prior balance
Related Methods: transfer
App Session Lifecycle Flow
Purpose
Create, update, and close a collaborative app session with multiple participants.
Actors
- Client A: Participant 1
- Client B: Participant 2
- Clearnode: Off-chain service provider
Scenario
Two-player chess game with 100 USDC stake each.
Sequence Diagram
Sequence (Create → Update → Close)
-
Create (off-chain):
create_app_session- Client signs request (all participants with non-zero allocations must sign).
- Clearnode validates protocol version (0.2/0.4), quorum, balances, allowances/session keys.
- Funds are locked from each signer’s unified balance into the app session account.
- Response (minimal):
app_session_id,status: "open",version: 1. Full metadata is not echoed; useget_app_sessionsto read it.
Example Request:
{
"req": [1,"create_app_session",{
"definition": {
"protocol": "NitroRPC/0.4",
"participants": ["0xA","0xB"],
"weights": [100,100],
"quorum": 200,
"challenge": 86400,
"nonce": 1699123
},
"allocations": [
{"participant": "0xA","asset": "usdc","amount": "100.0"},
{"participant": "0xB","asset": "usdc","amount": "100.0"}
],
"session_data": "{\"game\":\"chess\"}"
},1699123456789],
"sig": ["0xUserSig","0xCoSig"]
} -
State Updates (off-chain):
submit_app_state- v0.4 requires
version = current+1; v0.2 rejectsintent/versionand only allows a single update. - Intents:
operate: redistribute, sum must stay equal.deposit: sum must increase; depositor must sign and have available unified balance.withdraw: sum must decrease; session must have funds.
- Quorum required; session-key allowances enforced.
- Response (minimal):
app_session_id,status: "open",version(new). No metadata echoed. - Notifications:
asu(app session update) +bu(balance update for deposit/withdraw).
Example Request (deposit v0.4):
{
"req": [2,"submit_app_state",{
"app_session_id": "0xSession",
"intent": "deposit",
"version": 2,
"allocations": [
{"participant": "0xA","asset": "usdc","amount": "150.0"},
{"participant": "0xB","asset": "usdc","amount": "100.0"}
]
},1699123456790],
"sig": ["0xUserSig","0xCoSig"]
} - v0.4 requires
-
Close (off-chain):
close_app_session- Requires quorum signatures; final allocations must match total balances.
- Response (minimal):
app_session_id,status: "closed",version(incremented). No metadata echoed. - Funds are released to participants’ unified balances; notifications
asuandbuare sent.
Example Request:
{
"req": [3,"close_app_session",{
"app_session_id": "0xSession",
"allocations": [
{"participant": "0xA","asset": "usdc","amount": "180.0"},
{"participant": "0xB","asset": "usdc","amount": "20.0"}
]
},1699123456795],
"sig": ["0xUserSig","0xCoSig"]
}
Key Points
App sessions enable multi-party applications with custom governance rules. Funds are locked from unified balance for the duration of the session.
Related Methods: create_app_session, submit_app_state, close_app_session
Cooperative Closure Flow
Purpose
Close channel when all parties agree on final state.
Actors
- Client: User application
- Clearnode: Off-chain service provider
- Smart Contract: Custody Contract
- Blockchain: Ethereum-compatible network
Key Points
Cooperative closure is fast (1 transaction), cheap (low gas), and immediate (no waiting period). Always use this when possible.
Sequence Diagram
Sequence
-
Client → Clearnode:
close_channel(channel_id, funds_destination)- Authenticated request signed by the user (session key or wallet).
Example Request:
{
"req": [10,"close_channel",{
"channel_id": "0xChannel",
"funds_destination": "0xUser"
},1699123457000],
"sig": ["0xUserSig"]
} -
Clearnode: validates channel exists and is
open/resizing, checks challenged-channel guard, builds FINALIZE state:intent: FINALIZE,version = current+1,state_data: "0x", allocations split between user and broker based on channel balance.- Signs packed state (
keccak256(abi.encode(channelId, intent, version, data, allocations))).
-
Clearnode → Client: response with
channel_id,state,server_signature. -
Client: verifies server signature, signs the same packed state.
-
Client → Blockchain:
Custody.close(channel_id, state, userSig, serverSig)(one tx). -
Blockchain: verifies both signatures, closes channel, emits
Closed/Opened-equivalent event (implementation-specific), releases funds. -
Clearnode: observes event, updates DB, sends
cu(channel update) andbu(balance update) notifications.
Related Methods: close_channel
Challenge-Response Closure Flow
Purpose
Close channel when other party is unresponsive or disputes final state.
Actors
- Client: User application
- Clearnode: Off-chain service provider (may be unresponsive)
- Smart Contract: Custody Contract
- Blockchain: Ethereum-compatible network
Key Points
This method requires waiting for the challenge period (typically 24 hours) to elapse. Use only when cooperative closure fails.
Sequence Diagram
Sequence (User-initiated, clearnode unresponsive)
- Prerequisite: User holds the latest mutually signed state (or clearnode-signed latest) for the channel.
- Client → Blockchain:
Custody.challenge(channelId, state, sigs...)- Submits the latest signed state to start the challenge.
- Challenge Window: Other party can respond with a newer valid state before timeout.
- If no newer state is posted: After the challenge period, user calls
Custody.close(channelId, state, sigs...)to finalize. - Blockchain: finalizes channel, releases funds per challenged state, emits closure event.
- Clearnode (when responsive again): observes event, updates DB, sends
cu/bunotifications to participants.
Related Methods: On-chain Custody.challenge() and Custody.close()
Next Steps
Now that you understand how all protocol layers work together:
- Review Method Details: Visit Part 2 (Off-Chain RPC Protocol) for complete method specifications
- Explore Reference: See Protocol Reference for constants and standards
- Implementation Guide: Check Implementation Checklist for best practices
- Quick Start: Follow the Quick Start Guide to begin building
These flows represent the most common operations. For edge cases and error handling, consult the specific method documentation in Part 2.