Skip to content

Instantly share code, notes, and snippets.

@denniswon
Last active May 15, 2026 01:32
Show Gist options
  • Select an option

  • Save denniswon/2625d7112c3e9eea95c46c5a2be95a10 to your computer and use it in GitHub Desktop.

Select an option

Save denniswon/2625d7112c3e9eea95c46c5a2be95a10 to your computer and use it in GitHub Desktop.
Protocol state architecture migation
★ Insight ─────────────────────────────────────
- EigenDA was the trustless intermediary for upload broadcast — gateway posts blob, operators pull, operators verify the KZG cert
against an L1 anchor before applying. Direct gateway broadcast trades that property for latency.
- The 120s commit cadence (Q3) and the daCertHash field on StateCommit (IStateRootCommittable.sol, locked in §6, error selectors
0x822ef683/0xdc4e1d57) were both calibrated to EigenDA economics (~$/blob, 14-day retention cliff). Removing EigenDA from the hot path
unlocks both knobs.
- The compliance constraint in §S.2 ("gateway MUST NOT hold authoritative private data") was load-bearing for the original topology.
Dennis's meeting line — "if bootnode is also we are operating it, then it's not really a difference" — collapses gateway and bootnode
into one Newton-trust footprint, which means the §S.2 invariant needs to be re-derived from a new principle, not just relaxed.
─────────────────────────────────────────────────
Meeting outcome — my reading
Three locked shifts (please confirm before I go deeper):
1. EigenDA removed from upload hot path. Gateway broadcasts uploads directly to operators AND a single-instance bootnode. Operators
write to local redb on receipt; no EigenDA pull step.
2. Bootnode = single-instance Newton-operated HTTP server with redb (not Postgres, not load-balanced). Two roles: (a) replica receiver
for every gateway broadcast, (b) snapshot+delta index for new-operator catch-up.
3. Compliance constraint relaxed. Gateway holding encrypted-PII is now equivalent to bootnode holding it — both are Newton-operated.
Tentative (unresolved in the call): commit cadence may drop below 120s; EigenDA's role outside the hot path is unsettled — Alec is
holding his EigenDA PR pending your architectural decision.
Cascade in docs/PRIVATE_DATA_STORAGE.md
§3 Topology · §6 Commit Protocol (daCertHash, cadence, §6.6 CommitProposalV1) · §8 entire EigenDA section · §9 Data Flow upload path ·
§S.2 gateway data-plane invariant · §S.4 EigenDA cert rot · §S.18 retrieval ladder · §S.19 bootnode SPOF (amplified) · §F.6
drop-bootnode-for-P2P (now reframed) · Q2/Q12/Q13/Q14/Q18/Q21/Q29 in design-decisions.
First lock to pin down
The pivotal axis is EigenDA's role going forward — every other decision (cadence, daCertHash field, tiered recovery, catch-up SLA,
snapshot storage) cascades from it.
⏺ User answered Claude's questions:
⎿  · In the new architecture, what should EigenDA's role be going forward? → Defer to Phase 1.5+
⏺ ★ Insight ─────────────────────────────────────
- EigenDA's deferral cleanly removes a 14-day retention cliff that was forcing the §6/§S.4/§S.18 tiered-recovery scaffolding. But it
transfers the "trustless data availability" property to the bootnode's redb — which is operationally cheap but cryptographically opaque.
- The Q12 tiered recovery (0-7d / 7-14d / 14d+) collapses to a single tier. §F.6 ("drop bootnode, operator P2P") gets repositioned: P2P
becomes the trustless-recovery story, not a Phase 2 nice-to-have.
- A deferred-but-not-deleted EigenDA needs a clean re-entry seam in the spec — a Phase 1.5 marker noting which fields/flows would
re-activate. Otherwise re-introduction in 6 months means re-deriving the design from scratch.
─────────────────────────────────────────────────
Confirmed: EigenDA out of Phase 1
That locks the upload-path simplification and means I can now resolve the cascading questions in dependency order: contract surface
first (hardest to unwind), then config knobs, then operations.
Next dependency: StateCommit field semantics
The StateCommit struct in IStateRootCommittable.sol carries daCertHash: bytes32 — a field whose original purpose was binding the
BLS-signed commit to a specific EigenDA blob (the trustless integrity anchor). With EigenDA gone, this field is either obsolete,
repurposable, or a vestigial slot.
This matters because the field is in the BLS message preimage (keccak256(abi.encode(commit)) per CertificateMessageHashMismatch selector
0x822ef683). Whatever we do here affects every operator's signing path AND the on-chain verifier.
The principal cryptographic question: how do we prevent a Byzantine gateway from fork-broadcasting different delta bytes to different
operators? EigenDA's KZG cert was the public binding. In the new flow, we need a substitute.
My recommended option is (1) Repurpose daCertHash → deltaContentHash. Keep the field, change the semantic to
keccak256(canonical_delta_bytes). Operators compute on receipt and include in their BLS partial; mismatch on aggregation surfaces a
Byzantine gateway. This preserves the Q14 leader-ECDSA invariant and keeps the existing 7-selector taxonomy in error-selectors.md
intact.
⎿  Tool use rejected
⏺ The user wants to clarify the question before answering. Let me ask what needs explanation.
What would you like to clarify about the StateCommit.daCertHash question? Some dimensions you might want to dig into:
- What "delta canonical bytes" means — the exact serialization scope (just the writes? writes + ordering invariant? full Delta blob
frame including DELTA_BLOB_FORMAT_V1 header from Q29?)
- Why this field needs to change at all — i.e., what specifically breaks if we leave daCertHash semantics alone but stop populating
EigenDA
- What a Byzantine-gateway-fork attack looks like concretely — the scenario this field defends against, and whether existing primitives
(Q14 leader ECDSA + Q17 CommitProposalV1.proposer_id) already cover it
- Migration cost of bumping V1 → V2 — given that UnsupportedStateCommitVersion (0xb681668e) is already the typed selector, what
coordination is needed between gateway/operator/challenger versions
- The struct-V2 contract migration mechanics — given PR #589's StateCommitRegistry is still in review, whether Option 2/3 is "free"
right now versus a real upgrade later
- Whether this field is needed at all — broader architectural question: do we need any delta-content binding on-chain, or is off-chain
CommitProposalV1 + bootnode-served bytes sufficient
Or something else entirely about the framing of the question. Let me know what direction you want to take it.
✻ Baked for 5m 43s
▐▛███▜▌ Claude Code v2.1.132
▝▜█████▛▘ Opus 4.7 (1M context) with max effort · Claude API
▘▘ ▝▝ ~/projects/newton-prover-avs
✻ Conversation compacted (ctrl+o for history)
⎿  Read docs/CLAUDE.md (7 lines)
⎿  Read ../../.claude/RTK.md (30 lines)
⎿  Read ../../CLAUDE.md (39 lines)
⎿  Referenced file .claude/CLAUDE.md
⎿  Referenced file .claude/rules/architecture.md
⎿  Skills restored (superpowers:brainstorming)
⏺ ★ Insight ─────────────────────────────────────
EigenDA's removal collapses Phase 1 into a single trust path: gateway → operators → on-chain root. This is structurally simpler than the
prior tiered (S3 → EigenDA → blob recovery) ladder, but concentrates SPOF risk on the bootnode for catch-up. JMT determinism already
guarantees state correctness from (prevRoot, newRoot) alone, so the on-chain deltaContentHash is a fast-verification anchor for catch-up
consumers — not a state-integrity primitive. That separation cleans up the §S.18 retrieval ladder considerably.
─────────────────────────────────────────────────
Two decisions are locked (EigenDA → Phase 1.5+, on-chain deltaContentHash). Here's the hardened design for the remaining cascading
questions — push back on any section.
1. Commit cadence — keep 120s default, per-chain config seam
EigenDA economics no longer gate it, but BLS aggregation budget, gas, and operator processing windows are all calibrated to 120s. Add
gateway.state_commit.interval_seconds per-chain config so we can tune under real mainnet load without a contract change. Cadence is
decoupled from StateCommit semantics — timestamp is monotonic regardless.
2. Bootnode posture — single instance Phase 1, hot standby Phase 1.5
Phase 1 ships one Newton-operated bootnode with redb-backed delta archive (acknowledged SPOF, documented in §S.19). Phase 1.5 adds
hot-standby behind a load balancer with replicated redb. Avoids over-engineering before real load patterns; matches staged-rollout
philosophy.
3. §S.2 compliance principle — recast as "gateway authority + operator data-plane"
Original §S.2 said gateway never holds raw plaintext. EigenDA removal doesn't change this — gateway still terminates auth, dispatches
HPKE-wrapped blobs to operators, never persists. New framing: gateway is the authority/admission boundary; operators are the sole
data-plane. Closes one trust hop, doesn't add any.
4. Canonical delta bytes — RLP-encoded Vec<(LeafKey, LeafHeaderV1, ciphertext)> sorted by leaf_key
deltaContentHash = keccak256(rlp_encode(sorted_delta)). Sort BEFORE encoding so any two operators with the same logical delta compute
identical hashes. RLP avoids the JSON-ordering hazards (per lessons.md "Cross-service trust"). Reserve a version byte for migration.
5. Phase 2 P2P transition — §F.6 reframed
Original §F.6 was "drop bootnode for P2P". Reframe: operator-to-operator gossip for delta replication only (catch-up, not consensus).
Triggers: bootnode bandwidth saturation, geographic latency, trust diversification. StateCommitRegistry root remains the on-chain anchor
— P2P is data-plane only.
6. Cascade updates required
- docs/PRIVATE_DATA_STORAGE.md: §3 topology, §6 commit, §8 delete, §9 flow, §S.2 reframe, §S.4 delete, §S.18 rewrite, §S.19, §F.6
reframe
- docs/superpowers/specs/2026-04-20-private-data-migration-design-decisions.md: Q2, Q12, Q13, Q14, Q18, Q21, Q29 amendments
- docs/STATE_TREE_ARCHITECTURE.md: no change (interface preserved)
- .claude/rules/architecture.md: StateCommitRegistry row note (daCertHash → deltaContentHash)
Approve sections 1–5 to lock? On approval I'll write the spec to docs/superpowers/specs/2026-05-07-pds-eigenda-removal-design.md, run
spec self-review, then transition to writing-plans for the cascade.
✻ Cogitated for 4m 34s
❯ Approve except 1: commit cadence 120s is too long. Why is it set to 120 seconds? where does the requirement / bottleneck come from? 2:
operator p2p is for censorship resistance as well, deprecating gateway (replaced by p2p) and bootnode just become an archive node (it
already kind of is)
⏺ ★ Insight ─────────────────────────────────────
The 120s cadence is a composite number — three contributors stacked: operator response budget (~20s realistic), tx confirmation + reorg
margin (10-20s, chain-specific), and EigenDA blob cert confirmation (30-60s, NOW REMOVED). With EigenDA gone, the floor drops
dramatically. The remaining bottleneck is per-chain (gas, block time, finality), so cadence becomes a chain-specific knob, not a global
default.
─────────────────────────────────────────────────
1 (revised). Commit cadence — per-chain config, aggressive defaults
Where 120s came from:
- ~20s operator response budget (RPC fan-out → BLS partial sign → aggregator collect)
- ~30-60s EigenDA blob cert confirmation (REMOVED)
- ~10-20s tx confirmation + reorg margin (chain-specific)
- Historical reuse from prior policy-task consensus tick — no real consensus driver
Real floor now: operator budget (~20s) + tx confirmation. ~30s on fast L2s, ~45s on L1.
Per-chain interval_seconds config with proposed defaults:
- L1 mainnet: 60s — gas dominates; reorg margin meaningful
- L2 (Base, Arb): 30s — cheap gas, fast finality, write→read latency wins
- Local/dev: 5s — throughput testing
Halves staleness on L1, quarters it on L2. Operator budget stays comfortable. Going below ~25s risks stragglers on slow regional links
and butts up against tx confirmation.
★ Insight ─────────────────────────────────────
The P2P reframe is bigger than catch-up. Phase 1's hub-and-spoke (gateway = single ingress) becomes Phase 2's mesh (federated gateways +
operator gossip), and Phase 3's gateway deprecation. Bootnode contracts to archive — already its actual role; the redb delta history is
exactly an archive. Censorship resistance comes from redundancy at every layer: multiple gateways, operator-direct submission, multiple
archives.
─────────────────────────────────────────────────
5 (revised). P2P reframe — censorship resistance + gateway deprecation endgame
Phase 1: gateway → operators direct, bootnode broadcasts catch-up.
Phase 2 (P2P transition):
- (a) Operator-to-operator libp2p gossip for delta replication — catch-up without bootnode dependency
- (b) Federated gateways: anyone can run one; censorship resistance via gateway diversity
- (c) Optional direct user → operator P2P submission for advanced clients
- (d) Bootnode demoted to pure archive: passive gossip subscriber, redb persistence for cold queries. This just promotes its actual
existing role.
Phase 3 endgame: gateway deprecated. Operators are first-class P2P endpoints. Gateway optional as a paid fast-lane for high-throughput
users. Permissionless gossip with peer scoring, DoS protection, per-message sig verification at the operator boundary.
StateCommitRegistry on-chain root remains the anchor across all phases — P2P is data-plane only, never consensus.
§F.6 in PRIVATE_DATA_STORAGE.md becomes a 3-phase trajectory: catch-up → censorship-resistance → gateway-deprecation. Each compounds on
the prior. §S.19 bootnode-SPOF section evolves alongside: SPOF risk diminishes as Phase 2 lands, vanishes by Phase 3.
Sections 2, 3, 4, 6 locked. Cadence (1) and P2P (5) revised above — approve to lock the design and proceed to spec write?
✻ Crunched for 1m 53s
❯ 1. - ~20s operator response budget (RPC fan-out → BLS partial sign → aggregator collect) --> where did you get this number 20s from?
our goal is to be 1 second, and definitely it HAS to be (requirement) to be under 4 seconds max.
⏺ ★ Insight ─────────────────────────────────────
20s was wrong — I anchored on policy-task heuristics where stragglers and complex Rego evaluation extend the window. State commit is
purely root-read + BLS-sign, dominated by network RTT not computation. Real steady-state response is sub-second; 4s is generous
tail-budget; 1s target requires regional clustering or quorum-deadline tuning. Owning the bad number — that was a heuristic transplant
from the wrong domain.
─────────────────────────────────────────────────
You're right — 20s was wrong. Recomputing with accurate per-step costs:
Operator response breakdown (target 1s, max 4s)
┌──────────────────────────────────────────────────────────────────┬──────────────────┬───────────────────────┐
│ Step │ Steady state │ Worst case │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Aggregator → operator request (JSON-RPC) │ 50ms same-region │ 200ms cross-continent │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Operator: read JMT cached root from redb │ <5ms │ <50ms cold cache │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Operator: build StateCommit, BLS partial sign (BN254) │ 30-50ms │ 100ms │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Operator → aggregator return │ 50ms │ 200ms │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Aggregator: collect 2/3 quorum (waits for slowest qualifying op) │ 200ms │ 1.5s regional tail │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ BLS aggregation + verification │ 50-100ms │ 200ms │
├──────────────────────────────────────────────────────────────────┼──────────────────┼───────────────────────┤
│ Total │ ~400ms │ ~2.3s │
└──────────────────────────────────────────────────────────────────┴──────────────────┴───────────────────────┘
The 4s envelope holds with healthy margin. 1s target needs regionally-clustered operators or a quorum-collection deadline shorter than
the slowest-op RTT (excludes the tail from this cycle, includes it next).
Hard design requirements this exposes:
- Operators MUST maintain JMT root incrementally — no on-demand re-walk. Builder updates root on every leaf insert/update with sub-ms
cost. Already the STATE_TREE_ARCHITECTURE.md design assumption; making it an explicit invariant for state-commit RPC.
- BLS signing is host-only — no VSOCK round-trip into the enclave (already the case).
- Aggregator quorum-collection deadline ≤ 2s; stragglers dropped from current cycle.
★ Insight ─────────────────────────────────────
With response budget under 4s, the cadence floor shifts entirely to tx confirmation + reorg policy. StateCommitRegistry already has
SequenceGap + StateRootMismatch errors that cleanly reject stale views — so an aggressive-cadence + idempotent-retry strategy lets
reorged commits recover for free. The error semantics that were designed for "stale operator" failures double as the reorg-tolerance
primitive. That's an unexpected payoff from the existing design.
─────────────────────────────────────────────────
Revised cadence math
┌───────────────┬────────────┬──────────────┬─────────────────────────────────────────────────────────────────┐
│ Chain │ Aggressive │ Conservative │ Reorg posture │
├───────────────┼────────────┼──────────────┼─────────────────────────────────────────────────────────────────┤
│ L1 mainnet │ 12s │ 30-40s │ aggressive: accept reorg + retry; conservative: 3 confirmations │
├───────────────┼────────────┼──────────────┼─────────────────────────────────────────────────────────────────┤
│ L2 (Base/Arb) │ 5-10s │ 15s │ aggressive safe — low reorg + fast finality │
├───────────────┼────────────┼──────────────┼─────────────────────────────────────────────────────────────────┤
│ Local/dev │ 1-2s │ 5s │ n/a │
└───────────────┴────────────┴──────────────┴─────────────────────────────────────────────────────────────────┘
Recommendation: per-chain config with L1: 30s default (12s opt-in flag), L2: 10s default, Local: 2s. Drops L1 staleness 4× from 120s;
drops L2 staleness 12×. Operator response budget stays well under 4s; 1s aspirational target documented as "achievable with regional
clustering or sub-RTT quorum deadline."
Reorg-recovery as first-class flow — want me to fold a §6.x subsection into the spec covering: aggressive-cadence operator behavior on
SequenceGap/StateRootMismatch revert (re-read latest committed root, rebuild against fresh prev, re-aggregate)? That makes "aggressive
on L2" the safe default rather than a calculated risk.
Sections 2, 3, 4, 6 locked. P2P (5) locked from prior turn. Cadence (1) revised above — approve, or push the L2 default lower?
✻ Churned for 1m 22s
❯ approve
⏺ Writing the spec to docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md, then I'll run spec self-review.
⏺ Write(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Wrote 329 lines to docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md
1 ---
2 title: Private Data Storage — Architecture Pivot Design (EigenDA removal, cadence, P2P trajectory)
3 date: 2026-05-07
4 status: approved
5 applies_to: Phase 1 MVP and forward
6 supersedes_amendments:
7 - docs/superpowers/specs/2026-04-20-private-data-migration-design-decisions.md (Q2, Q12, Q13, Q14, Q18, Q21, Q29)
8 meeting_source: Newton PDE stand-up 2026-05-07 (Fireflies 01KQDY7XR840ANYYYCDT5GVVS9)
9 ---
… +392 lines (ctrl+o to expand)
⏺ Running spec self-review now per brainstorming workflow (placeholder scan, internal consistency, scope check, ambiguity check).
Findings from self-review:
1. Placeholder scan: No TBDs, TODOs, or vague requirements. "Open questions for implementation plan" section is intentional — frames
plan-stage decisions, not unresolved design.
2. Internal consistency: D4 bootnode 3-phase aligns with D7 P2P 3-phase. D8 reorg-recovery references the existing
SequenceGap/StateRootMismatch selectors correctly. Hard invariants don't contradict any decision.
3. Scope check: Single implementation plan size — sizable but cohesive (9 doc sections, 1 field rename, 1 config schema change, 1 helper
crate, 1 RPC handler enhancement).
4. Ambiguity check: Two minor fixes needed.
★ Insight ─────────────────────────────────────
Self-review caught two phrasings that read fine to the writer but mislead a fresh reader: (1) "active broadcast endpoint" for the
bootnode could be parsed as bootnode relaying to operators (it doesn't — gateway broadcasts to it); (2) hardcoded "2/3 quorum" in the
latency budget could be read as a locked threshold (it's illustrative; the real value is quorum_threshold_bps config). Both are
clarification edits, not design changes.
─────────────────────────────────────────────────
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
140
141 **Decision.** Bootnode role evolves alongside the P2P trajectory.
142
143 -**Phase 1 (this MVP).** Single Newton-operated bootnode. redb-backed delta archive. Active broadcast endpoint: gateway p
-ushes encrypted deltas to bootnode AND directly to operators. Bootnode serves catch-up to new operators on demand.
143 +**Phase 1 (this MVP).** Single Newton-operated bootnode. redb-backed delta archive. Receives gateway broadcasts and pers
+ists encrypted deltas to redb (passive in steady state). Serves catch-up to new operators on demand (active only when qu
+eried). Gateway dispatches encrypted deltas in parallel to operators (data plane) and to bootnode (archive).
144
145 **Phase 1.5.** Hot-standby bootnode behind a load balancer with replicated redb. SPOF mitigation. Triggered by either (a
) operational maturity milestone, (b) observed bootnode bandwidth pressure, or (c) regulatory requirement for redundancy
.
146
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
124 | Operator: read JMT cached root from redb | <5ms | <50ms cold cache |
125 | Operator: build `StateCommit`, BLS partial sign (BN254) | 30-50ms | 100ms |
126 | Operator → aggregator return | 50ms | 200ms |
127 -| Aggregator: collect 2/3 quorum (slowest qualifying op) | 200ms | 1.5s regional tail |
127 +| Aggregator: collect quorum per `quorum_threshold_bps` (slowest qualifying op) | 200ms | 1.5s regional tail |
128 | BLS aggregation + verification | 50-100ms | 200ms |
129 | **Total** | **~400ms** | **~2.3s** |
130
⏺ ★ Insight ─────────────────────────────────────
Self-review pass complete. Both ambiguity fixes were micro-edits (no design changes). The spec is the authoritative reference for the
implementation plan — it locks 8 decisions, lists 8 system-wide hard invariants the plan must respect, and explicitly fences off Phase
2/3 work to avoid scope creep. Note docs/superpowers/** is gitignored on this repo per project memory, so the spec lives locally —
committing it would be a no-op.
─────────────────────────────────────────────────
Spec written to docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md
Self-review pass: clean.
- Placeholder scan: no TBDs/TODOs (open questions section is plan-stage only, not design-stage).
- Internal consistency: D4 bootnode 3-phase ↔ D7 P2P 3-phase fully aligned; D8 reorg-recovery references correct selectors.
- Scope check: cohesive single-plan size.
- Ambiguity check: 2 wording fixes applied (bootnode role wording, quorum threshold reference).
Per the brainstorming workflow, this is the user review gate — please read the spec end-to-end and flag anything you want changed.
Particular things worth a fresh-eyes pass:
1. D3 cadence defaults — L1: 30s default / 12s aggressive opt-in, L2: 10s default. Are those numbers what you want shipped, or do you
want L1 lower?
2. D4 Phase 1.5 trigger — left intentionally soft (operational maturity OR bandwidth pressure OR regulatory). Want a concrete trigger
written into the spec?
3. D6 RLP vs alternatives — committed to RLP. If you'd rather use bincode or SSZ, now's the time.
4. D7 Phase 3 endgame — gateway-as-paid-fast-lane is one possible endpoint. If you want gateway gone entirely (not even as a paid tier),
the spec should say so.
5. D8 reorg recovery — adds operator behavior on SequenceGap/StateRootMismatch. Plan-level work; want it kept in this spec or split out?
6. Out-of-scope list — anything you want pulled into scope?
After your approval, I'll invoke the writing-plans skill to generate the implementation plan covering: field rename (daCertHash →
deltaContentHash), per-chain cadence config schema, RLP delta encoding helper crate, reorg-recovery RPC handler logic, and the 9-section
cascade through PRIVATE_DATA_STORAGE.md.
✻ Worked for 5m 13s
❯ 1. approve 2. soft 3. help me compare rlp vs alternatives 4. sounds good for now, with "ultimate" goal to be deprecated/dissolve
(potentially) 5. keep in this spec and plan 6. no. But before we move onto implementation plan, let's do a thorough, detailed,
/grill-me session that goes through every single component of the protocol under this direction
⏺ ★ Insight ─────────────────────────────────────
Encoding comparison reveals a non-obvious split: "deterministic by design" candidates are Borsh (literally designed for hashing), RLP,
and SSZ; bincode is the fastest in pure Rust but has determinism caveats fatal for chain anchoring. The choice between RLP and Borsh
hinges on whether a future zkVM verifier needs to consume delta bytes in-circuit — Borsh is leaner there. For Phase 1 with no zkVM
consumer of deltaContentHash, RLP's Ethereum-stack idiom wins.
─────────────────────────────────────────────────
Item 3: Encoding comparison
┌─────────────┬───────────────────┬──────────────────┬──────────┬─────────────────────┬─────────────┬────────────────────────────┐
│ Format │ Deterministic │ Ethereum fit │ Rust │ Cross-lang │ Versioning │ Verdict │
│ │ │ │ perf │ │ │ │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ RLP │ Structural │ Native │ Standard │ Yes │ Manual │ Current pick │
│ │ │ (alloy::rlp) │ │ │ │ │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ Borsh │ Structural │ None │ Fast │ Decent │ Manual │ Strong alt │
│ │ │ │ │ (Rust/JS/Go/Py) │ │ │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ bincode │ Configurable, │ None │ Fastest │ Rust-only really │ Built-in │ Determinism caveats — │
│ │ v1≠v2 │ │ │ │ │ fatal here │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ SSZ │ Structural │ Eth2 CL │ Fast │ Yes │ Type-pinned │ Heavy; merkleization │
│ │ │ │ │ │ │ unused │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ CBOR │ Canonical mode │ None │ Standard │ Excellent │ Manual │ Multi-encoding hazard │
│ │ only │ │ │ │ │ │
├─────────────┼───────────────────┼──────────────────┼──────────┼─────────────────────┼─────────────┼────────────────────────────┤
│ Custom │ Yours to make │ None │ Best │ Yours │ Yours │ Reinvention cost │
│ packed │ │ │ │ │ │ │
└─────────────┴───────────────────┴──────────────────┴──────────┴─────────────────────┴─────────────┴────────────────────────────┘
Why RLP still wins for Phase 1: catch-up consumers verifying deltaContentHash are already pulling alloy. No new dep. Ethereum-stack
idiom — anyone debugging keccak256(bytes) expects RLP first. Determinism is structural, no encoder-config flag to forget.
When to switch: Borsh if SP1/RISC0 ever verifies delta bytes in-circuit (smaller in-circuit decoder). Never bincode for hash anchoring.
Recommendation: keep RLP for Phase 1. Re-evaluate when/if a zkVM consumer enters the picture.
Item 4: Phase 3 spec edit (ultimate dissolution)
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 2 lines, removed 1 line
215
216 - Gateway optional; operators are first-class P2P endpoints.
217 - Permissionless gossip with peer scoring, DoS protection, per-message signature verification at the operator boundary.
218 -- Gateway lives on as a paid fast-lane for high-throughput users (think CDN tier on top of permissionless ingress).
218 +- Gateway initially persists as a paid fast-lane for high-throughput users (CDN tier on top of permissionless ingress).
219 +- **Ultimate trajectory: gateway potentially dissolves entirely.** The fast-lane role is itself transitional. If/when pe
+rmissionless ingress reaches throughput parity (peer scoring matures, DoS hardening stabilizes), the gateway has no rema
+ining function and the protocol becomes fully decentralized at the ingress layer. Whether to dissolve is a future decisi
+on contingent on ecosystem maturity, not committed here.
220
221 **Anchor invariant across all phases.** `StateCommitRegistry` on-chain root remains the consensus anchor. P2P is data-pl
ane only — never used for consensus or finality. This holds from Phase 1 through Phase 3 unchanged.
222
⏺ ★ Insight ─────────────────────────────────────
The grill-me sequence isn't arbitrary — it stacks bottom-up: state-tree invariants are foundation for consensus invariants, consensus
invariants are foundation for catch-up flow. If we grill out of order, later clusters keep referencing decisions from earlier clusters
that aren't locked yet, and the questions get hand-wavy. The 7-cluster sequence preserves that dependency order.
─────────────────────────────────────────────────
Phase 3 dissolution edit applied to spec.
Item 6: Grill-me agenda
Phase 3 dissolution edit applied to spec.
Item 6: Grill-me agenda
7 component clusters, ~10-15 probes each. Each cluster spans 1-3 turns. Locked answers numbered G-1, G-2, ..., folded back into spec
under a new "Grill amendments" section.
┌─────┬───────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ # │ Cluster │ Sample probes │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 1 │ Authority + │ Gateway auth termination on Phase 3 dissolution; per-chain dispatch fanout failure modes; │
│ │ admission │ plaintext-blind invariant under partial gateway failure; DoS surface │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 2 │ Operator │ Incremental JMT root invariant edge cases; redb write-tx contention with state-commit RPC; snapshot │
│ │ state-tree │ isolation for concurrent reads; leaf overwrite ordering; orphan ciphertext on aborted writes │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 3 │ State-commit │ Quorum-collection deadline straggler policy; BLS partial-sig retention/replay; reorg-recovery race │
│ │ consensus │ conditions; multi-aggregator scenarios; sequence-number gap policy │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 4 │ Catch-up + │ Delta retrieval API shape; consumer-side deltaContentHash verification; bootnode eviction policy; │
│ │ bootnode │ cold-start operator joining at sequence N≫0; hot-standby cutover semantics │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 5 │ P2P trajectory │ Phase 2 gossip topic structure; libp2p peer discovery via on-chain registry; federation cross-gateway │
│ │ │ state sync; Phase 3 fast-lane economic model; gossip-vs-consensus boundary enforcement │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 6 │ Cross-cutting │ New metrics surface (cadence, reorg counters, quorum-collection p99); error selector additions; │
│ │ │ multichain isolation; ECDSA key rotation impact on signed-read; EigenLayer slashing intersect │
├─────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 7 │ Test surface │ Fork-test reorg-recovery scenarios; P2P partition simulation; bootnode failover harness; multichain │
│ │ │ cadence matrix; Phase 1.5 hot-standby integration test │
└─────┴───────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Format per turn:
- One focused question (sometimes paired if naturally coupled).
- You answer; I push back if the answer leaves a gap or contradicts a locked decision.
- Lock with G-N numbering; brief rationale captured.
- Move to next cluster only when current cluster has no remaining gaps.
Estimated turn count: 60-90 total across all clusters. Front-loaded — clusters 1-3 are dense (foundational invariants), 4-7 lighter
(downstream consequences).
Approve agenda + sequence?
If yes, I'll start with Cluster 1, G-1: gateway authority termination semantics under Phase 3 dissolution — specifically what replaces
the gateway's API-key-to-PolicyClientRegistry-owner mapping when there's no gateway. That binding is load-bearing for newt_signedRead
admission and currently the strongest argument against full dissolution; resolving it first sets the ceiling on Phase 3 ambition.
If you want to reorder (e.g., grill state-tree first since it's the deepest invariant) or skip clusters, redirect now.
✻ Baked for 2m 25s
❯ grill state-tree first. We currently doont have a concrete plan or timeline to deprecate gateway, and will operator with that trust
assumption, but minimize the trust/authority that gateway has.
⏺ ★ Insight ─────────────────────────────────────
"Operate with gateway trust but minimize what we trust it for" is a directional principle, not a decision — it shapes every subsequent
grill answer without prescribing them. Folding it into the spec as a stated design philosophy makes future answers verifiable against
it. Without that explicit anchor, "trust minimization" gets re-litigated in every cluster instead of just being applied.
─────────────────────────────────────────────────
Adding the principle to spec, then starting state-tree grill.
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 14 lines
47
48 Newton has not deployed to mainnet. There are no live records, no state migration concerns, no operator-fleet upgrade ord
ering. The locked design ships as-is on first mainnet deploy. This removes a large class of compatibility constraints fro
m every downstream decision.
49
50 +# Design philosophy
51 +
52 +Directional principles informing every decision below. Recorded explicitly so future grill answers and implementation cho
+ices remain verifiable against them.
53 +
54 +## P1: Minimize gateway trust
55 +
56 +Gateway is operationally trusted in Phase 1 — there is no concrete plan or timeline to deprecate it. However, the protoco
+l design treats every gateway-asserted fact as something the operator should independently verify when verification is fe
+asible, and explicitly trust when it is not.
57 +
58 +- **When verification is feasible** (operator can derive the same fact from on-chain state, cryptographic primitives, or
+its own local data): operator verifies. Gateway's assertion is taken as a hint, not a source of truth.
59 +- **When verification is infeasible** (the fact is gateway-internal, e.g., API-key authentication state): operator trusts
+. Trust scope is enumerated explicitly in the implementation plan.
60 +- **Conflict resolution**: when this principle conflicts with operational simplicity (e.g., adding an on-chain read per w
+rite doubles latency), the principle wins unless an explicit decision waives it for that path. Waivers are documented; de
+faults are not.
61 +
62 +This principle takes effect immediately in Phase 1 design. Phase 2/3 evolution (federation, eventual potential dissolutio
+n per D7) builds on this foundation but is not what this principle is about — it's about shrinking the gateway's trusted
+surface NOW, regardless of when or whether it dissolves.
63 +
64 # Decisions
65
66 ## D1: EigenDA out of Phase 1, deferred to Phase 1.5+
⏺ Spec amendment applied. Design philosophy P1 (minimize gateway trust) anchored in the spec.
Reordered agenda: state-tree → state-commit consensus → catch-up + bootnode → P2P → authority/admission → cross-cutting → test surface.
★ Insight ─────────────────────────────────────
The state-tree's write path is the most important place to apply P1 — it's where untrusted data becomes structured, authenticated state.
Every read downstream (Rego eval, signed-read, state-commit root) trusts leaves to be well-formed. If garbage gets past the write
boundary, it propagates everywhere; catching at write is far cheaper than retrofitting validation at every read site. Trust minimization
at write = trust budget for everything downstream.
─────────────────────────────────────────────────
Cluster 1 (State-tree), G-1: Trust boundary on gateway-pushed writes
For each fact the gateway asserts on a write request, the operator either VERIFIES (independently derives the truth) or TRUSTS (accepts
gateway's word). The disposition sets the operator's validation surface.
┌─────────────────────────────────────────────────┬──────────────────────────────────────┬──────────────────────────┬──────────────┐
│ Asserted fact │ Verifiable by operator? │ Cost to verify │ Current │
│ │ │ │ default │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ Authenticated client (API key valid, signed │ No — gateway holds auth state │ n/a │ Trusted │
│ envelope) │ │ │ │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ policy_client_owner = client_id │ Yes — read PolicyClientRegistry │ 1 RPC (cacheable) │ Trusts │
│ │ on-chain │ │ │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ Leaf key/namespace inside policy client's │ Yes — derive from policy contract + │ Local computation │ Trusts │
│ authorized scope │ namespace prefix │ │ │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ Ciphertext well-formed HPKE/AEAD to operator's │ Yes — recipient-side decap probe │ ~1ms HPKE decap, no │ Undefined │
│ pubkey (per-op namespaces) │ │ plaintext stored │ │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ epoch_id in LeafHeaderV1 matches current │ Yes — operator already reads │ Local cache │ Should │
│ EpochRegistry epoch │ EpochRegistry │ │ verify │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ created_at monotonic w.r.t. operator's │ Partial — clamp to local clock + │ Local │ Should clamp │
│ last-write │ last-leaf-timestamp │ │ │
├─────────────────────────────────────────────────┼──────────────────────────────────────┼──────────────────────────┼──────────────┤
│ Leaf destined for THIS operator (not misrouted) │ Yes — HPKE recipient pubkey │ Local crypto │ Could verify │
└─────────────────────────────────────────────────┴──────────────────────────────────────┴──────────────────────────┴──────────────┘
Three sub-questions:
G-1a: For each row, does "Current default" align with P1? Specifically — should policy_client_owner and namespace_scope move from TRUSTS
→ VERIFIES in Phase 1?
G-1b: Is the recipient-side decap probe a Phase 1 requirement? Cost is ~1ms HPKE decap per leaf, no plaintext stored, catches
gateway-injected garbage and misrouted leaves in one check. Caveat: only applies to per-operator HPKE namespaces (identity, possibly
confidential). Threshold-encrypted secrets can't be probed by an individual operator.
G-1c: Missing rows you want added? Plausible candidates:
- Ciphertext size bounds (DoS protection)
- Per-policy-client leaf rate cap
- Delete-then-write atomicity (if deletes return in Phase 2)
- Tombstone validity (Phase 2)
- Replay protection — same (leaf_key, epoch_id) written twice with different ciphertext
G-1 cascades into:
- G-2 — write-handler RPC contract shape
- G-3 — validation order (fail fast on cheap checks per lessons.md "Admission gates ordering")
- G-4 — trust delta evolution as Phase 2 federation lands (multiple gateways means each operator-side check applies independently per
source gateway)
Answer G-1a, G-1b, G-1c — reorder if you want a different anchoring question.
✻ Sautéed for 2m 30s
※ recap: Goal: harden PDS architecture pivot (EigenDA out, on-chain deltaContentHash, per-chain cadence, P2P trajectory) before writing
the implementation plan. Current task: grill-me session through every protocol component. Next: you answer G-1a/b/c on state-tree
write-path trust boundary. (disable recaps in /config)
❯ G1a: Operator "Verifies" to minimize trust on gateway as much as possible, but if that "hurts" latency/performance, then we should make
some sacrifices there. Help me evaluate. But operators are trusting that "gateway is not malicious", but gateway stays minimal as an
orchestrator for bls aggregation/consensus as much as possible. (tldr: gateway -> until we have decentralized consensus, centralized
consensus orechestrator that is trusted to be honest but can be verified that gateway is honest, operated Newton DAO/foundation) G1b,c
--> while we do want to operator to verify as much as possible, again, "performance" is one of the most important so in the order
importance: security > reliabilty > performance > verifiability > decentralization (trustlessness)
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 25 lines
61
62 This principle takes effect immediately in Phase 1 design. Phase 2/3 evolution (federation, eventual potential dissolutio
n per D7) builds on this foundation but is not what this principle is about — it's about shrinking the gateway's trusted
surface NOW, regardless of when or whether it dissolves.
63
64 +## P2: Priority hierarchy
65 +
66 +When trade-offs arise across protocol design choices, prioritize in this order:
67 +
68 +1. **Security** — protocol correctness, cryptographic soundness, defense against malicious actors
69 +2. **Reliability** — uptime, fault tolerance, predictable behavior under load
70 +3. **Performance** — latency, throughput, resource efficiency
71 +4. **Verifiability** — operator-side independent verification of gateway-asserted facts (P1's operational expression)
72 +5. **Decentralization** — trustlessness, permissionless ingress
73 +
74 +Higher-ranked concerns trump lower-ranked when they conflict. Security is never sacrificed for performance. Performance c
+an be sacrificed for reliability. Verifiability (P1) is sacrificed for performance only when the cost is materially signi
+ficant; otherwise verify by default.
75 +
76 +This ordering matches Phase 1's mainnet-readiness posture. As Phase 2/3 land, decentralization rises in priority but neve
+r above security.
77 +
78 +## P3: Gateway trust model
79 +
80 +Gateway is a **centralized consensus orchestrator**, trusted to be honest but verifiable to be honest. Specifically:
81 +
82 +- **Trust assumption**: gateway is non-malicious. It does not forge requests, manipulate state assertions, or selectively
+ censor beyond DoS protection. This trust is operational, backed by Newton DAO/Foundation operation.
83 +- **Verifiable honesty**: every gateway-asserted fact that operators CAN verify (per P1 + P2 cost analysis), they MUST ve
+rify. The trust model is not "trust without checks" — it's "trust + auditability."
84 +- **Minimal scope**: gateway's role is BLS aggregation orchestration, request fanout, and admission control. It is NOT th
+e data plane (D5), NOT the consensus authority (`StateCommitRegistry` is), NOT the source of truth for any state operator
+ can derive elsewhere.
85 +- **Future**: this centralized trust model persists until decentralized consensus orchestration replaces it. Phase 2/3 P2
+P trajectory is the long-term evolution; no concrete timeline.
86 +
87 +Operator-side validation logic targets "honest-but-misconfigured gateway" failure modes (transient incorrectness) over "m
+alicious gateway" attacks (active subversion). The latter is out of scope for Phase 1 protocol design but IS in scope for
+ Phase 1 audit surface — every TRUST decision must be reviewable.
88 +
89 # Decisions
90
91 ## D1: EigenDA out of Phase 1, deferred to Phase 1.5+
⏺ Spec amendments P2 (priority hierarchy) + P3 (gateway trust model) anchored. Now applying them to G-1.
★ Insight ─────────────────────────────────────
The priority hierarchy makes G-1 a cost analysis problem, not a values problem. Security > reliability > performance forces VERIFY by
default. The interesting question is whether any single check crosses a performance threshold worth waiving for. With aggressive
caching, even the most expensive (HPKE decap probe at ~1ms) doesn't impact the state-commit RPC hot path — state-commit reads the cached
JMT root, not raw writes. The two paths are decoupled, so write-side cost stays in the write budget.
─────────────────────────────────────────────────
Cost evaluation per verification (hot path = write-handler):
┌────────────────────────────────────────────────┬──────────────────────┬───────────────────┬──────────────────┐
│ Verification │ Cost │ Write-path impact │ Verdict under P2 │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ policy_client_owner (cached, TTL=10min) │ 1µs warm / 50ms cold │ <0.01% warm │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ Namespace scope │ 10µs local │ <0.01% │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ HPKE decap probe (per-op HPKE) │ ~1ms │ dominant cost │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ Structural validation (threshold ns) │ 10µs │ <0.01% │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ epoch_id (cached EpochRegistry view) │ 1µs │ <0.01% │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ created_at clamp │ 1µs │ <0.01% │ CLAMP │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ Size bound check │ 1µs │ <0.01% │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ Per-policy-client rate counter │ 1µs │ <0.01% │ VERIFY │
├────────────────────────────────────────────────┼──────────────────────┼───────────────────┼──────────────────┤
│ Replay protection ((leaf_key, epoch_id) dedup) │ 5µs │ <0.01% │ VERIFY │
└────────────────────────────────────────────────┴──────────────────────┴───────────────────┴──────────────────┘
Aggregate verified write hot path: ~1.1ms (per-op HPKE namespaces) / ~150µs (threshold namespaces). Single-threaded write throughput
floor: ~900 writes/sec. Acceptable for Phase 1.
State-commit RPC reads the in-memory cached JMT root, so write-side verification cost does NOT cascade into the 1s/4s state-commit
response budget. The two paths are independent.
G-1 lock proposal:
- G-1a: All verifiable rows → VERIFIES. Only "client authenticated" stays TRUSTED (gateway-internal, infeasible to verify per P3).
- G-1b: HPKE decap probe is MANDATORY for per-operator HPKE namespaces (identity at minimum, confidential pending G-3
namespace-by-namespace review). Threshold-encrypted namespaces (privacy/secrets in threshold mode) get structural validation only —
individual operator lacks full share for decap.
- G-1c: Three rows added to validation surface:
- Ciphertext size bound (DoS protection — fail leaves over max_leaf_bytes config, default 1 MiB)
- Per-policy-client leaf rate cap (DoS defense-in-depth — operator-side counter, even though gateway should rate-limit upstream per
P3)
- Replay protection — (leaf_key, epoch_id) dedup catches double-write with different ciphertext, which is a clear
malicious-or-misconfigured gateway signal
Net write-handler validation surface: 9 checks, ~1.1ms warm-cache hot path, throughput floor 900 writes/sec, no cascade into
state-commit RPC budget.
Confirm G-1 lock? On approval I move to G-2: write-handler RPC contract shape — input type schema, validation order (cheapest-first per
lessons.md "Admission gates ordering"), atomicity boundary (per-leaf vs per-batch redb tx), error response variants (one selector per
failure class so off-chain diagnostics are unambiguous).
✻ Baked for 2m 27s
※ recap: Goal: harden the Private Data Storage architecture pivot (EigenDA out, P2P trajectory, minimize gateway trust) before writing the
implementation plan. Current task: grilling Cluster 1 state-tree, awaiting your G-1 lock confirmation so I can move to G-2
write-handler RPC contract. (disable recaps in /config)
❯ sounds good.but operators' rpc endpoints are authenticated by "gateway" right? how should we do this?
⏺ ★ Insight ─────────────────────────────────────
The two-layer auth model is exactly how P1 + P3 work together: P3 says "gateway is honest operationally" which gives the OUTER trust
hop; P1 says "verify what you can" which gives the INNER per-leaf verification. They're not in tension — they cover orthogonal concerns.
Outer layer = cross-service identity (gateway proving it's the gateway); inner layer = content truth (request contents matching
on-chain state). This matches lessons.md "Cross-service trust: single trust boundary per identity check" — operator verifies the
envelope, not per-client identity (that's the gateway's job, and reproducing it at every hop costs complexity for no gain).
─────────────────────────────────────────────────
Good catch — outer envelope auth was implicit in G-1 but never made explicit. Lock as G-1.5: Envelope authentication before G-2 builds
on it.
Existing production pattern. All operator non-DKG RPC endpoints (signed-read, get-state-commit-proposal, sign-state-commit,
simulate-policy variants) are already guarded by Authenticated<T> envelopes via authenticate_gateway_call. Verifies four things:
1. ECDSA signature recovers to an address registered as task-generator on OperatorRegistryEpochGovernance
2. chain_id matches operator's chain context
▐▛███▜▌ Claude Code v2.1.132
▝▜█████▛▘ Opus 4.7 (1M context) with max effort · Claude API
▘▘ ▝▝ ~/projects/newton-prover-avs
✻ Conversation compacted (ctrl+o for history)
⎿  Referenced file docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md
⎿  Read ../../.claude/RTK.md (30 lines)
⎿  Read ../../CLAUDE.md (39 lines)
⎿  Read docs/CLAUDE.md (7 lines)
⎿  Skills restored (superpowers:brainstorming)
Searched for 1 pattern, read 1 file (ctrl+o to expand)
⏺ ★ Insight ─────────────────────────────────────
- The Authenticated<T> pattern already exists in crates/chainio/src/operator_rpc_auth.rs for the newt_signedRead endpoint — its design
rationale (chain_id binding, expires_at, ParamsHashMismatch defense) is the canonical reference. Reusing the EXACT same envelope avoids
two implementations of "trust gateway as task generator."
- The "cheapest gates first" admission pattern in lessons.md (admission gates must run cheapest-first) is the lens for understanding
expires_at: it's a sub-microsecond timestamp comparison that filters out stale envelopes BEFORE the ~50µs ECDSA recover and BEFORE the
~5ms state lookup. Without it, an attacker can force expensive crypto on every replayed packet.
- The task-generator set is mutated through the epoch-governance queue (NEWT-1175), so it rotates on epoch boundaries — typically
minutes to hours. expires_at with 120s window guarantees a captured envelope cannot survive across an epoch rotation, defending against
the "former-but-now-revoked signer" attack class.
─────────────────────────────────────────────────
You're right, I had P3 framed as if multi-gateway were aspirational. Let me amend the spec to lock the production reality.
⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
82 - **Trust assumption**: gateway is non-malicious. It does not forge requests, manipulate state assertions, or selectively
censor beyond DoS protection. This trust is operational, backed by Newton DAO/Foundation operation.
83 - **Verifiable honesty**: every gateway-asserted fact that operators CAN verify (per P1 + P2 cost analysis), they MUST ve
rify. The trust model is not "trust without checks" — it's "trust + auditability."
84 - **Minimal scope**: gateway's role is BLS aggregation orchestration, request fanout, and admission control. It is NOT th
e data plane (D5), NOT the consensus authority (`StateCommitRegistry` is), NOT the source of truth for any state operator
can derive elsewhere.
85 +- **Multiple gateway instances are production reality, not Phase 2.** Stagef runs 1 gateway server. Prod runs 10 gateway
+servers for horizontal scaling, each with a distinct ECDSA signing key but all enrolled into the **single, shared task-ge
+nerator set** on `OperatorRegistryEpochGovernance`. From the operator's verification perspective there is no difference b
+etween "1 gateway" and "N gateways" — admission requires `recovered_signer ∈ task_generator_set` regardless of N. Adding/
+removing gateway instances is therefore an operational concern (provision a new key, queue `addTaskGenerator`, apply at e
+poch boundary, deprovision the old key, queue `removeTaskGenerator`), not a protocol change. Phase 1 ships this directly.
86 - **Future**: this centralized trust model persists until decentralized consensus orchestration replaces it. Phase 2/3 P2
P trajectory is the long-term evolution; no concrete timeline.
87
88 Operator-side validation logic targets "honest-but-misconfigured gateway" failure modes (transient incorrectness) over "m
alicious gateway" attacks (active subversion). The latter is out of scope for Phase 1 protocol design but IS in scope for
Phase 1 audit surface — every TRUST decision must be reviewable.
@denniswon

Copy link
Copy Markdown
Author

⏺ Let me apply the cascading edits to the spec. First I need to locate the exact text in each touchpoint.

Searched for 2 patterns, read 2 files (ctrl+o to expand)
⎿  PreToolUse:Read hook error ⎿  Failed with non-blocking status code: [TokenEfficiency] Hard
rule violation: Re-reading
2026-05-07-pds-architecture-pivot-design.md (2x) — file
unchanged since last read. Consider using cached knowled…
⎿  PreToolUse:Read hook error ⎿  Failed with non-blocking status code: [TokenEfficiency] Hard
rule violation: Re-reading
2026-05-07-pds-architecture-pivot-design.md (2x) — file
unchanged since last read. Consider using cached knowled…

⏺ Now I have all the touchpoints. Let me apply the edits in parallel — they're in non-overlapping regions of the file.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
342 - Explicit dependency declaration (G-2-Q6 lock). EvaluateAndEmit.intent.input_refs: Vec<leaf_key> enumerates every
leaf the policy will read. Operator validates each ref exists (pre-cycle JMT state OR same-tx earlier write within the
per-tx overlay) and didn't fail; closure is exactly the declared inputs.
343 - SDK-assembled, gateway-transported. Intent assembly (including input_refs, policy_ref, params) happens in th
e client SDK against the namespace addressing registry and the policy interface. Gateway wraps the assembled intent in Authenticated<T> and fanouts; gateway does NOT interpret policy semantics or compute input_refs. Preserves D5's autho
rity/admission boundary.
344 - Code-vs-data schema split (forward-compat). policy_ref stays a TYPED SIBLING field forever — even after Sub-B br
ings policy bytes into JMT (namespace 0x05), policy_ref does NOT collapse into input_refs. input_refs is "data the
policy reads"; policy_ref is "the logic to execute." Mirrors Ethereum's contract-address vs storage-slots distinction
; matches Newton's existing on-chain task interface (policyIdpolicyDataId ≠ private-data IDs). policyCodeHash v
erification remains a structural property of policy_ref, not a generic input check.
345 -- Same-leaf-key collision. Two ops in the same tx targeting the same leaf_key is a malformed tx; rejected with Du -plicateLeafKey. Combined with within-namespace independence this preserves cross-operator determinism: set semantics ca
-nnot tolerate ambiguous "winner" resolution within a namespace.
345 +- Same-leaf-key collision (G-2-Q5 revised + G-3-Q6 lock). Op-class-dispatched: PutLeaf vs PutLeaf to the same key in
+ a single tx is malformed (rejected TxError::DuplicateLeafKey — version-monotonic self-overwrite within a tx is pointl
+ess). EvaluateAndEmit vs EvaluateAndEmit emitting the same leaf_key (reachable only on 0x04 when senders supply identi
+cal (policy_id, addressing_input, params, nonce)) rejects as TxError::SameTxLeafCollision. Cross-tx-in-cycle or cros
+s-cycle re-emit to a leaf already present rejects as TxError::LeafExists — eval emissions are immutable. PutLeaf vs Ev
+aluateAndEmit is structurally impossible: namespaces are op-exclusive (registry below). Combined with within-namespace i
+ndependence this preserves cross-operator determinism: set semantics cannot tolerate ambiguous "winner" resolution withi
+n a namespace.
346 - Cycle non-atomicity. Failed ops/txs are recorded in WriteResponse.transactions[i].errors but do NOT roll back th
e cycle.
347 - Read-after-write scope. Visibility of prior emits is scoped to the transaction. A namespace-N op in tx K reads
leaves written by namespace-M (M < N) ops in the SAME tx K, but cannot see writes from tx K+1 or later, and does not se
e uncommitted writes from sibling within-namespace ops (they are unordered, so cross-reading is undefined).
348 - Determinism. Pre-cycle JMT state + canonical tx ordering + canonical within-namespace set application is sufficien
t for cross-operator determinism. Non-determinism inside Regorus eval (clock, network, randomness) is forbidden.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 7 lines
393 header: [u8; 20], // LeafHeaderV1 — see G-3-Q4 layout below
394 ciphertext: Vec,
395 },
396 + // G-3-Q6 lock — emitted leaves on namespace 0x04 are immutable (first-emit
397 + // wins; cross-tx-in-cycle and cross-cycle re-emit reject as
398 + // TxError::LeafExists). Within-tx same-emit rejects as
399 + // TxError::SameTxLeafCollision. Sender controls uniqueness via
400 + // PolicyEvalIntent.nonce: two emissions of an otherwise-identical intent
401 + // require distinct nonces (use case: amortize one policy evaluation into
402 + // N single-use authorizations consumed by N downstream actions).
403 EvaluateAndEmit {
404 intent: PolicyEvalIntent,
405 },

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 18 lines, removed 6 lines
529 target_namespace: u8, // namespace prefix the policy emits into (0x04, 0x05, ...)
530 }
531
532 -// G-2-Q7 lock — PolicyEvalIntent splits per-call data four ways:
532 +// G-2-Q7 + G-3-Q6 lock — PolicyEvalIntent splits per-call data five ways:
533 // - input_refs: what the policy READS (G-2-Q6 lock)
534 // - policy_ref: what code to run, and where output goes (code-vs-data lock)
535 // - addressing_input: input to target_namespace's frozen derivation rule
536 // - params: opaque eval params consumed by Regorus
537 +// - nonce: sender-supplied uniqueness tag (G-3-Q6 lock)
538 // The operator computes emit_leaf_key by applying the target_namespace's
538 -// frozen rule (Q5 lock) to addressing_input, then prepends target_namespace
539 -// as the leaf_key prefix. emit_leaf_value comes from Regorus inline execution
540 -// against input_refs + params. addressing_input is intentionally distinct from
541 -// params: keeps the namespace rule independent of the policy's eval-time
542 -// schema, so params can evolve without re-encoding addressing semantics.
539 +// frozen rule (Q5 lock) to (addressing_input, nonce), then prepends
540 +// target_namespace as the leaf_key prefix. emit_leaf_value comes from
541 +// Regorus inline execution against input_refs + params.
542 +//
543 +// Field-shape rationale:
544 +// - addressing_input is intentionally distinct from params: keeps the
545 +// namespace rule independent of the policy's eval-time schema, so
546 +// params can evolve without re-encoding addressing semantics.
547 +// - nonce is structurally separate from params: the policy's input space
548 +// is NOT polluted by uniqueness tags. Two emissions from the same
549 +// intent (e.g., two single-use authorizations) differ ONLY in nonce;
550 +// the policy sees identical params and produces identical results.
551 +// Senders supply distinct nonces (random u64, or per-(sender, policy)
552 +// monotonic counter) when they want N distinct emissions from one
553 +// evaluation.
554 struct PolicyEvalIntent {
555 input_refs: Vec<[u8; 33]>, // G-2-Q6 lock: leaf_keys this op reads
556 policy_ref: PolicyRef, // typed sibling (code identity + target namespace)
557 addressing_input: Bytes, // input to target_namespace's derivation rule
558 params: Bytes, // sender-supplied eval params (data)
559 + nonce: u64, // G-3-Q6 lock: sender-supplied uniqueness tag
560 }
561
562 struct WriteResponse {

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 7 lines, removed 7 lines
571 }
572 ```
573
574 -Namespace addressing registry (G-2-Q5 + G-2-Q7 lock). Each namespace registers its own leaf_key derivation rule at
- namespace creation. The rule is frozen thereafter; changing it is a namespace-version bump (analogous to `UnsupportedSt
-ateCommitVersion`). For `EvaluateAndEmit` namespaces, the rule's input is `intent.addressing_input` (Q7 lock); for `PutL
-eaf`-only namespaces, the rule documents how the sender computes `leaf_key` directly. Phase 1 + planned Phase 2 namespac
-es:
574 +Namespace addressing registry (G-2-Q5 lock + 2026-05-09 op-exclusivity revision + G-2-Q7 lock). Each namespace regis
+ters its own `leaf_key` derivation rule at namespace creation. The rule is frozen thereafter; changing it is a namespace
+-version bump (analogous to `UnsupportedStateCommitVersion`). Op-exclusivity invariant: each namespace supports exac
+tly one `TxOperation` variant. PutLeaf-only namespaces accept upload-class writes; EvaluateAndEmit-only namespaces emit
+eval-class results. Mixing rejects at dispatch as `TxError::OperationNotSupported`. The exclusivity isn't taste — it's a
+ security boundary: allowing PutLeaf to 0x04 would let users forge task responses by direct write (bypassing policy eval
+uation), and allowing EvaluateAndEmit to 0x05 would imply "policy bytes can be computed from a meta-policy" (sneaks Sub-
+B's content-addressing guarantee into eval-result territory). For `EvaluateAndEmit` namespaces, the rule's input is `(in
+tent.addressing_input, intent.nonce)` (Q7 + G-3-Q6 lock); for `PutLeaf`-only namespaces, the rule documents how the send
+er computes `leaf_key` directly. Phase 1 + planned Phase 2 namespaces:
575
576 -| Namespace | Prefix | Op types | leaf_key derivation rule (32-byte content key after prefix) |
576 +| Namespace | Prefix | Op type (exclusive) | leaf_key derivation rule (32-byte content key after prefix) |
577 |---|---|---|---|
578 -| Identity | 0x01 | `PutLeaf` only | sender computes per identity-domain (e.g., `keccak256(domain_tag || subject ||
-version)`) |
579 -| Confidential | 0x02 | `PutLeaf` only | sender computes `keccak256(provider_id || data_ref || version)` |
580 -| Secrets | 0x03 | `PutLeaf` only | sender computes `keccak256(policy_address || schema_field || version)` |
581 -| Task-response (Sub-C) | 0x04 | `EvaluateAndEmit` | operator: `keccak256(intent.addressing_input)` where `addressing_in
-put = abi.encode(task_id)` |
582 -| On-chain policy (Sub-B) | 0x05 | both | operator (`EvaluateAndEmit`): `keccak256(intent.addressing_input)` where `addr
-essing_input = abi.encode(policy_id)`; sender (`PutLeaf` for raw policy bytes): `keccak256(policy_id)` |
+intent.nonce))` where `addressing_input` carries Sub-C task-identity semantics (e.g., `abi.encode(policy_id, canonical_i
+nputs)`). Distinct nonces produce distinct emissions from otherwise-identical intents (G-3-Q6) |
582 +| On-chain policy (Sub-B) | 0x05 | `PutLeaf` | sender computes `keccak256(policy_id)` for raw policy bytes |
583
584 Namespace prefix ordering encodes dependency order: lower-prefix namespaces are written before higher-prefix namespaces
consume them via `EvaluateAndEmit` within the same tx. Namespace owners define the per-namespace derivation rule in thei
r respective design specs; this spec only locks the meta-rule that addressing is namespace-defined and frozen at creati
on, with `addressing_input` as the canonical input slot
.
585

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
632 14. Two-phase per-tx execution: writes serial, evals parallel-snapshot (G-3-Q3 lock). Within a tx, Phase 1 applies a
ll PutLeaf ops in declared order to a per-tx overlay; Phase 2 runs all EvaluateAndEmit ops in parallel against a sna
pshot of (pre-cycle JMT ⊕ Phase 1 overlay). Eval ops can read Phase 1 writes via input_refs (atomic "register-then-e
valuate" composition); siblings cannot read each other's emits (eval→eval composition requires multi-tx). Senders MUST d
eclare PutLeaf ops before EvaluateAndEmit ops; mixing rejects as TxError::OpOrderingViolation. Tail latency for N para
llel evals is max(eval_i), not sum(eval_i). Phase 2 emit order is by declared op index, not completion order — opera
tors converge on byte-identical TxResult regardless of internal scheduling.
633 15. LeafHeaderV1 is lean and reserved-heavy (G-3-Q4 lock). 20-byte fixed-size header carries only 3 load-bearing b
ytes (header_version, enc_scheme, flags); remaining 17 bytes are reserved and MUST be zero. Operator's apply_put
rejects non-zero reserved bytes as TxError::InvalidLeafHeader. V2 header bump (byte 0 = 0x02) claims reserved space f
or typed fields when concrete consumers materialize. No metadata tracking, no scheme-dependent payload, no defense-in-de
pth integrity check in V1 — Phase 1 MVP has no consumers for any of those.
634 16. Ciphertext format is enc_scheme-dispatched; HPKE binds leaf coordinates to AAD (G-3-Q5 lock). enc_scheme = 0x 00 → raw plaintext (no framing, used by Sub-B policy bytes). enc_scheme = 0x01 → RFC 9180 Base mode HPKE with DHKEM(X
25519, HKDF-SHA256) / HKDF-SHA256 / ChaCha20-Poly1305 (matching Newton's existing partial-DH suite). HPKE AAD = keccak2 56(leaf_key || version_le_bytes || header) — pins ciphertext to its JMT slot, prevents relocation/replay/header-tamperi
ng. Recipient reconstructs AAD entirely from JMT leaf data; no external state. Recipient pk sourcing deferred to per-nam
espace spec.
635 +17. Each namespace is op-type-exclusive; EvaluateAndEmit emissions are immutable (G-2-Q5 op-exclusivity revision +
+ G-3-Q6 lock). Op→namespace mapping is a fixed dispatch table: PutLeaf-only namespaces (0x01/0x02/0x03/0x05) reject Eval
+uateAndEmit ops as TxError::OperationNotSupported, and the EvaluateAndEmit-only namespace (0x04) rejects PutLeaf the s
+ame way. Eval emissions on 0x04 are immutable: first-emit wins; subsequent emits to the same leaf_key reject as TxErr +or::LeafExists (cross-tx-in-cycle and cross-cycle alike); same-tx duplicate rejects as TxError::SameTxLeafCollision.
+Sender controls uniqueness via PolicyEvalIntent.nonce: u64 — two emissions from the same (policy_id, addressing_input +, params) require distinct nonces. The asymmetry (PutLeaf uses monotonic version; EvaluateAndEmit uses opaque nonce) re
+flects op semantics: writes are mutable state, evals are immutable events.
636
637 # Cascade updates required
638

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
701 3. Bootnode hot-standby trigger — Phase 1.5 milestone. Plan should NOT include 1.5 work but should NOT block 1.5 by
hardcoding single-bootnode assumptions in the code.
702 4. Reorg recovery test plan — fork-test scenarios that exercise SequenceGap/StateRootMismatch retry. Likely a ne
w e2e --scenario reorg-recovery.
703 5. Delta canonical encoding helper cratecrates/state-tree/src/delta.rs is the natural home. Plan must define th
e public API surface.
704 -6. G-2-Q5 — Response-leaf addressing for EvaluateAndEmit. LOCKED 2026-05-08: per-namespace addressing poli
-cy. Each namespace registers its own derivation rule at creation, frozen thereafter. Initial registry in D9 covers 0x01-
-0x05.
704 +6. G-2-Q5 — Response-leaf addressing for EvaluateAndEmit. LOCKED 2026-05-08, REVISED 2026-05-09: per-names
+pace addressing policy. Each namespace registers its own derivation rule at creation, frozen thereafter. Initial registr
+y in D9 covers 0x01-0x05. 2026-05-09 op-exclusivity revision: each namespace supports exactly one TxOperation vari
+ant (0x01/0x02/0x03/0x05 = PutLeaf-only, 0x04 = EvaluateAndEmit-only); mixing rejects as TxError::OperationNotSupported +. Closes the "forge task response by direct write" attack and structurally collapses three of four cross-op-class same-
+leaf collisions to impossible.
705 7. G-2-Q6 — Cross-namespace dependency declaration. LOCKED 2026-05-08: explicit input_refs: Vec<[u8; 33]>
in PolicyEvalIntent. SDK assembles the list from the calling context; gateway transports as opaque bytes (preserves "g
ateway never holds plaintext" invariant + signed-read trust posture). Cross-namespace dependency closure is explicit per
-op, not whole-tx.
706 8. G-2-Q7 — PolicyEvalIntent shape. LOCKED 2026-05-08: PolicyRef { cid, policy_code_hash, target_namespac e: u8 } + PolicyEvalIntent { input_refs, policy_ref, addressing_input: Bytes, params: Bytes }. target_namespace is
policy-level metadata embedded in PolicyRef; Sub-C's RegorusInlineHandler verifies it matches a constant inside poli
cy bytes (policyCodeHash-bound, raises TargetNamespaceMismatch on divergence). addressing_input is the typed input s
lot consumed by the target namespace's frozen derivation rule (Q5).
707 9. G-2-Q8 — Per-tx execution timeout. LOCKED 2026-05-08: per-op wall-clock budget (default 200ms via state_ commit.per_chain[id].eval_timeout_ms), per-tx abort on exceed, gateway-config sourced. WriteHandler::apply_eval(op, bu dget: Duration) surfaces budget in the trait signature; Sub-C's RegorusInlineHandler honors it via regorus::Engine ::eval_with_deadline. Phase 1 DefaultHandler accepts the parameter and ignores it (returns OperationNotSupported be
fore timing). Per-policy or per-policy-class budgets are out of scope.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
714 13. G-3-Q3 — Read-after-write scope inside a tx. LOCKED 2026-05-08: two-phase per-tx execution. Phase 1 (Pu tLeafs) sequential into per-tx overlay; Phase 2 (EvaluateAndEmits) parallel against snapshot of (pre-cycle JMT ⊕ Pha se 1 overlay). Eval ops can read Phase 1 writes via input_refs (atomic "register-then-evaluate" composition); sibling
s cannot read each other's emits. Senders MUST declare PutLeaf-before-Eval; mixing rejects as TxError::OpOrderingViolat ion. Tail latency = max(eval_i) not sum(eval_i). Trade-off: eval→write→eval composition requires multi-tx.
715 14. G-3-Q4 — LeafHeaderV1: [u8; 20] byte layout. LOCKED 2026-05-08: lean V1 layout. byte 0 = header_versi on (=0x01), byte 1 = enc_scheme (0x00=plaintext, 0x01=HPKE-X25519-ChaCha20Poly1305, 0x02..0xFF reserved), byte 2 = f lags (bit 0 = tombstone, bits 1..7 reserved), bytes 3..19 = reserved (17B, MUST be zero). Decoder rejects non-zero rese
rved as TxError::InvalidLeafHeader. V2 header bump claims reserved bytes when consumers materialize. No metadata track
ing, no scheme-dependent payload, no defense-in-depth integrity check in V1.
716 15. G-3-Q5 — Ciphertext format binding. LOCKED 2026-05-08: enc_scheme = 0x00 → raw plaintext (Sub-B); enc _scheme = 0x01 → RFC 9180 HPKE Base mode, DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + ChaCha20-Poly1305 (matches Newton'
s existing partial-DH suite). HPKE AAD = keccak256(leaf_key || version_le || header) — pins to leaf coordinates; recip
ient reconstructs from JMT alone; no external state. Recipient pk sourcing deferred to per-namespace spec (Sub-A: EpochR
egistry MPK; Sub-C: TBD).
717 -16. G-3-Q6 — Same-cycle leaf_key collision policy. PutLeaf+PutLeaf, PutLeaf+EvaluateAndEmit, two EvaluateAndEmit c
-olliding, cross-tx within cycle — last-write-wins, reject, deterministic order?
717 +16. G-3-Q6 — Same-cycle leaf_key collision policy. LOCKED 2026-05-09: namespace op-exclusivity (G-2-Q5 rev
+ised) collapses three of four cross-op-class collisions to structurally impossible (PutLeaf+EvaluateAndEmit can't share
+a namespace). Remaining cases: (a) PutLeaf+PutLeaf same key in same tx → TxError::DuplicateLeafKey (version-monotonic
+self-overwrite within a tx is pointless); (b) EvaluateAndEmit+EvaluateAndEmit same emit on 0x04 → within-tx rejects as +TxError::SameTxLeafCollision, across-tx (cycle or beyond) rejects as TxError::LeafExists. Eval emissions are immutabl
+e. Senders supply distinct PolicyEvalIntent.nonce for distinct emissions of otherwise-identical intents (motivating us
+e case: amortize one policy evaluation into N single-use authorizations).
718 17. G-3-Q7 — Tombstone / deletion posture for Phase 1. Header flag bit reservation? Soft-delete vs hard absent? Futu
re-proofing for Sub-C secrets rotation.
719 18. G-3-Q8 — JMT root-computation cadence. Per-PutLeaf (every leaf rehashes the tree)? Per-tx? Per-cycle (one root p
er Layer 1 tick)?
720 19. G-3-Q9 — Namespace creation flow. Governance proposal? Code-only enum extension? Runtime config registry? Couple
d to namespace addressing rule (Q5).

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 2 lines
739 | 2026-05-08 | G-3-Q3 locked — two-phase per-tx execution: Phase 1 PutLeafs sequential into overlay; Phase 2 EvaluateAnd
Emits parallel against (pre-cycle JMT ⊕ Phase 1 overlay) snapshot; PutLeaf-before-Eval ordering enforced (TxError::Op OrderingViolation); tail latency = max-eval, not sum; eval→write→eval composition requires multi-tx | This document, gr
ill round G-3 |
740 | 2026-05-08 | G-3-Q4 locked — lean LeafHeaderV1: byte 0 = header_version (=0x01), byte 1 = enc_scheme (0x00=plain
text, 0x01=HPKE), byte 2 = flags (bit 0=tombstone), bytes 3..19 reserved (must be zero, decoder rejects as TxError::I nvalidLeafHeader); V2 header bump claims reserved when concrete consumers exist; YAGNI for Phase 1 | This document, gri
ll round G-3 |
741 | 2026-05-08 | G-3-Q5 locked — ciphertext format dispatch by enc_scheme: 0x00 = raw plaintext (Sub-B), 0x01 = RFC 9180
HPKE Base mode with X25519/HKDF-SHA256/ChaCha20-Poly1305 (matches Newton partial-DH suite); HPKE AAD = keccak256(leaf_ key \|\| version_le \|\| header) — pins to leaf coordinates, prevents relocation/replay/header-tamper, recipient recons
tructs from JMT alone; recipient pk sourcing deferred to per-namespace spec | This document, grill round G-3 |
742 +| 2026-05-09 | G-2-Q5 op-exclusivity revision — each namespace supports exactly one TxOperation variant (0x01/0x02/0x0
+3/0x05 = PutLeaf-only, 0x04 = EvaluateAndEmit-only); mixing rejects as TxError::OperationNotSupported. Closes "forge t
+ask response by direct write" attack on 0x04 and the symmetric "compute policy bytes via meta-policy" anti-pattern on 0x
+05; structurally collapses cross-op-class same-leaf collisions to impossible | This document, grill round G-3 |
743 +| 2026-05-09 | G-3-Q6 locked — eval emissions on 0x04 are immutable (first-emit wins; cross-tx-in-cycle and cross-cycle
+re-emit reject as TxError::LeafExists); sender controls uniqueness via PolicyEvalIntent.nonce: u64; within-tx same-e
+mit rejects as TxError::SameTxLeafCollision. PutLeaf uses monotonic version (mutable state) while EvaluateAndEmit uses
+ opaque nonce (immutable event) — asymmetry follows op semantics. Motivating use case: amortize one policy evaluation in
+to N single-use authorizations consumed by N downstream actions | This document, grill round G-3 |

⏺ Let me verify the edits are internally consistent — grep for any stale references.

Searched for 3 patterns (ctrl+o to expand)

⏺ All edits verified consistent — 18 G-3-Q6/nonce references aligned, 5 entries dated 2026-05-09, no stale "both" or
"DuplicateLeafKey-only" references.

G-2-Q5 op-exclusivity revision and G-3-Q6 are locked. Spec touchpoints applied:

┌────────────────────────────────────┬─────────┬────────────────────────────────────────────────────────────────────────────────────┐
│ Touchpoint │ Line │ Change │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ D9 same-leaf-key collision rule │ 345 │ Op-class-dispatched: DuplicateLeafKey / SameTxLeafCollision / LeafExists / │
│ │ │ structurally impossible │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ TxOperation::EvaluateAndEmit │ 396 │ Immutability + nonce uniqueness rationale │
│ comment │ │ │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ PolicyEvalIntent struct │ 532-560 │ Added nonce: u64; rationale comment for nonce-vs-params separation │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Namespace registry │ 574-583 │ Op-exclusivity invariant; 0x04 derivation includes nonce; 0x05 = PutLeaf only │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Hard Invariants │ 635 │ Added Invariant 17 (op-exclusivity + immutable-emit) │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Open Questions G-2-Q5 │ 704 │ LOCKED 2026-05-08, REVISED 2026-05-09 │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Open Questions G-3-Q6 │ 717 │ LOCKED 2026-05-09 │
├────────────────────────────────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ Decision History │ 742-743 │ Two new rows │
└────────────────────────────────────┴─────────┴────────────────────────────────────────────────────────────────────────────────────┘

★ Insight ─────────────────────────────────────
Notice the G-2-Q5 rule's input shifted from intent.addressing_input (Q7 lock) to (intent.addressing_input, intent.nonce) (G-3-Q6 lock).
This kind of "tuple input" frozen rule is forward-compatible: each namespace's rule can grow inputs over time, but only via
namespace-version bump that re-registers the rule. The freeze-at-creation property means existing leaves never re-derive —
(old_addressing_input, old_nonce) and (new_addressing_input, new_nonce, new_field) produce different content keys under different rule
versions, with no overlap. This mirrors how UnsupportedStateCommitVersion defends StateCommit schema evolution.
─────────────────────────────────────────────────


Next: G-3-Q7 — Tombstone / deletion posture for Phase 1.

The G-3-Q4 lock reserved LeafHeaderV1.flags bit 0 with the comment "tombstone (G-3-Q7 lock TBD)." Now we lock the actual semantics — or
defer.

The question. Do PutLeaf-class namespaces (0x01/0x02/0x03/0x05) need a delete primitive in Phase 1? If so, soft-delete (set flag, keep
leaf in tree) or hard absent (remove from tree)? Eval emissions on 0x04 are immutable (G-3-Q6 lock) so deletion doesn't apply there.

Phase 1 use cases for delete (skeptical pass):

  • 0x01 Identity: revoke credential? Achievable via version-monotonic update writing tombstone-marked successor — but no Sub-A Phase 1
    user story actually requires it.
  • 0x02 Confidential: revoke grant? Same — no current Phase 1 motivator.
  • 0x03 Secrets: rotation? Achieved by writing new version; old is implicitly stale via MVCC (newest version wins on read).
  • 0x05 Policy bytes (Sub-B): deprecate policy? Tombstone could mark deprecated, but no Phase 1 consumer.

Three options:

┌────────────────────┬──────────────────────────────────────────────────────────────┬───────────────────────────────────────────────┐
│ Option │ Phase 1 ships │ Phase 2+ │
├────────────────────┼──────────────────────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ A. Reserve only │ bit 0 = reserved; decoder rejects flag set │ Define semantics when concrete consumer │
│ (YAGNI) │ │ materializes │
├────────────────────┼──────────────────────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ B. Soft-delete via │ Tombstone-marked leaf still in JMT; reads return │ Supports audit + revocation today │
│ flag │ LeafTombstoned; version still monotonic │ │
├────────────────────┼──────────────────────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ C. Hard absent │ DELETE op variant on TxOperation; leaf removed from tree │ Cleaner storage but breaks "JMT is │
│ │ │ append-only audit log" invariant │
└────────────────────┴──────────────────────────────────────────────────────────────┴───────────────────────────────────────────────┘

⏺ User answered Claude's questions:
⎿  · G-3-Q7 — tombstone semantics for Phase 1? → B. one question from "Cross-tx within cycle: tx2 hits leaf created by tx1
│ TxError::LeafExists — tx2 rejects │
├─────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
│ Cross-cycle: leaf already in committed JMT │ TxError::LeafExists — rejects" --> help me
understand leaf exists error you mentioned here

⏺ Good clarifying question. Let me explain LeafExists concretely, then we'll come back to the G-3-Q7 selection.

What LeafExists means.

TxError::LeafExists { leaf_key: [u8; 33] } is the error variant returned when an EvaluateAndEmit op tries to emit a leaf at a leaf_key
that already exists in the JMT (or in the cycle's pending Phase 2 overlay). It's the runtime enforcement of Hard Invariant 17: "eval
emissions on 0x04 are immutable; first-emit wins."

Walk through the two scenarios:

Cross-tx-in-cycle (same cycle 100, two transactions):
tx1 (cycle 100): EvaluateAndEmit produces leaf_key 0x04 || K
→ applied; K now in cycle 100's Phase 2 overlay
tx2 (cycle 100): EvaluateAndEmit produces SAME leaf_key 0x04 || K
(because sender supplied identical
policy_id, addressing_input, params, nonce
across both tx1 and tx2)
→ operator's apply_eval sees overlay[K] exists
→ TxError::LeafExists { leaf_key: 0x04 || K }
→ tx2 fails; tx1 already succeeded (atomic per-tx)

Cross-cycle (cycle 100 committed, cycle 200 attempts re-emit):
Cycle 100: tx with eval producing leaf_key K → committed to JMT
... cycles 101..199 pass ...
Cycle 200: another tx with eval producing SAME leaf_key K
(sender reused same nonce — typically a bug:
they restarted SDK state and reset their nonce counter)
→ operator's apply_eval looks up JMT[K], finds it
→ TxError::LeafExists { leaf_key: K }
→ only the second tx fails; cycle 200 proceeds

Why it's needed and where it surfaces:

┌───────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Question │ Answer │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Where does the │ Inside apply_eval, after computing emit_leaf_key from (intent.addressing_input, intent.nonce), before writing │
│ operator check? │ to overlay │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ What does sender │ WriteResponse.transactions[i] = TransactionResult::Failed { tx_id, errors: vec![LeafExists { leaf_key }] } │
│ see? │ │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ How does sender │ Retry with a fresh nonce. SDK best practice: per-(sender_owner, policy_id) monotonic counter persisted to │
│ recover? │ durable storage │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ What's the │ Without this, an attacker who replays an older Authenticated envelope could overwrite a later eval result │
│ security │ with the older one. Immutability + LeafExists + content-addressed task_id together kill the replay class │
│ property? │ entirely │
└───────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

★ Insight ─────────────────────────────────────
Notice how LeafExists is the runtime mirror of the EvaluateAndEmit immutability guarantee. The G-3-Q6 lock declared "first-emit wins" as
policy; LeafExists is the mechanism. This pairing — declarative invariant + named runtime error — is how Newton makes invariants
debuggable: when a violation surfaces in production logs, the error name maps 1:1 to the spec invariant, and a sender reading the error
message can immediately find the relevant section. Compare with VersionConflict (G-3-Q2 → monotonic version) and OperationNotSupported
(G-2-Q5 revised → op-exclusivity) — same pattern.
─────────────────────────────────────────────────


Now back to G-3-Q7. You selected B (soft-delete via flag). Before I lock and apply edits, let me sanity-check the implications:

B implications I want to flag:

  1. apply_put accepts a tombstone-marked PutLeaf as a normal write (still version-monotonic).
  2. Reads return a typed LeafTombstoned { leaf_key, last_active_version } rather than the ciphertext.
  3. EvaluateAndEmit's input_refs validation: tombstoned leaf is "structurally present, semantically absent" — should apply_eval fail with
    TxError::InputLeafTombstoned, or silently treat as not-found, or pass through to Regorus and let the policy decide?
  4. Writing a fresh value AFTER tombstone is allowed (version-monotonic), so tombstone is reversible — is this the desired semantic, or
    should tombstone be terminal?

⏺ User answered Claude's questions:
⎿  · Confirm G-3-Q7 = soft-delete via flag, and how should EvaluateAndEmit.input_refs validation handle tombstoned leaves? → 1. also
for LeafExists question: with unique nonce, LeafExists error won't happen right? unless the SDK incorrectly submits the same nonce tx

⏺ Correct on both counts. Confirming and proceeding.

On LeafExists and nonce management:

Yes — with correct SDK nonce management, LeafExists should never fire in legitimate usage. It's a correctness fence, not an expected
operational error. Three distinct trigger paths:

┌─────────────────────────────────────────────┬──────────────────────────────────────────────────────────┬─────────────────────────┐
│ Trigger │ What it means │ Frequency │
├─────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼─────────────────────────┤
│ SDK supplies unique nonce per emission │ Flow correct → leaf_key always unique → no collision │ Never in production │
│ (monotonic counter or random u64) │ │ │
├─────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼─────────────────────────┤
451 // // (Sub-A 0x01/0x02/0x03)
452 // // 0x02..0xFF = reserved
453 -// byte 2 = flags (bitmap) // bit 0 = tombstone (G-3-Q7 lock TBD)
453 +// byte 2 = flags (bitmap) // bit 0 = tombstone (G-3-Q7 lock):
454 +// // set = leaf is soft-deleted;
455 +// // reads return LeafTombstoned;
456 +// // apply_eval rejects input_refs
457 +// // pointing at tombstoned leaves with
458 +// // TxError::InputLeafTombstoned;
459 +// // higher-version PutLeaf overwrites
460 +// // (tombstone is reversible).
461 // // bits 1..7 = reserved (must be 0)
462 // bytes 3..19 = reserved (17 bytes) // MUST be zero on write; V2 promotes
463 // to typed fields with concrete

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
640 15. LeafHeaderV1 is lean and reserved-heavy (G-3-Q4 lock). 20-byte fixed-size header carries only 3 load-bearing b
ytes (header_version, enc_scheme, flags); remaining 17 bytes are reserved and MUST be zero. Operator's apply_put
rejects non-zero reserved bytes as TxError::InvalidLeafHeader. V2 header bump (byte 0 = 0x02) claims reserved space f
or typed fields when concrete consumers materialize. No metadata tracking, no scheme-dependent payload, no defense-in-de
pth integrity check in V1 — Phase 1 MVP has no consumers for any of those.
641 16. Ciphertext format is enc_scheme-dispatched; HPKE binds leaf coordinates to AAD (G-3-Q5 lock). enc_scheme = 0x 00 → raw plaintext (no framing, used by Sub-B policy bytes). enc_scheme = 0x01 → RFC 9180 Base mode HPKE with DHKEM(X
25519, HKDF-SHA256) / HKDF-SHA256 / ChaCha20-Poly1305 (matching Newton's existing partial-DH suite). HPKE AAD = keccak2 56(leaf_key || version_le_bytes || header) — pins ciphertext to its JMT slot, prevents relocation/replay/header-tamperi
ng. Recipient reconstructs AAD entirely from JMT leaf data; no external state. Recipient pk sourcing deferred to per-nam
espace spec.
642 17. Each namespace is op-type-exclusive; EvaluateAndEmit emissions are immutable (G-2-Q5 op-exclusivity revision +
G-3-Q6 lock). Op→namespace mapping is a fixed dispatch table: PutLeaf-only namespaces (0x01/0x02/0x03/0x05) reject Eval
uateAndEmit ops as TxError::OperationNotSupported, and the EvaluateAndEmit-only namespace (0x04) rejects PutLeaf the s
ame way. Eval emissions on 0x04 are immutable: first-emit wins; subsequent emits to the same leaf_key reject as TxErr or::LeafExists (cross-tx-in-cycle and cross-cycle alike); same-tx duplicate rejects as TxError::SameTxLeafCollision.
Sender controls uniqueness via PolicyEvalIntent.nonce: u64 — two emissions from the same (policy_id, addressing_input on (=0x01), byte 1 = enc_scheme (0x00=plaintext, 0x01=HPKE-X25519-ChaCha20Poly1305, 0x02..0xFF reserved), byte 2 = f lags (bit 0 = tombstone, bits 1..7 reserved), bytes 3..19 = reserved (17B, MUST be zero). Decoder rejects non-zero rese
╭─── Claude Code v2.1.132 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │ What's new │
│ Welcome back Dennis! │ Added CLAUDE_CODE_SESSION_ID environment variable to the Bash tool subprocess … │
│ │ Added CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 env var to opt out of the fullscr… │
│ ▐▛███▜▌ │ Added a "Pasting…" footer hint while a Ctrl+V image paste is being read from the… │
│ ▝▜█████▛▘ │ /release-notes for more │
│ ▘▘ ▝▝ │ │
│ Opus 4.7 (1M context) with ma… · Claude API · │ │
│ Dennis Won │ │
│ ~/projects/newton-prover-avs │ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

✻ Conversation compacted (ctrl+o for history)

⎿  Referenced file docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md
⎿  Read ../../.claude/RTK.md (30 lines)
⎿  Read ../../CLAUDE.md (39 lines)
⎿  Read docs/CLAUDE.md (7 lines)
⎿  Skills restored (superpowers:brainstorming)

Read 1 file (ctrl+o to expand)

⏺ ★ Insight ─────────────────────────────────────
JMT (Jellyfish Merkle Tree) is intrinsically versioned — every leaf insert is associated with a version number, and every version has a
unique Merkle root. The question "when does the root recompute?" is really "what cadence advances the version counter, and which
versions get persisted vs which are ephemeral." This decision has direct cost implications: redb writes are durable I/O, so per-PutLeaf
versioning means N disk transactions per cycle while per-cycle versioning means one batched commit.

In streaming/blockchain systems, the "commit boundary" usually drives version cadence — Aptos commits per consensus round, Ethereum per
block. Newton's analogous boundary is the Layer 1 cycle (≤1s tick → BLS quorum → state root), not the transaction.
─────────────────────────────────────────────────

G-3-Q8 — JMT root-computation cadence

The question: when does the JMT advance its version (and produce a new root)?

Constraints from prior locks

┌─────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────┐
│ Locked decision │ Constraint on Q8 │
├─────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ D7 — Layer 1 (≤1s) + Layer 2 (~120s │ Every cycle must produce at least one root for BLS signing → state-commit │
│ on-chain) │ │
├─────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ D9 — transaction-level atomicity │ Tx ops either all apply or all reject — root must reflect tx boundaries cleanly │
├─────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ G-3-Q3 — two-phase per-tx execution │ Phase 2 evals snapshot (pre-cycle JMT ⊕ Phase 1 overlay) — overlay is in-memory, not │
│ │ JMT-persisted │
├─────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ G-3-Q2 — PutLeaf.version │ Leaf-version is decoupled from JMT-version (leaf's version is sender's MVCC, JMT's │
│ sender-supplied │ version is tree state) │
└─────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────┘

Options

A. Per-PutLeaf — each leaf insert advances JMT version

PutLeaf #1 → JMT v=k+1, root_1 (persisted to redb)
PutLeaf #2 → JMT v=k+2, root_2 (persisted to redb)
...
cycle-end → BLS-sign root_N

┌────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐
│ Pros │ Cons │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Simplest mental model — no overlay needed │ N redb-write transactions per cycle — durable I/O on hot path │
│ for Phase 2 reads │ │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Phase 2 reads against JMT @ version k+m │ N transient versions persisted; only cycle-end version matters for consensus │
│ directly via JMT API │ │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Read proofs available at every │ Snapshot management: Phase 2 must pin a specific version to avoid races with Phase 1 │
│ intermediate version │ of sibling tx (would need locks anyway) │
├────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ │ Still requires coordination across two-phase model (sibling tx's Phase 1 mustn't │
│ │ bleed into another tx's Phase 2 snapshot) │
└────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────┘

B. Per-tx — version advances once per applied transaction

tx_1 (3 PutLeafs + 1 Eval) → JMT v=k+1, root_1
tx_2 (1 PutLeaf)            → JMT v=k+2, root_2
...
cycle-end → BLS-sign root_M

┌────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│ Pros │ Cons │
├────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ Aligned with atomicity boundary │ Doesn't match commit cadence (BLS only signs cycle-end) │
│ (D9) │ │
├────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ Phase 1 of tx N+1 can read tx N's │ Conflicts with G-3-Q3: Phase 2 of tx N+1 snapshots "pre-cycle ⊕ overlay", NOT "post-tx-N │
│ writes via JMT │ JMT" — would have to redefine snapshot semantics │
├────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Persists M versions per cycle; only one matters │
└────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘

C. Per-cycle — version advances once at cycle-end (Recommended)

cycle starts: JMT @ version k, root_k (last committed)

for each tx in cycle:
  Phase 1 PutLeafs → in-memory PerTxEphemeralOverlay
  Phase 2 Evals    → read from (root_k JMT ⊕ Phase 1 overlay) snapshot
  on success: merge tx overlay into cycle-level WriteBatch
  on TxError: drop tx overlay, continue

cycle-end:
  JMT.batch_write(WriteBatch) → version k+1, root_{k+1}
  BLS-sign root_{k+1}
  submit StateCommit{prev=root_k, new=root_{k+1}, ...}

┌─────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────┐
│ Pros │ Cons │
├─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ One redb write transaction per cycle — amortizes I/O │ Phase 2 of sibling txs cannot read each other's Phase 1 writes │
│ │ (already enforced by G-3-Q3) │
├─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ Aligns with the only commit cadence that exists (BLS at │ Cross-tx-in-cycle composition requires multi-cycle (acceptable: D9 │
│ cycle-end) │ already chose this trade-off) │
├─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ Storage efficiency: 1 root per ~1s, not 1 root per leaf │ "Pre-cycle JMT" is a real materialized version (no version-pruning │
│ │ races) │
├─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ Snapshot for Phase 2 = JMT @ version k (immutable, no │ Requires WriteBatch type spanning all txs in cycle │
│ contention) │ │
├─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ Cleanest cancellation: a TxError just drops the per-tx │ │
│ overlay; nothing leaked into JMT │ │
└─────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘

Recommendation: Option C with the following sub-locks

  1. JMT version advances exactly once per cycle, at cycle-end commit.
  2. Per-tx ephemeral overlay (already in D9) is in-memory only; merged into a per-cycle WriteBatch on tx success, dropped on TxError.
  3. Phase 2 snapshot = (JMT @ version k, the immutable last-committed root) ⊕ (own-tx Phase 1 overlay). Sibling tx overlays are invisible
    — already enforced by G-3-Q3.
  4. BLS state-commit signs root_{k+1} exactly once per cycle. State-commit prev_state_root = root_k, new_state_root = root_{k+1}.
  5. Read proofs from newt_signedRead reference root_{k+1} for cycle-N reads, with a (cycle_id, jmt_version) tuple in the response so
    consumers can verify against the on-chain anchor.
  6. Read-your-writes within a tx is provided by the per-tx overlay (Phase 2 reads see Phase 1 writes); reads outside the tx (other
    operators, RPC consumers) only see committed cycles.

What this gives up (acceptable trade-offs)

  • Cross-tx-in-cycle reads: tx2 in cycle N cannot read tx1's writes from cycle N. Composition requires multi-cycle (cycle N writes →
    cycle N+1 reads). D9 already accepted this.
  • Per-leaf version proofs: reads always proof against the latest cycle root, never against intermediate "after PutLeaf #3 of tx 7"
    states. No consumer in Sub-A/B/C needs that granularity.

What this preserves

  • Phase 2 parallelism (G-3-Q3): all Phase 2 evals snapshot the same (root_k, own-overlay) pair, freeing them to run concurrently.
  • Tx atomicity (D9): a TxError drops the per-tx overlay before merging into the cycle WriteBatch — the JMT never sees a half-applied tx.
  • State-commit determinism: every operator computing the same root_{k+1} from the same (root_k, ordered tx batch) is the BLS-signing
    precondition. Per-cycle cadence makes this trivially aligned.

⏺ User answered Claude's questions:
⎿  · Lock G-3-Q8 — JMT root-computation cadence. Which option? → C — per-cycle (Recommended)

⏺ G-3-Q8 locked as Option C. Applying the cascading edits — I'll need to update the Open Questions entry, the Decision History, and
surface the per-cycle WriteBatch model where the spec describes operator-side execution and Phase 2 snapshot semantics. Let me read the
relevant sections first.

Read 1 file (ctrl+o to expand)
⎿  PreToolUse:Read hook error ⎿  Failed with non-blocking status code: [TokenEfficiency] Hard
rule violation: Re-reading
2026-05-07-pds-architecture-pivot-design.md (2x) — file
unchanged since last read. Consider using cached knowled…

⏺ Let me look at the Hard Invariants list and the operator execution flow description to find the right anchor points for the cascading
edits.

Read 1 file (ctrl+o to expand)
⎿  PreToolUse:Read hook error ⎿  Failed with non-blocking status code: [TokenEfficiency] Hard
rule violation: Re-reading
2026-05-07-pds-architecture-pivot-design.md (2x) — file
unchanged since last read. Consider using cached knowled…

⏺ Now I'll apply the four cascading edits for G-3-Q8: (1) mark the Open Questions entry locked, (2) add Hard Invariant 19, (3) add a
Decision History row, and (4) add a brief commentary block at the end of the G-3-Q3 schema comment so the per-cycle WriteBatch model is
visible inline with the two-phase execution narrative.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
724 15. G-3-Q5 — Ciphertext format binding. LOCKED 2026-05-08: enc_scheme = 0x00 → raw plaintext (Sub-B); enc _scheme = 0x01 → RFC 9180 HPKE Base mode, DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + ChaCha20-Poly1305 (matches Newton'
s existing partial-DH suite). HPKE AAD = keccak256(leaf_key || version_le || header) — pins to leaf coordinates; recip
ient reconstructs from JMT alone; no external state. Recipient pk sourcing deferred to per-namespace spec (Sub-A: EpochR
egistry MPK; Sub-C: TBD).
725 16. G-3-Q6 — Same-cycle leaf_key collision policy. LOCKED 2026-05-09: namespace op-exclusivity (G-2-Q5 rev
ised) collapses three of four cross-op-class collisions to structurally impossible (PutLeaf+EvaluateAndEmit can't share
a namespace). Remaining cases: (a) PutLeaf+PutLeaf same key in same tx → TxError::DuplicateLeafKey (version-monotonic
self-overwrite within a tx is pointless); (b) EvaluateAndEmit+EvaluateAndEmit same emit on 0x04 → within-tx rejects as TxError::SameTxLeafCollision, across-tx (cycle or beyond) rejects as TxError::LeafExists. Eval emissions are immutabl
e. Senders supply distinct PolicyEvalIntent.nonce for distinct emissions of otherwise-identical intents (motivating us
e case: amortize one policy evaluation into N single-use authorizations).
726 17. G-3-Q7 — Tombstone / deletion posture for Phase 1. LOCKED 2026-05-09: soft-delete via LeafHeaderV1.flag s bit 0. PutLeaf-class namespaces (0x01/0x02/0x03/0x05) use it for revocation; eval-class (0x04) doesn't (Invariant 17
— emissions are immutable). Reads return LeafTombstoned { leaf_key, last_active_version } instead of ciphertext. appl y_eval rejects input_refs pointing at tombstoned leaves as TxError::InputLeafTombstoned. Tombstone is *reversible
* — higher-version PutLeaf with the flag clear restores the leaf, version-monotonic rule (Invariant 13) still applies.
727 -18. G-3-Q8 — JMT root-computation cadence. Per-PutLeaf (every leaf rehashes the tree)? Per-tx? Per-cycle (one root p
-er Layer 1 tick)?
727 +18. G-3-Q8 — JMT root-computation cadence. LOCKED 2026-05-09: per-cycle. JMT version advances exactly once p
+er Layer 1 cycle, at cycle-end commit. Per-tx overlays (D9) are in-memory only; merged into a per-cycle WriteBatch on
+tx success, dropped on TxError. Phase 2 evals snapshot (JMT @ version k, the immutable last-committed root) ⊕ (own-tx + Phase 1 overlay) — sibling tx overlays are invisible. Cycle-end produces root_{k+1}; BLS signs that exactly once; st
+ate-commit binds prev=root_k, new=root_{k+1}. One redb-write per cycle (amortizes I/O); no transient versions persis
+ted; (cycle_id, jmt_version) returned with read responses for on-chain verification. Cross-tx-in-cycle composition def
+erred to multi-cycle (D9-accepted trade-off).
728 19. G-3-Q9 — Namespace creation flow. Governance proposal? Code-only enum extension? Runtime config registry? Couple
d to namespace addressing rule (Q5).
729
730 # Decision history

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
641 16. Ciphertext format is enc_scheme-dispatched; HPKE binds leaf coordinates to AAD (G-3-Q5 lock). enc_scheme = 0x 00 → raw plaintext (no framing, used by Sub-B policy bytes). enc_scheme = 0x01 → RFC 9180 Base mode HPKE with DHKEM(X
25519, HKDF-SHA256) / HKDF-SHA256 / ChaCha20-Poly1305 (matching Newton's existing partial-DH suite). HPKE AAD = keccak2 56(leaf_key || version_le_bytes || header) — pins ciphertext to its JMT slot, prevents relocation/replay/header-tamperi
ng. Recipient reconstructs AAD entirely from JMT leaf data; no external state. Recipient pk sourcing deferred to per-nam
espace spec.
642 17. Each namespace is op-type-exclusive; EvaluateAndEmit emissions are immutable (G-2-Q5 op-exclusivity revision +
G-3-Q6 lock). Op→namespace mapping is a fixed dispatch table: PutLeaf-only namespaces (0x01/0x02/0x03/0x05) reject Eval
uateAndEmit ops as TxError::OperationNotSupported, and the EvaluateAndEmit-only namespace (0x04) rejects PutLeaf the s
ame way. Eval emissions on 0x04 are immutable: first-emit wins; subsequent emits to the same leaf_key reject as TxErr or::LeafExists (cross-tx-in-cycle and cross-cycle alike); same-tx duplicate rejects as TxError::SameTxLeafCollision.
Sender controls uniqueness via PolicyEvalIntent.nonce: u64 — two emissions from the same (policy_id, addressing_input , params) require distinct nonces. The asymmetry (PutLeaf uses monotonic version; EvaluateAndEmit uses opaque nonce) re
flects op semantics: writes are mutable state, evals are immutable events.
643 18. Tombstones are soft-delete, reversible, and eval-fencing (G-3-Q7 lock). LeafHeaderV1.flags bit 0 set = leaf is
soft-deleted. PutLeaf-class namespaces (0x01/0x02/0x03/0x05) use this for revocation; EvaluateAndEmit-class (0x04) does
not (eval emissions are immutable per Invariant 17, so tombstone is meaningless). Read paths return a typed LeafTombst oned { leaf_key, last_active_version } instead of the ciphertext body — readers can distinguish "never written" from "e
xplicitly revoked." apply_eval validates input_refs: any ref pointing at a tombstoned leaf raises TxError::InputLea fTombstoned { leaf_key } and aborts the containing tx (preserves Sub-A revocation semantics — a policy cannot evaluate
against revoked input). Tombstone is reversible: a higher-version PutLeaf with the tombstone flag clear restores the lea
f. Version-monotonic rule (Invariant 13) still applies; tombstone-marked PutLeaf is a normal write that happens to set b
it 0.
644 +19. JMT version advances per cycle; per-tx overlays merge at cycle-end (G-3-Q8 lock). The JMT version counter advanc
+es exactly once per Layer 1 cycle. Within a cycle, each successful tx's two-phase overlay (Invariant 14) is merged into
+a cycle-level WriteBatch; tx-level failures (TxError::*) drop the overlay before merge. At cycle-end the operator ex
+ecutes JMT.batch_write(WriteBatch) once, materializing root_{k+1} from root_k. BLS signs exactly that root; the on
+-chain StateCommit binds prev_state_root = root_k, new_state_root = root_{k+1}. Phase 2 eval snapshots are formed
+from root_k (immutable, last-committed JMT version) ⊕ own-tx Phase 1 overlay — no sibling-tx visibility, no version-pr
+uning races. Read endpoints (newt_signedRead) return (cycle_id, jmt_version) alongside the leaf+proof so consumers v
+erify against the on-chain anchor. Storage cost is one redb write transaction per cycle (~1s cadence); transient intra-c
+ycle states never reach disk. Cross-operator determinism is preserved: every operator computing the same WriteBatch fr
+om the same canonically-ordered tx batch arrives at byte-identical root_{k+1}. The "register-policy then evaluate" com
+position (G-3-Q3) works because Phase 1 overlay flows through to Phase 2 within the same tx; cross-tx-in-cycle compositi
+on is the cycle-N → cycle-N+1 case (D9-accepted trade-off — single-tx covers the dominant Sub-A/B/C use cases).
645
646 # Cascade updates required
647

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
751 | 2026-05-09 | G-2-Q5 op-exclusivity revision — each namespace supports exactly one TxOperation variant (0x01/0x02/0x0
3/0x05 = PutLeaf-only, 0x04 = EvaluateAndEmit-only); mixing rejects as TxError::OperationNotSupported. Closes "forge t
ask response by direct write" attack on 0x04 and the symmetric "compute policy bytes via meta-policy" anti-pattern on 0x
05; structurally collapses cross-op-class same-leaf collisions to impossible | This document, grill round G-3 |
752 | 2026-05-09 | G-3-Q6 locked — eval emissions on 0x04 are immutable (first-emit wins; cross-tx-in-cycle and cross-cycle
re-emit reject as TxError::LeafExists); sender controls uniqueness via PolicyEvalIntent.nonce: u64; within-tx same-e
mit rejects as TxError::SameTxLeafCollision. PutLeaf uses monotonic version (mutable state) while EvaluateAndEmit uses
opaque nonce (immutable event) — asymmetry follows op semantics. Motivating use case: amortize one policy evaluation in
to N single-use authorizations consumed by N downstream actions | This document, grill round G-3 |
753 | 2026-05-09 | G-3-Q7 locked — soft-delete via LeafHeaderV1.flags bit 0; reads return LeafTombstoned { leaf_key, last _active_version }; apply_eval rejects tombstoned input_refs as TxError::InputLeafTombstoned; tombstone is reversi
ble (higher-version PutLeaf with flag clear restores). PutLeaf-class namespaces only (0x01/0x02/0x03/0x05); eval-class 0
x04 doesn't tombstone (emissions immutable per Invariant 17) | This document, grill round G-3 |
754 +| 2026-05-09 | G-3-Q8 locked — per-cycle JMT root computation: version advances exactly once per Layer 1 cycle at cycle-
+end; per-tx overlays (D9) merge into per-cycle WriteBatch on success, drop on TxError; one JMT.batch_write(WriteBat +ch) per cycle materializes root_{k+1}; BLS signs exactly that root; Phase 2 snapshots use root_k (immutable last-co
+mmitted) ⊕ own-tx overlay — no sibling visibility, no pruning races; read endpoints return (cycle_id, jmt_version) wit
+h leaf+proof; one redb-write per cycle amortizes I/O; cross-tx-in-cycle composition deferred to cycle-N→N+1 (D9-accepted
+) | This document, grill round G-3 |

⏺ Adding the cycle-end commit narrative inline with the schema so the WriteBatch model is visible alongside the two-phase execution
comment.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 20 lines
439 // declared operations Vec, not by completion order. Operators with different
440 // internal scheduling still produce byte-identical TxResult.
441
442 +// G-3-Q8 lock — Cycle-end commit pipeline (per-cycle JMT versioning).
443 +//
444 +// for each tx in cycle (canonical tx_id ASC order):
445 +// run Phase 1 PutLeafs into PerTxEphemeralOverlay
446 +// run Phase 2 EvaluateAndEmits against
447 +// snapshot(JMT @ root_k, tx.overlay)
448 +// if Ok: cycle_batch.merge(tx.overlay)
449 +// if Err: drop tx.overlay; record TxResult::Failed
450 +//
451 +// cycle-end:
452 +// JMT.batch_write(cycle_batch) -> version k+1, root_{k+1}
453 +// BLS partial-sign keccak256(StateCommit{prev=root_k, new=root_{k+1}, ...})
454 +// aggregator collects quorum, submits commitStateRoot
455 +//
456 +// JMT version advances exactly once per cycle (not per PutLeaf, not per tx).
457 +// One redb write transaction per ~1s cycle amortizes I/O; transient intra-
458 +// cycle states never reach disk. Read responses (newt_signedRead) include
459 +// (cycle_id, jmt_version) so consumers verify the proof against the on-chain
460 +// state-commit anchor for that exact version.
461 +
462 // G-3-Q4 lock — LeafHeaderV1 byte layout (lean, V1-only). The header is the
463 // decryption metadata visible at JMT-leaf granularity, parsed BEFORE the
464 // ciphertext body. Three bytes are load-bearing; the remaining 17 are

⏺ Verifying all four cascading edits landed consistently.

Searched for 1 pattern (ctrl+o to expand)

⏺ All four cascading edits landed:

  • Line 442–460: Inline G-3-Q8 commentary block in the schema (cycle-end commit pipeline pseudocode)
  • Line 664: Hard Invariant 19 — "JMT version advances per cycle; per-tx overlays merge at cycle-end"
  • Line 748: Open Questions list — G-3-Q8 marked LOCKED 2026-05-09
  • Line 774: Decision History — new row dated 2026-05-09

★ Insight ─────────────────────────────────────
Now that G-3-Q8 is locked, the operator-side execution pipeline has a complete shape: SDK → tx_id content-hash → gateway envelope →
operator two-phase per-tx execution (Phase 1 PutLeaf overlay → Phase 2 parallel eval against pre-cycle JMT ⊕ overlay) → cycle-level
WriteBatch merge → single JMT.batch_write → BLS partial-sign of root_{k+1} → aggregator collection → on-chain commitStateRoot. Every
primitive needed for this pipeline is now spec'd; the only remaining lock (G-3-Q9) is administrative — how new namespaces enter the
registry.

This convergence is what makes the "Sub-A is the platform; Sub-B/C are namespace prefixes" claim concrete: at the operator runtime
level, all three sub-projects share one execution loop, one persistence layer, one consensus boundary, one BLS commit per cycle.
─────────────────────────────────────────────────

G-3-Q9 — Namespace creation flow

This is the last open architectural question. The earlier locks have already constrained the answer surface significantly — let me walk
through what's already pinned and where the genuine choice lies.

What's already pinned by prior locks

┌─────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Constraint source │ Constraint on Q9 │
├─────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ G-2-Q5 (revised │ Each namespace registers a frozen derivation rule + op-class at creation. Changing it is a │
│ 2026-05-09) │ namespace-version bump (analogous to UnsupportedStateCommitVersion). │
├─────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Hard Invariant 17 │ Op-namespace exclusivity is a security boundary; cannot be reconfigured at runtime without adversarial │
│ │ implications. │
├─────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ D9 + G-3-Q3 │ Operators dispatch on the namespace prefix byte to select handler; the handler set must be known to all │
│ │ operators identically (cross-operator determinism, Invariant 11). │
├─────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Hard Invariant 1 │ Pre-mainnet — no migration logic. Whatever shape we pick must be the shape forever. │
└─────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

The frozen-rule semantics mean a namespace is closer to a chain ID than a database table — once live, it's permanent identity, not
configuration.

Three candidate flows

A. Code-only enum extension (Recommended)

// crates/state-tree/src/namespace.rs
#[repr(u8)]
pub enum NamespacePrefix {
Identity = 0x01,
Confidential = 0x02,
Secrets = 0x03,
TaskResponse = 0x04,
OnchainPolicy= 0x05,
// future namespaces append here
}

impl NamespacePrefix {
pub fn op_class(&self) -> OpClass { /* match / }
pub fn derive_leaf_key(&self, input: &Bytes) -> [u8; 33] { /
match */ }
}

┌───────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐
│ Pros │ Cons │
├───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Adding a namespace = code change → reviewed PR → coordinated operator │ Adding a namespace requires a network-wide │
│ upgrade. Same lifecycle as adding a new opcode to an EVM. │ upgrade window │
├───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Single source of truth in the operator binary; no runtime config drift across │ Cannot be done by governance vote alone — needs │
│ operators │ implementation work │
├───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Cross-operator determinism trivial: every operator running version V has the │ Slower iteration than runtime config — but the │
│ same enum. Operators on V+1 reject pre-V txs only if the namespace appeared │ frozen-rule invariant says we don't want fast │
│ in V+1 (no silent partition) │ iteration here │
├───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ Matches Solana's program-ID model and Cosmos's keeper-module model │ │
└───────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘

B. Governance proposal (on-chain registry)

contract NamespaceRegistry {
struct NamespaceConfig {
uint8 prefix;
OpClass opClass;
bytes32 derivationRuleHash; // commitment to off-chain Rust code
bool frozen;
}
function proposeNamespace(NamespaceConfig) external; // gov-only
function activateNamespace(uint8 prefix) external; // after timelock
}

┌──────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Pros │ Cons │
├──────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Namespace activation is │ The actual derivation logic still lives in operator binary (the registry can only commit to a hash) │
│ on-chain auditable │ │
├──────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Governance veto possible │ Operators must still upgrade binaries to recognize new prefixes — registry alone is insufficient │
├──────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Two sources of truth (registry + binary); drift is possible if not coordinated. Adds attack surface │
│ Clear formal lifecycle │ (compromised gov key adds malicious prefix? but binaries don't recognize it, so safe — but then why │
│ │ have it?) │
├──────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Forces a question: what does "namespace exists in registry but operator binary doesn't recognize it" │
│ │ mean? Likely just dead bytes. │
└──────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┘

C. Runtime config registry (gateway-config.json)

{
"namespaces": {
"0x06": {
"name": "MyNewThing",
"op_class": "PutLeaf",
"derivation_rule": "..."
}
}
}

┌─────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Pros │ Cons │
├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Fastest iteration — new │ Derivation rule cannot be expressed in JSON; needs code anyway │
│ namespace = config push │ │
├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Cross-operator drift: operators with different config files diverge on cross-namespace dispatch → │
│ │ BLS quorum fails silently (Invariant 11 violation surfacing as InvalidQuorumApkHash) │
├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Pre-mainnet "no live state" makes runtime flexibility unnecessary │
├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Operator drift → consensus partition is exactly the failure mode lessons.md "Operator-set caches │
│ │ must rebuild on every EpochAdvanced signal" warns against │
└─────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────┘

Recommendation: Option A — code-only enum extension

Why A wins decisively:

  1. Derivation rules ARE code. A namespace's leaf-key derivation rule is a function — (input: Bytes) → [u8; 33]. There's no
    JSON-expressible form. Any approach that pretends otherwise (B's derivationRuleHash, C's config field) is leaning on code regardless and
    just adding an indirection layer.
  2. Cross-operator determinism is binary-pinned. Invariant 11 (pre-cycle JMT state + canonical tx ordering = sufficient for
    cross-operator determinism) requires that "what is namespace 0x06" be unambiguous across operators. The cleanest way is "namespace 0x06
    exists in operator binary version V+; reject txs targeting unknown prefixes."
  3. Pre-mainnet flexibility cost is zero. Hard Invariant 1 (no migration logic) makes runtime flexibility worthless — every namespace
    decision is final at first-deploy time anyway. Adding a runtime registry to satisfy a "what if we need to add later" concern that the
    existing locks already handle (coordinated upgrades) is YAGNI.
  4. Failure mode is loud. An operator on V+1 receiving a tx with prefix 0x06 (added in V+1) processes it correctly; an operator on V
    receiving the same tx rejects with TxError::UnknownNamespace. The aggregator sees mixed responses → quorum fails → operators are forced
    to upgrade. This is the good kind of failure: visible, recoverable, no slashing risk because the tx never gets a quorum signature.
  5. Matches existing Newton patterns. EigenLayer middleware uses Solidity enums for operator-set kinds; Solana uses program IDs
    (compiled-in); Cosmos uses keeper modules registered at app-init time. None use runtime registries for protocol-level dispatch tables.

Sub-locks for Option A

  1. NamespacePrefix enum lives in crates/state-tree/src/namespace.rs (single source of truth shared by operators, gateway, SDK).
  2. Operators reject unknown namespace prefixes as TxError::UnknownNamespace — never silently process. This is the
    TxError::OperationNotSupported sibling for unrecognized prefixes (current variant covers known prefix + wrong op-class only).
  3. Adding a namespace requires coordinated network upgrade — release notes flag the new prefix, all operators upgrade, then the SDK can
    start emitting txs targeting it. Pre-upgrade SDK + post-upgrade operators is fine (operators reject pre-existing prefix logic only if
    the rule changed, which is forbidden by frozen-rule semantics).
  4. Namespace removal is not supported — once a prefix is allocated, it's permanent. (We can mark a namespace as deprecated with a
    binary-level rejection, but the prefix slot can never be reassigned.)
  5. Phase 1 ships with prefixes 0x01–0x05. Future Phase 2 additions (e.g., 0x06 task-response history, 0x07 Sub-D batched proofs) are
    deferred to their respective sub-project specs and require coordinated operator upgrades.

