the manifest holds only what the tree cannot say
lean it to identity facts and human choices: publisher, version, platform range, the four presentation fields, and permissions. everything structural becomes convention, where presence is the declaration — sidecar/, api/router.ts, db/schema.ts, web/Router.tsx, web/panels.ts. appName comes from the directory name, so the id cannot disagree with where the code sits. the dock tile and page title needed no fields at all: the tile is label + icon + color + mountPrefix, and the title is label. writing them again was duplication that could only drift. runtime is the file extension. index.mjs is node, index.ts is bun — implicit, but already the rule here, since officer-pty runs under node for node-pty's abi and everything else is bun. better than a field that can contradict the file. dependsOn is gone; nothing read it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+37
-37
@@ -465,32 +465,20 @@ from nothing includes what nothing needs and misses what is awkward. The field s
|
||||
is the floor, not the ceiling.
|
||||
|
||||
```ts
|
||||
// plugins/offscale/manifest.ts
|
||||
export const manifest = {
|
||||
// ── Identity ─────────────────────────────────────────────────────────────
|
||||
/** THE id. Route segment, table prefix, sidecar suffix, install key. One name, everywhere. */
|
||||
appName: 'offscale',
|
||||
/** Who published it. The ONLY input that decides the mount prefix — see `mountPrefix()`. */
|
||||
/** Constant today. The one input to `mountPrefix()`, and the seam third parties hang off later. */
|
||||
publisher: 'officerdev',
|
||||
/** The plugin's own semver. Updates compare against this. */
|
||||
version: '1.0.0',
|
||||
/** Which platforms this build is good for. Refused at install when it does not match. */
|
||||
platform: '>=1.0.0 <2.0.0',
|
||||
|
||||
// ── Presentation ─────────────────────────────────────────────────────────
|
||||
label: 'Offscale',
|
||||
summary: 'Your tailnet — machines, users, pre-auth keys and access policy',
|
||||
icon: 'Network',
|
||||
color: '#818cf8',
|
||||
|
||||
// ── Backend ──────────────────────────────────────────────────────────────
|
||||
sidecar: {
|
||||
/** PM2 process name. Always `officer-<appName>`, but written out because PM2 is told it verbatim. */
|
||||
process: 'officer-offscale',
|
||||
/** `bun` or `node`. A field rather than an assumption: officer-pty needs node for node-pty's ABI. */
|
||||
runtime: 'bun',
|
||||
script: 'sidecar/index.ts',
|
||||
},
|
||||
|
||||
// Named `permissions`, NOT `capabilities`. That word already means three different things here — the
|
||||
// permission registry, the officer-items store, and the sidecar's routing keys — and a fourth would be
|
||||
// one too many. `permissions` is accurate and free: the old table of that name went in 044aacf4.
|
||||
@@ -503,32 +491,44 @@ export const manifest = {
|
||||
ownerOnly: true,
|
||||
},
|
||||
],
|
||||
|
||||
// ── Data ─────────────────────────────────────────────────────────────────
|
||||
/** Drizzle schema. Every table must be prefixed `offscale_`; the installer enforces it. */
|
||||
schema: 'db/schema.ts',
|
||||
|
||||
// ── Frontend ─────────────────────────────────────────────────────────────
|
||||
frontend: {
|
||||
/** Default export is mounted at `<prefix>/*` by the generated Plugins.tsx. */
|
||||
router: 'web/Router.tsx',
|
||||
/** `appRegistryMetas` — the panel apps this plugin contributes. */
|
||||
panels: 'web/panels.ts',
|
||||
dock: { label: 'Offscale', to: '/offscale' },
|
||||
title: 'Offscale',
|
||||
},
|
||||
|
||||
// ── Optional dependencies ────────────────────────────────────────────────
|
||||
// Informational, not enforced: both are service dependencies that already degrade. They exist so the
|
||||
// store can say "the Console section needs the terminal plugin" instead of the section silently not
|
||||
// working, which is the difference between a missing feature and a broken one.
|
||||
dependsOn: [
|
||||
{ appName: 'anthropic-proxy', optional: true, reason: 'the ACL drafting assistant' },
|
||||
{ appName: 'pty', optional: true, reason: 'the SSH console section' },
|
||||
],
|
||||
} as const;
|
||||
```
|
||||
|
||||
### Everything the tree can say, the tree says
|
||||
|
||||
The manifest holds only what a directory listing genuinely cannot tell you: an identity fact, or something
|
||||
a human chose. Everything structural is convention, and **presence is the declaration**:
|
||||
|
||||
| Path | Means |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| _the directory name_ | `appName` — `plugins/offscale/` **is** the id, so it cannot disagree with where the code sits |
|
||||
| `sidecar/index.ts` | there is a sidecar; PM2 gets an entry. `.mjs` instead means node — see below |
|
||||
| `api/router.ts` | there is a backend router, mounted at `mountPrefix(manifest)` |
|
||||
| `db/schema.ts` | there are tables; pushed on install, every name prefixed `offscale_` |
|
||||
| `web/Router.tsx` | there is a frontend; its default export mounts at `<prefix>/*` |
|
||||
| `web/panels.ts` | it contributes panels; exports `appRegistryMetas` |
|
||||
|
||||
The dock tile and the page title need no fields either — the tile is `{ label, icon, color, to:
|
||||
mountPrefix(manifest) }` and the title is `label`, all of which are already above. Writing them again was
|
||||
duplication that could only ever drift.
|
||||
|
||||
**The runtime is the file extension.** `sidecar/index.mjs` runs under node, `sidecar/index.ts` under bun.
|
||||
Implicit, but it is the rule this repo already follows — `officer-pty` is `pty/index.mjs` under node
|
||||
because node-pty is a native module built against Node's ABI, and everything else is bun. Better than a
|
||||
field that can contradict the file it describes.
|
||||
|
||||
### Dropped from the first draft
|
||||
|
||||
- **`dependsOn`** — nothing read it and nothing enforced it. Both of offscale's dependencies already
|
||||
explain themselves where it matters (`assistant_unavailable`; "no SSH host configured"). A field whose
|
||||
only job is to be displayed, that nothing displays, is stale the first time anyone looks at it. Add it
|
||||
when something consumes it.
|
||||
- **`kind`** — see below.
|
||||
- **`sidecar` / `schema` / `frontend` objects** — all convention now.
|
||||
|
||||
`[open]` A plugin with a frontend that should NOT get a dock tile has no way to say so: `web/` present
|
||||
means a tile. Fine for offscale; add a flag the first time something needs it.
|
||||
|
||||
### `admin` has to be allowed, and the pilot proved it immediately
|
||||
|
||||
The earlier rule here was "a plugin may declare `app`, and nothing else". **That is wrong, and offscale is
|
||||
|
||||
Reference in New Issue
Block a user