Skip to content

Identity Claims v1 + RLS everywhere + base-integrity guard (canonical) - #1

Merged
AntoineToussaint merged 8 commits into
mainfrom
feat/identity-claims-and-rls
Jun 17, 2026
Merged

Identity Claims v1 + RLS everywhere + base-integrity guard (canonical)#1
AntoineToussaint merged 8 commits into
mainfrom
feat/identity-claims-and-rls

Conversation

@AntoineToussaint

@AntoineToussaint AntoineToussaint commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Promotes the generic identity + RLS improvements into the canonical saas-starter — the single source of truth every consumer (warden, mind) derives from. These changes were originally made inline in the warden copy; per the "fix the original module" rule they belong upstream here, after which consumers re-derive cleanly.

Contents (8 commits)

  • Identity Claims v1 — team hierarchy (migration 40), Principal.created_by (41), claims on validate (team paths / roles / attributes), org-scoped revoke, plaintext-to-server validate (key_hashkey).
  • RLS everywhere — identity-bearing store + RLS-aware tests across all gap tables (gdpr_requests, magic_links, user_identities, delegation_grants, users self+co-member+System), plus a no-raw-SQL-in-tests guard.
  • Plugin auto-discovery — frontend self-registers plugins/*.ts, so side-modules (e.g. the warden console) register with zero base edits.
  • Base-integrity guardtools/base-integrity.mjs (sha256 manifest); consumers ADD files, never modify the base. Composition-aware (a consumer may compose a service subset). Wired into CI; later folded into codefly verify.

Verification (local, this machine)

  • API go build/vet ✓ · full go test ./... -count=1 green incl. infra Postgres/RLS (Docker up)
  • auth-sidecar build/vet/test ✓
  • frontend vitest26 files / 205 tests
  • codefly verify — base intact, 744 base files ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced plugin auto-discovery system for extensible admin dashboard configuration
    • Added local development API proxy configuration for internal service traffic
  • Infrastructure & Testing

    • Enhanced E2E test infrastructure with dynamic service resolution
    • Improved authentication flow with client-side navigation
    • Added plugin registry validation tests
    • Established base file integrity verification checks

AntoineToussaint and others added 8 commits June 16, 2026 09:00
…e edits

Phase 2 composition seam: an AdminPlugin dropped in src/plugins/ is now
auto-registered, so a side-module (e.g. the warden console) composes in WITHOUT
editing admin-config.ts or any base file.

- scripts/generate-plugin-registry.mjs: scans src/plugins/ and writes a static
  registry barrel (registry.generated.ts). Static output (no require.context /
  import.meta.glob) so it works identically under Next (webpack/turbopack) and
  the vitest (Vite) suite. Wired to predev/prebuild/pretest.
- admin-config.ts: composes buildAdminConfig(discoveredPlugins) — no manual list.
- src/plugins/__tests__/registry.test.ts: enforces the registry is current
  (one AdminPlugin per plugin file) + names unique.
- vitest.config.ts: scope to src unit tests (include src/**/*.test) so vitest
  stops collecting the Playwright tests/e2e specs (upstream of the warden-copy fix).

FE suite green: 26 files, 205 tests (incl. the registry test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The saas-starter is composed into each consumer by codefly install/sync (a copy).
A copy only stays faithful by discipline, so make the discipline mechanical:
tools/base-integrity.mjs records a sha256 manifest of every base file (generated
FROM canonical, shipped into consumers via sync). `check` re-hashes those files and
fails on any modified/missing base file; files not in the manifest are legal
side-additions. Same command both sides: in canonical it keeps the manifest current
(edit a base file without regen → CI red); in a consumer it proves no-drift.

Also promotes a generic tsconfig exclude (tests/e2e) that had been a consumer-local
edit, and adds a base-integrity CI job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d .gitignore

- A consumer may compose a SUBSET of the base's services (mind takes the backend
  + frontend but brings its own gateway, omitting auth-sidecar). The guard now
  reads module.codefly.yaml's `services:` and skips files under non-composed
  services — they're legitimately absent, not "missing". module-level files stay
  fully enforced.
- Exclude build artifacts from the manifest (*.tsbuildinfo, test-results/,
  playwright-report/) — they were polluting it via filesystem walk.
- Add a frontend .gitignore so the web foundation is self-contained (node_modules/
  .next/etc.) instead of relying on a per-machine global gitignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
global-setup + playwright.config hardcoded the api's REST/Connect ports (5962/
44790) — only correct in this workspace's hash. A consumer (warden, mind) hashes
to different ports, so the readiness probe and the frontend→api env wiring missed.

