Expand description
One-shot startup migration that seeds a “Default” identity bundle from
ambient OAuth credentials living in the user’s home dir
(<HOME>/.<auth_dir_name>/.credentials.json for each oauth-class
provider).
Per SPEC_OAUTH_IDENTITY_BUNDLES_2026_05_22.md §5 (OAuth Bundles PR E):
when a user upgrades from a pre-bundle build that wrote OAuth tokens
to the legacy ambient location (e.g. Claude Code’s ~/.claude/),
AgentMux should auto-detect those credentials and bind them into a
“Default” identity bundle so launches keep working without forcing
the user to re-OAuth. Empty / "blank" identity_id rows on
db_agent_instances are back-filled to point at the new Default
bundle so the resolver picks them up at next spawn.
§Idempotency contract
The migration is safe to call on every srv startup:
- For every oauth-class provider in the registry we check whether
ANY identity bundle already has a binding for that provider. If
yes → that provider is already covered (either by a prior run of
this migration, or by a user-driven
auth.startflow), skip it. - The “Default” bundle is upserted (not unconditionally
INSERT’d) — running the migration twice produces no extra row. - The IdentityAccount uuid is reused on the re-bind path
(
bundle_identity_bindingslookup), matching thepersist_oauth_binding_or_syntheticpattern from PR C, so a second run doesn’t orphan rows indb_identity_accounts. - Back-fill only runs when the Default bundle exists OR was created
this run — so a fresh install with no ambient creds never
rewrites
identity_idto a non-existent FK.
Failure modes are warn-don’t-block (same as inject_identity_env):
account upsert / bind / publish errors are logged and skipped, the
srv keeps coming up.
Structs§
- Migration
Stats - Summary of what the migration did. Returned for testability + log observability; the production caller only inspects field counts (e.g. logging “0 providers seeded” at info level for visibility).
Constants§
- DEFAULT_
BUNDLE_ ID - Id used for the seeded Default bundle. Fixed so the migration is
idempotent across restarts (a second run looks up by id, sees the
row, reuses it instead of minting a new uuid). Not the same as the
"blank"singleton — the blank bundle has no bindings by contract. - DEFAULT_
BUNDLE_ NAME - Human-readable name for the seeded bundle. Per spec §5.1.
Functions§
- bound_
providers 🔒 - Collect the set of providers that are bound in ANY identity bundle today. The migration’s idempotency gate — if a provider is already in here, the ambient-creds seed is a no-op for that provider.
- ensure_
default_ 🔒bundle - Look up the Default bundle by id; create it (via
bundle_identity_upsert) if missing. Returnstruewhen a new row was inserted,falsewhen an existing row was reused. - run_
default_ bundle_ migration - Entry point — call once on srv startup, after WaveStore is open and
before the srv begins accepting requests.
home_dir_overrideisNonein production (resolvesdirs::home_dir()); tests useSome(tempdir)so they can plant fake~/.<auth_dir_name>/.credentials.jsonfiles without touching the user’s real home.