What this gives up

  • Ability to "soft-launch" new namespaces via config without binary redeploy. Acceptable: protocol-level dispatch should never be
    soft-launched, the security implications of getting it wrong are catastrophic.
  • Ability for governance to add namespaces unilaterally. Acceptable: the operators run the code, and operators control upgrade pace.
    Governance can propose prefix allocations in a separate process (e.g., a Newton Improvement Proposal) but the binary is the executable
    spec.
    ⏺ G-3-Q9 locked as Option A. This is the final architectural lock — applying the cascading edits, then I'll do the spec self-review pass
    before handing it back to you for review.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
746 16. G-3-Q6 — Same-cycle leaf_key collision policy. LOCKED 2026-05-09: namespace op-exclusivity (G-2-Q5 rev
ised) collapses three of four cross-op-class collisions to structurally impossible (PutLeaf+EvaluateAndEmit can't share
a namespace). Remaining cases: (a) PutLeaf+PutLeaf same key in same tx → TxError::DuplicateLeafKey (version-monotonic
self-overwrite within a tx is pointless); (b) EvaluateAndEmit+EvaluateAndEmit same emit on 0x04 → within-tx rejects as TxError::SameTxLeafCollision, across-tx (cycle or beyond) rejects as TxError::LeafExists. Eval emissions are immutabl
e. Senders supply distinct PolicyEvalIntent.nonce for distinct emissions of otherwise-identical intents (motivating us
e case: amortize one policy evaluation into N single-use authorizations).
747 17. G-3-Q7 — Tombstone / deletion posture for Phase 1. LOCKED 2026-05-09: soft-delete via LeafHeaderV1.flag s bit 0. PutLeaf-class namespaces (0x01/0x02/0x03/0x05) use it for revocation; eval-class (0x04) doesn't (Invariant 17
— emissions are immutable). Reads return LeafTombstoned { leaf_key, last_active_version } instead of ciphertext. appl y_eval rejects input_refs pointing at tombstoned leaves as TxError::InputLeafTombstoned. Tombstone is *reversible
* — higher-version PutLeaf with the flag clear restores the leaf, version-monotonic rule (Invariant 13) still applies.
748 18. G-3-Q8 — JMT root-computation cadence. LOCKED 2026-05-09: per-cycle. JMT version advances exactly once p
er Layer 1 cycle, at cycle-end commit. Per-tx overlays (D9) are in-memory only; merged into a per-cycle WriteBatch on
tx success, dropped on TxError. Phase 2 evals snapshot (JMT @ version k, the immutable last-committed root) ⊕ (own-tx Phase 1 overlay) — sibling tx overlays are invisible. Cycle-end produces root_{k+1}; BLS signs that exactly once; st
ate-commit binds prev=root_k, new=root_{k+1}. One redb-write per cycle (amortizes I/O); no transient versions persis
ted; (cycle_id, jmt_version) returned with read responses for on-chain verification. Cross-tx-in-cycle composition def
erred to multi-cycle (D9-accepted trade-off).
749 -19. G-3-Q9 — Namespace creation flow. Governance proposal? Code-only enum extension? Runtime config registry? Couple
-d to namespace addressing rule (Q5).
749 +19. G-3-Q9 — Namespace creation flow. LOCKED 2026-05-09: code-only NamespacePrefix enum in crates/state-t +ree/src/namespace.rs. Adding a namespace is a coordinated operator upgrade (binary release flagged in release notes; SD
+K starts emitting txs targeting the new prefix only after upgrade rolls out). Operators reject txs with unknown prefixes
+ as TxError::UnknownNamespace (loud failure → mixed quorum responses → forced upgrade). Namespace removal is not suppo
+rted; prefix slots are permanent. Governance proposes allocations via Newton Improvement Proposal process; the binary is
+ the executable spec. Matches Solana program-ID model and Cosmos keeper-module model. Phase 1 ships 0x01–0x05; future pr
+efixes (0x06+) deferred to their sub-project specs.
750
751 # Decision history
752

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
662 17. Each namespace is op-type-exclusive; EvaluateAndEmit emissions are immutable (G-2-Q5 op-exclusivity revision +
G-3-Q6 lock). Op→namespace mapping is a fixed dispatch table: PutLeaf-only namespaces (0x01/0x02/0x03/0x05) reject Eval
uateAndEmit ops as TxError::OperationNotSupported, and the EvaluateAndEmit-only namespace (0x04) rejects PutLeaf the s
ame way. Eval emissions on 0x04 are immutable: first-emit wins; subsequent emits to the same leaf_key reject as TxErr or::LeafExists (cross-tx-in-cycle and cross-cycle alike); same-tx duplicate rejects as TxError::SameTxLeafCollision.
Sender controls uniqueness via PolicyEvalIntent.nonce: u64 — two emissions from the same (policy_id, addressing_input , params) require distinct nonces. The asymmetry (PutLeaf uses monotonic version; EvaluateAndEmit uses opaque nonce) re
flects op semantics: writes are mutable state, evals are immutable events.
663 18. Tombstones are soft-delete, reversible, and eval-fencing (G-3-Q7 lock). LeafHeaderV1.flags bit 0 set = leaf is
soft-deleted. PutLeaf-class namespaces (0x01/0x02/0x03/0x05) use this for revocation; EvaluateAndEmit-class (0x04) does
not (eval emissions are immutable per Invariant 17, so tombstone is meaningless). Read paths return a typed LeafTombst oned { leaf_key, last_active_version } instead of the ciphertext body — readers can distinguish "never written" from "e
xplicitly revoked." apply_eval validates input_refs: any ref pointing at a tombstoned leaf raises TxError::InputLea fTombstoned { leaf_key } and aborts the containing tx (preserves Sub-A revocation semantics — a policy cannot evaluate
against revoked input). Tombstone is reversible: a higher-version PutLeaf with the tombstone flag clear restores the lea
f. Version-monotonic rule (Invariant 13) still applies; tombstone-marked PutLeaf is a normal write that happens to set b
it 0.
664 19. JMT version advances per cycle; per-tx overlays merge at cycle-end (G-3-Q8 lock). The JMT version counter advanc
es exactly once per Layer 1 cycle. Within a cycle, each successful tx's two-phase overlay (Invariant 14) is merged into
a cycle-level WriteBatch; tx-level failures (TxError::*) drop the overlay before merge. At cycle-end the operator ex
ecutes JMT.batch_write(WriteBatch) once, materializing root_{k+1} from root_k. BLS signs exactly that root; the on
-chain StateCommit binds prev_state_root = root_k, new_state_root = root_{k+1}. Phase 2 eval snapshots are formed
from root_k (immutable, last-committed JMT version) ⊕ own-tx Phase 1 overlay — no sibling-tx visibility, no version-pr
uning races. Read endpoints (newt_signedRead) return (cycle_id, jmt_version) alongside the leaf+proof so consumers v
erify against the on-chain anchor. Storage cost is one redb write transaction per cycle (~1s cadence); transient intra-c
ycle states never reach disk. Cross-operator determinism is preserved: every operator computing the same WriteBatch fr
668

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line
773 | 2026-05-09 | G-3-Q6 locked — eval emissions on 0x04 are immutable (first-emit wins; cross-tx-in-cycle and cross-cycle
re-emit reject as TxError::LeafExists); sender controls uniqueness via PolicyEvalIntent.nonce: u64; within-tx same-e
mit rejects as TxError::SameTxLeafCollision. PutLeaf uses monotonic version (mutable state) while EvaluateAndEmit uses
opaque nonce (immutable event) — asymmetry follows op semantics. Motivating use case: amortize one policy evaluation in
to N single-use authorizations consumed by N downstream actions | This document, grill round G-3 |
774 | 2026-05-09 | G-3-Q7 locked — soft-delete via LeafHeaderV1.flags bit 0; reads return LeafTombstoned { leaf_key, last _active_version }; apply_eval rejects tombstoned input_refs as TxError::InputLeafTombstoned; tombstone is reversi
ble (higher-version PutLeaf with flag clear restores). PutLeaf-class namespaces only (0x01/0x02/0x03/0x05); eval-class 0
x04 doesn't tombstone (emissions immutable per Invariant 17) | This document, grill round G-3 |
775 | 2026-05-09 | G-3-Q8 locked — per-cycle JMT root computation: version advances exactly once per Layer 1 cycle at cycle-
end; per-tx overlays (D9) merge into per-cycle WriteBatch on success, drop on TxError; one JMT.batch_write(WriteBat ch) per cycle materializes root_{k+1}; BLS signs exactly that root; Phase 2 snapshots use root_k (immutable last-co
mmitted) ⊕ own-tx overlay — no sibling visibility, no pruning races; read endpoints return (cycle_id, jmt_version) wit
h leaf+proof; one redb-write per cycle amortizes I/O; cross-tx-in-cycle composition deferred to cycle-N→N+1 (D9-accepted
) | This document, grill round G-3 |
776 +| 2026-05-09 | G-3-Q9 locked — code-only NamespacePrefix enum (crates/state-tree/src/namespace.rs) as single source
+of truth; adding a namespace = coordinated operator upgrade; unknown prefixes reject as TxError::UnknownNamespace (lou
+d failure surfaces version skew); namespace removal not supported (permanent identity); no on-chain registry, no runtime
+ config — derivation rules are code; matches Solana program-ID + Cosmos keeper-module patterns | This document, grill ro
+und G-3 |

