Skip to content

feat(auth)!: drop better-auth and the legacy permission tables (RFC v3 Phase 6) - #93

Merged
toto04 merged 1 commit into
mainfrom
feat/idp-phase6-cleanup
Oct 26, 2026
Merged

toto04 merged 1 commit into
mainfrom
feat/idp-phase6-cleanup

Conversation

@BIA3IA

@BIA3IA BIA3IA commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

RFC v3 Phase 6 (§9.3, §9.4, §13) for the backend. Since Phase 5, roles, group admins and Telegram links live in the IdP, so this PR removes the backend's own login and the tables behind it.

Removed

  • better-auth: email OTP, passkeys, the Telegram link plugin, the /api/auth/* routes and their CORS, the OTP email template, and the better-auth / @better-auth/* dependencies.
  • Settings: BETTER_AUTH_SECRET, PUBLIC_URL and TRUSTED_ORIGINS. They are no longer read, so leaving them set is harmless.
  • Tables (migration 0028_idp_phase6_cleanup): tg_permissions, tg_group_admins, tg_link and auth_*.
  • Legacy tg_grants: not migrated (§4 decision 6). tg_grants_v2 is renamed to tg_grants, together with its sequence, index and constraints.
  • SDK exports: AUTH_PATH, USER_ROLE and TelegramPlugin. No consumer imports them (I checked telegram, admin and web).
  • Scripts: idp:export / idp:reconcile (they read tg_permissions) and seed:user (better-auth). Also the tg_link cleanup cron and the auth_sessions retention cron (cleanSessions, from feat(retention): delete expired sessions and ended moderation records #71): both tables are gone.

Changed

  • Author columns (§9.4): the columns that referenced tg_permissions (web_* and common_group_labels) become legacy_created_by_tg_id / legacy_modified_by_tg_id.
    • They are renamed, not dropped, so the old Telegram IDs stay for history.
    • The foreign keys are removed.
    • The *_creator_check constraints now require legacy_created_by_tg_id or created_by_sub.
    • New writes set only *_by_sub. A modification clears the legacy modifier.
  • Freshman writes no longer accept modifiedBy. The admin dashboard never sent it.
  • People no longer reads tg_group_admins. Group admins are native Telegram status (decision 2), which the backend does not store, so telegramGroups() now returns admins: null (unknown).
    • While a group's admins are unknown, People keeps the groups it last knew for each person and leaves pending admin removals pending. Without this, an empty list would have marked every pending removal as "confirmed by Telegram".
    • The Capo area's per-group admins count is now number | null.
    • A live source (e.g. the bot reporting native admins) is a People follow-up; this PR only stops depending on the legacy table.
  • Grants read from a single table. key, source and grantedBy stay in the output, now always idp:<id>, "idp" and null, and marked @deprecated. Today's admin uses them, so it keeps working at runtime until it updates the SDK.

⚠️ Before merging: backup

Merging is deploying. Docker CI pushes backend:latest and Flux deploys its new digest by itself (polinetwork-cd/clusters/k3s/apps/backend.yaml). Migrations run when the backend starts (src/db/db.ts), so the first pod running this image deletes the tables, a few minutes after the merge. Rolling back the image does not bring them back, and the previous image no longer works once the migration has run.

  1. Take a full dump of the production database, from wherever you can reach Postgres (e.g. the dbpg route):
    pg_dump --format=custom --file=backend-pre-phase6-$(date +%F).dump \
      -h <host> -p <port> -U <user> <db>
    pg_restore --list backend-pre-phase6-*.dump | grep -E 'tg_permissions|tg_group_admins|tg_link|tg_grants|auth_users'
  2. Store the dump somewhere safe (e.g. Azure Blob). It is the archive of the legacy grants required by decision 6.
  3. Re-enter any still-needed legacy grants by hand after the deploy: they are not migrated.

Rollback: restore the dump and redeploy the previous image.

Order

  1. Backup (above).
  2. Merge this PR. Flux deploys it.
  3. Check the pod ([DB] Migration completed).
  4. Merge feat(backend): close public access and allow only in-cluster callers polinetwork-cd#28, which removes the Ingress and the old env vars and adds the NetworkPolicy. It must come after, because the current image still requires BETTER_AUTH_SECRET.

Release

This is a breaking change for the SDK (exports removed), so it releases 4.0.0 (3.0.0 went out with #96). #89 is merged, so the ! in the title is enough; the commit body also has the BREAKING CHANGE: footer.

Verification

  • bun run typecheck, bun run biome ci ., bun run build and bun run build:npm: OK.
  • bun run test with the Postgres integration suites enabled (SEED_TEST_DB_PORT, FRESHMAN_TEST_DB_PORT): 297 passed, 1 skipped (after rebasing onto feat(retention): delete expired sessions and ended moderation records #71, feat(people): add the people api for the admin dashboard #83, fix(people): classify pending telegram admin removals #94, fix(azure)!: remove group membership writes #96).
  • New test: People keeps groups and pending removals while group admins are unknown.
  • Migration on a throwaway Postgres 18:
    1. Seeded with main (8 tg_permissions, 12 tg_group_admins, 1 legacy grant, 11 tg_grants_v2, legacy-authored labels and web content, a better-auth user with a tg_link).
    2. Started this branch. Result:
      • the legacy tables are gone;
      • the 11 IdP grants are in tg_grants and inserts continue from the sequence;
      • the legacy authors are kept in legacy_*_tg_id;
      • no foreign key is left dangling.
  • After rebasing onto 0027_people: a database migrated with today's main (with rows in tg_permissions and tg_group_admins) upgrades cleanly; the legacy tables are gone and the people_* tables are untouched.
  • After the migration, drizzle-kit generate reports "No schema changes".
  • The freshman integration fixture now applies only the migrations before 0026. Before, it applied "all but 0026", which would have run the later migrations first.

Not in this PR

  • tg_audit_log.admin_id: the bot still reads it until it updates the SDK.
  • tg_test / web_test: tg_test is the startup DB check.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b77546eb-cf0e-4a1e-b939-929bb61b963d
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

…3 Phase 6)

Roles, group admins and Telegram links live in the IdP since Phase 5,
so the backend's own login and the tables behind it go:

- better-auth (email OTP, passkeys, Telegram link plugin), its
  /api/auth routes and CORS, the OTP email, and the BETTER_AUTH_SECRET,
  PUBLIC_URL and TRUSTED_ORIGINS settings
- tg_permissions, tg_group_admins, tg_link and auth_* (migration 0028)
- the legacy tg_grants table, not migrated (decision 6); tg_grants_v2
  takes its name
- the AUTH_PATH, USER_ROLE and TelegramPlugin exports
- the idp:export / idp:reconcile migration tooling and seed:user
- the cleanSessions and cleanLinkCodes cron jobs, whose tables are gone

The author columns that referenced tg_permissions keep the old Telegram
IDs as legacy_created_by_tg_id / legacy_modified_by_tg_id, without the
foreign keys (§9.4). Freshman writes no longer accept `modifiedBy`.
Grants still return `key`, `source` and `grantedBy`, now constant, so
dashboards built against older SDKs keep working.

People no longer reads tg_group_admins. Group admins are native Telegram
status (decision 2), which the backend does not store, so Telegram groups
now report `admins: null` (unknown). While unknown, People keeps the groups
it last knew and leaves pending admin removals pending instead of
confirming them; the Capo area reports `admins: null` per group.

BREAKING CHANGE: the SDK no longer exports AUTH_PATH, USER_ROLE and
TelegramPlugin, the backend serves no /api/auth, the Capo area's group
`admins` count is nullable, and migration 0028 drops the legacy tables on
startup. Back them up before deploying.
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