Tracking epic for how Deckard connects to third-party dapps. Decision record and cited evidence are on main:
Decision (summary)
Deckard's model: key in deckard-signerd, everyone else is a key-less proposer submitting typed Intents; the daemon's policy gate decides; the human approves via a native clear-signing card (deckard-mcp is the reference proposer).
- Reject the embedded in-app browser —
wry = three engines (WebView2/WKWebView/WebKitGTK); WebKitGTK on Linux patches via distro lag with default-off renderer sandbox + a live 2025 RCE stream; Tauri itself says "don't load untrusted remote content." Largest, worst-patched TCB next to the keys.
- Reject + shelve WalletConnect — centralized Reown relay + Project-ID metadata leak (against offline-first/privacy), a QR/relay UX we don't want, and no maintained Rust wallet SDK. Rationale recorded; not on the roadmap.
- Pursue universal reach via a Deckard-native bridge we own end-to-end — a first-party, key-less connector injects a standard EIP-1193/EIP-6963 provider into the user's browser and forwards over a Deckard-owned local wire (native messaging preferred) to a key-less proposer mirroring
deckard-mcp. No relay, no embedded engine, no store as a trust anchor. Native, local, instant approval UX.
Honest cost: a browser connector is desktop-only, so mobile universal-reach is left unsolved (consistent with "don't compromise desktop for it"). Supply-chain containment: a fully compromised connector can only propose — never sign, self-approve, or exfiltrate a key.
Amendment (2026-07-10 — ADR 0006, via #202)
What actually shipped for the alpha demo (#62/#63/#139/#141/#146/#148/#198) is a 127.0.0.1:8765 loopback bridge — the wire ADR 0001 had named the rejected alternative. ADR 0006 accepts it as the temporary, experimental alpha transport (no further hardening; acceptable only under the testnet-keys-only posture in SECURITY.md). Native messaging stays the post-audit end state — the spike (#49) and PRD-04 (#50) now describe the replacement of the interim wire, not the first wire. The connect-with-no-approval gap is assigned to #48; per-site revoke is a session-scoped disconnect, not a ban (#199).
Phasing
- Phase 0 — curated dapps as native Rust integrations (no web transport). → PRD-03
- Phase 1 — foundations (independent, ship on their own): resolver auth (PRD-01) + clear-signing v2 / message signing (PRD-02)
- Interim (alpha, shipped) — localhost loopback bridge as the temporary transport (ADR 0006) — not hardened, replaced by Phase 2
- Phase 2 (post-audit) — Deckard-native bridge (PRD-04, starts with the spike) + per-origin permissions & registry (PRD-05); replaces the interim localhost wire
Shared Definition of Done (every child issue)
cargo fmt --all --check clean.
just check green — clippy -D warnings, default and --features tray.
cargo test --workspace green.
- No new/changed deps (
Cargo.toml/Cargo.lock) unless explicitly approved.
deckard-core lints respected (no unwrap/expect/panic!/raw indexing in non-test code; #[forbid(unsafe_code)]); untrusted bytes via the bounded Reader.
- No secret logged or
Debug-printed (Zeroizing).
- Every new user-facing action registers a ⌘K
Command (palette_commands.rs + Shell::run_palette_command).
- Any UI matches
DESIGN.md (clear-signing card anatomy, amber=human/cyan=agent, danger-early, hold-to-confirm).
Children (recommended order)
PRD-01 + PRD-02 in parallel → PRD-03 → PRD-05 → spike → PRD-04 (gated on external audit per SECURITY.md).
(Checklist linked to sub-issues below once created.)
Tracking epic for how Deckard connects to third-party dapps. Decision record and cited evidence are on
main:docs/adr/0001-dapp-connectivity-architecture.md(the decision)docs/adr/0006-localhost-dapp-bridge-transport.md(2026-07-10 amendment: the alpha's interim transport)docs/research/10-dapp-connectivity.md(five-angle research synthesis, cited)Decision (summary)
Deckard's model: key in
deckard-signerd, everyone else is a key-less proposer submitting typedIntents; the daemon's policy gate decides; the human approves via a native clear-signing card (deckard-mcpis the reference proposer).wry= three engines (WebView2/WKWebView/WebKitGTK); WebKitGTK on Linux patches via distro lag with default-off renderer sandbox + a live 2025 RCE stream; Tauri itself says "don't load untrusted remote content." Largest, worst-patched TCB next to the keys.deckard-mcp. No relay, no embedded engine, no store as a trust anchor. Native, local, instant approval UX.Honest cost: a browser connector is desktop-only, so mobile universal-reach is left unsolved (consistent with "don't compromise desktop for it"). Supply-chain containment: a fully compromised connector can only propose — never sign, self-approve, or exfiltrate a key.
Amendment (2026-07-10 — ADR 0006, via #202)
What actually shipped for the alpha demo (#62/#63/#139/#141/#146/#148/#198) is a
127.0.0.1:8765loopback bridge — the wire ADR 0001 had named the rejected alternative. ADR 0006 accepts it as the temporary, experimental alpha transport (no further hardening; acceptable only under the testnet-keys-only posture inSECURITY.md). Native messaging stays the post-audit end state — the spike (#49) and PRD-04 (#50) now describe the replacement of the interim wire, not the first wire. The connect-with-no-approval gap is assigned to #48; per-site revoke is a session-scoped disconnect, not a ban (#199).Phasing
Shared Definition of Done (every child issue)
cargo fmt --all --checkclean.just checkgreen — clippy-D warnings, default and--features tray.cargo test --workspacegreen.Cargo.toml/Cargo.lock) unless explicitly approved.deckard-corelints respected (nounwrap/expect/panic!/raw indexing in non-test code;#[forbid(unsafe_code)]); untrusted bytes via the boundedReader.Debug-printed (Zeroizing).Command(palette_commands.rs+Shell::run_palette_command).DESIGN.md(clear-signing card anatomy, amber=human/cyan=agent, danger-early, hold-to-confirm).Children (recommended order)
PRD-01 + PRD-02 in parallel → PRD-03 → PRD-05 → spike → PRD-04 (gated on external audit per
SECURITY.md).(Checklist linked to sub-issues below once created.)