Skip to content

feat(contract): deckard-contract crate + virtual Cargo workspace (freeze-first wire)#3

Merged
hellno merged 2 commits into
mainfrom
hellno/deckard-contract
Jun 7, 2026
Merged

feat(contract): deckard-contract crate + virtual Cargo workspace (freeze-first wire)#3
hellno merged 2 commits into
mainfrom
hellno/deckard-contract

Conversation

@hellno

@hellno hellno commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Closes #2.

Freezes the shared Intent / Decision / Policy types + signer-daemon RPC as a standalone crate with an in-memory MockSigner, so T-Agent / T-UX / the test harness can build and run the acceptance scenario before the real daemon exists. Zero key material.

⚠️ The issue was written against a stale main — adaptations I made

Issue #2 describes a single deck crate (0BSD, crates.io GPUI). main has since: renamed deckdeckard, switched to AGPL-3.0, moved GPUI to Zed-git deps, and already introduced a workspace (root app + crates/deckard-core), plus DESIGN.md/specs/. So I implemented the issue's intent against today's main, deviating from the literal text where it conflicts:

Issue said I did (to match main)
binary stays deck binary stays deckard (renaming it back would be the regression)
0BSD AGPL-3.0-or-later (matches deckard-core)
members = app + contract members = app + core + contract (kept the existing core crate)
git mv src → crates/deckard-app done; also preserved the Zed-git GPUI deps + zeroize

If you'd rather I hadn't moved the app / kept it literal, easy to adjust.

What's here

  • crates/deckard-contract (std + serde): frozen Intent (carries chain_id, no nonce), IntentKind, Decision, Policy, ApprovalMode, and the SignerRequest/SignerResponse/ExecuteResult/ApprovalStatus/BalanceReport RPC enums. A sync Signer trait + a deterministic MockSigner (address 0x11.., tx_hash 0xAB.., request_ids 0x01,0x02,..) implementing the full decision matrix incl. the TOCTOU revoke guard.
  • Virtual workspace: root is now a virtual manifest (default-members = deckard-app so cargo run still launches the GUI; [profile.release] + [workspace.dependencies] at the root). App moved to crates/deckard-app via git mv (pure renames, history preserved).
  • justfile + CI build/test --workspace.
  • Separate docs commit lands docs/build/ + docs/research/ (the specs the contract README points at — they were untracked working-tree files). Droppable if another PR owns them.

Acceptance criteria — all green (verified locally)

  1. cargo build --workspace (1m02s) + cargo test --workspace45 pass / 0 fail (contract 32, core 13, app 0).
  2. cargo run builds/launches the app (target/debug/deckard); just bundleDeckard.app with Resources/icon.icns from crates/deckard-app/assets/ + CFBundleIconFile set.
  3. cargo tree -p deckard-contract normal deps = only alloy-primitives + serde; ciborium/serde_json are dev-deps.
  4. ✅ Every type round-trips serde_json and ciborium (CBOR) byte-stably.
  5. MockSigner: Signer; Box<dyn Signer> usable.
  6. ✅ Decision matrix incl. TOCTOU revoke (over-cap and within-cap Allow paths), unknown-id, double-execute, auto-shield.
  7. ✅ No key material in crates/deckard-contract/src (grep clean; repeat_byte consts, no 64-hex literals).
  8. ✅ README: "Frozen contract owned by docs/build/30-mcp-shape.md — do not redefine these types elsewhere."