⏺ All G-3 locks are in. Running the spec self-review pass now: placeholder scan, internal consistency check, scope check, ambiguity check.

Searched for 1 pattern, read 1 file (ctrl+o to expand)

⏺ These TBDs are legitimate Sub-C cross-spec deferrals (Sub-C owns its own key sourcing decision), but the wording should make scoping
unambiguous so future readers don't read "TBD" as a Sub-A gap. Tightening to "owned by Sub-C spec."

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 4 lines, removed 3 lines
538 // state lookup required. This matches Newton's "AAD must bind only what
539 // recipient can independently reconstruct" rule (partial_dh_aad pattern).
540 //
541 -// Recipient pk sourcing — deferred to per-namespace spec:
541 +// Recipient pk sourcing — owned by per-namespace spec, NOT a Sub-A gap:
542 // - Sub-A 0x01/0x02/0x03: EpochRegistry MPK (per privacy epoch)
543 -// - Sub-C 0x04: TBD per task model (likely per-task ephemeral)
544 -// - Sub-B 0x05: N/A (plaintext)
543 +// - Sub-C 0x04: owned by Sub-C spec (likely per-task ephemeral key
544 +// derived from task_id; see Sub-C settlement-design doc)
545 +// - Sub-B 0x05: N/A (enc_scheme = 0x00 plaintext)
546 //
547 // Suite evolution: future schemes claim enc_scheme bytes 0x02..0xFF.
548 // Reserve 0x02 = "HPKE-AES" (RFC 9180 with AES-128-GCM AEAD) for HW path.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
744 12. G-3-Q2 — JMT leaf version: u64 semantics. LOCKED 2026-05-08: sender-supplied + monotonic-per-leaf_key.
apply_put rejects supplied <= existing as TxError::VersionConflict { leaf_key, supplied, existing }. First write
requires version > 0. EvaluateAndEmit's emitted leaf is operator-versioned (existing + 1) since sender cannot pre-
compute the namespace-derived leaf_key. Provides MVCC, replay protection, optimistic concurrency in one mechanism.
745 13. G-3-Q3 — Read-after-write scope inside a tx. LOCKED 2026-05-08: two-phase per-tx execution. Phase 1 (Pu tLeafs) sequential into per-tx overlay; Phase 2 (EvaluateAndEmits) parallel against snapshot of (pre-cycle JMT ⊕ Pha se 1 overlay). Eval ops can read Phase 1 writes via input_refs (atomic "register-then-evaluate" composition); sibling
s cannot read each other's emits. Senders MUST declare PutLeaf-before-Eval; mixing rejects as TxError::OpOrderingViolat ion. Tail latency = max(eval_i) not sum(eval_i). Trade-off: eval→write→eval composition requires multi-tx.
746 14. G-3-Q4 — LeafHeaderV1: [u8; 20] byte layout. LOCKED 2026-05-08: lean V1 layout. byte 0 = header_versi on (=0x01), byte 1 = enc_scheme (0x00=plaintext, 0x01=HPKE-X25519-ChaCha20Poly1305, 0x02..0xFF reserved), byte 2 = f lags (bit 0 = tombstone, bits 1..7 reserved), bytes 3..19 = reserved (17B, MUST be zero). Decoder rejects non-zero rese
rved as TxError::InvalidLeafHeader. V2 header bump claims reserved bytes when consumers materialize. No metadata track
ing, no scheme-dependent payload, no defense-in-depth integrity check in V1.
747 -15. G-3-Q5 — Ciphertext format binding. LOCKED 2026-05-08: enc_scheme = 0x00 → raw plaintext (Sub-B); enc -_scheme = 0x01 → RFC 9180 HPKE Base mode, DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + ChaCha20-Poly1305 (matches Newton'
-s existing partial-DH suite). HPKE AAD = keccak256(leaf_key || version_le || header) — pins to leaf coordinates; recip
-ient reconstructs from JMT alone; no external state. Recipient pk sourcing deferred to per-namespace spec (Sub-A: EpochR
-egistry MPK; Sub-C: TBD).
747 +15. G-3-Q5 — Ciphertext format binding. LOCKED 2026-05-08: enc_scheme = 0x00 → raw plaintext (Sub-B); enc +_scheme = 0x01 → RFC 9180 HPKE Base mode, DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + ChaCha20-Poly1305 (matches Newton'
+s existing partial-DH suite). HPKE AAD = keccak256(leaf_key || version_le || header) — pins to leaf coordinates; recip
+ient reconstructs from JMT alone; no external state. Recipient pk sourcing is owned by per-namespace spec (Sub-A 0x01/0x
+02/0x03: EpochRegistry MPK; Sub-C 0x04: per Sub-C settlement-design doc; Sub-B 0x05: N/A — plaintext).
748 16. G-3-Q6 — Same-cycle leaf_key collision policy. LOCKED 2026-05-09: namespace op-exclusivity (G-2-Q5 rev
ised) collapses three of four cross-op-class collisions to structurally impossible (PutLeaf+EvaluateAndEmit can't share
a namespace). Remaining cases: (a) PutLeaf+PutLeaf same key in same tx → TxError::DuplicateLeafKey (version-monotonic
self-overwrite within a tx is pointless); (b) EvaluateAndEmit+EvaluateAndEmit same emit on 0x04 → within-tx rejects as TxError::SameTxLeafCollision, across-tx (cycle or beyond) rejects as TxError::LeafExists. Eval emissions are immutabl
e. Senders supply distinct PolicyEvalIntent.nonce for distinct emissions of otherwise-identical intents (motivating us
e case: amortize one policy evaluation into N single-use authorizations).
749 17. G-3-Q7 — Tombstone / deletion posture for Phase 1. LOCKED 2026-05-09: soft-delete via LeafHeaderV1.flag s bit 0. PutLeaf-class namespaces (0x01/0x02/0x03/0x05) use it for revocation; eval-class (0x04) doesn't (Invariant 17
— emissions are immutable). Reads return LeafTombstoned { leaf_key, last_active_version } instead of ciphertext. appl y_eval rejects input_refs pointing at tombstoned leaves as TxError::InputLeafTombstoned. Tombstone is *reversible
* — higher-version PutLeaf with the flag clear restores the leaf, version-monotonic rule (Invariant 13) still applies.
750 18. G-3-Q8 — JMT root-computation cadence. LOCKED 2026-05-09: per-cycle. JMT version advances exactly once p
er Layer 1 cycle, at cycle-end commit. Per-tx overlays (D9) are in-memory only; merged into a per-cycle WriteBatch on
tx success, dropped on TxError. Phase 2 evals snapshot (JMT @ version k, the immutable last-committed root) ⊕ (own-tx Phase 1 overlay) — sibling tx overlays are invisible. Cycle-end produces root_{k+1}; BLS signs that exactly once; st
ate-commit binds prev=root_k, new=root_{k+1}. One redb-write per cycle (amortizes I/O); no transient versions persis
ted; (cycle_id, jmt_version) returned with read responses for on-chain verification. Cross-tx-in-cycle composition def
erred to multi-cycle (D9-accepted trade-off).

