covenant-said-bridge: register, lookup, verify (live mainnet)#109
covenant-said-bridge: register, lookup, verify (live mainnet)#109mizuki0x wants to merge 14 commits into
Conversation
…rify/validate-work)
…e-work as pending sdk surface
- agent_card/xchain: validate base58 wallet/address + chain charset before interpolating into the REST path (closes a path-injection vector) - cursor: confirm requires tx_sig IS NULL, no silent overwrite; next_index uses checked_add for u64 saturation; new test pins the double-confirm - anchor: refuse to submit when a prior claim is still pending so a failed worker leaves the cursor recoverable instead of poisoned; tracing::error before bubbling a confirm failure after a successful on-chain submit so the operator has the tx_sig to reconcile; atomic single-write JSONL line - worker: cap subprocess stdout 1 MiB / stderr 256 KiB; first parseable envelope wins (a trailing log line no longer shadows a real success); ok=false always routes to Upstream with a Worker name fallback, leaving BridgeError::Rest reserved for the reqwest layer - rest: cap response body via content-length pre-check + post-read guard - config: reject COVENANT_SAID_API_BASE_URL that is not http(s) at parse - worker (TS): stdin read times out (default 10s, env-overridable); stdin capped at 1 MiB; register-agent payload validated (string, non-empty, http(s) URL, ≤200 chars); BigInt-safe JSON replacer - index (TS): said-sdk calls race a 30s timeout; getVerified fetches the real slot via getTransaction instead of hardcoding 0 - keypair (TS): validate JSON array shape, length=64, byte range before fromSecretKey; wrap web3 errors with a Covenant-prefixed message - covenantd: said_status has_signer now stat()s the keypair file path, not just whether the env var is set; said_anchor + said_anchor_status refuse to materialise the cursor on a disabled bridge; said_anchor_status gains the same not-wired guard every other verb has; verb-prefix on said_anchor-status corrected
- packages/said-bridge: add README with the worker calling convention, the full env-var inventory, the cluster default + how to flip it, and the daemon vs shell modes a cold reader needs to onboard - worker (TS): BridgePayloadError for input-shape failures so the error envelope's 'name' carries class info instead of generic 'Error' - worker (TS): explicit 'expected a JSON payload on stdin' when a verb that needs a body is invoked with no stdin (previously the missing body surfaced as 'metadataUri must be a non-empty string' which sent cold users hunting for an argv format that doesn't exist) - worker (TS): hasSigner now stat()s the keypair file instead of just checking env presence — a typo'd path no longer claims a usable signer - config: accept COVENANT_SAID_ALLOW_PAID_VALIDATE_WORK alongside the legacy ..._VALIDATE so the gate name matches the verb (validate-work) on both Rust and TS sides
- zero em dashes in scope (was 9 across 3 test files); parentheticals or periods where the em dash narrated - lib.rs module header drops the false 'off-chain register' claim that was deleted in c71db2c of the original said-bridge spike - cursor pub fn docs trimmed from 2-3 narrating lines to single-line contract statements - said_dispatch module header collapsed to 4 lines; one verbose stub-fn comment replaced with the single non-obvious why (whitespace-split env) - rest_roundtrip module header + serve_once doc trimmed similarly - worker error fallback reads 'worker error (no message)' instead of the polite-AI 'worker reported an error' - said paths handler error says '$COVENANT_HOME is unset' instead of hedging with 'cannot resolve'
Matches the docs/integrations/krexa.md format. Covers the env contract, the read-only vs paid-gated surface, the anchor cursor invariants, and the said-sdk dependency posture (stale account-size constant + missing submitAnchor/validateWork) so a reviewer has a single place to read instead of grepping the diff.
Reviewer guideAddressing the diff-size and review-surface notes: this PR is large because it touches a crate, a TS worker, IPC, the daemon, and the CLI in one cut. To make the review tractable, here is a path through the diff that mirrors how the bridge composes at runtime. Start at the new docs. Then read in dependency order:
On the said-sdk dependency risk (acknowledged, but worth being explicit): the bridge surfaces the 4 paid verbs even though the SDK only ships 2 of them today. The two findings going back to the SAID team (the stale Test totals:
Happy to split this if the reviewer would rather see the crate alone first, then the daemon wiring as a follow-up. Left as a single PR because the dispatch tests in |
The harden round-3 reflow formatted the crate but missed the `covenant said` CLI arm in crates/covenant/src/main.rs and the covenantd boot/dispatch wiring, so cargo fmt --check flagged those hunks on CI. Reflow them with stable rustfmt (1.8.0); scoped to the said-authored lines only. No behaviour change; the 22 said_dispatch tests still pass.
Summary
Bridge between Covenant and SAID Protocol (Solana Agent Identity). Lets a Covenant agent register, get verified, and resolve identity through SAID's program + REST API. Replaces the paused #86 — that branch was forked 158 commits behind main and the said-sdk API has narrowed since.
Live mainnet, our agent on SAID:
AdChcSmDKX57rU9qChMJ3MKnqNZbmiQAjuns9VCjzqRbWexMEMKv1dRttTGcZUdn6ugxzvdUnjrtj5c1iEZLhDC(owned by SAID program5dpw6KEQPn248pnkkaYyWfHwu2nfb3LUMbTucb6LaA8G)RegisterAgenttx:3tD6Ab8PvhzBdQmAXRA5A368JSB1u1pH3XkGRbS3aMzcdZUh8jZ9ZKQjHh4f5TRv6ejhCg9UzEaNCm9itJMDZTV6(finalized)VerifyAgenttx:479fec3iqhmYGa9FVU3D7AzvRnzA7QgMcpnezMT4zkn88axS9LHS1wsTyoaUF6ZkzAJB3kCTEHnotjSYLP3Ymr1E(finalized; isVerified=1 at byte 136 of the PDA data)What ships
covenant-said-bridge(Rust):Config, paid-gate guards, REST client for/api/agents/:wallet+/xchain/{inbox,free-tier,message}, SQLite-backed anchor cursor, subprocess worker invoker.@covenant/said-bridge(TypeScript): subprocess worker that drivessaid-sdkfor the on-chain instructions.status,register-agent,get-verifiedwork live against5dpw6KEQ…on mainnet today.submit-anchorandvalidate-workreturn a cleanBridgeUnsupportedErrorenvelope because they aren't in the published SDK surface (see below).covenantdwiring:said_bridge: Option<SaidBridge>field, 10 dispatch handlers, IPCSaid*variants, boot wiring inmain.rs.covenant said …CLI verbs:status,register,verify,validate-work,lookup,anchor,inbox,free-tier,send,anchor-status.landing/public/.well-known/said-agent.json: hosted at https://opencovenant.org/.well-known/said-agent.json. Already served viaf432f538to main so register_agent could resolve it.Architecture
~/.config/solana/id.json) signs Covenant settlement. SAID owner key (COVENANT_SAID_KEYPAIR) signs SAID instructions. Live run used the existing Covenant agent identity (covenant-agent.json→AdChc…).@covenant/sap-bridge.cursor.rsand is fully covered; the on-chain submit path is the part waiting on the SAID SDK.)Notes for the SAID team (cc @kaiclawd)
Two real findings worth flagging:
said-sdk@0.3.4AGENT_ACCOUNT_SIZE is stale. The on-chainAgentaccount for our PDA is 342 bytes; the SDK'slookup/lookupByPDAreject anything that isn't 263 bytes, so freshly-registered agents look like they don't exist via the SDK. We have a working manual parse — happy to PR the SDK fix once we know the new authoritative struct layout (we readdisc[8] | owner[32] | authority[32] | uri_len[u32] | uri[..] | registeredAt[i64] | isVerified[u8] | verifiedAt[i64]+ trailing space). OurgetAgentagainstAdChc…returnednullfrom the SDK while the on-chain PDA reads asisVerified = true.submitAnchorandvalidateWorkaren't exposed by the SDK. We built the full bridge surface and gates anyway and gracefully error today — if you ship those instructions, the bridge picks them up the moment we bump the peer dep.What we deliberately don't do
cov9UDyp…, separate slash authority).inbox/sendare explicit CLI verbs, routing throughcovenant-a2ais a follow-up.Test plan
cargo build(workspace) cleancargo test -p covenant-said-bridge— 49/49 (30 unit + 11 worker_roundtrip + 8 rest_roundtrip)cargo test -p covenantd --test said_dispatch— 21/21pnpm -F @covenant/said-bridge typecheck+pnpm -F @covenant/said-bridge build— clean,dist/worker.jsshipsapi.saidprotocol.com: lookup, free-tier, inbox confirmed shape-compatible (a few benign trailing fields the bridge ignores viaserde(default))register_agentagainst5dpw6KEQ…on ourAdChc…identity — PDA created, finalizedverify_agent(0.01 SOL fee) — finalized, isVerified=1 on chainsubmit_anchor— blocked on SDK surfacevalidate_work— blocked on SDK surface