Skip to content

Inventory: Phase 1 (schema + RLS + audit trigger) + Phase 2 (auth + roles) — gates passed#6192

Merged
Addisons820 merged 11 commits into
mainfrom
claude/phase-2-auth-roles-rls-sISt0
May 31, 2026
Merged

Inventory: Phase 1 (schema + RLS + audit trigger) + Phase 2 (auth + roles) — gates passed#6192
Addisons820 merged 11 commits into
mainfrom
claude/phase-2-auth-roles-rls-sISt0

Conversation

@maximdolphin
Copy link
Copy Markdown
Contributor

Draft — in progress, gates not yet passed. Review surface for the inventory auth/roles work.

Phase 1 — Schema + RLS + audit trigger (this commit set)

Source-of-truth SQL authored in inventory/sql/ for the owner to run in the Supabase SQL editor (Claude has no privileged DB access; service_role forbidden).

  • inventory/sql/phase1.sqlprofiles, items, field_definitions, change_log; current_user_role() (SECURITY DEFINER, SET search_path='', no params, returns role for auth.uid() only, EXECUTE → authenticated only); handle_new_user trigger (auto-provision viewer) + one-time admin elevate; log_item_change audit trigger (who/what/when, field-level before→after); change_log immutability (RLS no-UPDATE/DELETE + BEFORE U/D trigger that RAISEs); RLS ENABLE (not FORCE) on every table; least-privilege policies scoped to role and operation; no USING(true); anon default-denied.
  • inventory/sql/proofs/phase1_proofs.sql — privileged-path immutability proofs + broken-vs-fixed, inside one BEGIN…ROLLBACK (no artifacts).
  • inventory/sql/proofs/anon_checks.md — anon-returns-[] (proof Round 27 — plugin API + governance split + memory-in-repo #3) + logged-in-user-refused-by-RLS (proof deps: Bump FsUnit.xUnit from 7.1.0 to 7.1.1 #1 client path).
  • inventory/sql/RUN.md — run order + owner/Claude responsibility split.

Owner-approved decisions: no client INSERT on change_log; single category column; status text+CHECK; changed_at rename; GIN index deferred to Phase 5.

Phase 1 gate (passes only with observed output)

  1. logged-in client UPDATE/DELETE on change_log → refused (RLS 0-rows);
  2. broken-vs-fixed: guard disabled → tamper succeeds, restored on rollback;
  3. unauthenticated anon read on each sensitive table → [].

PROGRESS.md will be updated (Phase 0b → [x] with evidence; Phase 1 stays [ ] until proofs land).

Phase 2 — Auth + roles

Planned next on this branch after Phase 1's gate passes: email/password sign-in (SIGN-IN form only — no sign-up/create-account/reset), getUser()-verified trust, role via current_user_role() single-source, sign-out clears session + DOM/memory. Proofs (a) Viewer edit refused by DB, (b) RLS role per user, (c) sign-out clears rendered data.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX


Generated by Claude Code

claude added 2 commits May 31, 2026 03:12
Source-of-truth migration (inventory/sql/phase1.sql) per spec.md, to be run
by the owner in the Supabase SQL editor.

- Tables: profiles, items, field_definitions, change_log.
- current_user_role(): SECURITY DEFINER, SET search_path='', no params,
  returns role for auth.uid() only; EXECUTE to authenticated (not anon).
- handle_new_user trigger -> auto-provision profile default 'viewer';
  one-time admin backfill/elevate.
- log_item_change audit trigger -> who/what/when field-level before->after.
- change_log immutability: RLS (no UPDATE/DELETE policy) + BEFORE U/D trigger
  that RAISEs (blocks even the privileged owner).
- RLS ENABLE (not FORCE) on every table; least-privilege policies scoped to
  role AND operation; no USING(true); anon default-denied everywhere.

Owner-approved decisions: no client INSERT on change_log; single category
column; status text+CHECK; changed_at rename; GIN index deferred to Phase 5.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
- proofs/phase1_proofs.sql: privileged-path immutability proofs (#1a UPDATE,
  #1b DELETE blocked) + broken-vs-fixed (#2: guard disabled -> tamper succeeds),
  all inside one BEGIN..ROLLBACK so no artifacts persist; returns a results table.
- proofs/anon_checks.md: proof #3 (anon reads return []) + proof #1 client path
  (logged-in user refused by RLS). Public anon key referenced via env var only.
- RUN.md: run order + owner/Claude responsibility split.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copilot AI review requested due to automatic review settings May 31, 2026 03:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

claude added 2 commits May 31, 2026 03:33
CREATE OR REPLACE cannot change a function's return type. A prior
current_user_role() (different return type) existed, so the migration failed at
section 2. Add DROP FUNCTION IF EXISTS ... CASCADE before the create so the
script is fully idempotent across return-type changes; section 7 recreates the
dependent policies regardless.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
…dence

Three proofs landed with observed output:
- #3 anon reads all [] (+ RPC 42501 permission denied) — Claude ran
- A: SQL-editor immutability all PASSED incl. broken-vs-fixed — owner ran
- B: client editor session PATCH/DELETE change_log -> [] / [] / unchanged — Claude ran

Phase 0b [x] (URL + publishable key delivered, admin user created). Decisions log
updated with Phase 1 schema decisions + pre-Phase-3 proof-item cleanup follow-up.
Phase 2 stays [ ].

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copilot AI review requested due to automatic review settings May 31, 2026 04:35
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

The required markdownlint gate failed on PR #6192 because the
"Follow-ups recorded in the Decisions log:" paragraph butted directly
against the list beneath it with no separating blank line (MD032).
Insert the blank line; verified locally with markdownlint-cli2 (exit 0)
using the repo's .markdownlint-cli2.jsonc config.

The other two red lints (tick-history order, backlog ID uniqueness) were
transient install-step flakes — mise 502 Bad Gateway fetching
actionlint@1.7.12 from the GitHub releases CDN — not content failures;
they re-run green on push.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AceHack
Copy link
Copy Markdown
Member

AceHack commented May 31, 2026

🤖 CI fix only — not touching your draft hold.

Pushed 2d9bed804: fixed the one required blocking failure, markdownlint MD032/blanks-around-lists at inventory/PROGRESS.md:248 (the "Follow-ups recorded in the Decisions log:" paragraph needed a blank line before its list). Verified locally with markdownlint-cli2 (exit 0) using the repo's .markdownlint-cli2.jsonc. Re-poll now shows requiredChecks.failed: 0.

The other two reds you may have seen — lint (tick-history order) and lint (backlog ID uniqueness) — were transient install-step flakes, not content failures: mise ERROR 502 Bad Gateway fetching actionlint@1.7.12 from the GitHub releases CDN. The actual lints never ran; they re-run green on push (both non-required regardless).

Left the PR in draft deliberately — inventory/CLAUDE.md says you review and merge every PR and the project is phase-gated with you self-certifying the security checks, so the undraft/merge decision is yours. Ready for your review whenever you choose to take it out of draft.

— Otto (background worker)

… trust, role single-source, sign-out clears data)

inventory/index.html — self-contained static client:
- SIGN-IN form ONLY (no sign-up / create-account / reset-via-signup).
- Trust via getUser() (verified), never getSession(); role via rpc current_user_role()
  (same single source RLS uses); autoRefresh on; password cleared from DOM after login.
- Edit probe button (visible to all roles) -> UPDATE items; DB/RLS decides (proof a).
- One sample-item read as sign-out scaffolding (NOT Phase 3 read path).
- Sign-out: signOut() + null in-memory state + wipe rendered DOM + verify session ended.
- Baseline CSP meta (connect-src pinned to the Supabase host; form-action/object-src 'none').
  SRI + exact-version pin + drop 'unsafe-inline' tracked for Phase 7 (not pulled forward).
- publishable anon key only (public by design).

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copilot AI review requested due to automatic review settings May 31, 2026 05:44
… RLS

Owner-run, rolled-back. Simulates the viewer (set local role authenticated +
request.jwt.claims) and shows the least-privilege items_update policy refuses a
viewer UPDATE (0 rows); adding a permissive USING(true) policy makes it succeed
(1 row = the breach); ROLLBACK restores least-privilege. Mirrors Phase 1 proof #2.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

claude added 2 commits May 31, 2026 06:30
…ess)

