docs: record the Authorization-strip root cause and the relay ownership fix

Item 1 was not an interception problem and needed no session-aware rule —
the gate was deleting the app's own Authorization header. Item 4's /relay
502 was a root-owned volume, not networking. Both deployed and verified on
archi-dev-box; the extension login itself still needs a human in a browser.
This commit is contained in:
archipelago
2026-08-06 19:48:43 -04:00
parent d9592c7212
commit f1c350040c
+38 -8
View File
@@ -29,12 +29,34 @@ as `archipelago`.
## Ordered work list
### 1. Gate vs app-owned auth (fleet-wide) — HALF DONE
Done + deployed + verified: credential-less allowlist, `manifest.json` 401→200, root and
`/api/auth/*` still 401 (`is_credentialless_public_path`, exact match only, 2 tests).
**Remaining:** the app's own `/api/auth/*` is still intercepted, so IndeeHub cannot establish
its Nostr session. Needs a session-aware rule: once a VALID gate session exists, proxy the
app's auth endpoints through instead of challenging. Security-critical — justify in code.
### 1. Gate vs app-owned auth (fleet-wide) — DONE, deployed + verified (`d9592c72`)
Round 1: credential-less allowlist, `manifest.json` 401→200 (`is_credentialless_public_path`).
Round 2 (2026-08-06 ~19:50) — the actual cause of "Nostr signer doesn't work anywhere".
It was **not** a challenge/interception problem, and no session-aware rule was needed: the
gate was **deleting the app's own `Authorization` header** on every proxied request
(`parts.headers.remove(header::AUTHORIZATION)`, unconditional, `appgate/mod.rs`). IndeeHub
sends `Authorization: Nostr <NIP-98 event>` to its own `/api/auth/nostr/session`; the header
arrived stripped and its backend answered `401 "Authorization header is missing"`. No signer
could ever satisfy that — which is exactly why a NIP-07 **extension in a tab**, the **iframe**
bridge (`nostr-provider.js`) and **AIUI** all failed at once while the signing was fine.
Fix: `authorize()` now reports WHICH credential allowed the request. The header is dropped
only when it WAS the gate's own `Bearer <device token>`; every other scheme (Nostr, Basic,
an app-issued bearer) is forwarded. Mirrors the surgical cookie strip above it. The
credential-less allowlist still drops it (nothing there needs auth).
Live proof on archi-dev-box, authenticated with a real gate session:
- before → `401 {"message":"Authorization header is missing"}`
- after → `400 {"message":"Event is not a valid NIP-98 HTTP auth event"}` — identical to
the same POST on loopback, i.e. the signed event now reaches the app
- unauthenticated → still `401` (gate still challenges; boundary intact)
**Blast radius was much wider than IndeeHub**: 27 apps are gated, and this broke any of them
that authenticate with the `Authorization` header (Vaultwarden, Jellyfin, Nextcloud/WebDAV,
Gitea tokens, Grafana). Same mechanism — not individually retested.
Still needs a human: an actual extension login in a browser.
### 2. AI Data Access grants → node-side (unblocks everything AI)
`aiPermissions.ts` uses `localStorage` (`archipelago-ai-permissions`), which is PER-ORIGIN.
@@ -51,8 +73,16 @@ Fix the pairing + exclude headers; the real answer is structured model output, n
- Content source: films are `projects`, `GET /api/projects` via its nginx on :7778.
Public count on this node is **0**. Port 4000 is not host-mapped. `/graphql` is the SPA.
- Private films need a **Nostr session** (`/api/projects/private` says Cognito is disabled).
- `/relay` is 502 **direct on loopback**IndeeHub's own nginx can't reach its relay
container. Independent of the gate. Fix separately.
- ~~`/relay` is 502 **direct on loopback**~~✅ FIXED 2026-08-06. Not a networking
problem: DNS resolved (`relay` → 10.89.1.3) and nothing was listening. The relay's volume
`/usr/src/app/db` was owned by **root** while nostr-rs-relay runs as `appuser` (uid 1000),
so it crash-looped on `unable to open database file: .../nostr.db`. Repair (volume was
empty, no data at risk):
`podman unshare chown 1000:1000 ~/.local/share/containers/storage/volumes/indeedhub-relay-data/_data`
then `podman restart indeedhub-relay`. DB v18 built; `/relay` now 200 with its NIP-11 doc.
**Same ownership-bug family still open elsewhere** — the reconciler logs
`reconcile failed app_id=btcpay-server error=chown /var/lib/archipelago/postgres-btcpay failed`.
Worth a sweep: rootless volume dirs created root-owned for non-root container users.
### 5. Node-side Nostr signer — the highest-leverage piece
Collapses IndeeHub's private auth, the app-auth half of item 1, and Phase C's zaps into one