Now both resolve via the SDK: global-setup uses readyService:"api" (withDependencies
resolves the REST address), and playwright.config resolves REST+Connect via
resolveServiceAddressSync for the webServer env. Also exclude next-env.d.ts (a
Next-generated file) from the base-integrity manifest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dev-admin seeding called Store().AddTeamMember directly with no tenant context,
so the org-scoped team_members RLS policy rejected the insert (SQLSTATE 42501) —
team memberships were silently dropped. Wrap it in Store().WithBypass, the same
audited bootstrap-bypass RegisterUser/GrantPlatformRole already use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
handleFixtureLogin did window.location.href="/" — a full reload that drops the
in-memory authed state login() just set and forces a cross-origin /v1/auth/refresh
round-trip to re-establish it. When the FE and api are on different ports (the
e2e direct-to-api setup), the user landed back on /auth/login. Use router.push("/")
so the root AuthProvider state is preserved and the dashboard renders immediately.
Turns 2+ admin-flow e2e tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The e2e hardcoded the frontend port (21931) and api ports across
playwright.config + global-setup. Those are workspace-hash-derived (the
frontend's real codefly address is e.g. :10571, not :21931), so hardcoding
only works in the authoring workspace. Now every address is resolved via
`codefly get endpoints` (resolveServiceAddressSync) and the config THROWS if
resolution fails rather than guessing. The browser talks same-origin to the
frontend's resolved URL; next.config rewrites proxy /v1/* + /customers.* to
the api's resolved ports (first-party cookies). Prod-build webServer too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@slopbuster

slopbuster Bot commented Jun 17, 2026

Copy link
Copy Markdown

💳 Subscription Required

codefly-dev has used all free reviews this month.

Subscribe to continue

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: edc4c711-dd84-4fb7-8b45-b8a03a7642fe

📥 Commits

Reviewing files that changed from the base of the PR and between 1945da3 and 0988b5b.

📒 Files selected for processing (16)
  • .github/workflows/ci.yml
  • module/services/api/code/fixtures/seed.go
  • module/services/frontend/code/.gitignore
  • module/services/frontend/code/next.config.mjs
  • module/services/frontend/code/package.json
  • module/services/frontend/code/playwright.config.ts
  • module/services/frontend/code/scripts/generate-plugin-registry.mjs
  • module/services/frontend/code/src/features/auth/ui/login-page.tsx
  • module/services/frontend/code/src/lib/admin-config.ts
  • module/services/frontend/code/src/plugins/__tests__/registry.test.ts
  • module/services/frontend/code/src/plugins/registry.generated.ts
  • module/services/frontend/code/tests/e2e/global-setup.ts
  • module/services/frontend/code/tsconfig.json
  • module/services/frontend/code/vitest.config.ts
  • module/tools/base-integrity.mjs
  • module/tools/base-manifest.json

📝 Walkthrough

Walkthrough

Three independent workstreams: (1) A new base-integrity.mjs CLI tool and generated base-manifest.json hash all 744 module base files, with a CI job enforcing no unauthorized drift. (2) Plugin registration switches from a manual list to a code-generated registry.generated.ts auto-discovered at build time. (3) Playwright E2E is rewired to use codefly-resolved service addresses instead of hardcoded ports, with matching Next.js API proxy rewrites, and a Go API fixture seed bypasses RLS for bootstrap inserts.

Changes

Base-file integrity tooling and CI enforcement

Layer / File(s) Summary
base-integrity CLI, manifest, and CI job
module/tools/base-integrity.mjs, module/tools/base-manifest.json, .github/workflows/ci.yml
base-integrity.mjs adds gen (walk + hash → manifest) and check (manifest vs. filesystem drift detection with allowlist escape hatch) commands; base-manifest.json captures 744 file hashes; a new base-integrity CI job runs the check on each push.

Frontend plugin auto-discovery and E2E codefly wiring

Layer / File(s) Summary
Plugin registry generator and npm lifecycle hooks
module/services/frontend/code/scripts/generate-plugin-registry.mjs, module/services/frontend/code/package.json
generate-plugin-registry.mjs scans src/plugins/ for .ts files and writes registry.generated.ts; package.json wires it into predev, prebuild, and pretest hooks.
Generated registry, admin-config wiring, and registry tests
module/services/frontend/code/src/plugins/registry.generated.ts, module/services/frontend/code/src/lib/admin-config.ts, module/services/frontend/code/src/plugins/__tests__/registry.test.ts
registry.generated.ts exports discoveredPlugins by shape-filtering the three plugin module imports; admin-config.ts replaces the manual plugin array with discoveredPlugins; Vitest tests assert registry length and name uniqueness.
Next.js API proxy rewrites for dev/e2e
module/services/frontend/code/next.config.mjs
Adds async rewrites() that forwards /v1/:path*API_REST_INTERNAL and /customers.:path*API_CONNECT_INTERNAL when env vars are set; no-op in production.
Playwright config and global setup with codefly address resolution
module/services/frontend/code/playwright.config.ts, module/services/frontend/code/tests/e2e/global-setup.ts
Playwright config introduces mustResolve, switches webServer to build && start on the codefly-resolved frontend port, and rewires env vars for same-origin API proxying; global setup resolves the frontend address dynamically and sets PLAYWRIGHT_BASE_URL.
Dev config cleanup and login-page client-side navigation
module/services/frontend/code/.gitignore, module/services/frontend/code/tsconfig.json, module/services/frontend/code/vitest.config.ts, module/services/frontend/code/src/features/auth/ui/login-page.tsx
Adds frontend .gitignore, excludes tests/e2e from TypeScript scope, restricts Vitest to src/** unit tests, and replaces window.location.href with router.push("/") in the fixture login flow.

API fixture seed RLS bypass

Layer / File(s) Summary
AddTeamMember store bypass in seedTeams
module/services/api/code/fixtures/seed.go
AddTeamMember is now called inside service.Store().WithBypass(ctx, ...) with comments explaining that bootstrap seeding lacks tenant context and requires an audited/System bypass to pass RLS.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 A rabbit scurried through the code one day,
Hashing each file along the way,
Plugins discovered — no list to maintain,
Codefly resolves ports, no hardcodes remain,
The seed bypassed RLS with a knowing wink,
All tied with a manifest, neat as you think!


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@AntoineToussaint
AntoineToussaint merged commit 1916777 into main Jun 17, 2026
4 of 5 checks passed
@AntoineToussaint
AntoineToussaint deleted the feat/identity-claims-and-rls branch June 17, 2026 14:51
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.

1 participant