⏺ Re-running placeholder scan to confirm clean:

Searched for 1 pattern (ctrl+o to expand)

⏺ Now consistency check — verifying the count and numbering of Hard Invariants matches Open Questions and Decision History entries.

Searched for 1 pattern (ctrl+o to expand)

⏺ Internal consistency verified — every G-3 Q-lock (1-9) maps to its Hard Invariant (12-20) and Decision History row. Let me confirm the
total Hard Invariant count:

⏺ Bash(grep -nE "^[0-9]+. **"
/Users/denniswon/projects/newton-prover-avs/docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md | sed -n '20,40p')
⎿  328:4. Latency budget — Regorus eval is ~1-50ms per policy; ~10 evals per cycle fits comfortably within the Layer 1 ≤1s budg
et alongside JMT root advance, BLS partial sign, and aggregator collection.
647:1. Pre-mainnet — no live state. Schema, struct, and protocol changes ship as-is on first mainnet deploy. No migration lo
… +71 lines (ctrl+o to expand)

⏺ One more ambiguity check — let me verify there are no duplicate or conflicting TxError::* variants across the spec.

Searched for 2 patterns (ctrl+o to expand)

⏺ Found a minor naming drift — line 334 says ExecutionTimeout but the locked variant is TxError::EvalTimeout. Also flags LeafInvalid as
the "existing family" but the actual error family is TxError::*. Tightening this for consistency.

