docs: record the intent architecture (venue adapters, egress guard)#132
docs: record the intent architecture (venue adapters, egress guard)#132mfw78 wants to merge 5 commits into
Conversation
|
Required action to land as M1 train step 0 (design gate for #137). This PR is the intent-architecture design authority (
Closes #131 on merge. |
| } | ||
|
|
||
| variant intent-status { pending, open, settled(option<list<u8>>), failed(fail-reason), expired, cancelled } | ||
| type receipt = list<u8>; // venue-scoped stable id (CoW: the 56-byte order UID) |
There was a problem hiding this comment.
A list<u8> receipt is hashable, but the WIT sketch does not specify an encoding, and different venues may produce receipts that differ in length, encoding, and uniqueness scope. For the CoW case (56-byte order UID), this is fine, but the "venue-scoped stable id" needs to be more precisely specified -- stable within a session? Across restarts? Globally? The SDK conformance kit (golden vectors, classification tables) is where this belongs, but the doc should name the stability requirements the host relies on.
| offchain(offchain-desc), // RWA: deed, chattel, ... | ||
| } | ||
|
|
||
| record asset-amount { asset: asset, amount: list<u8> } // big-endian unsigned |
There was a problem hiding this comment.
There is no length constraint and no maximum on "amount: list". For the purposes of WIT, this is correct (WIT has no fixed-size integer arrays), but downstream code parsing this will need to bound-check and handle values that overflow any native integer type.
Suggestion: a one-sentence note in the vocabulary section: "policy implementations must treat any amount exceeding the precision of the native type as exceeding any configured limit."
|
|
||
| ### No direct module-to-adapter linking | ||
|
|
||
| Component-model composition (linking the module's `pool` import straight to the adapter's exports) looks like an optimisation and is a correctness bug three ways: the host must interpose policy between `derive-header` and `submit`; wasmtime fuel is per-store, so host-in-the-middle is what keeps module work on the module's meter and adapter work on the adapter's; and an adapter trap must not poison the calling module (separate stores, separate restart policies). Every hop is module to host to adapter, and the SDK's job is to make that feel like a typed function call. |
There was a problem hiding this comment.
The rule does not address the case where a strategy module needs to use multiple venues in a single logical operation (e.g., a cross-venue arbitrage that must submit to venue A and venue B atomically, or fail gracefully). With the current pool interface, the module submits twice and handles partial failure. That is probably the right answer, but the atomicity story is silently delegated to the strategy author.
| interface types { | ||
| variant settlement { | ||
| evm-chain(u64), | ||
| offchain(string), // jurisdiction / venue-defined domain |
There was a problem hiding this comment.
This offchain(string) is likely to become a registry of venue-specific domain identifiers, and the guard will likely accumulate pattern-matching on it. A structured type (even just an enum with an other(string) arm) would be easier to extend safely.
| - Interactive signing acquires a latency budget shared by simulation and analysers. Deadlines are enforced with the existing metering machinery (fuel, epoch interruption); partial verdicts render as "analysis incomplete" per profile. | ||
| - The engine grows three surfaces: the `simulate` primitive with a backend seam, the fact-bundle assembly, and the analyser host-call path with deadline scheduling. The analyser world finally gives the experimental `query-module` lineage a shipping consumer. | ||
| - Verdict aggregation across multiple analysers starts as max-severity-wins; contradictions and reputation are open questions recorded in the design doc. | ||
| - Every embedder profile (server, wallet, super-app) must declare its fail-open/fail-closed matrix; the embedding API exposes verdicts and consent hooks so wallets render them natively. |
There was a problem hiding this comment.
Before the guard ships, that API needs a concrete WIT sketch -- even a rough one -- because it is the surface wallets depend on, and "explicit configuration" without an API shape is an extensibility promise that cannot be verified.
| native-token(settlement), | ||
| erc20(tuple<u64, list<u8>>), // (chain, address) | ||
| erc721(tuple<u64, list<u8>, list<u8>>), // (chain, address, id) | ||
| erc1155(tuple<u64, list<u8>, list<u8>>), |
There was a problem hiding this comment.
| erc1155(tuple<u64, list<u8>, list<u8>>), | |
| erc1155(tuple<u64, list<u8>, list<u8>>), // (chain, address, id) |
| // narrow, manifest-declared, per-adapter-scoped imports: | ||
| import http; // e.g. the CoW adapter: api.cow.fi only | ||
| import messaging; // e.g. a Waku venue: its content topics only | ||
| import chain; // read-only lookups where needed |
There was a problem hiding this comment.
nitpick: for more clarity
| import chain; // read-only lookups where needed | |
| import chain; // on-chain state reads; no signing, no send |
| interface types { | ||
| variant settlement { | ||
| evm-chain(u64), | ||
| offchain(string), // jurisdiction / venue-defined domain |
There was a problem hiding this comment.
A separator makes confusing when each one applies and when, removing jurisdiction.
Note: The prose section already says venues define this string, so the "why a string" question is answered elsewhere.
| offchain(string), // jurisdiction / venue-defined domain | |
| offchain(string), // venue-defined domain |
| wants: list<asset-amount>, | ||
| valid-until: option<u64>, | ||
| settlement: settlement, | ||
| authorisation: auth-scheme, // eip712 | eip1271 | presign | offchain-sig | unsigned |
There was a problem hiding this comment.
Accuracy risk: auth-scheme is not defined anywhere in the snippet or in the PR. If auth-scheme gains or loses arms, the comment will silently drift.
| authorisation: auth-scheme, // eip712 | eip1271 | presign | offchain-sig | unsigned | |
| authorisation: auth-scheme, // see auth-scheme variant; |
| interface pool { | ||
| submit: func(venue: string, body: list<u8>) -> result<receipt, venue-error>; | ||
| status: func(venue: string, receipt: receipt) -> result<intent-status, venue-error>; | ||
| cancel: func(venue: string, receipt: receipt) -> result<_, venue-error>; |
lgahdl
left a comment
There was a problem hiding this comment.
Adding a few observations
The M0 train adds ADR-0011 (per-interface typed errors), which collides with the egress-guard record this branch introduced at the same number. Renumber it to 0012 and update the two cross-references so both records land with distinct numbers.
…sions Updates docs/09 + ADR-0010 + ADR-0012 against the shipped M1 tree and the seven platform decisions (2026-07-14): marks the shipped surfaces as shipped (venue-adapter kind, nexum:intent/pool, nexum-venue-sdk, #[nexum::venue], echo-venue, cow-venue, ADR-0013 Verdict seam), reframes the egress-guard pipeline as target design deferred to the egress-guard epic (M1 ships an advisory AllowAllGuard no-op, R3), corrects the world-composition category error (an adapter targets one world + wasi:http; there is no "cow-protocol WASI"), and folds in transport-only adapters (Q1), opaque host status (Q2/R6), install-time schema handshake (R7), EVM-only 0.1 (Q5), and #[venue] as the blessed path (Q6).
2a49526 to
23aa95d
Compare
What
Adds
docs/09-intent-architecture.mdplus ADR-0010 (venues as dynamically installed adapter components; CoW becomes the first adapter) and ADR-0011 (one egress guard pipeline for intent submissions, EIP-712 signing, and transaction signing, with analysers as installable components andsimulateas a pluggable host primitive).A second commit folds in the red-team amendments: authorisation-source classification in the guard (pre-authorised EIP-1271 flows are audit-plus-advisory, never prompted or blocked, with the limits-are-observability caveat stated plainly), chain routing carried in venue bodies with
settlementexposed in the derived header, appData travelling as the on-chain hash with no venue read path (see #133 for the corresponding 0.2 code cleanup), a read-onlyqueryverb deferred to open questions, and a full SDK-surfaces section: the two authoring personas and crate map, the one-crate-per-venue rule, borsh as the body codec with venue schemas as language-neutral specifications (golden vectors, classification tables as data), the no-composition rule (module to host to adapter, for policy interposition, fuel attribution, and fault isolation), metering attribution including router-level per-caller quotas, and the non-Rust module path via componentize-py.Why
Closes #131. Records the July 2026 architecture review outcome: the CoW integration generalizes into a venue-adapter layer over a shared
nexum:valuevalue-flow vocabulary, venues become user-installable without host releases, and the wallet embedding's signing analysis unifies with intent policy in a single guard pipeline. ADR-0010 supersedes the compiled-in backend framing of ADR-0005; the strategy-vs-protocol boundary of ADR-0006 is preserved.Testing
Documentation only; no code paths changed. Checked for style conformance (no em-dashes, no hard-wrapped paragraphs, no issue references inside the docs).
AI Assistance: Claude Code used for drafting the design doc and ADRs from the architecture review discussion.