delete /api/vpn

it had no caller. verified three ways before removing: nothing in the mobile
monorepo reaches it (enrollVpn's only call site is behind `if (embedded)`, and
the one app rendering VpnScreen never passes embedded), nothing in the officer
web app references it, and the live database holds no vpn grants. the companion
was checked separately by its own author — zero references there either.

and it will not come back. offscale is permanently standalone: the thing that
gets you to the platform cannot itself need the platform, or a broken tailnet
locks you out of both.

gone: api/vpn/router.ts, its mount, and the `vpn` capability. the registry keeps
a comment where the capability was, because its removal has a cost worth
recording — headscale is admin-only, so no member-grantable headscale surface
remains, and reintroducing one is a deliberate act rather than an oversight.

kept: the sidecar's enroll.ts. its bare POST /_officer/enroll handler is now
unreachable, but the file is also the dispatcher for /enroll/invites, which is
live and fundamental. the header comment now says so, so nobody deletes it
looking for dead code.

also records the third component in the doc. two of the three have an "enroll"
surface and only one is ours: /api/v1/enroll/* belongs to the companion, is
where the phone actually goes, and must not be collapsed into /api/offscale/*.

capabilities tests: 17 pass / 8 fail both before and after, stash-verified — the
8 are pre-existing, in totality and path-to-capability, which is precisely the
machinery dynamic mounting will rework.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 17:40:44 +00:00
co-authored by Claude Opus 5
parent bbc60b34ac
commit 88a44ec4a7
6 changed files with 36 additions and 63 deletions
+11 -10
View File
@@ -236,16 +236,17 @@ export const CAPABILITIES: Capability[] = [
api: [],
routes: ['/invoices'],
},
{
key: 'vpn',
label: 'VPN',
description: 'Enrol your own devices on the tailnet',
kind: 'app',
api: ['/vpn'],
// Minting a pre-auth key for your own device is the entire point of the capability, and the key is
// bound to the caller. Administering the tailnet is `headscale`, which is admin-only.
personal: ['/'],
},
// `vpn` (POST /api/vpn/enroll) was here until 2026-08-14 — the one member-grantable piece of headscale,
// minting a pre-auth key bound to the caller. Deleted because it had no caller anywhere: the standalone
// OffScale app gates it on `embedded`, which it never sets, and it never will — the app is permanently
// independent of the platform, since the thing that gets you to the platform cannot itself need it.
//
// Device enrolment did not go away, it moved out. A phone claims an invite from the Companion at
// `${invite.base}/api/v1/enroll/claim`, which is a different component entirely and does not involve
// Officer. Confirmed against the mobile monorepo and the companion repo before removal.
//
// What this does cost: `headscale` is `admin`, so with `vpn` gone no member-grantable headscale surface
// remains. Reintroduce one here if members ever need to enrol their own devices through Officer.
// Core, not app — and this was a real defect, not a preference. `/api/dashboards` is not a feature, it is
// the per-user key-value store where EVERY workspace screen keeps its layout (`screens/files`,
// `ws-layout-*`, panel config). `WorkspaceView` renders nothing until that store has loaded, so gating it