Skip to content

Connections: daemon-held dapp sessions, listed in the app, revocable per site #199

Closed

Description

@hellno

Part of #44 — the thin, forward-compatible slice of #48 (registry, anti-phishing, connect-time approval, per-origin policy all stay in #48). This is the ending beat of the browser demo: connect → attribute → revoke → the site is locked out.

Why: a connection you can't see or kill isn't under your control. This makes every connected site visible in the app and revocable in two keystrokes.

Corrected 2026-07-10 (codebase re-verified + scope decision): the structural claims below are confirmed. Decision (2026-07-10): revoke is a session-scoped disconnect, NOT a persistent ban — see "Scope decision". That keeps this light and likely avoids the heavy daemon-held-store move the original design assumed.

Problem

Dapp sessions live only in the bridge process RAM (DappSessionStore, crates/deckard-browser-bridge/src/lib.rs:49-102); its revoke() is dead code (#[allow(dead_code)], lib.rs:91, only caller is the unit test at lib.rs:1395); the app's Connections sidebar is a hardcoded, non-interactive "No connected sites yet" placeholder (shell_chrome.rs:191-199). The app has no channel to the bridge — it speaks only SignerRequest to the daemon, and the bridge is a separate process the app never launches. Sessions are invisible and unkillable.

Scope decision (2026-07-10)

Revoke = disconnect, not ban. Clicking revoke drops the current session so the site is
disconnected; its next request fails 4100 Unauthorized and it must re-run eth_requestAccounts to
get a fresh session. There is NO persistent blocklist and NO long-term "who is banned" memory —
this is the "disconnect" model (like MetaMask), not a permanent block. Rationale: keep it light for the
alpha; the richer per-origin permission/registry model is #48. See ADR 0006 (#202).

Consequence for the build: because a revoked site is simply disconnected (and may reconnect), we do
not need a persistent daemon-held grant store. The lightest thing that gives a real disconnect beat
is enough — expose the bridge's live session list to the app and let the app clear a session:

  • Sessions can stay where they already live (bridge RAM, DappSessionStore) — the missing piece is
    an app ↔ bridge channel to list + clear them (the app speaks only SignerRequest to the daemon
    today, and the bridge is a separate process). The PR settles whether that channel is a minimal
    app→bridge call over the existing loopback or a small daemon relay; pick the smaller diff. The heavy
    daemon-held-store + full Wire-contract evolution: Hello capability discovery + the five evolution rules #31 wire tax the original design assumed is not required for a
    disconnect-only revoke.
  • Wake the dead code: revoke() is #[allow(dead_code)] today (lib.rs:91) — this issue gives it
    a real caller. Keep the 4100 re-connect semantics (lib.rs:364-367).

App: the Connections sidebar lists live origins (mirror the refresh_activity poll shape,
shell.rs:2585-2638); new ⌘K command revoke-site ("Revoke connected site", aliases: disconnect /
revoke site / unlink) in palette_commands.rs + Shell::run_palette_command. A registry test asserts
revoke-all is the LAST entry (palette_commands.rs:416) — insert the new command before the
revoke-all entry (:178) and that assertion still holds.

Lifecycle: Lockdown (RevokeAll) and Lock clear all sessions — the global stop reduces ALL authority
(#203). Bridge/daemon restart clears them too (in-memory).

Reality check (2026-07-10)

Tests

Acceptance

  1. Connect from the fixture dapp → the origin appears under Connections (screenshot).
  2. ⌘K → Revoke connected site → the next dapp request fails 4100; the row is gone (screenshot).
  3. Lockdown clears all sessions.
  4. DoD green; before/after screenshots.

Activity

  1. added 3 commits that reference this issue on Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions