auth-yes/tasks/new/2026-0827.02.gem.feat.security.p2p-vouching-and-web-of-trust-1040.md

6.8 KiB

TASK METADATA

  • Target Files:
    • server/db.ts
    • server/routes/events.ts
    • server/routes/sessions.ts
    • server/routes/forward_auth.ts
    • ui/components/events/VouchingModal.tsx
    • ui/components/sessions/EventGuestsDrawer.tsx
    • ui/components/sessions/SessionsScript.tsx
    • server/tests/vouching.test.ts
  • Core Objective: Implement a decentralized Peer-to-Peer (P2P) Vouching Web of Trust for high-security events, where joining guests enter a quarantined read-only state until verified by a peer or host via QR/Emoji pairing, with recursive cascade revocation.
  • Dependencies:
    • Existing event_passes schema and users.event_pass_id relational linking.
    • Valkey L1/L2 cache for real-time session state propagation.
  • Additional Important Notes:
    • Must remain opt-in per event via require_vouching BOOLEAN DEFAULT FALSE.
    • UI must remain 100% React-free Hono SSR JSX with vanilla JavaScript DOM state manipulation.

2. Architectural Considerations & Risks

Risks

  1. Vouching Graph Cycles & Endless Loops: If attendee A vouches for B, B vouches for C, and C attempts to vouch for A, cyclical graphs could corrupt hierarchy metrics or create infinite loops during cascade revocation.
    • Mitigation: Enforce an acyclic tree constraint. A user can only be vouched for once (setting an immutable vouched_by UUID pointing to their verified parent). Vouchers must already be in an active (non-quarantined) state.
  2. Cascade Revocation Overhead & Blast Radius: If an organizer revokes a rogue voucher at the root of a large subtree, revoking dozens of downstream guest sessions could block the event loop or leave orphaned cache records.
    • Mitigation: Use a PostgreSQL Recursive Common Table Expression (CTE) to fetch all downstream descendant session IDs in a single atomic query (WITH RECURSIVE subordinates AS (...)), followed by bulk atomic cache eviction in Valkey using pipelined UNLINK.
  3. UX Friction in Standard Workshops: Mandating vouching for open public demos or casual workshops adds unnecessary friction.
    • Mitigation: Make P2P vouching an opt-in toggle (require_vouching) in the [ Delegate Session ] creation drawer.

Alternatives

  • Centralized Host-Only Approval Queue: Instead of P2P vouching, all attendees could wait in a lobby for the host to click "Approve". While simpler, this creates an operational bottleneck for workshops with 50+ attendees. P2P vouching allows any already-admitted participant to onboard their neighbor, enabling rapid, distributed verification.
  • WebRTC / Bluetooth Proximity: We considered WebRTC or WebBluetooth for physical co-location verification. However, browser support and permission prompts make this fragile. A 3-emoji visual sequence + 1-click QR code camera scan provides instant, high-friction-to-bots physical co-presence verification with zero native permission dependencies.

3. Proposed Implementation

Phase 1: Database Schema & Entity Relationships (server/db.ts)

  1. Add vouching metadata columns to event_passes:
    • require_vouching BOOLEAN DEFAULT FALSE
    • vouch_depth_limit INT DEFAULT 2 (0 = host-only, 1 = direct guests only, 2+ = transitive)
  2. Add verification tracking to users:
    • is_quarantined BOOLEAN DEFAULT FALSE
    • vouched_by UUID REFERENCES users(id) ON DELETE SET NULL
    • vouched_at TIMESTAMP WITH TIME ZONE
  3. Add ephemeral vouching challenge table vouch_challenges:
    • id UUID PRIMARY KEY DEFAULT gen_random_uuid()
    • target_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
    • event_id UUID NOT NULL REFERENCES event_passes(id) ON DELETE CASCADE
    • emoji_sequence TEXT NOT NULL (e.g. 🚀-🦊-⚡)
    • pairing_code TEXT UNIQUE NOT NULL (6-char alphanumeric code or QR token)
    • created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
    • expires_at TIMESTAMP WITH TIME ZONE NOT NULL

Phase 2: Ingress Quarantine & Verification API (server/routes/events.ts)

  1. Quarantined Ingress:
    • In POST /api/join and GET /join/:slug, if event.require_vouching = TRUE, set users.is_quarantined = TRUE and tag the session with custom scope ['guest:quarantined'].
    • Issue a vouch_challenge returning the 3-emoji sequence, QR payload, and pairing code.
  2. ForwardAuth Guard:
    • In server/routes/forward_auth.ts, check if the session contains guest:quarantined. If quarantined, block access to internal apps (return 403 or redirect to /vouch/pending).
  3. Vouching Endpoints:
    • POST /api/events/:id/vouch/verify:
      • Authenticated voucher submits the 3-emoji sequence or scans the QR pairing token.
      • Verify the voucher is an active (non-quarantined) participant of the same event.
      • Check vouch_depth_limit.
      • Atomically update users.is_quarantined = FALSE, set users.vouched_by = voucher.id, delete the challenge, and broadcast session upgrade to Valkey.

Phase 3: Recursive Cascade Revocation (server/routes/sessions.ts)

  1. Refactor DELETE /api/sessions/:id:
    • If the target session belongs to a guest user who has vouched for others, execute a recursive CTE query:
      WITH RECURSIVE tree AS (
        SELECT id, event_pass_id FROM users WHERE id = ${targetUserId}
        UNION ALL
        SELECT u.id, u.event_pass_id FROM users u
        INNER JOIN tree t ON u.vouched_by = t.id
      )
      SELECT s.id as session_id, t.id as user_id FROM sessions s
      JOIN tree t ON s.user_id = t.id;
      
    • Delete all discovered sessions in PostgreSQL and evict all matching keys from Valkey.

Phase 4: UI & Mobile Pairing Interface

  1. Attendee Waiting Screen (ui/components/events/QuarantineLobby.tsx):
    • Render large high-contrast 3-emoji sequence and QR code: "Show this to the workshop host or an admitted attendee to unlock full access".
    • Poll or listen for activation event via SSE/fetch.
  2. Voucher Action Modal (ui/components/events/VouchingModal.tsx):
    • Admitted attendees see a [ 🤝 Vouch for Peer ] button in their top bar or Guest Drawer.
    • Opens simple camera QR scanner or 3-emoji selector grid to confirm the peer in front of them.
  3. Guest Drawer Web of Trust Tree:
    • In EventGuestsDrawer.tsx, show vouching tree indentation / badge (Vouched by Tyler G).

Phase 5: Quality Gates & Integration Testing

  1. Authored unit and integration tests in server/tests/vouching.test.ts:
    • Verify unvouched attendees cannot access ForwardAuth-protected endpoints.
    • Verify valid emoji/QR verification lifts quarantine immediately.
    • Verify cascading revocation cleans up all child and grandchild sessions recursively.
  2. Run deno fmt, deno task lint, deno task check, and deno test -A --no-check.