150 lines
6.8 KiB
Markdown
150 lines
6.8 KiB
Markdown
# 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:
|
|
```sql
|
|
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`.
|