Bundle fix worth a look (AC#2)

cargo-bundle 0.11 resolves icon paths against the CWD (not the manifest) and its PNG→icns converter rejects the 1024px icon.png (No matching IconType). So just bundle now cds into crates/deckard-app and bundles the prebuilt icon.icns directly. (main's cargo bundle --release from root would hit the same converter error — this actually fixes it.)

Review

Cross-checked by codex (GPT-5) cross-model review + a 5-agent adversarial workflow. Both found one real latent bug — the MockSigner request-id counter was a u8 with wrapping_add (256th proposal silently collided, defeating idempotency). Fixed: keep the spec-pinned repeat_byte(n) ids, track last_id directly, and panic loudly on exhaustion. Added boundary + within-cap-TOCTOU + exhaustion tests.

Out of scope (separate tickets, untouched)

Real deckard-signerd UDS impl · deckard-mcp server · Railgun calldata · Helios · GPUI approval card · keystore/daemon key handling.

hellno added 2 commits June 5, 2026 20:13
Freeze the shared wire so the agent surface, custody/daemon, and test
harness can build in parallel before the real signer daemon exists.

New crate `crates/deckard-contract` (std + serde, zero key material):
- Frozen types: Intent (carries chain_id; daemon owns the nonce),
  IntentKind, Decision, Policy, ApprovalMode, and the SignerRequest/
  SignerResponse/ExecuteResult/ApprovalStatus/BalanceReport RPC enums.
- A sync `Signer` trait + an in-memory `MockSigner`: deterministic and
  pinned (address 0x11.., tx_hash 0xAB.., request_ids 0x01,0x02,..),
  implementing the full policy decision matrix incl. the TOCTOU revoke
  guard at execute time. `Box<dyn Signer>` works.
- Tests: serde_json + ciborium (CBOR) byte-stable round-trip per type;
  the decision matrix; and the daemon-free slice of 30-mcp-shape T1-T8.
- Normal deps are only alloy-primitives + serde; ciborium/serde_json are
  dev-deps. id minting panics loudly on >255 proposals instead of wrapping.

Workspace: root becomes a virtual manifest (members deckard-app/-core/
-contract, default-members deckard-app so `cargo run` still launches the
GUI; [profile.release] + [workspace.dependencies] at the root). The app
moves to `crates/deckard-app` via `git mv` (binary stays `deckard`).
justfile + CI build/test `--workspace`; `just bundle` bundles the prebuilt
icon.icns from crates/deckard-app/assets (cargo-bundle 0.11 can't convert
the 1024px png, and resolves icon paths against the CWD).
These were authored via the /spec process but were only ever untracked
working-tree files. deckard-contract's README and crate docs point at
docs/build/30-mcp-shape.md as the contract's owner, so land the set the
links resolve to:
- docs/build/ — the parallelizable v1 build specs (30-mcp-shape owns the
  Intent/Decision/daemon-socket contract; 00/10/20 reference it).
- docs/research/ — the research KB + v1-demo-plan the build specs cite.

Additive markdown only. If another PR already owns these, drop this commit.
hellno added a commit that referenced this pull request Jun 6, 2026
…gun's EIP-1193 provider (T-Trustless #3)

Proves the v1 seam end to end (against kohaku@618c53f): Helios's localhost
JSON-RPC server (.rpc_address) is the EIP-1193 provider Railgun reads through.

- IntoEip1193Provider is Kohaku's OWN 7-method trait (eip-1193-provider crate);
  an alloy DynProvider satisfies it via Kohaku's shipped Alloy adapter — no
  custom adapter for v1. Read/sync path = exactly eth_blockNumber + eth_getLogs
  + eth_call, all in Helios's served set.
- One required fix: alloy's Provider::call defaults to the `pending` tag, which
  Helios (light client) can't serve; build the provider with
  ProviderBuilder::with_default_block(BlockId::latest()). One line, Deckard-side.
- Tier-2 (default) drives the adapter + logs methods via a pass-through proxy;
  Tier-1 (--features railgun) links the full railgun ZK crate and drives the real
  RpcSyncer/RailgunBuilder through Helios (414 SyncEvents). railgun compiles
  standalone (retires 10's R1c). Loopback hop ~0.3 ms/call (release).

Findings written into 20-helios-sidecar.md (Integration + Measured + open-questions).
@hellno
hellno merged commit 3f56cbf into main Jun 7, 2026
2 checks passed
hellno pushed a commit that referenced this pull request Jun 14, 2026
PR #52 resolved the dapp-connectivity exploration and created canonical
issues (epic #44, PRD-01..05 #45-50) plus an issues-not-files convention.
Reconcile this doc to a rubric + decision record, not a parallel tracker:

- Replace the work-item sequencing backlog and the EIP-7702 spike brief
  (duplicated existing issue #33) with a Tracking & cross-references block
- Fix the now-outdated connection-surface framing: #25 browser integration
  is planned/phased via the owned EIP-6963 bridge (ADR 0001), not 'no dapps';
  #11 isolation maps to per-origin permissions (PRD-05 #48), not a wire token
- Point #3 clear-signing at PRD-02 #46; note PRD-01 #45 resolver-auth
- Update Stages + status notes to match
hellno added a commit that referenced this pull request Jun 20, 2026
Inverts the signer daemon's auto-approval guardrail from fail-open (armed only on chain_id==1) to fail-safe default-deny (armed on every chain except an explicit exempt testnet/dev allowlist {Sepolia 11155111, anvil 31337}; unknown chain-ids guarded). mainnet_guardrail_active -> guardrail_active, mainnet_override -> autonomy_override (env var DECKARD_I_KNOW_THIS_IS_MAINNET unchanged, now disarms on any real-value chain, documented in THREAT-MODEL.md only). Adds Base-8453 + unknown-id classification tests, an exec-level not_approved test, and a structural pin that 1/8453/10/42161 are never exempt. Broadens README/THREAT-MODEL/CONTRIBUTING/STATUS + agent docs (kept honest: exempt-chain within-cap auto-allow is by-design, default policy still OverCap, residual-risk #3 narrowed not resolved). Guard-shape only; no policy fields, no new deps.

Closes #76. Reviewed via /autoplan (CEO + Eng dual-voice, Claude + Codex).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set up the deckard-contract crate + Cargo workspace (freeze-first wire)

1 participant