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. // // `ownerOnly` because the credential behind it is a Headscale ADMIN api key that can delete every node // on a tailnet, and there is no read-only version of it. A read grant would still be reading through // that key; the protection is that non-owners cannot reach the routes at all. // // Read/write for members is the model recorded in docs/offscale-plugin.md and deliberately not enabled // here yet: it needs the queries to resolve to the OWNER's rows rather than the caller's, which is a // change inside this plugin and not a flag. permissions: [ { key: 'offscale', label: 'Offscale', description: 'The tailnet: machines, routes, keys and ACLs', ownerOnly: true, // 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'], }, ], };