⏺ Update(docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md)
⎿  Added 1 line, removed 1 line
331
332 - Operator write-handler complexity — spans validation + Regorus eval + leaf emission. Documented as a deliberate am
endment to prover-security in the implementation plan.
333 - Per-tx ephemeral overlay — operators maintain a per-tx scratchpad so writes from earlier ops in the same tx are vi
sible to later ops. Standard EVM-style pattern.
334 -- Failure modes multiplyPolicyExecutionFailed, InputLeafNotFound, ExecutionTimeout, OperationNotSupported
- join the existing LeafInvalid family.
334 +- Failure modes multiply — the TxError::* family (locked: TxIdMismatch, VersionConflict, OpOrderingViolation
+, InvalidLeafHeader, OperationNotSupported, DuplicateLeafKey, SameTxLeafCollision, LeafExists, InputLeafTombs +toned, EvalTimeout, UnknownNamespace, TargetNamespaceMismatch) is the canonical taxonomy. Sub-C's RegorusInlineH +andler will introduce additional Regorus-specific variants (PolicyExecutionFailed, InputLeafNotFound, etc.) when it
+ swaps in for the stub.
335
336 Hard invariants implied.
337

⏺ Self-review pass complete:

┌──────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────┐
│ Check │ Result │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Placeholder scan │ Clean — both prior TBD markers reframed as "owned by Sub-C spec" │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Hard Invariant numbering │ 1-20 contiguous, no gaps │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ G-3 Q-locks (Q1-Q9) │ All marked LOCKED in Open Questions list │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Decision History 2026-05-09 entries │ 5 rows (G-2-Q5 revision + G-3-Q6/Q7/Q8/Q9) — match invariants │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ TxError::* variant taxonomy │ 12 distinct, no duplicates; line 334 narrative reconciled with locked names │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Sub-A Phase 1 vs Phase 1 terminology │ Consistent — Sub-A Phase 1 ships during PDS Phase 1 │
└──────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────┘

