docs: add task plan for phase 1 of events guest ingress

Adds the task definition markdown file `2026-0826.01.jul.story.events.phase-1-guest-ingress-1400.md` detailing the architectural scope and plan for enabling Traefik ForwardAuth guest ingress, bridging custom scopes to the Launchpad UI via UNION query, and wiring event claim audit logs using event.id.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot] 2026-08-26 22:00:56 +00:00
parent e9060eee5a
commit 4ec15fe35f

View File

@ -0,0 +1,85 @@
# TASK METADATA
- **Target Files:** `server/routes/auth_forward.ts`, `ui/db_queries.ts`,
`ui/mod.ts`, `server/routes/events.ts`, `server/tests/forward_auth.test.ts`,
`server/tests/events.test.ts`
- **Core Objective:** Implement Phase 1 of the Event & Session Overhaul: Enable
Traefik ForwardAuth guest ingress for scoped event attendees, bridge session
custom scopes to the Launchpad UI, and wire audit logging on event seat
claims.
- **Dependencies:** None.
- **Additional Important Notes:** Follow zero-trust default-deny principles for
ForwardAuth. Guest sessions minting needs to correctly log to the Merkle audit
ledger for SIEM visibility.
---
### 2. Architectural Considerations & Risks
- **Risks:**
- **ForwardAuth Security:** Modifying ForwardAuth to accept `guest` accounts
risks inadvertently allowing guests to access applications they are not
explicitly scoped for. Strict validation of `auth.customScopes` against the
requested `appRecord.name` is critical.
- **Launchpad Leakage:** The `getDashboardApps` query modification must be
robust. If the UNION query is not correctly structured, it could leak
visibility of unregistered or unauthorized apps to guests.
- **Audit Logging Integrity:** Failure to capture the audit events properly
during the `join` phase would break SIEM visibility. Using the non-blocking
`auditWrapper.auditLog` is required to avoid impacting user request latency
while ensuring compliance.
- **Alternatives:**
- We considered a bypass in `ui/db_queries.ts` where guests completely skip
the `grants` JOIN. However, using a `UNION` is structurally superior as it
seamlessly supports both pure guests and regular active users who might hold
delegated scoped passes. It provides a unified data retrieval flow.
### 3. Proposed Implementation
#### Phase 1: ForwardAuth Guest Ingress (`server/routes/auth_forward.ts`)
1. **Permit Guest Accounts:** Update the account status validation to allow
`user.account_status === 'guest'` in addition to `'active'`.
2. **Custom Scopes Validation:** If the user is a guest (or relying on custom
scopes), verify that `auth.customScopes` contains the explicit application
grant (e.g., `app:${appRecord.name}`). If not, return a 403 Forbidden.
3. **Header Injection:** For guest sessions, compute the scopes to inject into
`X-Forwarded-Scopes` by taking the comma-joined list of `auth.customScopes`
(`auth.customScopes.join(",")`). This propagates the app scope (e.g.,
`app:ed-droid`) and role (e.g., `viewer`) to downstream reverse proxies.
#### Phase 2: Launchpad App Query (`ui/db_queries.ts` & `ui/mod.ts`)
1. **Function Signature Update:** Update
`getDashboardApps(userId: string, isAdmin: boolean, customScopes?: string[])`
to accept the session's custom scopes.
2. **UNION Query:** Update the non-admin SQL query to use a `UNION`.
- The first part queries explicitly granted apps via the `grants` table.
- The second part parses the provided `customScopes` array to extract allowed
app names (e.g., stripping the `app:` prefix), querying the `apps` table
for those matching names, and assigning them a pseudo-role such as
`"Guest (Viewer)"` (or extracting the role from the scopes if possible).
3. **UI Integration:** In `ui/mod.ts`, pass `auth.customScopes` from the
resolved session into `getDashboardApps`.
#### Phase 3: Join Audit Logging (`server/routes/events.ts`)
1. **Audit `POST /api/join` (Web):** Immediately after minting the guest
session, invoke
`auditWrapper.auditLog(guestUuid, "event_seat_claimed", event.id, { slug: event.slug, name: event.name, seatNumber: event.seats_claimed, method: "web" }, getClientIp(c))`.
2. **Audit `GET /join/:slug` (CLI):** Similarly, wire
`auditWrapper.auditLog(guestUuid, "event_seat_claimed", event.id, { slug: event.slug, name: event.name, seatNumber: event.seats_claimed, method: "cli" }, getClientIp(c))`
into this endpoint before returning the token.
#### Phase 4: Unit Testing
1. **ForwardAuth Tests (`server/tests/forward_auth.test.ts`):**
- Add tests simulating a guest session with an `app:{name}` scope to ensure a
`200 OK` response with correctly formatted `X-Forwarded-Scopes`.
- Add tests simulating a guest session without the necessary `app:{name}`
scope to ensure a `403 Forbidden` response.
2. **Events Tests (`server/tests/events.test.ts`):**
- Ensure the `POST /api/join` and `GET /join/:slug` assertions capture the
`auditLog` spy calls and verify the exact payload (`event_seat_claimed`
action, `event.id` as resource, correct metadata structure).