Cloud: read current chat-start grant without opening another VM link (W2-15)

Draft
TE
4d ago
+122-1

W2-15 — backend prerequisite for delegated-chat revocation

Tracks HQ #5944 W2-15 and the source-backed finding on team-machine owner issue #5353. Classic backend owner prior art: merged #29217. Draft / no production deployment or merge requested.

Why

A teammate's NEXT Mac link keeps its attach-time vmKind: owner after the creator deletes their chat grant. App code needs a current authorization answer before attempting another delegated chat start. Reusing POST /api/vm/:id/attach-endpoint to check that permission is wrong: the attach path can wake a VM, provision an endpoint, write a lease and record an attach event.

Change

  • GET /api/vm/:id/chat-start-access on the existing authenticated, billing/team-scoped classic VM route. Response: {"chatStart":"owner"|"granted"|"none"} for the requesting person only, with Cache-Control: no-store.
  • Rechecks current team members via the bounded Stack directory read and the persisted grant via the existing repository method. Creator's personal VM: owner; creator's team VM: owner only while a member; granted teammate: granted only while still a member. Removed member or revoked grant: none. Missing or timed-out checks: retryable 503, never authorization.
  • Does not call provider attach, pause/resume, mint a token, create a lease, or emit usage/billing mutations.
  • Focused deterministic service tests for owner, current grant, revoke, departed member, admin without grant, unavailable team directory and unavailable grant store.

Review and deployment order

  1. Verify handler authentication, source team scope, strict no-store response and focused tests. Local full web test dependencies were unavailable in this chat; CI is the requested executable proof.
  2. Land/deploy this read-only endpoint before the dependent NEXT Mac change, so new clients never make a preflight request to a missing route.
  3. Verify two-person revoke during a still-connected link. This is client-side defense in depth, with a remaining short check-to-daemon-start race. Strong immediate revocation still needs a revocable capability verified by the remote daemon for every privileged start (separate authorization design/review). A machine shell grant remains broader than an app chat-start grant.

No cross-owner branches modified, no migrations, no permission expansion, no VM activity or production changes.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds a read-only GET /api/vm/:id/chat-start-access endpoint so the Cloud app can check current chat-start authority before delegating another chat start, instead of trusting the stale attach-time owner label on an existing link.

  • Returns owner, granted, or none for the requesting user only, with Cache-Control: no-store.
  • Rechecks current team membership and the persisted grant; revoked grants and departed members resolve to none.
  • Does not wake the VM, create an endpoint or lease, or emit usage or billing events.
  • Unavailable team directory or grant store returns a retryable 503, never an authorization decision.
  • Land and deploy before the dependent NEXT Mac change so new clients never preflight a missing route.
  • Client-side defense in depth only; closing the remaining check-to-daemon race needs server-enforced revocable capabilities.