Files
offscale/manifest.ts
T
pastilhasandClaude Opus 5 8a446bb4b5 offscale, extracted from the platform into its own repository
The tailnet plugin — machines, users, pre-auth keys, access policy and device
invites. Moved out of officerdev/platform, where it had lived in plugins/ since
the plugin system was built.

Until now this code existed in exactly one place: the platform repository. That
made "gitignore the plugins directory" impossible to do safely, because
untracking it would have left 49 files on a single disk with no remote. This
repository is what makes that move safe.

Same extraction as plugins/music before it: source only, no history. The
platform's history still holds every commit that shaped this, and the SHAs cited
across the codebase keep resolving — replaying it here would have created a
second, divergent account of the same work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 18:12:57 +00:00

48 lines
2.4 KiB
TypeScript

import type { PluginManifest } from '@@/plugins/manifest';
// Offscale — Headscale, plus the Companion that ships beside it.
//
// Not a rename of Headscale and not a fork: the server underneath is stock, and the Companion adds what
// Headscale itself does not do — the invite flow being the first of them. The distinct name marks a
// distinct product rather than a badge on someone else's.
//
// The first real plugin, extracted from the platform on 2026-08-15. Everything it needs is here:
//
// api/router.ts a thin auth-gated proxy — no Headscale knowledge, and it must never grow any
// sidecar/ the whole Headscale contract, holding the admin API keys
// db/ offscale_servers, and the only table this plugin owns
// web/ panels and a layout; the shell renders the Workspace
export const manifest: PluginManifest = {
publisher: 'officerdev',
version: '1.0.0',
platform: '>=1.0.0',
label: 'Offscale',
summary: 'Your tailnet — machines, users, pre-auth keys, access policy and device invites',
icon: 'Network',
color: '#818cf8',
// One permission gating the whole surface, grantable per role at read or write like every other.
//
// `[open]` What a member's grant MEANS here is this plugin's own job and is not finished. The queries
// still scope by the caller (`listHeadscaleServers(userId)`), so a granted member would see their own
// empty server list rather than the owner's, and could register a Headscale of their own. The model in
// ./PLUGIN.md is one shared resource: read sees what the owner sees, write can change it.
// That is a change inside these queries, not a flag on the manifest.
//
// Worth knowing while it is unfinished: the stored credential is a Headscale ADMIN api key that can
// delete every node on a tailnet, and there is no read-only version of it — so `write` here is close to
// full control of the tailnet, which is the owner's decision to make deliberately.
permissions: [
{
key: 'offscale',
label: 'Offscale',
description: 'The tailnet: machines, routes, keys and ACLs',
// Two POSTs that are really reads — a reachability probe and a policy DRAFT that never saves.
// Without declaring them a read-level account meets a broken feature where a withheld permission
// should be. Inert while ownerOnly, and correct the moment that changes.
readOnlyWrites: ['/ssh-test', '/policy/assist'],
},
],
};