wip(rls): engine-side fail-loud + harness seed identity [pin: work interrupted]

Design correction discovered by the auth chicken-and-egg: the
decorator-level refusal was re-created a convention gate (plan B
thinking). ScopedDB now NEVER refuses — it declares the GUC when the
context carries an identity and declares nothing otherwise; the ENGINE
is the fail-loud layer (policies raise on governed tables, ungoverned
tables like auth lookups simply work). rls.WithInternal removed as a
dead concept.

- search service's raw DBTX path now receives the ScopedDB itself
  (main.go + harness) — a bare pool bypassed the GUC.
- database/schema.go: migration tx declares a synthetic identity
  (schema.sql's dedup DO block SELECTs media_items; with policies
  live, boot 2 raised 'unrecognized configuration parameter' — A11).
- harness: TestServerSetup.DB is a raw-pool SEED handle (write
  policies permissive, other seeded tables ungoverned); the server
  under test keeps the scoped wiring. 33 CreateMediaItem sites use
  seedCtx() — a seed identity with no visibility rows — because
  RETURNING requires SELECT visibility of the new row.
- invariant test: regex UUID-scraping replaced with a structured JSON
  decode of the sections payload.

INTERRUPTION NOTE: an edit to this same effect (seedCtx) was applied,
confirmed by the tool, and subsequently vanished from disk while an
'opencode serve' process (PID 84551) is alive on the machine. This
commit pins the tree against further loss; implementation is PAUSED
pending the user killing that process.
This commit is contained in:
John O'Keefe committed 2026-10-29 08:44:12 -04:00
1 parent 004d4197a4
commit 8c3d919aa0
19 files changed
+124 -111

No files matched your search

+3 -28
View File
@@ -21,20 +21,9 @@ import (
type ctxKey int
const (
// CtxKeyUserID is the context key the auth middleware bridge sets and
// ScopedDB reads. Typed key — no string-key collisions.
CtxKeyUserID ctxKey = iota
// CtxKeyInternal marks a context as INTERNAL (startup, system
// jobs): the statement runs WITHOUT the RLS identity GUC. This
// grants NOTHING — the policies still apply to the table owner
// (FORCE) — so any query it runs against a POLICY'D table (today:
// media_items) still RAISES at the engine. It only unblocks
// statements on tables RLS does not govern (system_settings,
// users counts, sessions…), which a user-less startup path must
// be able to read. NEVER wrap a request context with this.
CtxKeyInternal
)
// CtxKeyUserID is the context key the auth middleware bridge sets and
// ScopedDB reads. Typed key — no string-key collisions.
const CtxKeyUserID ctxKey = iota
// FromContext returns the authenticated end user's UUID.
// ok=false means the caller forgot the middleware bridge — the
@@ -55,20 +44,6 @@ func WithUser(ctx context.Context, uid pgtype.UUID) context.Context {
return context.WithValue(ctx, CtxKeyUserID, uid)
}
// WithInternal marks a context as internal (startup paths, system
// settings): statements run WITHOUT the RLS GUC. Grants nothing —
// policy'd tables still raise at the engine; only RLS-ungoverned
// tables become readable. See CtxKeyInternal.
func WithInternal(ctx context.Context) context.Context {
return context.WithValue(ctx, CtxKeyInternal, true)
}
// IsInternal reports whether the context opted into internal semantics.
func IsInternal(ctx context.Context) bool {
v, _ := ctx.Value(CtxKeyInternal).(bool)
return v
}
// DeclareUser sets the RLS identity GUC inside an ALREADY-OPEN
// transaction. The `true` argument is is_local: transaction-scoped,
// evaporating at COMMIT/ROLLBACK — NEVER session-scoped on a pooled