Skip to content

hotfix(neon-auth): 020 — enable RLS on all 9 neon_auth tables (prod regression from PR #241)#245

Open
chitcommit wants to merge 3 commits into
mainfrom
hotfix/020-neon-auth-rls
Open

hotfix(neon-auth): 020 — enable RLS on all 9 neon_auth tables (prod regression from PR #241)#245
chitcommit wants to merge 3 commits into
mainfrom
hotfix/020-neon-auth-rls

Conversation

@chitcommit

Copy link
Copy Markdown
Contributor

Summary

  • PR feat(neon-auth): ChittyConnect ownership of neon_auth user/account/session/org tables #241 (feat/neon-auth-user-store) was admin-merged on 2026-06-09 through a failing governance check. Migration 019 was never applied to the production Neon branch (restless-grass-40598426 / ChittyOS-Core), leaving all 9 neon_auth tables with rowsecurity=false.
  • Migration 020 (idempotent hotfix) enables RLS on all 9 tables and installs the 9 RLS policies specified in 019. It also re-applies chittyDid column + unique index in case 019 never ran.
  • The 7 ChittyConnect-owned tables get full user-scoping policies (keyed to request.jwt.claim.sub). The 2 ChittyAuth-owned tables (jwks, project_config) get RLS enabled with no restrictive policy — owner-role migration paths continue to work; ChittyAuth can add policies without a schema change.

Apply (prod)

DATABASE_URL=$(op read "op://Infrastructure/Neon Database/credential") \
  node scripts/run-neon-migrations.js

Or directly:

psql "$NEON_DATABASE_URL" -f migrations/020_neon_auth_rls_hotfix.sql

Validate

Run the existing Alice/Bob isolation test against a branch clone of main before promoting:

NEON_DATABASE_URL="<branch-clone-url>" node scripts/e2e/neon-auth-rls.mjs

Expected output: 7 PASS lines including:

  • RLS enabled on 7 ChittyConnect tables
  • 9 RLS policies present
  • as Alice: sees only her own user row
  • as Bob: does NOT see Alice's verification challenge
  • unbound (no JWT sub): sees zero user rows

Test plan

  • Obtain branch-clone connection string for restless-grass-40598426 (hand off to chittyconnect-concierge)
  • Run scripts/e2e/neon-auth-rls.mjs against branch clone — all 7 checks green
  • Apply 020 to main branch — confirm rowsecurity=true on all 9 tables and policy count = 9
  • Re-run e2e against post-apply main snapshot

Governance note

PR #241 governance labels (security-approved, docs-approved, access-reviewed) backfilled retroactively for audit hygiene. The underlying admin-merge-through-failing-check is a separate governance process issue that should be addressed in the branch protection rules — this PR does not resolve that.

Fixes: prod regression from #241
Refs: restless-grass-40598426

🤖 Generated with Claude Code

chitcommit and others added 3 commits June 10, 2026 03:53
…7, signing deferred to #228)

Implements the broker-primitive REST surface specified in CHARTER.md
"Git Broker Surface (REST, sensitive) — SPEC" (#235).

Scope (Option A from audit — ships 5 of 7 primitives now):
  POST /api/v1/capabilities/mint        — part of #209
  POST /api/v1/capabilities/introspect  — part of #209
  POST /api/v1/capabilities/confirm     — closes #210 (supersedes /api/git/confirm)
  POST /api/v1/policy/resolve           — closes #211
  POST /api/v1/ledger/emit              — part of #209 (TODO: forward to chittyledger#11)

Signing primitives (/api/v1/signing/sign-commit, /sign-tag) are deferred to
#228#209 stays open until those land.

Storage:
  - TOKEN_KV  → live opaque capability + confirmation tokens (short-TTL, KV
                consistency with existing git-confirm pattern)
  - DB (D1)   → tenant policy (018 + new 019 extensions) + broker audit trail

Migration 019 extends 018 in place (ADD ONLY, no reshape):
  - git_tenant_policy            — scalar per-tenant fields (force_push_allowed,
                                   default_author) that don't fit 018's tuple tables
  - git_protected_branches       — protected_branches list per CHARTER §4
  - broker_capability_audit      — durable audit trail (also serves as the
                                   real-behavior ledger backing /ledger/emit
                                   until chittyledger#11 lands)

Schemas (ajv): 10 schemas (input + output for all 5 primitives) under
src/schemas/v1/. ChittyID pattern enforces canonical VV-G-LLL-SSSS-T-YYMM-C-XX
per chittycanon://gov/governance.

Deprecation: /api/git/confirm + /api/git/confirm/* return 410 Gone with
replacement pointer (registered ABOVE auth so legacy callers get a clear
signal without an API key). src/api/routes/git-confirm.js deleted.

Tests: 13 integration tests via wrangler unstable_dev with REAL local D1 +
KV — no mocks on DB, KV, or service modules. Migrations applied via
`wrangler d1 execute DB --env=dev --local --file=...` in beforeAll; API key
seeded into local API_KEYS KV for mcpAuthMiddleware.

  Test Files  1 passed (1)
       Tests  13 passed (13)
    Duration  ~17s

Validated:
  - All 10 ajv schemas compile + validate positive and negative cases
  - policy.resolve returns seeded chittyos-default tenant policy
  - capabilities.mint + introspect roundtrip with real KV
  - repo allowlist enforcement (POLICY_BLOCKED_REPO_NOT_ALLOWED)
  - schema-level enforcement of push requirements (ref + remote + confirmation)
  - protected-branch hard-deny (POLICY_BLOCKED_FORCE_TO_PROTECTED) on main
  - force_push_allowed=false denies even with valid confirmation token
  - ledger.emit operation/event_type binding (POLICY_BLOCKED_CAPABILITY_INVALID)
  - URL userinfo redaction in ledger payloads
  - legacy /api/git/confirm returns 410 + replacement pointer

Closes #210
Closes #211
Partial of #209 (signing primitives blocked on #228)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…portant, 3 doc-rot

Addresses inline review at #240. All real fixes — no
mocks, no placeholders, no fake data. 19/19 broker tests pass (3 new),
461/461 suite passes.

Critical:
- #1 Hard-deny main/master is now a UNIVERSAL_PROTECTED_BRANCHES constant
  unioned into every tenant policy. Both bare (main/master) and prefixed
  (refs/heads/*) forms covered. Applied to mint AND confirm.
- #2 Introspect now validates expires_at parses to a finite number. NaN
  (the previous fail-open) is treated as expired+invalid with audit.
- #3 auditEvent split into best-effort (denied/expired/invalid) and
  critical (ok mint/confirm/ledger_emit). Critical throws → 503
  AUDIT_WRITE_FAILED → KV rollback. Preserves audit-before-issue invariant.
- #4 /policy/resolve now requires caller-tenant membership. No membership
  table exists in 016 yet (tenant_projects maps tenant→Neon only), so per
  the task constraint we fail-closed to canonical chittyos-default with a
  clear POLICY_BLOCKED_TENANT_MEMBERSHIP_UNVERIFIED reason. Tracking
  follow-up needed before widening. Emits policy_resolve audit rows.
- #5 /capabilities/confirm now rejects when policy.force_push_allowed is
  false with POLICY_BLOCKED_FORCE_DISABLED, not just at mint time.
- #6 Three new real-D1+KV tests added: expired-token introspect, four
  scope-mismatch redeem cases (#235 fix coverage), cross-tenant replay
  (#210 reopen reason).

Important:
- #7 git_remote_allowlist.match_type now dispatched (exact/prefix/glob);
  unknown match_type fails closed instead of falling through to glob.
- #8 migration 019 CHECK constraint widened to allow expired/invalid/
  policy_resolve outcomes; route handlers now emit them.
- #9 TOKEN_KV.put on mint+confirm wrapped; KV failure returns 503
  POLICY_BLOCKED_CHITTYCONNECT_UNAVAILABLE with TOKEN_STORE_UNAVAILABLE
  audit row.

Doc rot:
- #10 Removed incorrect chittyledger#11 reference (that was a merged
  Dependabot PR, not a tracking issue).
- #11 src/index.js + broker-primitives.js header now reference #242 for
  signing-deferral; #228 is the credentials/secrets issue.
- #12 ChittyID schema description left as-is (YYMM, matches regex);
  reported as canon-vs-schema mismatch needing reconciliation rather than
  silent edit — every existing fixture matches schema, not canon.

Tracks: #209 #210 #211 #235

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…l 9 policies

Migration 019 (PR #241) was admin-merged through a failing governance check
and never applied to prod (restless-grass-40598426 / ChittyOS-Core main).
All 9 neon_auth tables have rowsecurity=false in production.

Migration 020 is an idempotent hotfix that:
- Enables RLS on all 9 neon_auth tables (7 ChittyConnect-owned + jwks + project_config)
- Installs/replaces the 9 RLS policies from 019 (DROP IF EXISTS before each CREATE)
- Re-applies the chittyDid column + unique index in case 019 never ran

Apply: DATABASE_URL=$(op read "op://Infrastructure/Neon Database/credential") \
         node scripts/run-neon-migrations.js
Validate: scripts/e2e/neon-auth-rls.mjs against a branch clone before promoting.

Refs: PR #241 (bypassed governance), restless-grass-40598426

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 04:03
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cloudflare-workers-and-pages

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
chittyconnect 20c7d50 Jun 10 2026, 04:04 AM

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@chitcommit, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 32 minutes and 38 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f94f19b0-7db8-4403-95e0-c93a2180b656

📥 Commits

Reviewing files that changed from the base of the PR and between c27eab2 and 20c7d50.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • migrations/019_policy_resolve_extensions.sql
  • migrations/020_neon_auth_rls_hotfix.sql
  • package.json
  • src/api/routes/broker-primitives.js
  • src/api/routes/git-confirm.js
  • src/index.js
  • src/schemas/v1/capabilities.confirm.input.json
  • src/schemas/v1/capabilities.confirm.output.json
  • src/schemas/v1/capabilities.introspect.input.json
  • src/schemas/v1/capabilities.introspect.output.json
  • src/schemas/v1/capabilities.mint.input.json
  • src/schemas/v1/capabilities.mint.output.json
  • src/schemas/v1/index.js
  • src/schemas/v1/ledger.emit.input.json
  • src/schemas/v1/ledger.emit.output.json
  • src/schemas/v1/policy.resolve.input.json
  • src/schemas/v1/policy.resolve.output.json
  • tests/api/v1/broker-primitives.test.js
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/020-neon-auth-rls

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chitcommit

Copy link
Copy Markdown
Contributor Author

Migration 020 validated on disposable Neon branch br-dark-feather-ae9n10e6 (off main of restless-grass-40598426)

Pre-state confirmed regression: all 9 neon_auth tables had rowsecurity=false (migration 019 never reached prod).

Post-migration state:

  • 9 tables: rowsecurity=true
  • 9 policies on the 7 ChittyConnect-owned tables (user×2, account, session, verification, organization, member, invitation×2) — matches spec exactly
  • chittyDid column + unique partial index applied idempotently

Alice/Bob isolation (non-owner role rls_test_app, JWT claim request.jwt.claim.sub):

  • Alice sees only Alice's rows (user/session/account) — Bob invisible. PASS
  • Bob sees only Bob's rows — Alice invisible. PASS
  • Unset JWT claim: 0 rows across all user-scoped tables. PASS (fails closed)

Anomaly to flag: jwks and project_config have RLS enabled with zero policies — effectively locked to non-owner roles. Intentional per migration header (ChittyAuth-owned), but ChittyAuth must add its own policies before any non-owner role can read them. Tracked separately.

Branch dropped post-validation. Ready for governance review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR combines a Neon Postgres production hotfix (migration 020 to enable RLS + recreate neon_auth policies) with a large set of new “broker primitives” API work (/api/v1 capability mint/introspect/confirm, policy resolve, ledger emit) including new D1 schema, Ajv-backed JSON schemas, and integration tests.

Changes:

  • Add Neon migration 020_neon_auth_rls_hotfix.sql to enable RLS on all 9 neon_auth tables and (re)install the 9 RLS policies from migration 019.
  • Introduce /api/v1/* broker primitives routes with Ajv JSON-schema validation, token storage in KV, and durable auditing in D1.
  • Add D1 migration extensions + integration tests to cover policy/force-push confirmation/capability flows and ledger emit redaction.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
migrations/020_neon_auth_rls_hotfix.sql Neon hotfix migration to enable RLS across neon_auth and recreate policies + chittyDid artifacts.
migrations/019_policy_resolve_extensions.sql Extends local D1 schema for policy-resolve scalars/protected branches and broker audit trail.
src/api/routes/broker-primitives.js New /api/v1/* broker primitives implementation (capabilities, confirm, policy.resolve, ledger.emit).
src/index.js Wires in /api/v1 broker primitives and returns 410 Gone for legacy /api/git/confirm endpoints.
src/api/routes/git-confirm.js Removes the legacy git-confirm route implementation.
src/schemas/v1/index.js Adds Ajv compiler/registry for v1 input/output JSON schemas.
src/schemas/v1/capabilities.mint.input.json Schema for capability mint input with conditional requirements.
src/schemas/v1/capabilities.mint.output.json Schema for capability mint output.
src/schemas/v1/capabilities.introspect.input.json Schema for capability introspect input.
src/schemas/v1/capabilities.introspect.output.json Schema for capability introspect output.
src/schemas/v1/capabilities.confirm.input.json Schema for force-push confirmation input.
src/schemas/v1/capabilities.confirm.output.json Schema for force-push confirmation output.
src/schemas/v1/policy.resolve.input.json Schema for policy.resolve input.
src/schemas/v1/policy.resolve.output.json Schema for policy.resolve output.
src/schemas/v1/ledger.emit.input.json Schema for ledger.emit input.
src/schemas/v1/ledger.emit.output.json Schema for ledger.emit output.
tests/api/v1/broker-primitives.test.js New integration tests using wrangler unstable_dev with local D1 + KV.
package.json Adds direct dependencies on ajv and ajv-formats.
package-lock.json Locks ajv/ajv-formats versions (and updates Ajv resolution).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

);
}

const repoOk = policy.repos.some((r) => prefixMatch(r.path_prefix, repo_path));
Comment on lines +362 to +364
if (operation === "push") {
const remoteOk = policy.remotes.some((r) => remoteMatches(r, remote));
if (!remoteOk) {
Comment on lines +429 to +436
if (!craw) {
return jerror(
c,
403,
"POLICY_BLOCKED_CONFIRMATION_INVALID",
"Confirmation token missing or expired",
);
}
Comment on lines +468 to +475
if (!scopeMatches) {
return jerror(
c,
403,
"POLICY_BLOCKED_CONFIRMATION_INVALID",
"Confirmation token scope mismatch",
);
}
Comment on lines +925 to +930
const required = {
"git.commit": "commit",
"git.push": "push",
"git.tag": "tag",
}[event_type];
if (capRec.scope.operation !== required) {
Comment on lines +260 to +263
expect(confRes.status).toBe(403);
const body = await confRes.json();
expect(body.error.code).toBe("POLICY_BLOCKED_FORCE_DISABLED");
});
Comment on lines +806 to +816
// Tenant-membership enforcement: there is no caller↔tenant membership
// table in the schema yet (016 tenant_projects only maps tenant→Neon
// project). Fail-closed: only allow callers to resolve policy for the
// canonical `chittyos-default` tenant until membership lands. Other
// tenant_ids return 403 with a clear follow-up reason. Tracked: tenant
// membership lookup must be implemented before this gate is widened.
//
// This is the explicit "fail-closed and file a follow-up" path called for
// by the PR review rather than fabricating a check.
const MEMBERSHIP_ALLOWED_TENANTS = new Set(["chittyos-default"]);
if (!MEMBERSHIP_ALLOWED_TENANTS.has(tenant_id)) {
Comment thread src/index.js
Comment on lines 1501 to 1504
import { discoveryRoutes } from "./api/routes/discovery.js";
import { githubActionsRoutes } from "./api/routes/github-actions.js";
import { gitConfirmRoutes } from "./api/routes/git-confirm.js";
import { brokerPrimitivesRoutes } from "./api/routes/broker-primitives.js";

Comment on lines +20 to +24
-- Run with:
-- DATABASE_URL=$(op read "op://Infrastructure/Neon Database/credential") \
-- node scripts/run-neon-migrations.js
-- Or directly:
-- psql "$NEON_DATABASE_URL" -f migrations/020_neon_auth_rls_hotfix.sql
Comment on lines +11 to +15
-- This migration applies ONLY the RLS enable + policy creation from 019 and extends
-- coverage to jwks and project_config (ChittyAuth-owned) as a belt-and-suspenders
-- measure — those two tables get RLS enabled with no restrictive policy, meaning
-- owner-role migrations continue to work but a future policy can be added by ChittyAuth
-- without a schema change.
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.

2 participants