Skip to content

migrate/supabase: emit the users module + JWT only when auth.users is in the export; gap dangling auth references (conformance M3) - #315

Merged
Sorcecoder merged 2 commits into
mainfrom
fix/m3-migrate-gate-users-on-auth
Sep 1, 2026
Merged

Sorcecoder merged 2 commits into
mainfrom
fix/m3-migrate-gate-users-on-auth

Conversation

@Sorcecoder

Copy link
Copy Markdown
Contributor

Docs↔codegen conformance sweep. 19-migrate-supabase.md:72 frames the users module as derived from auth.users, but authmap::build_auth ran unconditionally — a table-only export got a full users module, register/login, and AuthModel::Jwt ("migrated 2 entities" for one source table): an unrequested auth surface, violating never-guess.

Fix: export_has_auth_users gates the users module on the auth.users table, or any fk with ref_table == auth.users, or data/auth.users.csv — the fk/csv signals matter because a supabase db dump --schema public (very common) drops the auth.users DDL but keeps the references; a table-only gate would have silently stripped auth from those exports. Separately, auth_needed (tenancy or any non-NoRls table, but no auth.users) keeps auth.model: jwt without an identity surface and raises a blocking gap — the only shape that neither drops auth nor widens access (dropping it would abort on a no-endpoint module or silently unguard restricted tables).

Verified: (a) table-only export → no auth block, 1 entity, deps [db]; (b) normal export with auth.users → entire generated app tree byte-identical (diff -r); (c) RLS referencing auth.uid() with no auth.users → blocking gap, endpoints stay guarded. Fixtures updated to declare auth.users per the documented export layout; 19-migrate-supabase.md documents the auth-less and dangling-reference behaviours.

Test: the_users_module_follows_auth_users_and_a_dangling_auth_reference_is_a_blocking_gap. Full cargo test -p jerrycan green. No version bump.

Sorcecoder and others added 2 commits September 1, 2026 22:39
… in the export; gap on dangling auth references (M3)

The translator called authmap::build_auth unconditionally and pushed the
users module as modules[0], so an export with a single `todos` table came
back with a User entity, register/login and auth.model=jwt — "migrated 2
entities" for one source table, an auth surface the source never had.

The users module is now emitted only when the export really carries
auth.users: its dumped DDL, a foreign key referencing it (a `--schema
public` dump keeps the reference), or its exported rows. The auth MODEL is
gated separately — a source that restricts rows by the authenticated user
(auth.uid(), membership) keeps jwt auth so its guards survive, with a
blocking gap naming the missing identity surface; dropping the model there
would have silently unguarded every restricted table. With no auth
anywhere, the design has no auth block, no auth dependency and unguarded
CRUD (as open as the source, stated as an advisory), and storage/realtime
blocks that need an identity are gap-reported instead of emitted.

Output is byte-identical for any export that carries auth.users. The
migrate fixtures now declare auth.users, matching the export layout the
docs prescribe.
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