Cloud attach-endpoint: report vmKind (owner only for the caller's own personal VM)
What
POST /api/vm/:id/attach-endpoint now answers a top-level vmKind: "owner" | "team" | null on every transport (cmux-remote, websocket).
The Mac app will use it to give up its local sandbox for the caller's own session only when the Cloud API says the machine is the caller's own personal machine. The guest has passwordless sudo, so no in-guest marker can be trusted for this. The app reads the field per link and never caches it; a missing, null or other value stays sandboxed. The app and acpmux sides are separate PRs.
The rule (strict)
New vmKindForCaller(row, callerUserId) in web/services/vms/vmKind.ts:
"owner"only when the machine is on the caller's own personal account (owner_team_idequals the caller's user id, the synthetic personal team) and the caller created it (user_idequals the caller)."team"for every Stack team, a one-member team included, and for any machine the caller can reach but did not create. A team member attaching to a teammate's machine gets"team".nullwhen the row has no usable billing owner (blankowner_team_id). The access check already refuses such a row, so in practice clients seeownerorteam.
resolveVmGuestKind and its callers (the guest's /etc/cmux/vm-kind via /api/vm/reflection/kind) are unchanged. On main that function calls a one-member team owner and re-asks every minute; the attach answer deliberately does not reuse it, so it can't turn into owner for a team.
owner_team_id is text NOT NULL DEFAULT '' on main and feat-cmux-next alike, so "personal account" means owner_team_id = user_id on both. There is no null case.
Where production serves this
Both the stable app and the nightly-next app call https://cmux.com/api/vm/:id/attach-endpoint:
- classic:
Packages/macOS/CmuxCloud/Sources/CmuxCloud/Environment/AuthEnvironment.swift:324-336, where a Release build is alwayshttps://cmux.com; - next:
Packages/macOS/CmuxNext/Sources/CmuxNextCloud/Config/CloudConfiguration.swift:69,95-99andAPI/CloudAPIClient.swift:117-122, where a Release build usesapiBaseURL=https://cmux.com.
cmux.com is the Vercel web/ app. web/vercel.json deploys main only, and a live request answers server: Vercel, x-matched-path: /api/vm/[id]/attach-endpoint. So this lands on main for production. A twin PR carries the same change to feat-cmux-next so the branches don't drift.
Tests
web/tests/vm-attach-vm-kind.test.ts, through openVmCmuxRemote and openAttachEndpoint, plus the pure function:
- the creator on their personal machine gets
owner; - a team machine gets
team, for its creator too; - another account that can attach gets
team, neverowner; - a one-member team gets
team; - an unusable owner gets null, and its attach is refused.
Three existing vm-workflows equality checks now expect the extra field.
Linux CI: 14 related files, 325 pass and 0 fail; tsgo, eslint and complexity are clean.
Each test fails without its part of the change:
- with the workflow wiring removed, 7 tests fail;
- with
ownerfor any personal machine whoever calls, the "another account" tests fail (2); - with a one-member team as
owner, the one-member test fails (1).
Risk
The response gains a field; Swift decoders ignore unknown keys. A wrong owner would let a session skip the local sandbox, so the rule fails closed: only the exact creator-on-personal-account case is owner.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by CodeRabbit
- New Features
- VM attach responses now include a caller-specific classification: a VM is labeled “owner” when the caller matches both its user and billing owner; otherwise, it is labeled “team.”
- The classification appears in standard and remote attach responses, including successful recovery results.
- Bug Fixes
- VMs with a blank or whitespace-only billing owner are handled as unavailable during attach workflows.