★ Insight ─────────────────────────────────────
The spec is structurally complete: every architectural decision (D1-D9), every grill question (G-1, G-2-Q5–Q9, G-3-Q1–Q9), and every
Hard Invariant (1-20) is locked with explicit rationale and cross-reference. The TxError taxonomy is closed for Sub-A Phase 1; Sub-C
will extend it when it swaps in RegorusInlineHandler. The 20-invariant set forms a closed reasoning system — every pair I cross-checked
is internally consistent.

The spec lives at docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md (gitignored per project policy — local-only
artifact, not committed).
─────────────────────────────────────────────────

Ready for your review

Spec is at docs/superpowers/specs/2026-05-07-pds-architecture-pivot-design.md. All architectural decisions for the PDS pivot are now
locked through the G-3 grill series. Please review and let me know if you want to make any changes before we move to writing the
implementation plan.

The implementation plan would scope to Sub-A Phase 1 delivery:

  • crates/state-tree/src/namespace.rs (NamespacePrefix enum, frozen derivation rules)
  • WriteRequest / Transaction / TxOperation schema + WriteHandler trait
  • PerTxEphemeralOverlay scaffold + per-cycle WriteBatch cycle-end commit pipeline
  • TxError::* taxonomy
  • DefaultHandler stub returning OperationNotSupported for evals
  • Operator-side namespace dispatch (op-class enforcement)
  • HPKE leaf-coordinate AAD wiring (Sub-A namespaces 0x01/0x02/0x03 use EpochRegistry MPK)
  • Cascade doc updates per the "Cascade updates required" section

Sub-B and Sub-C are explicitly out of scope for this plan — they swap their handlers in via the WriteHandler constructor injection point
that Sub-A Phase 1 ships.

✻ Cogitated for 20m 14s

❯ so how long is the cycle? and how many transactions can be in one cycle? what is the maximum performance/throughput that we can
achieve? The requirement is each task evaluation request to be evaluated and returned back to the user to use the attestation to
actually execute the intent in sub-second latency (max 2 seconds)

⏺ Excellent question — this is the SLA gravity check that exposes whether the locked architecture can actually meet the requirement. Let
me walk through what the spec pins, what it implies for throughput, and where there's a gap that needs an additional lock.

★ Insight ─────────────────────────────────────
There are TWO BLS aggregation flows in the locked architecture and they have very different latency profiles. The spec discusses both
but doesn't crisply separate them. Conflating them is the most common reading error in this design, and it's what makes "sub-second SLA"
sound dubious when the spec mentions "120s on-chain commit." The user-visible task attestation is NOT the on-chain state commit — it's
a per-task quorum signature delivered via the existing TaskManager flow.
─────────────────────────────────────────────────

Two BLS aggregation flows, two cadences

┌────────────────┬─────────────────────────────────────────────────┬──────────────────────────────────┬─────────────────────────────┐
│ Flow │ What it signs │ Cadence │ User-facing? │
├────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Per-task │ keccak256(EvaluateAndEmit result) — the leaf │ Triggered per task; aggregator │ YES — this is what the user │
│ attestation │ the user consumes │ returns when quorum reached │ uses on-chain to execute │
│ │ │ │ the intent │
├────────────────┼─────────────────────────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Per-cycle │ keccak256(abi.encode(StateCommit{prev, new, │ ~120s on-chain (configurable per │ NO — this is the │
│ state-commit │ ...})) — the JMT root anchor │ chain: L1 30s aggressive / 120s │ slashing/audit anchor, │
│ │ │ default; L2 10s/30s) │ async to user latency │
└────────────────┴─────────────────────────────────────────────────┴──────────────────────────────────┴─────────────────────────────┘

The user's 2-second SLA is bounded by the per-task flow, NOT the state-commit flow. The state-commit is happening in the background to
anchor the JMT root for slashing-eligibility — it doesn't gate the user-visible attestation.

Locked numbers from the spec

┌───────────────────────────────────────────┬────────────────────────────────┬─────────────────────────────────────────────────────┐
│ Number │ Value │ Source │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Layer 1 cycle (operator-local + BLS │ ≤1s target │ D7 / Invariant 4 │
│ state-commit quorum) │ │ │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Per-eval-op wall-clock budget │ 200ms default │ G-2-Q8 lock │
│ │ │ (state_commit.per_chain[id].eval_timeout_ms) │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Aggregator collection deadline │ ≤2s │ Hard Invariant 4 │
│ (state-commit) │ │ │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Regorus eval cost (typical) │ 1-50ms │ D9 rationale point 4 │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Operator response budget │ 4s hard cap, 1s target │ Invariant 3 │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ Phase 2 evals per tx │ parallel-snapshot, tail = │ G-3-Q3 lock │
│ │ max(eval_i) │ │
├───────────────────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────────┤
│ State-commit on-chain cadence (L1) │ 30s default, 12s aggressive │ Invariant 3 │
└───────────────────────────────────────────┴────────────────────────────────┴─────────────────────────────────────────────────────┘

End-to-end per-task latency budget

Walking the hot path for a single task evaluation:

SDK builds intent + tx_id ~5ms (content-hash + bincode)
SDK → Gateway (HTTP/RPC) ~10ms (datacenter round-trip)
Gateway envelope + auth check ~5ms
Gateway → all operators (parallel) ~10ms (fanout)
Per operator:
Phase 1 PutLeafs to overlay ~5ms (typically 0 for pure-eval task)
Phase 2 Regorus eval ~10ms (typical; up to 200ms cap)
BLS partial sign over response ~3ms
Operators → Aggregator ~10ms
Aggregator quorum collection ~30-100ms (waits for 2/3+ stake)
Aggregator → Gateway → SDK ~15ms
─────────────────────────────────────────────
Happy-path total ~100-150ms
With straggler operator (worst-case
one operator hits 200ms cap) ~200-250ms
With network jitter / partial failures ~300-500ms
Hard cap (existing Invariant) 2s

Sub-second is achievable on the happy path; sub-2-second is the hard cap.

Throughput per operator

Within a cycle (≤1s), an operator can process N transactions in parallel because:

  • Within-namespace independence (D9 invariant): ops in same namespace are unordered, parallelizable
  • Cross-tx independence: per Hard Invariant 14, "siblings cannot read each other's emits" — txs in the same cycle never have data
    dependencies on each other
  • Phase 2 parallelism: per Invariant 14, "tail latency for N parallel evals is max(eval_i), not sum(eval_i)"

Operator throughput estimate (16-core production node):

Eval-thread pool 16 concurrent evaluations
Typical eval cost 10ms (Regorus, simple policy)
──────
Per-second throughput ~1,600 evals/sec (one operator)

But end-to-end is bounded by:
- Network bandwidth (RPC fanout)
- BLS partial signing rate (~3ms per sig with rust-bls-bn254)
- Aggregator's quorum collection scheduler

Realistic sustained throughput per operator ~500-1,000 tasks/sec
With more complex policies (50ms eval) ~200-400 tasks/sec

Per-cycle (≤1s) capacity: at ~500 tasks/sec sustained, one cycle holds ~500 transactions per operator, or higher for simpler policies.

Gateway-side bottleneck

The gateway is the fan-out bottleneck:

Gateway responsibilities:
- Auth / API key check ~1ms
- Authenticated envelope build ~2ms (bincode + signature)
- Fan-out to N operators parallel network sends
- Receive from aggregator waits for quorum cert
- Return to SDK ~5ms

Pipelining (Tokio async): hundreds of in-flight requests per gateway worker.
With 8-core gateway → ~5,000-10,000 RPS for envelope+fanout overhead alone.
End-to-end task throughput ~1,000-3,000 tasks/sec/gateway.

Multi-gateway (Phase 2 federated): linearly scales — N gateways × per-gateway throughput.

Where the spec has a GAP — needs an additional lock

The spec heavily details the per-cycle state-commit BLS aggregation, but does NOT explicitly nail down:

  1. The per-task BLS attestation flow in the D9 transaction model — is the BLS partial signature over EvaluateAndEmit.result emitted
    immediately upon evaluation (the existing TaskManager pattern), or batched into the per-cycle TxResult set?
  2. Per-task aggregator deadline — Invariant 4 specifies ≤2s for state-commit; no explicit deadline for per-task attestation. The user's
    SLA implies it must match (≤2s hard cap, sub-second target).
  3. Whether quorum must agree on the EvaluateAndEmit leaf_key (deterministic) AND the emitted ciphertext (also deterministic) — both are
    derived from canonical inputs per Invariant 11, so should match across operators byte-for-byte. But the current spec doesn't explicitly
    say "operators BLS-sign the per-tx TxResult."

This is the natural G-4 grill — locking the per-task attestation cadence and its relationship to the cycle-bound state-commit.

Recommendation

The 2-second SLA is achievable with the locked architecture, but I want to lock one more thing explicitly before writing the
implementation plan:

⏺ User answered Claude's questions:
⎿  · How should we handle the per-task attestation cadence relative to cycle-bound state-commit? → Lock now as G-4-Q1 (Recommended)

⏺ Locking as G-4-Q1. Let me lay out the full proposed lock with the three coupled sub-decisions, present my recommendation, and get your
sign-off before applying.

★ Insight ─────────────────────────────────────
The reason this needs to be locked now (not deferred) is that the per-task BLS digest shape determines what bytes operators must agree
on — and that determines the operator-side write-handler interface that Sub-A Phase 1 ships. Get the digest shape wrong and Sub-C's
RegorusInlineHandler won't compose with Sub-A's per-tx execution. Worth 5 minutes now to prevent a cascading refactor later.
─────────────────────────────────────────────────

G-4-Q1 — Per-task attestation cadence, digest, and deadline

Three coupled sub-decisions, presented together because they bind:

Sub-decision 1.1 — What does the per-task BLS sign over?

The cleanest digest is keccak256(canonical_bincode(TxResult)) where TxResult is:

struct TxResult {
tx_id: B256,
cycle_id: u64,
emits: Vec, // ordered by op index (G-3-Q3 lock)
}

struct EmittedLeaf {
leaf_key: [u8; 33], // namespace prefix + content key
version: u64,
header: [u8; 20], // LeafHeaderV1
ciphertext: Vec,
}

PutLeaf-only txs have empty emits; pure-eval txs have one entry per EvaluateAndEmit. The signature covers ONLY emit-bearing data —
PutLeaf payloads are JMT-anchored at cycle-end and don't need per-task attestation (the user already sent those bytes; they don't need
them signed back).

Why this digest shape:

  • Cycle-id binding prevents cross-cycle replay.
  • Ordered emits guarantees byte-identical TxResult across operators (G-3-Q3 lock — Phase 2 emits are ordered by op index, not completion
    order).
  • Bincode-canonical keeps the same encoding family as tx_id (G-3-Q1 lock) and Authenticated envelopes (D9), so verification code is
    unified.
  • Excludes PutLeaf payload bytes keeps per-task signature size bounded (only emits matter for user attestation; storage writes are
    anchored separately by state-commit).

Sub-decision 1.2 — When is BLS aggregation triggered?

Per-task, immediately upon operator Phase 2 completion (NOT batched per-cycle):

operator timeline within one cycle:
receive tx → execute Phase 1 → execute Phase 2

└── compute TxResult

├── BLS partial-sign keccak256(bincode(TxResult))
│ │
│ └── send partial to aggregator
│ │
│ └── aggregator collects quorum
│ │
│ └── returns cert to user
│ (~100-500ms after submit)

└── add to per-cycle WriteBatch

... rest of cycle ... │

cycle-end: batch_write(WriteBatch) ────────────────────────────────┤

BLS partial-sign StateCommit{prev, new, ...} │
│ │
└── separate aggregator collects → commitStateRoot │

(state-commit ~1-2s + 120s on-chain)

Per-task attestation is independent of cycle-end commit. The two flows share the same operator set and BLS keys but sign different
digests over different cadences.

Why per-task, not per-cycle batch:

  • User-visible latency ≤ network round-trip + max(eval_i) + quorum collection. Sub-second achievable.
  • Per-cycle batch would force users to wait for cycle close (≤1s) PLUS quorum collection (~100-500ms) PLUS one extra round of
    operator-aggregator messaging. Adds 100-500ms to every task.
  • Per-task aggregator can pipeline many tasks in parallel (Tokio async); per-cycle batch is a synchronization point.

Why this preserves Invariant 11 cross-operator determinism:

  • All operators with same pre-cycle state + same canonical tx batch + same ordering compute byte-identical TxResult (per G-3-Q3 lock).
  • Therefore all per-task BLS partials sign the same digest → quorum trivially met → no fork risk.

Sub-decision 1.3 — Per-task aggregator deadline

≤1s default, ≤2s hard cap (per-chain configurable via gateway.task_attestation.per_chain[id].deadline_ms):

┌─────────────────┬─────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Setting │ Value │ Rationale │
├─────────────────┼─────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Target │ sub-second (sub-500ms typical) │ User experience; aggressive cadence chains (Layer 1 ≤1s) │
│ │ │ match this │
├─────────────────┼─────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Configured │ 1000ms │ Matches existing Aggregator collection deadline ≤2s │
│ default │ │ relaxed to per-chain │
├─────────────────┼─────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Hard cap │ 2000ms │ Matches user's stated SLA + Invariant 4 │
├─────────────────┼─────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Quorum │ reuses operator set's stake-weighted threshold │ No new threshold concept │
│ threshold │ (existing pattern) │ │
└─────────────────┴─────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────┘

If quorum not reached within deadline → return TaskAttestationTimeout error; SDK retries OR escalates. (This is the same behavior as
today's QuorumNotReached; no new code path.)

Hard Invariant 21 (proposed)

▎ Per-task attestation is decoupled from per-cycle state-commit; both flows share operator set + BLS keys but sign distinct digests over
▎ distinct cadences (G-4-Q1 lock). Per-task BLS signs keccak256(canonical_bincode(TxResult)) immediately upon operator Phase 2
▎ completion; aggregator collects per-task quorum and returns cert to user (target sub-second; hard cap ≤2s configurable via
▎ gateway.task_attestation.per_chain[id].deadline_ms). Per-cycle BLS signs keccak256(abi.encode(StateCommit{prev, new, ...})) after
▎ cycle-end batch_write; submitted on-chain at the chain's state-commit cadence (Invariant 3). Cross-operator agreement on TxResult
▎ bytes (per Invariants 11 + 14) implies all operators sign the same per-task digest AND the same state-commit digest — quorum trivially
▎ met for both. PutLeaf payloads are NOT included in per-task TxResult digest (only emits); storage writes are anchored solely by
▎ per-cycle state-commit.

Throughput implications, locked

With this decoupling:

┌─────────────────────────────────────────────────┬─────────────────────────────────────────────┐
│ Metric │ Locked value │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Per-task end-to-end happy-path latency │ ~100-200ms │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Per-task hard-cap latency │ 2s (matches user SLA) │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Per-operator sustained throughput (10ms eval) │ ~500-1,000 tasks/sec │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Per-operator sustained throughput (50ms eval) │ ~200-400 tasks/sec │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Per-cycle transactions (≤1s, parallel exec) │ ~500-1,000 (typical eval) │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ State-commit on-chain cadence (parallel, async) │ 30s default L1 / 12s aggressive │
├─────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ Multi-gateway scaling (Phase 2) │ linear; N gateways × per-gateway throughput │
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
☐ G-4-Q1 lock

Confirm the G-4-Q1 lock for per-task attestation cadence?

❯ 1. Yes — lock as proposed (Recommended)
Per-task BLS signs TxResult bincode digest; immediate aggregation upon Phase 2 completion; ≤1s default deadline / ≤2s hard cap;
decoupled from per-cycle state-commit. Add Hard Invariant 21 + Decision History row + Open Questions entry.
2. Lock with deadline tightened to 500ms default
Same as Recommended but with aggressive sub-second target enforced as default. Risk: legitimate slow operators (busy node, transient
GC) may be excluded from quorum more frequently.
3. Lock structure but defer deadline to per-chain config (no Hard Invariant cap)
Same digest + flow lock but treat the deadline as pure config (no Hard Invariant constraint). User's 2s SLA becomes a configuration
choice not an architectural invariant.
4. Type something.
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
5. Chat about this

@denniswon

Copy link
Copy Markdown
Author
⏺ Re-sending the full throughput / cadence answer:

Cycle Duration

There are two distinct cycles running concurrently — this distinction is what motivated G-4-Q1:

┌────────────────────────┬──────────────────┬───────────────────────────────────────┬──────────────────────────────────────────────┐
│ Cycle │ Duration │ Purpose │ What signs │
├────────────────────────┼──────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────┤
│ Layer 1 — Per-task │ ≤1s typical, 2s │ User-facing SLA: returns BLS cert so │ keccak256(canonical_bincode(TxResult)) │
│ attestation │ hard cap │ user can execute intent │ │
├────────────────────────┼──────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────┤
│ Layer 2 — Per-cycle │ ~120s │ On-chain slashing anchor; advances │ keccak256(abi.encode(StateCommit{prev, new, │
│ state-commit │ │ JMT version once │ ...})) │
└────────────────────────┴──────────────────┴───────────────────────────────────────┴──────────────────────────────────────────────┘

The "cycle" in G-3-Q8 lock = Layer 2 (120s). JMT advances version once per 120s, batching all tx WriteBatches between commits. Per-task
attestation does NOT wait for the on-chain anchor.

★ Insight ─────────────────────────────────────
The two-layer model exists because the on-chain commit cost (gas + L1/L2 confirmation) cannot fit a sub-second SLA, but cross-operator
determinism (Invariants 11+14) means every operator computes the same TxResult for the same tx — so per-task BLS aggregation is just
signing already-deterministic bytes. The 120s state-commit anchors the JMT root retrospectively for slashing; the per-task cert is what
the user uses to execute the intent.
─────────────────────────────────────────────────

Transactions Per Cycle (120s)

Per-operator throughput × 120s = transactions per cycle. The actual ceiling depends on dominant eval cost:

┌────────────────────────────────────────────────┬────────────────────────┬────────────────┐
│ Eval cost (per tx) │ Per-operator sustained │ Per 120s cycle │
├────────────────────────────────────────────────┼────────────────────────┼────────────────┤
│ 10ms (simple Rego policies) │ 500–1,000 tx/sec │ 60,000–120,000 │
├────────────────────────────────────────────────┼────────────────────────┼────────────────┤
│ 50ms (complex policies, multiple data.* reads) │ 200–400 tx/sec │ 24,000–48,000 │
├────────────────────────────────────────────────┼────────────────────────┼────────────────┤
│ 200ms (heavy crypto / fanout) │ 50–100 tx/sec │ 6,000–12,000 │
└────────────────────────────────────────────────┴────────────────────────┴────────────────┘

Bottlenecks beyond raw eval:

  • Aggregator BLS aggregation rate: ~low-thousands partial sigs/sec per aggregator → not the bottleneck for ≤1k tx/sec
  • redb I/O: one batch_write per 120s cycle (G-3-Q8 lock) amortizes to negligible per-tx
  • HPKE encryption: ~50-100µs per leaf payload (libsodium tuned), parallelizable per-tx
  • Network round-trip operator↔aggregator: ~10-50ms typical, dominates BLS sign time

Maximum Throughput

┌────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Layer │ Bound │
├────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Per-operator │ 500-1,000 tx/sec (10ms eval, 16-core node, parallelizable Phase 2) │
├────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Cluster │ min(per-operator-rates) — every operator signs every task, so throughput is the slowest │
│ │ operator, not the sum │
├────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ BLS aggregation │ Aggregator handles thousands of partials/sec → not the cluster bottleneck │
├────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Per-cycle (120s) absolute │ ~120,000 tx at 1k tx/sec sustained │
│ ceiling │ │
└────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────┘

Sub-Second SLA Path (Happy Path)

End-to-end latency budget for a single task evaluation request:

Gateway receives request t = 0
↓ Forward to operator(s)
Operator runs Phase 1 (PutLeafs into overlay) t ≈ 1-5ms

Operator runs Phase 2 (Rego eval, EmitLeafs) t ≈ 10-50ms (eval cost)

Operator computes TxResult, BLS partial-sign t ≈ +1ms
↓ Send to aggregator
Aggregator collects quorum partial sigs t ≈ +50-200ms (network)

Aggregator BLS-aggregates t ≈ +5ms
↓ Return to gateway
Gateway returns cert to user t ≈ +10-50ms
────────────
≈ 80-310ms typical

2s hard cap accounts for: slow operators (tail latency), network jitter, RPC retries, aggregator quorum-wait timeout. If quorum doesn't
reach in 2s, gateway returns timeout error — task can be retried or deadlined out.

Why This Lock Made G-4-Q1 Necessary

The spec already had per-cycle BLS state-commit nailed down (Hard Invariants 19, 20). It did NOT explicitly nail per-task attestation
under the new D9 transaction model. Sub-second SLA forces:

  1. Per-task BLS aggregation must NOT wait for cycle-end (would push latency to 120s)
  2. Digest must be deterministic across operators (Invariants 11, 14) so partial sigs aggregate
  3. TxResult shape must be locked so canonical encoding is unambiguous

That's the three sub-decisions G-4-Q1 wraps. Now — back to your clarification: which axis of G-4-Q1 do you want to probe?

✻ Baked for 1m 20s

❯ help me understand this: while all other operations other policy evaluation task is "fast", policy evaluation is intrinsically slow:
it runs rego policy (inside TEE wasm if private data is involved + two-phase consensus for threshold decryption) / potentially also
wasm for policy data generation (inside TEE enclave if secrets are involved). This seems to be the fundamental bottleneck of low
latency, high throughput policy evaluation. The problems I see: 1) putting putleafs and evalauteandemit in the same path/transaction or
cycle seems we should more carefully evaluate this design. maybe we should separate policy data generation (wasm execution) and rego
evaluation as two separate operations that are batched/linked? unless is there a way to remove "two-phase" consensus, which was
introduced because policy data could produce numeric data and there could be small discrepancies across operations' wasm execution
results (rounding errors, etc.), by making all wasm executions to be deterministic. or maybe we can remove wasm entirely if there is a
way for rego policies to get supplied with outside external data for policy evaluation (which is the wasm's purpose in the first place,
to run some arbitrary process/code to generate some data with flexibility for policy evaluation) 2. what if the wasm execution for
policy data generation itself takes a long time and that slows down the entire transaction increasing the max(tx latency) and slows
down the process? /clarify-plan