Keeps the vendored supabase-js copy + same-origin proof proxy + proof page
(used to drive the real-browser (a)/(b)/(c) proofs around a test-proxy TLS
cert wall) out of the PR. Committed index.html stays CDN-based as approved.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
…~]; burn-test-users -> risk register + Phase 7

Real-browser (Playwright) proofs vs live Supabase:
- sign-in-only UI verified in live DOM (no signup/create-account)
- (b) role per user from current_user_role(): viewer->viewer, editor->editor
  (caught+fixed a profiles data discrepancy: test2 was editor, owner set viewer)
- (a) viewer 'Attempt edit' -> DB REFUSED 0 rows (RLS); editor same button -> ALLOWED (negative control)
- audit row written on the editor UPDATE (Phase-1 trigger confirmed under client edit)
- (c) sign-out -> DOM cleared + in-memory nulled + localStorage token gone + verified getUser() null

Phase 2 left at [~]: owner still runs broken-vs-fixed (a) SQL (privileged set role) before [x].
Risk register + Phase 7 gate updated: burn/rotate all build test users; supabase-js SRI+pin;
drop CSP 'unsafe-inline'.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copilot AI review requested due to automatic review settings May 31, 2026 06:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

claude added 2 commits May 31, 2026 06:56
…OTICE

The Supabase SQL editor surfaces the results grid, not NOTICES, so the
0-row/1-row evidence was invisible. Stash each UPDATE row_count in a txn-local
GUC (survives the set role/reset role switch) and SELECT a 2-row results table
at the end. Same observable-grid pattern as the Phase 1 proof. Still one
BEGIN..ROLLBACK; least-privilege restored, no artifacts.

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
…ce recorded

Owner ran proofs/phase2_rls_brokenfix.sql (results grid): FIXED viewer UPDATE = 0
rows (refused by least-privilege items_update); BROKEN (added USING(true)) viewer
UPDATE = 1 row (breach); ROLLBACK restored least-privilege (no _tmp_permissive_update
persisted). Combined with the real-browser (a)/(b)/(c)+negative-control proofs, all
Phase 2 gate criteria met with observed evidence. Phase 2 [x].

https://claude.ai/code/session_011CojXdZyNjq4449qBWGYJX
Copilot AI review requested due to automatic review settings May 31, 2026 06:59
@maximdolphin maximdolphin changed the title Inventory: Phase 1 (schema + RLS + audit trigger) → Phase 2 (auth + roles) Inventory: Phase 1 (schema + RLS + audit trigger) + Phase 2 (auth + roles) — gates passed May 31, 2026
@maximdolphin maximdolphin marked this pull request as ready for review May 31, 2026 06:59
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Addisons820 Addisons820 merged commit 5a2f183 into main May 31, 2026
29 of 47 checks passed
@Addisons820 Addisons820 deleted the claude/phase-2-auth-roles-rls-sISt0 branch May 31, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants