Summary
launch-auth never reads or persists a unix_username claim from the launch/handoff assertion. On any deployment running unix_user_mode: strict, every user provisioned via launch auth therefore has unix_username = null, and all user impersonation fails — executor spawn, branch git-state auto-population, Codex auth, scheduled sessions.
Reported by @Antonio-Rivero from the dogfood workspace: agent prompts never start, the session just spins.
❌ [Daemon] Executor spawn failed for session=019fa7093f017d72829d5448
task=019fa7093f5b79faa64da93b agent=codex unix_username=null:
Strict Unix user mode requires unix_username to be set.
Thrown from resolveUnixUserForImpersonation — packages/core/src/unix/user-manager.ts:430.
Root cause (verified against main @ 0409b302b)
unix_username appears nowhere in apps/agor-daemon/src/auth/launch-auth.ts — grep -i unix on that file returns zero hits. Specifically:
- The claim isn't declared.
interface LaunchClaims extends JwtPayload (launch-auth.ts:79–94) lists iss, sub, aud, email, email_verified, name, picture, avatar, role, provider, instance_id, runtime_instance_id, jti, nonce. No unix_username.
- The existing-user branch doesn't set it.
upsertLaunchUser (launch-auth.ts:320) updates only name, role, updated_at, and data (avatar + external identities).
- The new-user branch doesn't set it either. The insert sets
user_id, email, password, name, emoji, role, timestamps, onboarding_completed, must_change_password, data — and nothing else.
The only code that ever writes users.unix_username is admin-gated:
apps/agor-daemon/src/services/users.ts:455 (users-service patch)
apps/agor-daemon/src/mcp/tools/users.ts:282,342 (admin MCP tools)
Per apps/agor-daemon/src/register-hooks.ts:2230, unix_username is explicitly excluded from self-service profile updates — so a user cannot set it themselves.
Important correction to the initial hypothesis
The first diagnosis assumed the claim is mapped only at user-record creation, so only records predating the change would lack it. That is not what the code does — the claim is never consumed on either branch. Consequences:
- Brand-new users are affected too. "Recreate the user" or "new signups will be fine" will not work as a workaround.
- The fix is different: add the claim to
LaunchClaims and set it on both branches, plus backfill existing rows — not merely "also map it on update."
Why nothing surfaces in the UI
The spawn failure leaves the task in a non-terminal state, and the containment that would surface it as startup_timeout never runs, because the executor supervisor is separately broken on multi-tenant runtimes:
[executor-heartbeat] Supervisor check failed: Failed to find orphaned tasks:
Missing tenant database scope for daemon database access
That is tracked in preset-io/agor-cloud#191 (ADR: tenant-safe executor supervision). Note findOrphaned() throws (packages/core/src/db/repositories/tasks.ts:444) and getOrphaned() is the first await inside checkOnce()'s try block (executor-heartbeat-supervisor.ts:46-48) — so the entire supervisor tick aborts, taking the 5-minute dispatch-connect timeout down with it. A hard failure consequently reads as "still working."
These are two independent bugs. This issue is only the missing claim mapping; the masking is #191's scope.
Expected behavior
A user provisioned or refreshed through launch auth should carry the unix_username supplied by the identity provider, so strict-mode impersonation works without manual admin intervention.
Proposed fix
- Add
unix_username?: string to LaunchClaims.
- Set it in
upsertLaunchUser on both the insert and the update branch — update should fill it when currently null (and decide explicitly whether a changed claim should overwrite an existing value).
- Respect the existing uniqueness guard (
packages/core/src/db/repositories/users.ts:318-322) and surface a clear error on collision rather than failing the launch opaquely.
- Backfill existing users whose records were created before the mapping exists.
- Fail loudly at launch time when
unix_user_mode: strict and no unix_username can be resolved, instead of deferring the failure to first spawn.
This stays deployment-neutral: unix_username is a generic OSS user field and the launch assertion is a generic OSS mechanism, so no Agor Cloud concepts leak into the runtime (per the boundary rule in agor-cloud#191).
Immediate workaround
An admin can set the field directly via the users service or the users_update MCP tool. Requires admin — the affected user cannot self-serve.
Blockers / Dependencies
- None blocking the claim mapping itself; it is self-contained in
launch-auth.ts.
- Masking dependency:
preset-io/agor-cloud#191 — until the supervisor is tenant-safe, failures of this kind stay invisible in the UI. Fixing this issue removes this trigger but not the class.
- Cross-repo coordination: confirm the Agor Cloud control plane emits
unix_username in the assertion under exactly that name. Reported as stamped at launchCodes.ts:358 with consumed launch codes showing "unix_username": "antonio_rivero" — not independently verified, since the Cloud control-plane source was not available to me.
Verification notes / caveats
- Verified against
preset-io/agor main @ 0409b302b as deployed on the sandbox host. The affected dogfood runtime may run a different version — worth confirming before relying on the line references.
- Not reproducible on the sandbox instance: 0
Strict Unix user mode failures, 0 Missing tenant database scope, orphan cleanup working, and unix_user_mode unset in config.yaml. This needs a strict-mode multi-tenant runtime to reproduce.
Summary
launch-authnever reads or persists aunix_usernameclaim from the launch/handoff assertion. On any deployment runningunix_user_mode: strict, every user provisioned via launch auth therefore hasunix_username = null, and all user impersonation fails — executor spawn, branch git-state auto-population, Codex auth, scheduled sessions.Reported by @Antonio-Rivero from the dogfood workspace: agent prompts never start, the session just spins.
Thrown from
resolveUnixUserForImpersonation—packages/core/src/unix/user-manager.ts:430.Root cause (verified against
main@0409b302b)unix_usernameappears nowhere inapps/agor-daemon/src/auth/launch-auth.ts—grep -i unixon that file returns zero hits. Specifically:interface LaunchClaims extends JwtPayload(launch-auth.ts:79–94) listsiss,sub,aud,email,email_verified,name,picture,avatar,role,provider,instance_id,runtime_instance_id,jti,nonce. Nounix_username.upsertLaunchUser(launch-auth.ts:320) updates onlyname,role,updated_at, anddata(avatar + external identities).user_id,email,password,name,emoji,role, timestamps,onboarding_completed,must_change_password,data— and nothing else.The only code that ever writes
users.unix_usernameis admin-gated:apps/agor-daemon/src/services/users.ts:455(users-service patch)apps/agor-daemon/src/mcp/tools/users.ts:282,342(admin MCP tools)Per
apps/agor-daemon/src/register-hooks.ts:2230,unix_usernameis explicitly excluded from self-service profile updates — so a user cannot set it themselves.Important correction to the initial hypothesis
The first diagnosis assumed the claim is mapped only at user-record creation, so only records predating the change would lack it. That is not what the code does — the claim is never consumed on either branch. Consequences:
LaunchClaimsand set it on both branches, plus backfill existing rows — not merely "also map it on update."Why nothing surfaces in the UI
The spawn failure leaves the task in a non-terminal state, and the containment that would surface it as
startup_timeoutnever runs, because the executor supervisor is separately broken on multi-tenant runtimes:That is tracked in
preset-io/agor-cloud#191(ADR: tenant-safe executor supervision). NotefindOrphaned()throws (packages/core/src/db/repositories/tasks.ts:444) andgetOrphaned()is the first await insidecheckOnce()'s try block (executor-heartbeat-supervisor.ts:46-48) — so the entire supervisor tick aborts, taking the 5-minute dispatch-connect timeout down with it. A hard failure consequently reads as "still working."These are two independent bugs. This issue is only the missing claim mapping; the masking is #191's scope.
Expected behavior
A user provisioned or refreshed through launch auth should carry the
unix_usernamesupplied by the identity provider, so strict-mode impersonation works without manual admin intervention.Proposed fix
unix_username?: stringtoLaunchClaims.upsertLaunchUseron both the insert and the update branch — update should fill it when currently null (and decide explicitly whether a changed claim should overwrite an existing value).packages/core/src/db/repositories/users.ts:318-322) and surface a clear error on collision rather than failing the launch opaquely.unix_user_mode: strictand nounix_usernamecan be resolved, instead of deferring the failure to first spawn.This stays deployment-neutral:
unix_usernameis a generic OSS user field and the launch assertion is a generic OSS mechanism, so no Agor Cloud concepts leak into the runtime (per the boundary rule in agor-cloud#191).Immediate workaround
An admin can set the field directly via the users service or the
users_updateMCP tool. Requires admin — the affected user cannot self-serve.Blockers / Dependencies
launch-auth.ts.preset-io/agor-cloud#191— until the supervisor is tenant-safe, failures of this kind stay invisible in the UI. Fixing this issue removes this trigger but not the class.unix_usernamein the assertion under exactly that name. Reported as stamped atlaunchCodes.ts:358with consumed launch codes showing"unix_username": "antonio_rivero"— not independently verified, since the Cloud control-plane source was not available to me.Verification notes / caveats
preset-io/agormain@0409b302bas deployed on the sandbox host. The affected dogfood runtime may run a different version — worth confirming before relying on the line references.Strict Unix user modefailures, 0Missing tenant database scope, orphan cleanup working, andunix_user_modeunset inconfig.yaml. This needs a strict-mode multi-tenant runtime to reproduce.