⏺ ★ Insight ─────────────────────────────────────
You've identified the cleanest structural tension in the current design: the two-phase per-tx model (D9) treats state mutation and
policy evaluation as a single atomic unit, but they have fundamentally different latency characteristics. State mutation is fast (memory
write + redb batch), but policy evaluation can be 10ms–10s+ depending on WASM cost, threshold decryption rounds, and Rego complexity.
Coupling them forces the slow path to dominate cycle scheduling and per-tx attestation budgets.
─────────────────────────────────────────────────

Let me walk through your three concerns in order, because they're each correct in different ways.

Where the Real Coupling Lives

The current D9 model couples PutLeaf + EvaluateAndEmit at the per-tx level, but parallel-snapshot Phase 2 means slow tx don't block fast
tx. The real coupling is more subtle:

┌─────────────────────────────────┬─────────────────────────┬──────────────────────────────────────────────────────────────────────┐
│ Concern │ Coupled today? │ Why │
├─────────────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────┤
│ Slow tx blocks fast tx │ No │ Phase 2 parallel-snapshot — each tx evaluates independently against │
│ │ │ (JMT @ root_k, own-tx overlay) │
├─────────────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────┤
│ Slow tx blocks cycle-end commit │ Maybe — depends on │ If cycle-end waits for all in-flight tx, yes; if cycle-end is fixed │
│ │ cycle-end semantics │ wall-clock and defers in-flight to next cycle, no │
├─────────────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────┤
│ Slow tx blocks own attestation │ Yes │ Per-task TxResult digest only signs after Phase 2 completes — so │
│ │ │ slow Phase 2 = slow attestation │
├─────────────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────┤
│ WASM non-determinism forces │ Yes (today) │ Median/modal aggregation in Prepare/Commit handles operator output │
│ two-phase consensus │ │ drift │
└─────────────────────────────────┴─────────────────────────┴──────────────────────────────────────────────────────────────────────┘

Your point #1 is asking whether the per-tx coupling is the right model when most txs aren't mixed (PutLeaf vs Eval are usually
disjoint). Your point #2 is asking whether tail latency on a single slow WASM gates the whole thing.

Concern 1.a — Separating Policy Data Generation from Rego Evaluation

The proposal: split policy_data_gen(WASM) → rego_eval(Rego) into two separate ops that are batched/linked, rather than fused into one
tx's Phase 2.

This already half-exists in Newton via the policy_data registry pattern:

[once per policy_data refresh] [per task evaluation]
WASM(raw_inputs) → policy_data Rego(intent, policy_data, identity, ...)
Sign + register policy_data at version V Read policy_data @ version V
Anchor to JMT (immutable until next refresh) Run eval
Emit attestation
↑ Slow path, runs offline ↑ Fast path, runs per-task

Why this works: WASM execution is moved out of the hot eval path. All operators read the same anchored policy_data deterministically.
Rego itself is fast (10-50ms typical) because it's pure logic over already-fetched data.

The gap right now: the spec doesn't explicitly forbid inline WASM in Phase 2. If a policy's WASM runs at task-eval time (not
pre-cached), you get exactly the latency problem you're describing.

★ Insight ─────────────────────────────────────
The cleanest version of "separate WASM from Rego" is to disallow inline WASM in Phase 2 entirely for the task namespace (0x04). WASM
only runs in policy_data registration paths, which are NOT in the per-task hot path. Per-task eval becomes pure Rego over pre-anchored
state — sub-second by construction. This is the architectural decision that needs explicit locking.
─────────────────────────────────────────────────

Concern 1.b — Making WASM Deterministic to Drop Two-Phase Consensus

You're right that two-phase consensus exists primarily because WASM outputs can drift across operators. The drift sources:

┌───────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Source │ Deterministic if... │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Floating-point │ WASM spec mandates IEEE 754 — already deterministic if all operators use same wasmtime version │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Time / Random │ Disallow clock_time_get / random_get from imports — pure compute │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Network I/O │ Disallow fd_read / sockets — no ambient I/O │
├───────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ HTTP fetch │ Replicate-fetch (each operator fetches independently and votes) OR pre-attest (one fetch, signed feed, all │
│ results │ operators read) │
└───────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

If we constrain WASM to strict deterministic mode (no syscalls, no I/O, no clock, no random, pinned wasmtime version), then for the same
input bytes every operator produces the same output bytes. Two-phase median aggregation becomes redundant.

Proposed lock: All WASM in PDS Phase 1 must be deterministic. Outputs must be bytewise-identical across operators. One-phase BLS
aggregation suffices. Two-phase consensus retired for the new PDS path (orthogonal to existing AVS task lifecycle which can keep its
semantics).

Concern 1.c — Removing WASM Entirely?

WASM serves two distinct purposes — keep one, retire one:

┌─────────────────────────────────────┬────────────────┬───────────────────────────────────────────────────────────────────────────┐
│ Purpose │ Keep / retire? │ Why │
├─────────────────────────────────────┼────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ Pre-compute policy_data from raw │ Keep │ Rego can't do HTTP fetches, signed feed parsing, or cryptographic │
│ external inputs │ │ processing efficiently │
├─────────────────────────────────────┼────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ Inline computation during task │ Retire for hot │ Forces operators to run identical untrusted code at request time; any │
│ evaluation │ path │ drift = slashing risk; high latency │
└─────────────────────────────────────┴────────────────┴───────────────────────────────────────────────────────────────────────────┘

Rego is intentionally restricted (no recursion, no I/O) — that's its safety property. Retrying to push everything into Rego loses you
that. So WASM stays, but its execution boundary moves out of the per-task eval path.

Concern 2 — Tail Latency on Slow WASM

Even with all the above, a single policy's WASM might still take a long time when it runs (just not on the hot path). What about that?

Slow path (offline, batched per policy_data refresh):
WASM execution can be 1s, 10s, even minutes
Doesn't block any task eval
Anchored result becomes available at version V
All subsequent task evals at version ≥ V use it deterministically

Fast path (online, per-task):
Pure Rego eval against pre-anchored data
Bounded by Rego policy complexity (typically 10-50ms)
EvalTimeout (TxError variant) caps at ~500ms operator-side
Per-task attestation budget: 2s end-to-end (G-4-Q1 proposed lock)

The 2s hard cap belongs to the fast path only. Slow WASM lives in the slow path and never gates user-facing latency.

If a policy's WASM is so slow that policy_data refresh can't keep up with the desired refresh frequency — that's a policy-author
problem, not a protocol problem. The protocol guarantees: whatever policy_data is anchored at version V, all operators agree on it
deterministically.

Recommended Refined Model

Consolidating: I'd propose a clean three-axis decoupling, possibly worth opening G-5 grill series:

┌─────────────────┬──────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Axis │ Current (D9) │ Proposed │
├─────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ State mutation │ Per-tx Phase 1, batched per │ Unchanged — per-tx Phase 1, cycle-batched │
│ cadence │ cycle (120s) │ │
├─────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Policy data │ Implicitly inline in Phase 2 │ Decoupled — runs offline in policy_data registration paths; pre-anchored to JMT │
│ generation │ WASM │ │
├─────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Per-task │ Phase 2 = WASM + threshold │ Pure Rego — reads pre-anchored policy_data + identity/confidential/privacy │
│ evaluation │ decrypt + Rego + emit │ state; emits attestation │
├─────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Consensus model │ Two-phase Prepare/Commit │ One-phase — deterministic WASM + deterministic Rego = bytewise-identical │
│ │ with median │ TxResult across operators │
├─────────────────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Threshold │ Two-phase consensus during │ Inline one-pass — partial DH gather happens during Phase 2 but doesn't need │
│ decryption │ eval │ median aggregation since each operator's partial is provably correct via DLEQ │
└─────────────────┴──────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘

★ Insight ─────────────────────────────────────
The key reframing: policy evaluation is read-mostly compute, not state mutation. The D9 two-phase per-tx model was designed for atomic
state transitions, but task evaluation isn't really that — it reads state, evaluates, emits an attestation that stores in JMT only as
audit trail. Treating eval as a separate operation class (not "another tx with Phase 1 = empty") simplifies the latency story
dramatically.
─────────────────────────────────────────────────

What I Need You to Clarify Next

Before I propose this as cascading edits to the spec (Hard Invariants, Open Questions G-5-Q*, Decision History), I need to know which of
these resonate vs which you want to push back on:

  1. Drop inline WASM in per-task Phase 2 — only pre-anchored policy_data is readable in eval. Agree?
  2. Mandate deterministic WASM for all PDS paths (no clock, no random, no network I/O, pinned wasmtime version). Agree?
  3. Drop two-phase consensus for PDS task evaluation in favor of one-phase BLS aggregation over deterministic TxResult. Agree?
  4. Keep threshold decryption as a Phase 2 sub-step but treat partial DH outputs as deterministic (DLEQ-verified) so no median
    aggregation. Agree?
  5. Treat task evaluation as a distinct operation class from JMT mutation (different latency budgets, different consensus shapes). Agree?

Which of these (or which combination) do you want to dig into, push back on, or refine?

✻ Churned for 2m 35s

❯ no one of the core value props of newton protocol is the flexibilty that wasm provides where developers can use arbitrary code logic
and data, including making network I/O so that those offchain process/compute/data could be used for policy evaluation for onchain
intents. But essentially, what I do like is this potential approach of separating data generation with policy evaluation. The intuition
comes from what Accountable is doing https://docs.accountable.capital/accountable-documentation/vault-as-a-service
https://docs.accountable.capital/accountable-documentation/data-verification-network-dvn/nodes . this connector approach sounds
interesting https://docs.accountable.capital/accountable-documentation/data-verification-network-dvn/connectors. Is there anything from
Accountable design/approach that we can get insights from? (we already have TEE integration where wasm and rego are executed for
private data involved or secrets involved cases)

⏺ ★ Insight ─────────────────────────────────────
You're right — I overcorrected. Network I/O flexibility through WASM is exactly what makes Newton policies more powerful than pure
on-chain rule engines. Killing inline WASM would kill that. But Accountable's design points to a way to keep full WASM flexibility AND
get sub-second eval: separate the data layer (connectors with network I/O, possibly slow, possibly multi-phase consensus) from the
evaluation layer (pure read + Rego, sub-second, one-phase). The flexibility lives in connectors, not in per-task hot paths.
─────────────────────────────────────────────────

Let me walk through what's transferable from Accountable's design.

Accountable's Relevant Primitives

┌────────────────────┬────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ Accountable │ What it does │ Insight for Newton │
│ concept │ │ │
├────────────────────┼────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Vault-as-a-Service │ Programmable vault that executes strategies under │ Newton's policy contracts already serve this role. Less │
│ │ verifiable constraints │ novel transfer here. │
├────────────────────┼────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ DVN Nodes │ Network of operators that fetch + verify off-chain │ Map to Newton operators, but with a separate consensus │
│ │ data, reach consensus │ lane for connector outputs (not policy eval) │
├────────────────────┼────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Connectors │ Pluggable adapters with their own input/output │ The core insight — promote WASM data fetchers to │
│ │ schemas, refresh cadence, attestation lifecycle │ first-class network primitives, not per-policy concerns │
├────────────────────┼────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ Verified data │ Connector outputs attested + made available to │ Map to JMT-anchored connector output leaves, read at │
│ feeds │ consumers │ policy eval time │
└────────────────────┴────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────┘

The conceptual shift: in Accountable, data fetching is a network service with its own consensus and refresh schedule. Vaults consume
already-verified data. Newton currently has WASM as a per-policy implementation detail — same code might run inside a TEE during a
privacy task, or on the host for a public task, with no formal abstraction binding the data lifecycle.

What This Lets Us Do Without Sacrificing Flexibility

The Accountable-inspired refinement keeps WASM's network I/O fully intact, but moves where it runs:

┌───────────────┬──────────────────────────────────────┬────────────┬──────────────────────────┬──────────────────────────────────┐
│ │ │ Network │ │ │
│ Layer │ What runs │ I/O │ Cadence │ Consensus shape │
│ │ │ allowed? │ │ │
├───────────────┼──────────────────────────────────────┼────────────┼──────────────────────────┼──────────────────────────────────┤
│ Connector │ WASM connectors — full flexibility │ │ Per-connector schedule │ Two-phase with median/modal — │
│ layer │ (HTTP, oracle feeds, signed APIs, │ Yes — full │ (e.g., 100ms, 1s, 10s, │ handles natural drift in fetched │
│ │ complex preprocessing) │ │ 60s, on-demand) │ data │
├───────────────┼──────────────────────────────────────┼────────────┼──────────────────────────┼──────────────────────────────────┤
│ Privacy │ WASM connectors handling │ Yes — but │ │ Two-phase + TEE attestation │
│ connector │ secrets/confidential data │ inside TEE │ Same as above │ (existing pattern) │
│ layer │ │ │ │ │
├───────────────┼──────────────────────────────────────┼────────────┼──────────────────────────┼──────────────────────────────────┤
│ Evaluation │ Pure Rego over connector outputs + │ No — pure │ │ One-phase BLS — │
│ layer │ identity + confidential + privacy │ compute │ Per-task (sub-second) │ bytewise-deterministic Rego │
│ │ state │ │ │ │
└───────────────┴──────────────────────────────────────┴────────────┴──────────────────────────┴──────────────────────────────────┘

★ Insight ─────────────────────────────────────
The key architectural shift is connectors are anchored, policies are reactive. A policy doesn't fetch data anymore — it consumes the
latest anchored connector outputs at eval time. The connector network refreshes data continuously at whatever rate the connector spec
demands; policies read whatever's current. This is the same model Chainlink uses (price oracles refresh on their own cadence; consumers
read the latest), but generalized to arbitrary WASM-based data sources.
─────────────────────────────────────────────────

Concrete Mapping to Newton's Architecture

Here's how Accountable's primitives translate into Newton's existing PDS components:

                  Slow path (continuous, scheduled)
                  ─────────────────────────────────
  [External Source]         (HTTP API, oracle, RPC, signed feed)
         │
         ▼
  [WASM Connector]          (registered in ConnectorRegistry, versioned)
         │
         ▼  Two-phase consensus across operators
  [Median/Modal Output]     (handles natural network drift)
         │
         ▼
  [JMT-anchored Leaf]       (namespace 0x06: ConnectorOutput, leaf_key = connector_id)
         │
         │  Available to all policy evaluations
         ▼
                  Fast path (per-task, sub-second)
                  ────────────────────────────────
  [User Intent Request]
         │
         ▼
  [Operator reads]          (identity + confidential + privacy + connector outputs from JMT)
         │
         ▼
  [Pure Rego eval]          (deterministic, ~10-50ms)
         │
         ▼  One-phase BLS aggregation (deterministic TxResult bytes)
  [Per-task attestation]    (returned to user, sub-second)

This preserves everything you care about:

  • Full WASM flexibility — any code, any I/O — lives in connectors
  • TEE-isolated WASM for secrets — connectors that fetch secrets run inside the TEE (existing infrastructure reused)
  • Threshold decryption for privacy — happens during Phase 2 eval but is deterministic (DLEQ-verified partials) so doesn't need median
    aggregation
  • Newton's value prop — "policies can consume arbitrary off-chain data" — fully intact
  • Sub-second SLA — eval layer is pure Rego, deterministic, one-phase

Specific Insights from Accountable's Connector Model

Three patterns from Accountable that directly translate:

  1. Connectors are versioned + registered

Every connector has a unique ID, a code hash (WASM bytes), an input/output schema, a refresh cadence. The ConnectorRegistry (analogous
to existing PolicyClientRegistry) tracks them. Operators sync connector code on registration. New connectors are added via governance /
policy_client registration flow.

This eliminates the current ambiguity where each policy might have its own bespoke WASM. Connectors become reusable across policies.

  1. Connector outputs are anchored separately from policy state

Newton already has multiple namespace prefixes. Adding 0x06: ConnectorOutput (or extending 0x03 policy_data semantics) gives connector
outputs their own JMT subtree. Connector layer writes here on its schedule; eval layer reads here at task time. This isolates connector
consensus drama from policy eval.

  1. Connectors can have configurable consensus thresholds

Some connectors fetch from a single canonical source (e.g., a signed Chainlink feed) — for these, deterministic re-fetch + bytewise
comparison works.
Others aggregate from multiple sources (e.g., median of 5 price APIs) — for these, two-phase with median is appropriate.

The connector spec declares its consensus model. The DVN-equivalent layer (Newton operators) runs the appropriate aggregation. This is
more flexible than the current implicit "everything goes through two-phase consensus" model.

What Doesn't Transfer

A few things from Accountable that we should NOT copy:

┌──────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐
│ Their pattern │ Why we shouldn't copy │
├──────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Vaults as protocol-level abstraction │ Newton's policy contracts already serve this; vaults are application-layer in Newton │
├──────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Their specific staking economics │ EigenLayer restaking is our staking primitive; their model is different │
├──────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ DVN as a separate network │ Newton's same operator set should run both layers (avoids splitting security budget) │
└──────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────┘

Open Questions This Reframing Surfaces

If we adopt the connector + evaluation two-layer model, several decisions need locking:

  1. Connector cadence model — fixed schedule (every N seconds), pull-based (refresh when stale), or hybrid?
  2. Connector output staleness — how do policies declare "I need data fresher than X"? Reject eval if connector output is too old?
  3. Connector failure handling — if a connector can't reach consensus (e.g., upstream API down), do dependent policies fail gracefully or
    block?
  4. Connector composition — can a connector consume another connector's output? Or only external data?
  5. TEE-vs-host connector dispatch — same code, different execution strategy based on connector spec? (Newton already has
    EnclaveWasmStrategy — this maps.)
  6. Connector slashing — if a connector operator emits a wrong output, what's the slashing condition? Same as policy slashing or
    different?
  7. Migration path — existing per-policy WASM patterns need to convert to connectors. What's the boundary?

★ Insight ─────────────────────────────────────
Q4 (connector composition) is interesting because it could enable layered abstractions: a "ETH-USD price" connector can consume a
"BTC-USD price" connector + a "ETH-BTC price" connector. This composition lets developers build sophisticated data pipelines without
writing one giant connector. It's also where you can get into trouble — composition cycles, refresh-cascade timing, version-skew issues.
Likely needs to be locked carefully or deferred to Phase 2.
─────────────────────────────────────────────────

My Recommendation

This is significant enough to warrant a new grill series — call it G-5: Connector / Evaluation Separation — alongside (not replacing)
G-4. The cascading edits would touch:

  • New Hard Invariants (likely 21-24 range)
  • Possibly a new namespace prefix in NamespacePrefix enum (e.g., 0x06 ConnectorOutput)
  • New ConnectorRegistry contract spec (parallel to PolicyClientRegistry)
  • Refined evaluation flow under D9 — Phase 2 reads connector outputs but doesn't run WASM
  • Decision History rows
  • Possibly a new architecture section in the spec doc

