Repository navigation
Conversation
A one-shot, resumable boot migration (TURMA_MIGRATE_OWNER) binds every untenanted host to the owner's tenant (hosts keep their tokens) and re-keys every partitioned store: org stores, queue, devices, routing policy, the three ledgers and the archive rows. Tenant users get 503 until it completes; TURMA_MIGRATE_DRY_RUN logs counts per store. Leftovers follow at each host's bind beat and in a 14-day sweep. Rollback notes in docs/multi-tenant-adr.md.
Resolves the external-stores test list (keeps both tenantMigration and adminAudit). Main's XERK-2618 charges pre-partition rows to their row's tenant quota, so the migrated archive now counts toward the owner's ARCHIVE_TENANT_MAX_BYTES: the run and the dry run warn when it would exceed it, and the ADR's upgrade notes say to size it first.
The warning summed only rows movable at once, which skips every host still in the registry (their rows move at the bind beat) — so a real upgrade never warned and the owner's pushes were then refused as full (QA pass 6).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary: An existing single-tenant hub can now be upgraded in place: on boot it moves all of its pre-tenancy hosts and data into one named owner's tenant. Hosts keep their tokens and nobody re-enrolls.
Fixes XERK-2554
Why
Multi-tenancy (XERK-2529) left today's fleet "untenanted": only break-glass and admin operators can reach it, and binding a host never carried its history. The production hub needs a safe, one-time path to hand its existing fleet, settings, usage and archive to the person who owns them.
What changed
TURMA_MIGRATE_OWNER(local:<user>,oidc:<sub>, or a bare existing user) names the owner. A prefixed owner is created if missing; a bare unknown name is refused.turma/tenant-migration.js). Each step is flushed to disk before the marker records it.TURMA_MIGRATE_DRY_RUN=1logs counts per store and changes nothing.roll-token?bind=1; a host still on a name or master token is handed its identity token. Org stores, the ticket queue, push devices, the routing policy, the three ledgers and the archive rows are re-keyed into the tenant. A live host's rows move at its own bind beat.Risk
Medium. The migration is one-way and touches every store, but it is gated by an env var that is unset by default, and with it unset nothing changes. Under HA the Postgres re-key paths are unit-tested only. Before migrating, size the owner's archive quota: the moved history counts toward it (the run warns when it would be exceeded). Rollback: restore the pre-migration
/datasnapshot (plus a Postgres dump under HA) and unset the variable. Steps are indocs/multi-tenant-adr.md.Testing
QA result: PASS on the 7th pass (passes 1–4 and 6 failed, all fixed) — real
node server.jsboots with name-token, master-token and identity-token hosts; kill -9 right after completion; dry run; bad owner; operator host reusing a migrated name; revoke; ledger, outcome-row and archive re-key; trigger and meetings tokens.Not tested: HA (Valkey and Postgres), real OIDC owner creation, and a real hub-agent adopting
setToken(simulated by heartbeats) — QA has no HA stack or IdP here.Test evidence
cd turma && node --test tests/*.test.js(after merging main): 3586 tests, 0 fail.tenant-migration.test.js(12, own process: every partitioned store, the gate, the bind beat, rerun no-op, leftovers, reused names, revoke, lost setToken),tenant-migration-runner.test.js(6),tenant-migration-dryrun.test.js(1);XERK-2554:cases in the usage, permission and outcome ledger suites./verify: real boot with the owner set → migration done → name-token host handed setToken → bind beat 200 → operator view no longer lists it.uidHostRefusalchecked the usage ledger before the tenant-bind exception, so a migrated host whose rows were already moved got 403 on its bind beat. Fixed here.Follow-ups
ARCHIVE_TENANT_MAX_BYTES: fixed here with a warning (dry run too) and an upgrade note; the quota itself is the operator's to size.?bind=1).Review focus
The
hostMovable/siteMovable/migratingRecordrules inturma/server.js(who counts as "the migrated host"), and the moved tenant-bind line inuidHostRefusal.