Commit graph

2 commits

Author SHA1 Message Date
iamtoruk
67320432ca feat(plugins): ed25519 release-key verification and plugin add/remove (9b)
Ships ed25519 signature verification for plugins with two new components:

- src/plugins/keys.ts: RELEASE_PUBLIC_KEYS map with one release keypair
- scripts/sign-plugin.mjs: keygen and sign commands for plugin developers
- verifyPlugin() in loader.ts: validates ed25519 signatures, rejects unsigned plugins unless CODEBURN_PLUGIN_DEV=1
- plugin add <path>: installs signed plugins to ~/.config/codeburn/plugins/
- plugin remove <name> --confirm: removes installed plugins

All three gates pass: tsc clean, vitest zero new failures, smoke test complete.

Design decision: public keys stored as base64-encoded PEM format rather than raw 32-byte keys. Node.js crypto.verify requires PEM/DER format or KeyObject for ed25519; raw bytes alone fail. PEM is standard and portable.

Private key written to: /tmp/codeburn-signing/codeburn-signing-key.pem
2026-08-28 00:30:43 -07:00
iamtoruk
52bde7c4dd feat(plugins): CB-3 plugin socket — wire guard, CLI inspector, payload seam
Teams issue #3 introduces the codeburn plugin socket: an opt-in escape
hatch for third parties to add sync attributes, payload sections, and CLI
commands without touching the codeburn CLI source tree. This commit ships
the first half (CB-3) — the manifest layer, the wire guard, and the user-
facing inspector. Release-key signing (the 9b seam) lands separately.

Why split now:
  - The wire guard (filterPluginAttributes in sync/otlp.ts) is the load-
    bearing piece. With it, no plugin code can run until the user opts
    in by installing one — the default empty socket keeps every byte of
    the existing telemetry wire byte-identical. That is the contract the
    existing four sync test files were already pinning; this commit
    re-pins it explicitly with a new test (tests/plugin-socket.test.ts).
  - The inspector () makes rejections
    visible. Without it, a misconfigured plugin would just vanish
    silently — exactly the foot-gun the socket exists to prevent.

What ships:
  - src/plugins/loader.ts: enumerates ~/.config/codeburn/plugins/<name>/,
    reads + parses each codeburn-plugin.json, applies name/dir match and
    cliCompat check, hands off to verifyPlugin (the 9b seam). Rejected
    plugins contribute nothing to the wire.
  - src/plugins/manifest.ts: parses + validates the manifest shape
    (name, version, cliCompat, capabilities.{commands, syncAttributes,
    payloadSections, spanKinds}). Returns {ok, reason} on any failure.
  - src/plugins/cli.ts: . Read-only;
    never mutates the plugins directory.
  - src/sync/otlp.ts: filterPluginAttributes strips any key not in the
    declared set, called from buildOtlpPayload when pluginAttributes are
    supplied. With no plugin installed, the call path is a no-op.
  - src/sync/push.ts, src/sync/cli.ts: thread pluginAttributeKeys
    through sendBatches -> buildOtlpPayload. The wire is unchanged when
    no plugin declares anything.
  - src/usage-aggregator.ts, src/menubar-json.ts: add the
    MenubarPayload.plugins field and populate it from pluginPayloadSections
    (empty when no plugin installed).
  - src/main.ts: register the plugin subcommand.
  - tests/plugin-socket.test.ts: 12 tests covering the wire guard, the
    byte-identical default, the loader rejection paths, and the CLI.
  - scripts/smoke-plugin-socket.mjs: end-to-end smoke test against the
    built CLI (13 assertions).

Byte-identical guarantee:
  - With no plugin installed: loadPlugins() returns [], pluginPayloadSections
    returns {}, filterPluginAttributes(empty) returns []. The OTLP payload
    and the menubar payload are bit-for-bit the same as before this commit.
  - The dry-run output gains one new line listing the active plugin names
    (empty string when no plugin). User-facing only; not on the wire.

What's intentionally NOT here:
  - Plugin command invocation:  lands when at
    least one community plugin publishes a command (deferred).
  - : shipping this with no signed-plugin
    ecosystem in place would invite supply-chain risk. Deferred.
  - Release-key signing (the verifyPlugin return path). Deferred to 9b.

Refs: teams issue #3, CB-3 sync boundary spec section 9a.
2026-08-27 09:29:15 -07:00