Before I draft G-5 questions formally, three things I want to confirm:

  1. Direction confirmed? — Is the "connector layer + evaluation layer" two-tier model the direction you want to lock, with WASM
    flexibility moved to the connector layer?
  2. Naming — "Connector" (Accountable's term) vs "DataFeed" vs "Oracle" vs "PolicyDataProvider" (extending existing Newton vocab)? I'd
    lean toward "Connector" for clarity, but it's bikesheddable.
  3. Scope for Phase 1 MVP — should connector model be in scope for the first PDS migration, or deferred to a Phase 2 (and Phase 1 keeps
    the current ad-hoc per-policy WASM but with the eval-vs-mutation separation we're proposing)?

Which of those should we settle first?

✻ Brewed for 1m 52s

@denniswon

Copy link
Copy Markdown
Author

◼ Cascade G-6 locks into spec doc
for operators to process every block (around 1 second block time). No atomocity at the block level. Currently we dont have fee
mechanism so it is first come first served in linear order for operations, but ONLY for put leaf operations. TLDR: put leaf operations
(deploy policy, register policy client to a policy, user upload identity, grant authorization to a poicy client for identity usage,
etc.) should ALL be linearly ordered like blockchain transactions from the gateway sequencer mempool (should we have ordering per
user/client/sender nonce, like ethereum EOA nonce? For atomicity and guaranteed ordering, the responsibility can be moved to the
operation/task request sender to specifically "batch" operations. Every "block" with 1 second block time, operations are sequentially
ordered for each policy client/operation sender account nonce for putleaf operations only. ex: if the client wants to ensure that they
want to have their put leaf operation of register policy client to a policy before the task evaluation request, they need to set the
nonce correctly to ensure that order. If they want to batch it for atomocity for all or nothing experience, they need to specifically
batch them into a batched operation.) Then, from the mempool, the gateway continuously "sequences" operations (in phase 1, just first
come first served, sequential sequencing) and constructs a block. And each block, at the beginning of the block, each operator take a
snapshot of the state, and execute in order one by one to the snapshot, 1 batch state write at the end of the block. Some operations
can succeed, some operations fail. No atomocity in the block. Another important thing is that in phase 1 before the fee market, ONLY
Put leaf "operations" are sequentially ordered like this in blocks. All policy evaluation "Tasks" or all policy data ingestion "Tasks"
are executed in "parallel" (important) separately. we should have 3 separate block pipeline that runs in parallel where 1: first
pipeline for all put leaf operations, in operation linear sequential processing (again should we have a concept of account nonce for
put leaf operations only? not sure if we need this), 2: policy evaluation task block pipeline, no account nonce involved (account nonce
only relevant for putleaf operations if we need), all tasks executed in parallel, takes a state snapshot at the beginning of the
block, 1 batch write at the end. Could involved threshold decryption with 2 phase consensus for certain operations in the block. No
atomocity in the block. 3: Similar to 2, a separate policy data ingestion task block pipeline where all tasks are executed in parallel.
No atomocity in the block. Does encryption for policy data feed if the policy data template is a private data. Having such 3 separate
pipelines of blocks, this means sender can ONLY batch and guarantee specific order in a block for put leaf operations, not tasks.
(Conceptually, operations --> customer writes, Tasks --> operators executes per request/cadence). This structure is for maximal,
optimal latency for policy evaluation and policy data ingestion tasks, and separation of failure points in the protocol. #Point 2#
operators should not operate under the assumption that other operators are honest --> meaning BLS aggregation/offchain finality after
consensus is required for operators to finalize the state transition, just like ethereum block. Before we have bft consensus with
operator p2p, operators need someone/some source to tell them what the consensus-achieved/bls aggregate signed state is. Because state
commit happens ONLY every cycle not every block, after each block, Gateway can "broadcast" the finalized state root is for each block.
If operators see discrepancy, they need to "FIRST" reconcile the discrepancy by getting the finalized block delta state from the
bootnode (in later phase, from other operators once we have p2p layer). TLDR: before bfs consensus with p2p layer, operators do need to
trust gateway and bootnode operated by newton. If no discrepancy, they just finalize to actually update the state db (before this
block confirmation from gateway, not actually written to db yet). #Point 3# Let's actually remove active_binding mapping and do not
strictly enforce validateAttestationDirect to check the policy client policy binding (only optionally). Optimizing for user experience
and performance/speed. #Point 4# I have some thoughts on attestation being spent flow. Let's do a grill session on this. High level, I
want to introduce the concept of "Relayers" and improve the developer/user experience to the contract "callback" pattern while the
current only supported pattern is users/clients getting back the attestation and directly spending it. (They still can but this
callback pattern support is an improvement) The concept is similar to how Chainlink oracle works. INewtonPolicyClients contracts of
developers can implement the callback function where the "relayers" will relay the attestation on the behalf of the users (the
attestation already contains the exact transaction intent (from, to, data, value)). This is an improvement in developer experience
because for example: let's say the developer want to guard its mint() function on the contract. Currently, they have to change their
existing mint(some params...) function scheme to mint(NewtonAttestation) function scheme. But with the callback approach, they can
leave mint() function scheme as is, and just call requestPolicyEvaluation() inside the mint() function, then just implement an
additional separate callback function of INewtonPolicyClient for "relayers" to call (function evaluationCallback(NewtonAttestation))
and that callback just then executes the intent contained inside the attestation.

⏺ Four major design changes — three architectural restructures (Points 1, 2, 3) and one new feature with grill request (Point 4). Let me
work through cascade impact for each, then kick off the Point 4 grill.

★ Insight ─────────────────────────────────────
Points 1+2 together resolve a tension I'd been quietly carrying: the current spec has the gateway as "admission boundary, not
correctness path" (D5/D14), but the unified Transaction model (D9) implicitly required gateway-blind sequencing — which is incoherent
before P2P+BFT. Reframing as gateway-as-trusted-sequencer with operator-side verification + bootnode reconciliation is honest about
Phase 1's actual trust posture (Newton operates both gateway and bootnode → effective 1-of-1 trust until Phase 2 federation lands).
─────────────────────────────────────────────────

Point 1: Three-pipeline architecture (block-level, not tx-level)

What this invalidates in the current spec:

Spec section: D9 unified Transaction model
Impact: Removed. Replaced by 3 separate pipelines
────────────────────────────────────────
Spec section: G-3-Q3 (two-phase per-tx execution: Phase 1 PutLeafs → Phase 2 Evals)
Impact: Removed. Cross-op-class composition within one unit no longer exists
────────────────────────────────────────
Spec section: G-3-Q8 (per-cycle JMT versioning + cycle-end commit pipeline)
Impact: Restructured. Each pipeline has its own block-end snapshot+writeback
────────────────────────────────────────
Spec section: Invariant 14 (two-phase per-tx execution)
Impact: Removed
────────────────────────────────────────
Spec section: Invariant 9 ("Transactions are atomic; cycles are not")
Impact: Restructured. Atomicity moves to "batched operations within Pipeline 1 only"
────────────────────────────────────────
Spec section: G-3-Q1 content-hash tx_id
Impact: Repurposes as "operation_id" or "batch_id" depending on whether ordering or atomicity is the primary use
────────────────────────────────────────
Spec section: G-2-Q5 op-exclusivity invariant
Impact: Strengthened. Op-class doesn't just bind namespace — it binds pipeline

New conceptual split — operations vs tasks:

Pipeline: 1: PutLeaf
Unit: Operation (or Batch[Operation] for atomicity)
Trigger: Client-submitted via gateway mempool
Ordering: Linear (sender-nonce-ordered?)
Atomicity: Batch-scoped if explicitly batched
Cadence: ~1s block
────────────────────────────────────────
Pipeline: 2: Policy evaluation
Unit: Task
Trigger: Per-client-request
Ordering: Parallel within block
Atomicity: None
Cadence: ~1s block, possibly 2-phase for private inputs
────────────────────────────────────────
Pipeline: 3: PolicyData ingestion
Unit: Task
Trigger: Per-cadence IngestionTick
Ordering: Parallel within block
Atomicity: None
Cadence: ~1s block, encrypts for private feeds

The nonce question — my recommendation:

★ Insight ─────────────────────────────────────
The right answer depends on whether the SDK or the protocol owns ordering responsibility. Without nonces, "first-come-first-served at
gateway" is non-deterministic from the sender's perspective — network jitter between two PutLeaf submissions can flip their order. That
breaks the canonical use case "register policy client to a policy BEFORE uploading identity that references it."
─────────────────────────────────────────────────

Yes to nonces for Pipeline 1, modeled on Ethereum EOA nonces:

  • Tuple: (sender_owner, chain_id, nonce: u64). Per-(sender, chain) nonce space.
  • Gateway tracks last_committed_nonce per (sender_owner, chain_id) in the existing Postgres journal (Invariant 36 — same table, new
    column).
  • Submission rule: nonce > last_committed_nonce. Gap-fills queue up to a bound (say 32 pending); past that, gateway rejects with
    NonceGapTooWide.
  • Mempool ordering: ascending nonce per sender, FCFS across senders.
  • Replay protection comes for free (same (sender, nonce) cannot land twice).
  • SDK auto-increments; pre-fetches last_committed_nonce on session start.
  • Batching: Batch[Operation] consumes ONE nonce slot and is atomic (all-or-nothing within Pipeline 1's block).

Why not "first-come-first-served + opt-in batching only":

  • Bare FCFS gives no ordering guarantee for a single sender across blocks. SDK would need a separate request-ordering protocol layer on
    top — re-inventing nonces.
  • The mental model "Pipeline 1 is Ethereum-tx-like" is load-bearing for developer onboarding. Nonces are the part of EOA semantics that
    developers already know.
  • Gateway-side nonce tracking is one column in the existing replication journal — zero new infrastructure surface.

Trade-off accepted: Phase 1 single-gateway means nonces are gateway-scoped, not chain-scoped. If gateway state is lost without a
graceful failover, in-flight nonces stall. The Phase 1.5 hot-standby (D4) inherits the nonce ledger from replicated Postgres —
recoverable. Phase 2 federated gateways need a cross-gateway nonce reservation protocol (likely OPTIMISTIC reservation with
conflict-resolution at submission). Defer Phase 2 mechanism design; Phase 1 is bottleneck-free.

Cascade — what needs to be written/rewritten:

  • New D-decision block: "D17: Three-pipeline architecture"
  • Rewrite D9 entirely
  • New invariant block replacing Invariants 9, 14
  • Drop G-3-Q3, G-3-Q8 lock entries; mark superseded
  • New G-7 round to lock pipeline boundary semantics (open questions: cross-pipeline read snapshots, cross-pipeline failure isolation,
    cross-pipeline observability)

Point 2: Block vs cycle, gateway-broadcast finalization, bootnode reconciliation

Terminology lock proposal:

  • Block (~1s): unit of operation/task processing per pipeline. Each pipeline produces one block per ~1s tick.
  • Cycle (≥1 block, network-dependent): unit of on-chain state commit. Cycles aggregate K blocks. K is network-class config: mainnet K=60
    (60s cadence per D3), testnet K=30, local K=2.

New finalization flow:

t=0 Block N starts. Each pipeline snapshots local JMT @ root_k.
t=0+ Pipeline 1: operations sequential into block N's writeset.
Pipeline 2: tasks parallel against snapshot.
Pipeline 3: tasks parallel against snapshot.
t=1s Block N ends. Each pipeline computes its block-end root.
t=1s+ Gateway aggregates per-pipeline roots → broadcasts canonical (root_1, root_2, root_3)_block_N.
t=1s+ Operators independently compute their own per-pipeline roots.
MATCH: operator writes block N to local redb (finalized).
MISMATCH: operator stops, fetches block N delta from bootnode,
reconciles, then finalizes.
t=K×1s Cycle end. BLS quorum signs StateCommit over the cycle's aggregated root.
Aggregator submits commitStateRoot on-chain.

Trust topology:

┌────────────┬───────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Actor │ Role │ Trust model │
├────────────┼───────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Gateway │ Sequencer + finalizer broadcaster │ Trusted to sequence and broadcast canonical block deltas │
├────────────┼───────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Bootnode │ Tiebreaker delta source │ Trusted as canonical reconciliation source │
├────────────┼───────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Operators │ Verifiers + BLS signers │ Not trusted; must verify gateway broadcasts │
├────────────┼───────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ Aggregator │ Cycle-end BLS collector │ Trusted to assemble quorum │
└────────────┴───────────────────────────────────┴──────────────────────────────────────────────────────────┘

This is Phase 1 reality — Newton operates both gateway and bootnode, so the trust set is effectively {Newton}. Phase 2 federated
gateways spread that trust horizontally; Phase 3 P2P+BFT removes the trust assumption entirely (operators reach consensus among
themselves).

What this clarifies:

  • D5 reframe stays — gateway is still plaintext-blind on private data; "trusted sequencer" doesn't mean "trusted with content."
  • D7's 3-phase P2P trajectory stays unchanged.
  • The Layer 1 (≤1s) vs Layer 2 (60s mainnet) distinction from D3 holds — block finalization is Layer 1, cycle commit is Layer 2.
  • Invariant 11 (pre-cycle JMT state + canonical operation ordering is sufficient for cross-operator determinism) holds — gateway
    broadcast IS the canonical ordering signal.
  • Invariant 38 (90s replication SLO) still holds for cross-chain replication of cycle commits.

Cascade:

  • New D18: "Gateway-as-trusted-sequencer model + bootnode reconciliation"
  • Rewrite D3 with block vs cycle distinction made explicit
  • New invariant: "Pre-finalization state is not durable; durability requires gateway-broadcast match OR bootnode reconciliation"
  • New gateway component: BlockFinalizer (broadcasts (block_id, per_pipeline_roots, block_delta_hash) over the existing dispatch channel)
  • New operator component: FinalizationVerifier (compares local roots against gateway broadcast, triggers bootnode catch-up on mismatch)
  • Bootnode HTTP API extension: GET /block-delta/{block_id} (already implied by D4/Invariant 29 — make it explicit)

Point 3: Remove active_binding map; validateAttestationDirect binding check optional

Reverts D13 entirely. The commitStateRoot(StateCommit, NewBinding[]) calldata simplifies to commitStateRoot(StateCommit). No more
active_binding mapping, no more BindingActivated event.

Resolves Q5 (the race I flagged). With no on-chain active_binding map, there's no stale-read window to engineer around. The two consumer
paths simplify:

Path: Optimistic (off-chain or on-chain via attestation arg)
Verification: BLS aggregate sig inline
Cost: BN254 pairing, ~2-3M gas
────────────────────────────────────────
Path: Canonical post-commit (JMT inclusion proof)
Verification: Merkle proof against StateCommitRegistry.lastCommittedStateRoot
Cost: ~50-100K gas

The on-chain cheap-SLOAD path is gone. ~50K vs ~2K is acceptable — the SLOAD shortcut was a perf optimization, not a correctness
primitive.

validateAttestationDirect becomes structurally lenient. Policy-client-policy binding check moves from "MUST" to "MAY" — DApps that need
binding verification do it via JMT inclusion proof against lastCommittedStateRoot; DApps that trust the BLS attestation's policy field
alone skip the binding check entirely.

Cascade:

  • Strike D13 from the spec entirely
  • Strike Invariant 33 (read-path cost asymmetry table) — replace with simpler "post-commit reads use JMT inclusion proof"
  • Strike NewBinding[] from commitStateRoot signature in §6 cascade items
  • Strike BindingActivated event
  • Strike active_binding[bindingKey] mapping from docs/PRIVATE_DATA_STORAGE.md §6
  • Update validateAttestationDirect ABI to reflect optional binding check
  • Q5 grill round prediction → closed by design change, no grill round needed

Point 4: Relayer/callback pattern — kicking off the grill

★ Insight ─────────────────────────────────────
The pattern you're describing is the Chainlink VRF / CCIP request-callback model: developer contract makes a request, gets back a
callback when the off-chain work completes. Newton's twist: the "off-chain work" is policy evaluation producing a BLS-signed
attestation, and the callback delivers (NewtonAttestation, intent). This is genuinely better DX for the "guard an existing function"
case — mint() stays as mint(), with an internal requestPolicyEvaluation() call and a separate evaluationCallback(NewtonAttestation)
handler.

The interesting design surface is who runs the relayer + how is it economically sustainable. Chainlink relays are economically gated by
LINK payment from the requesting contract. Newton has no analogous fee token yet (Phase 1 is gateway-gas-subsidized per D12), so the
relayer economics inherit the same Phase 1-vs-Phase 2 split.
─────────────────────────────────────────────────

To structure this grill, here's the question set I'd like to walk through one at a time. Let me start with the most foundational one —
once this locks, the others narrow:

G-7-Q1: Relayer trust + operation model in Phase 1

Who runs the relayer that calls INewtonPolicyClient.evaluationCallback(NewtonAttestation)?

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
☐ Relayer operator

G-7-Q1: Who runs the relayer that submits attestations to dApp callbacks in Phase 1?

❯ 1. Gateway-operated (Newton-only)
The Phase 1 gateway runs the relayer as a built-in service. Gas paid from the same per-chain hot-wallet that subsidizes
commitStateRoot per D12. Simplest Phase 1 ops; one trust point; tight integration with attestation production. Phase 2 federation
gives multi-gateway relay redundancy.
2. Permissionless Phase 1, gas-paid by requester
Anyone can be a relayer. Requesting contract escrows native gas at requestPolicyEvaluation() time; relayer collects it on successful
callback. Open marketplace from day one; no Newton ops cost; but harder to bootstrap (relayer infra must exist before adoption) and
adds escrow contract complexity.
3. Gateway-operated Phase 1, permissionless from Phase 2
Newton's gateway is the sole relayer in Phase 1 (matches D7 hub-and-spoke posture and D12 gas-subsidy economics). When federation
lands in Phase 2, third-party relayers can opt in alongside Newton's gateway. Mirrors the Phase 1 → Phase 2 trust expansion across
gateway, bootnode, and now relayers.
4. Optional relayer; client-direct stays first-class
Both patterns are supported equally. Client gets the attestation back from gateway as today (direct spend pattern). Separately, dApps
that want callback ergonomics implement INewtonPolicyClient and a permissionless / Newton-operated relayer service picks up
requests. No primary pattern; developer chooses per-contract.
5. Type something.
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
6. Chat about this

Enter to select · ↑/↓ to navigate · Esc to cancel

@denniswon

Copy link
Copy Markdown
Author

❯ consolidate the G-7 round into a structured lock summary before we move on

⏺ G-7 Lock Summary — Relayer-Callback Pattern + Attestation Lifecycle

Round scope

G-7 covers the post-pivot attestation consumption model: how attestations are delivered (direct vs callback), who can consume them, how
single-consumption is enforced, what the attestation contains, and how dApps integrate. It sits on top of three foundational changes
from earlier in this round (Points 1-3) which restructured the protocol layers below it.


Foundation locks (Points 1-3 of this round; outside G-7 numbering but G-7 depends on them)

Lock: Three-pipeline architecture (replaces D9 unified Transaction model)
Decision: Pipeline 1 = PutLeaf operations, gateway-sequenced, sender-nonce-ordered. Pipeline 2 = Policy evaluation tasks, parallel.
Pipeline 3 = PolicyData ingestion tasks, parallel. ~1s blocks per pipeline. No cross-pipeline atomicity.
Cascade: Strike D9, G-3-Q3, G-3-Q8, Invariants 9 + 14. New D17 + replacement invariants. New per-(sender, chain_id) nonce ledger in
gateway redb.
────────────────────────────────────────
Lock: Operator pending vs confirmed state
Decision: Per-block computation produces in-memory pending root; gateway broadcast of canonical finalization root is the durability
signal. Match → flush pending to redb. Mismatch → fetch canonical block delta from bootnode → apply → flush.
Cascade: Rewrite Invariant 11 + 19. New D18: "Gateway-as-trusted-sequencer + bootnode reconciliation." New gateway BlockFinalizer
component, operator FinalizationVerifier.
────────────────────────────────────────
Lock: Single gateway + sidecar bootnode
Decision: Single active gateway + hot-standby (10-instance load-balanced topology dropped — original scaling rationale no longer
applies). Bootnode runs as co-located sidecar with own redb.
Cascade: Drop Postgres for protocol coordination (keep for API keys + indexer, UX only). Drop Redis entirely. Strike Invariants 36-39
(G-6 round mostly obsolete).
────────────────────────────────────────
Lock: Remove active_binding map
Decision: commitStateRoot(StateCommit) — no NewBinding[] parameter. validateAttestationDirect policy-binding check optional, not
enforced. Post-commit reads use JMT inclusion proof (~50-100K gas) instead of SLOAD (~2K gas).
Cascade: Strike D13. Strike Invariant 33. Strike BindingActivated event. Update validateAttestation* ABI.

★ Insight ─────────────────────────────────────
The foundation changes drop Postgres + Redis dependencies, multi-instance coordination, and the on-chain binding cache all in one move.
Phase 1 protocol storage collapses to a single primitive: redb. The dev/ops surface shrinks dramatically — operators, gateway, and
bootnode all run the same storage backend; no replication-journal complexity; no multi-instance lease primitives; no per-instance state
coordination. This is the architectural simplification that makes the rest of G-7 cleanly designable.
─────────────────────────────────────────────────


G-7 locks (in question order)

G-7-Q1: Relayer pool + registration

Lock: Registered pool of relayers; gateway broadcasts to all registered relayers via long-poll claim queue (see Q1.B below). Stagef pool
size = 1; prod pool size = 10 candidate (pending scalability evaluation — see open questions). On-chain RelayerRegistry contract owns
the allowlist; DAO-gated in Phase 1, permissionless in Phase 2.

Cascade:

  • New contract RelayerRegistry.sol — DAO-gated allowlist; emits RelayerRegistered/RelayerRevoked
  • New contract NewtonMulticallRelayer.sol — dispatcher; onlyRegisteredRelayer modifier reads RelayerRegistry
  • New gateway module crates/gateway/src/relayer/ — claim queue + broadcast logic, redb-backed
  • New ref-impl binary bin/newton-relayer/

G-7-Q1.B (sub-lock): Claim queue transport — HTTP long-poll (30s timeout). Gateway holds connection open until claim available or
timeout. Defers WebSocket to Phase 2 contingent on relayer_claim_latency_p99_ms measurement.

G-7-Q2: Delivery lifecycle + path opt-in

Lock: Both client-direct and callback paths coexist; neither is first-class. Mechanism:

  • Client SDK passes wantsCallback: bool (or DeliveryFlags) at requestPolicyEvaluation() time.
  • dApp's INewtonPolicyClient.acceptedPaths() returns a bitmap declaring which paths it accepts.
  • Gateway always returns attestation to client (zero gateway cost — connection is open).
  • Gateway additionally broadcasts to relayer pool iff client.wantsCallback AND dApp.acceptedPaths() & RELAYER_CALLBACK_BIT != 0.
  • AttestationValidator.spent[attestationHash] enforces single consumption across all delivery paths.

Cascade:

  • New acceptedPaths() method in INewtonPolicyClient interface
  • RELAYER_CALLBACK_BIT = 0x02, CLIENT_DIRECT_BIT = 0x01 constants
  • Gateway routing logic at requestPolicyEvaluation handler

G-7-Q3: Relayer economics

Lock: Phase 1 Newton-subsidized (mirrors D12 gateway gas-subsidy posture). Phase 2 fee mechanism designed as a single coherent question
covering replication (D12) + relay (G-7) + future Newton-on-behalf-of-user chain calls.

Cascade:

  • Per-chain operational treasury for relayer pool; separate hot-wallet from commitStateRoot wallet for clean accounting
  • New metric relayer_subsidy_remaining_native_units{chain_id} mirroring gateway_subsidy_remaining_native_units{chain_id}
  • Out-of-scope (Phase 2): RelayerEscrow, permit-style allowances, on-chain payment rails

G-7-Q4: Consume permissioning

Lock: Hybrid of consume-folded-into-validate (Option 2) + permissioned caller (Option 4). validateAttestation* is the atomic
verify+consume primitive, permissioned to msg.sender ∈ {policyClient, MULTICALL_RELAYER, approved_delegate}. Public preview() view
function exists for off-chain inspection. Relayer multicall pre-consumes before invoking dApp's evaluationCallback (centralized
enforcement; dApp can't forget).

Cascade:

  • AttestationValidator._checkAuthorized enforces three authorized callers (policyClient / multicall / delegate)
  • AttestationValidator.preview(att) view function — verify without consume
  • Multicall dispatcher pattern: validate → invoke dApp callback → emit event per item; per-item revert is non-fatal to multicall

G-7-Q5a: Intent signature handling — revised per last turn

Lock: intentSignature: bytes stays in schema (variable-length, can be empty). AttestationValidator does NOT verify intent sig — defers
to dApp via separation of digest helper from verification scheme. AttestationValidator.intentDigest(intent) is the canonical helper.

Cascade:

  • New abstract NewtonPolicyClientBase with _verifyIntentSignatureEcdsa, _verifyIntentSignatureErc1271, _verifyIntentSignatureAuto
    helpers
  • Per-dApp guidance docs: three integration patterns (EOA-signed, smart-account ERC-1271, trust-protocol-only)
  • Smart-account integration uses _verifyIntentSignatureErc1271; intent.from = smart account address
  • Gas saving: dApps that opt out save ~3-5K gas per validate

G-7-Q5b: intent.to vs policyClient relationship

Lock: Allow intent.to != ctx.policyClient via delegate pattern. INewtonPolicyClient.isApprovedDelegate(address) view returns (bool).
AttestationValidator._checkAuthorized adds delegate clause: msg.sender == intent.to && intent.to != policyClient &&
policyClient.isApprovedDelegate(intent.to).

Cascade:

  • New isApprovedDelegate method on INewtonPolicyClient
  • Three-clause authorization check in AttestationValidator._checkAuthorized
  • Documented use case: router patterns (account abstraction, multicall)

G-7-Q5c: Expiration semantics

Recommended (pending explicit confirmation): cycleId-based expiration. Intent.expirationCycle: uint64 — absolute cycleId after which
attestation is invalid. Validate reads StateCommitRegistry.currentSequenceNo() (~2100 gas) and reverts on expiry. Rationale:
cadence-uniform across chains, reuses existing ctx.cycleId temporal axis, off-chain relayer filtering simpler, gas overhead negligible
vs validate's other costs. Eliminates expireAfter=0 misconfiguration trap by moving expiration to intent-level (SDK-set), not
policy-level.

Cascade (pending confirm):

  • StateCommitRegistry.currentSequenceNo() view exists per existing design — no contract change
  • SDK pattern: intent.expirationCycle = ctx.cycleId + sensibleDefault (e.g., 600 cycles)
  • Strike per-policy expireAfter config

G-7-Q4 (sub-lock): validateAttestationDirect smart-fallback

Lock: Unified entry point (Option A) — single schema carries both proofs optionally populated; validator picks cheapest available.

  • AttestationProof = { BlsProof, JmtProof } — JmtProof empty if cycle not committed
  • Validator checks att.ctx.cycleId <= currentSequenceNo() && jmtProof.siblingPath.length > 0 → use cheap JMT path; otherwise BLS-inline
    path
  • validateAttestationDirect kept as legacy-compat alias delegating to validateAttestation

Cascade: Single validateAttestation entry handles both pre-commit and post-commit. SDK constructs both proofs when cycle is committed;
just BLS otherwise.


Final locked schemas

contract NewtonMessage {
struct Intent {
address from;
address to;
uint256 value;
bytes data;
uint256 chainId;
uint64 expirationCycle; // Q5c recommended; awaiting explicit confirm
}

  struct AttestationContext {
      bytes32 policyId;
      address policyClient;
      bytes32 taskId;
      uint64 cycleId;
  }

  struct BlsProof {
      BN254G1Point signature;
      bytes32 operatorSetHash;
      uint64 referenceBlock;
  }

  struct JmtProof {
      bytes32 stateRoot;
      uint64 jmtVersion;
      bytes32[] siblingPath;
      uint64 anchorSequenceNo;
  }

  struct AttestationProof {
      BlsProof blsProof;
      JmtProof jmtProof;  // empty if cycle not yet committed
  }

  struct NewtonAttestation {
      AttestationContext ctx;
      Intent intent;
      bytes intentSignature;  // opaque to validator; dApp interprets
      AttestationProof proof;
  }

}

interface INewtonPolicyClient {
function isApprovedDelegate(address delegate) external view returns (bool);
function acceptedPaths() external view returns (bytes1);
function evaluationCallback(NewtonAttestation calldata att) external;
}

// Nullifier key — path-agnostic
function attestationHash(NewtonAttestation memory att) internal pure returns (bytes32) {
return keccak256(abi.encode(att.ctx, att.intent));
}


New contracts (cascade summary)

Contract: RelayerRegistry
Role: DAO-gated allowlist of registered relayers
Phase 1 trust gate: Newton DAO multisig
────────────────────────────────────────
Contract: NewtonMulticallRelayer
Role: Batch dispatcher to dApp callbacks; pre-consumes via validateAttestation
Phase 1 trust gate: onlyRegisteredRelayer via RelayerRegistry
────────────────────────────────────────
Contract: AttestationValidator
Role: Unified validate+consume primitive; smart-fallback proof routing; nullifier map
Phase 1 trust gate: Permissioned to {policyClient, MULTICALL_RELAYER, approved_delegate}
────────────────────────────────────────
Contract: NewtonPolicyClientBase (abstract)
Role: dApp inheritance helpers (ECDSA / ERC-1271 / Auto sig verification)
Phase 1 trust gate: dApp inherits
────────────────────────────────────────
Contract: NewtonAddressesProvider
Role: (existing) Adds multicall_relayer slot
Phase 1 trust gate: Newton DAO


Cascade — spec doc updates required

File / section: pds-architecture-pivot-design.md D9
Edit: Replace with D17 three-pipeline architecture
────────────────────────────────────────
File / section: Same doc — D13
Edit: Strike; replaced by Point 3 active_binding removal
────────────────────────────────────────
File / section: Same doc — Invariants 9, 11, 14, 19
Edit: Rewrite for block-vs-cycle distinction + pending/confirmed state
────────────────────────────────────────
File / section: Same doc — Invariants 33, 36-39
Edit: Strike (G-6 multi-instance round obsolete)
────────────────────────────────────────
File / section: Same doc — new section
Edit: "G-7 attestation consumption model" with lock summary above
────────────────────────────────────────
File / section: Same doc — new D17, D18
Edit: "Three-pipeline architecture" + "Gateway-as-trusted-sequencer"
────────────────────────────────────────
File / section: architecture.md Key Contracts
Edit: Add RelayerRegistry, NewtonMulticallRelayer, updated AttestationValidator; strike ConfidentialDataRegistry row
────────────────────────────────────────
File / section: error-selectors.md
Edit: Add: Unauthorized (consume), AlreadySpent, InvalidIntentSignature, DelegateNotApproved, AttestationExpired (cycleId-based),
NotRegisteredRelayer, others as they're assigned selectors
────────────────────────────────────────
File / section: RPC_API.md
Edit: Document gateway relayer claim endpoint (long-poll); deliveryFlags param on requestPolicyEvaluation
────────────────────────────────────────
File / section: OPERATOR.md
Edit: No changes (operators uninvolved in callback path); document attestation construction includes cycleId
────────────────────────────────────────
File / section: DEVELOPER_GUIDE.md (or equivalent)
Edit: New patterns section: "Integration with NewtonPolicyClientBase" with three example dApps


Open / pending

Item: G-7-Q5c expiration semantics
Status: Recommended cycleId-based; awaiting explicit confirmation
────────────────────────────────────────
Item: 10 vs N relayers in prod
Status: Locked direction (multi-relayer better for parallelism); exact N TBD on capacity benchmarks during implementation
────────────────────────────────────────
Item: Multicall failure modes
Status: Need explicit design: per-callback gas cap, retry semantics, on-chain accounting of failed deliveries. Likely G-8 grill.
────────────────────────────────────────
Item: Permit-style allowance for Phase 2 economics
Status: Deferred to Phase 2 grill round (combined with D12 federation econ)
────────────────────────────────────────
Item: Cross-pipeline read snapshot semantics
Status: Locked at block boundary (Pipeline 2/3 reads against confirmed root); concrete operator-side implementation detail open
────────────────────────────────────────
Item: EIP-712 domain separator scope
Status: Single per-chain DOMAIN_SEPARATOR in AttestationValidator? Or per-policyClient? Likely the former for simplicity; minor open.


What's intentionally deferred to other grill rounds

  • Phase 2 federation economics (gateway + relayer + future infra) — single Phase 2 round combining D12 + G-7-Q3
  • Phase 2 P2P design (libp2p protocol, peer scoring) — separate Phase 2/3 spec
  • N-shot attestation semantics — explicitly out of scope; the 1-shot nullifier in AttestationValidator.spent is the locked Phase 1 model
  • Permissionless relayer registration economics — Phase 2 expansion of RelayerRegistry model

This consolidates everything G-7 produced. Ready to write up the spec edits, or move to the next grill round (probably G-8 around
multicall failure modes + cross-pipeline read semantics) — your call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment