System Design
Four Anchor programs arranged as a strict DAG. External protocols only ever interact with the top layer. The layers below it form the state machine the top layer reads from.
Layering
Dependencies flow downward. writ_gate reads from reputation,delegation, and writ_registry. reputation reads from delegation and writ_registry. delegation reads from writ_registry. writ_registry has no upstream dependencies — it is the root of trust.
There are no upward CPIs. writ_registry does not call delegation. The crate graph is acyclic, enforced at build time.
Account model
Registry entries (L1)
One WritAccount per human. Created once via register, never closed. Keyed by the authority wallet; seeds ["writ", authority].
#[account]
#[derive(InitSpace)]
pub struct WritAccount {
pub authority: Pubkey, // 32 — the human's wallet
pub nullifier: [u8; 32], // 32 — Poseidon commitment
pub sbt_mint: Pubkey, // 32 — Token-2022 NonTransferable mint
pub created_slot: u64, // 8 — for age bonus
pub bump: u8, // 1
}Delegations (L2)
Up to five per WritAccount. Keyed by ["delegation", writ, index] where index is a u8 in [0, 5). Each delegation is independent; revoking one does not affect the others.
#[account]
#[derive(InitSpace)]
pub struct Scope {
pub writ: Pubkey, // 32 — parent WritAccount
pub agent: Pubkey, // 32 — the delegated wallet
pub programs: [Pubkey; 8], // 256 — whitelisted programs (zero = slot empty)
pub budget_lamports: u64, // 8 — SOL ceiling for this delegation
pub spent_lamports: u64, // 8 — cumulative drawn
pub expires_at: i64, // 8 — unix seconds
pub actions: u16, // 2 — bitmask of ActionKind
pub revoked: bool, // 1 — set on revoke_delegation
pub index: u8, // 1 — slot position in [0, 5)
pub bump: u8, // 1
}Reputation ledger (L3)
One ReputationAccount per writ. Score is recomputed on every submit_report or resolve_dispute; intermediate reports are stored in a ring buffer of the last 64 entries.
#[account]
#[derive(InitSpace)]
pub struct ReputationAccount {
pub writ: Pubkey, // 32
pub score: u16, // 2 — 0..=10_000
pub total_reports: u32, // 4
pub ring_head: u8, // 1 — index into recent[]
pub recent: [Report; 64], // 64 × sizeof(Report) = 4_096
pub bump: u8, // 1
}Gate state (L4)
Gate state is not a long-lived account. It is a transient PDA derived from the agent pubkey, read-only, computed on each verify CPI.
CPI boundaries
Each program exposes a narrow, typed CPI surface. The tables below list the external instructions (callable via CPI) for each program.
writ_gate
| Instruction | Mutates | Returns |
|---|---|---|
verify | none | AgentStatus |
verify_strict | none | err on any fail condition |
verify_and_record | delegation.Scope.spent_lamports | AgentStatus |
delegation
| Instruction | Mutates | Authority |
|---|---|---|
create_delegation | Scope | human |
revoke_delegation | Scope.revoked | human |
record_spend | Scope.spent_lamports | gate (CPI) |
reputation
| Instruction | Mutates | Authority |
|---|---|---|
submit_report | ReputationAccount | any program |
open_dispute | Dispute | staker |
resolve_dispute | Dispute | resolver quorum |
writ_registry
| Instruction | Mutates | Authority |
|---|---|---|
register | WritAccount + SBT mint | human (with ZK proof) |
rotate_authority | WritAccount.authority | human |
Data flow: one verify call
The steps below describe what happens inside a single writ_gate::verify CPI. The entire sequence runs in one slot, no clock skew, no network RTT.
- Caller passes
agentandgate_statetowrit_gate. writ_gatederives the writ PDA from agent via the delegation lookup table.- It loads the
WritAccountand verifies the SBT mint is still held. - It scans the five
ScopePDAs; picks the first unrevoked, unexpired one matchingctx.program_id. - It loads
ReputationAccount.score. - It packs
AgentStatusand returns.
Total reads: 3–8 accounts. Estimated compute: 18–42k CU depending on which delegation slot matches.
Compute budget
register, which runs once per human per lifetime. Prepend a ComputeBudgetProgram.setComputeUnitLimit(220_000) to be safe.Failure modes
| Code | Condition | Raised by |
|---|---|---|
WritNotFound | agent has no registry entry | writ_gate |
SbtMissing | SBT mint account closed or transferred | writ_gate |
NoActiveDelegation | all 5 slots revoked or expired | writ_gate |
ProgramNotAllowed | caller not in scope.programs | writ_gate |
BudgetExceeded | scope.spent + amount > scope.budget | writ_gate |
ActionNotPermitted | action bit unset in scope.actions | writ_gate |
NullifierCollision | register with a nullifier already on chain | writ_registry |
ProofInvalid | Groth16 verification failed | writ_registry |
Program references
- writ_registry — ZK verify, nullifier, SBT.
- delegation — scope accounts and revocation.
- reputation — scoring and dispute resolution.
- writ_gate — CPI interface and return types.