5.2 KiB
Certified Plugins
Admission boundary
Core verifies a plugin archive before artifact storage, PluginSetting
persistence, or a Plugin Runtime apply request. It calls the SDK public
langbot_plugin.certification.verify_archive() API, which reads the strict
certificate envelope from the ZIP comment and verifies the signed normalized
ZIP digest without extracting the payload.
Core retains the artifact SHA-256, normalized digest
(normalized_zip_digest()), verification state, declared shared-runtime
profile, key ID, selected admission profile, and stable admission code in the
durable plugin install_info._certification record. The record belongs to the
installation row; no schema migration is needed for this additive JSON
metadata.
Trusted issuer configuration
Configure the non-secret Ed25519 public-key ring in data/config.yaml:
plugin:
certification:
trusted_public_keys:
issuer-2026-q3: "<base64 encoded 32-byte Ed25519 public key>"
Key IDs must match the SDK envelope. Values are standard base64 raw public keys, not private/signing keys. An invalid key-ring configuration is rejected rather than weakening verification. Keep active issuer keys during a rotation until archives signed by retired IDs are no longer installed.
The ring may also be supplied out-of-band, which is how hosted deployments provision it:
PLUGIN__CERTIFICATION__TRUSTED_PUBLIC_KEYS_JSON='{"ed25519:issuer":"<base64>"}'
An empty ring is a supported state, not a misconfiguration. OSS defaults to it, so a self-hosted instance that has not provisioned any issuer key still installs packages (see the admission matrix below). Configure the ring to grant the shared-runtime profile; leave it empty to keep every package on the dedicated profile.
Admission matrix
| Deployment | SDK verification | Explicit administrator_force |
Result |
|---|---|---|---|
| Cloud | valid envelope declaring shared-runtime-v1 |
any | admitted to the shared profile |
| Cloud | absent | any | reject before storage with CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_REQUIRED |
| Cloud | malformed, untrusted, invalid, or non-shared | any | reject before storage with CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID |
| OSS | absent legacy envelope | any | admitted to the dedicated profile |
| OSS | valid envelope declaring shared-runtime-v1 |
any | selected shared profile |
| OSS | declaration signed by a key this instance resolves | false | reject with CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED |
| OSS | declaration signed by a key this instance resolves | true | admitted to the dedicated profile |
| OSS | declaration this instance cannot resolve (empty ring) | any | admitted to the dedicated profile |
The OSS row that matters for availability is the last one. Marketplace
packages are signed by the marketplace issuer and declare
shared-runtime-v1, while OSS ships an empty key ring by default. Treating that
as a rejection made every certified marketplace package uninstallable with
CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED before artifact storage. Because the
certificate is signed by an issuer the instance does not declare trusted, no
shared-runtime privilege may be granted, so admission degrades the install to
the existing oss_dev dedicated profile and records
CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED. This is not an escalation: it
withholds the shared profile rather than granting it.
A declaration is "resolvable" only when its key_id is present in the
configured ring. When the ring is configured and the declaration still fails
(malformed, signature_invalid, digest_mismatch, unsupported_schema, ...),
admission stays explicit and requires administrator_force.
administrator_force is deliberately strict: it is recognized only when the
install request carries boolean true. The local upload endpoint accepts the
multipart field administrator_force=true; GitHub and marketplace install
payloads carry the same field. The existing resource-manage authorization fence
protects those endpoints. A force never creates a Cloud dedicated fallback.
Runtime and logs
SDK 0.6.2 carries an installation-level execution mode in both apply and
authoritative reconcile payloads. Core selects shared-runtime-v1 only when
the persisted certification record says verification was valid, both the
certificate and admission profiles are shared-runtime-v1, the admission code
is shared-eligible, and the record's artifact SHA-256 exactly matches the
installation row. Missing, malformed, stale, invalid, or dedicated admission
facts select dedicated. Install, upgrade, configuration revision, restart,
and reconnect all use this same persisted-fact derivation.
The existing public plugin-log boundary already applies the immutable
installation binding (including workspace UUID) through
RuntimeConnectionHandler.installation_scope() before requesting logs. This is
the actual tenant exposure boundary, so valid shared certificates use that
binding-scoped transport; Core does not invent a second log stream or expose
process-wide log output. Dedicated and invalid/legacy installations use the
same existing installation scope.
SDK versioning
Core pins langbot-plugin==0.6.2, the first published SDK release carrying the
canonical installation execution-mode contract.