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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user