first dated version. plugins were all sitting on 1.0.0, which said nothing about when a build was made or which of two copies is newer — the platform already uses YYYY.MM.DD.NN and this follows it. the version lives in manifest.ts because a plugin has no package.json; that is the field the marketplace publishes and the installer compares. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
66 lines
3.5 KiB
TypeScript
66 lines
3.5 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: '2026.08.27.01',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Offscale',
|
|
summary: 'Your tailnet — machines, users, pre-auth keys, access policy and device invites',
|
|
/** the tailnet: who reaches what. */
|
|
tags: ['Server Management', 'Homelab', 'Security'],
|
|
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 (`listOffscaleServers(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.
|
|
// The Console shells into a machine by spawning `ssh` (sidecar/ssh.ts), and so does the reachability
|
|
// probe behind /ssh-test. This manifest said "Offscale needed nothing" until 2026-08-16, which meant a
|
|
// host without an ssh client passed the install checks and then failed at runtime with a spawn error —
|
|
// the one failure mode `osDependencies` exists to convert into a refusal at install time. PLUGIN.md had
|
|
// listed it as a host dependency all along; the manifest was the file that was wrong.
|
|
//
|
|
// Present on effectively every Linux install, which is exactly why it was missed: the declaration costs
|
|
// nothing on the machines that have it and is the whole difference on the machines that do not.
|
|
osDependencies: [
|
|
{
|
|
binary: 'ssh',
|
|
reason: 'Opens the Console shell to a machine, and probes reachability before you try.',
|
|
packages: { apt: 'openssh-client', pacman: 'openssh', dnf: 'openssh-clients', brew: 'openssh' },
|
|
},
|
|
],
|
|
|
|
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'],
|
|
},
|
|
],
|
|
};
|