the platform stops explaining what Headscale is

Correcting my own reasoning from the last commit. I left "Headscale's API", "the
Headscale admin credential" and similar alone on the grounds that they name the
thing the plugin drives, so renaming them would make the sentences false. True as
far as it goes, and it misses the point: those sentences are in the PLATFORM. It
has no business knowing what Headscale is at all. Correct language in the wrong
repository is still a remnant.

Ten of them, each replaced with the platform's own vocabulary rather than the
plugin's domain:

- `crypto.ts` and `secret-store.ts` listed "the Headscale admin credential" among
  what one key used to protect. It is "the tailnet's admin credential" — the
  purpose, which is a word this repository owns.
- `catalogue.ts` and `availability.ts` explained `alsoServes` — a LIVE mechanism —
  entirely through a Headscale example that left the repository. Restated as the
  general shape, with one parenthesis saying which case produced it.
- The photos types file explained another plugin's sidecar.
- `App.tsx` cited `/headscale/:section` as the example of a route pair; that route
  does not exist. `/photos/:section` does.
- `plugins/router.ts` pointed at `useCompanionLogStream`, a hook that lives in a
  plugin now.
- `registry.ts` said in the present tense that `headscale` is `admin` — the
  permission is the plugin's and it declares its own, so a member-facing enrolment
  surface would be the PLUGIN's to add, not this file's.
- `proxy.sh` cited `headscale/schema.ts` as a style reference. That file is not in
  this repository.
- machine-setup said "Its Headscale app" — the app is the Offscale plugin.

`machine-setup/lib/tailscale.sh` keeps all ten of its mentions: it exists to
explain tailscale, headscale and offscale to somebody choosing between them, on a
core install, before any plugin exists. That is machine setup's subject, not the
plugin's domain leaking upward.

What survives elsewhere is only dated history — "was here until 2026-08-15", "It
was: music, vault, slskd, headscale, …" — plus the notes about the real
`headscale_servers` orphan and the secret-store key removed yesterday. Those record
that something called headscale used to be here, which is a different claim from
explaining what it does.

tsgo clean, frontend builds, 873 pass / 7 fail unchanged, setup scripts parse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 11:33:28 +00:00
co-authored by Claude Opus 5
parent 27aa72d923
commit 0230e62a71
10 changed files with 17 additions and 16 deletions
+1 -1
View File
@@ -211,7 +211,7 @@ pluginsRouter.post('/:appName/disable', async (ctx) => {
//
// POST rather than GET, so `EventSource` cannot be used: it sends no `Authorization` header, and these
// routes are owner-only. The client reads the body and parses frames itself, which is exactly what
// `useCompanionLogStream` already does for the headscale container logs.
// a plugin's own log-stream hooks already do.
// `userId` is the caller, threaded through so install can pin the new tile to THEIR dock rather than
// guessing at the owner. Only install uses it; the others take it and ignore it so the map stays one shape.
+3 -3
View File
@@ -24,9 +24,9 @@ import { CATALOGUE, type CatalogueEntry } from './catalogue';
/**
* Permission key → the sidecar that has to be installed for it to mean anything.
*
* Includes `alsoServes`, because one sidecar can back more than one permission: Headscale serves both the
* owner's tailnet administration and a member enrolling their own device, and only listing the first left the
* second looking available on a machine that had no Headscale.
* Includes `alsoServes`, because one sidecar can back more than one permission — an owner-only admin
* surface and a member-facing one, say. Listing only the first left the second looking available on a
* machine where the sidecar was not installed at all.
*/
const PERMISSION_TO_SIDECAR = new Map(
CATALOGUE.flatMap((entry) =>
+4 -4
View File
@@ -118,10 +118,10 @@ export type CatalogueEntry = {
/**
* Other permissions that stop working when this sidecar is absent, for availability only.
*
* Headscale is the case: one sidecar serves both `headscale` (administering the tailnet, owner-only) and
* `vpn` (a member enrolling their own device). With only `permission` to go on, `vpn` was never subtracted,
* so the Permissions screen offered it on a machine with no Headscale at all — a grant that would have
* produced a refusal the owner could not account for.
* The case that produced it: one sidecar served both an owner-only admin permission and a member-facing
* one. With only `permission` to go on, the second was never subtracted, so the Permissions screen offered
* it on a machine where that sidecar was not installed — a grant that would have produced a refusal the
* owner could not account for. (It was the tailnet, which is a plugin now, but the shape is general.)
*/
alsoServes?: string[];
/**
+3 -2
View File
@@ -243,8 +243,9 @@ const CORE_REGISTRY: Permission[] = [
// `${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.
// What this cost: the tailnet's own permission was `admin`, so with `vpn` gone no member-grantable
// tailnet surface remained. That permission has since left with the offscale plugin, which declares its
// own — so a member-facing enrolment surface would be the PLUGIN's to add, not this file's.
// 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