Plugin host architecture
How the plugin host isolates first-party plugins from the Platform Orchestrator, and what the v1alpha contract covers.
This chapter describes the plugin foundation released with chart 0.8.0.
The changelog
lists the compatible component versions and the upgrade behavior changes.
The Platform Orchestrator contains a neutral plugin host. It lets an operator add a separately packaged Stellwerk plugin without changing, rebuilding, or forking the Control Plane, Data Plane, IAM, or Console images. A plugin runs as its own Kubernetes workload, owns its own database, and reaches the Orchestrator only through documented, delegated APIs.
v1alpha and support
first-party Stellwerk plugins only. v1alpha is not a stable partner
contract. Read the release notes of every component before an upgrade.Terms
| Term | Meaning |
|---|---|
| Core | The Control Plane, Data Plane, IAM, Console, the octl CLI, the Terraform/OpenTofu provider, and the Helm chart |
| Plugin package | An immutable, signed OCI artifact with a plugin manifest, one static Linux executable per platform, and optional UI assets |
| Plugin runtime | The generic runtime image and Helm chart that verify and run a package. Every plugin uses the same runtime image |
| Plugin ID | The immutable reverse-DNS identity of a plugin, for example dev.stellwerk.progressive-rollouts |
| Installation | The deployment-wide Core record of one installed package version, kept by the Plugin Manager, identified by an immutable installation UUID |
| Enablement | The availability state of an installation for one organization. One installation can hold an enablement for every organization |
| Platform principal | An IAM service user that belongs to no organization and holds no role. An installation acts as its platform principal |
| Entitlement | A signed, offline-verifiable license grant for plugin features, for one organization of an installation or for the whole installation |
Components
Browser ── Console ──(sandboxed iframe)──> plugin UI (separate hostname)
│
▼
API gateway (IAM authentication) <─────────────────────────────┐
│ │
▼ │ plugin calls Core with
Control Plane (plugin host) │ a short-lived workload
│ checks enablement, license, and the registered operation │ token
│ signs a request context for the delegated user │
▼ │
plugin workload (generic runtime image) ───────────────────────┘
├─ plugin executable ──> plugin database (owned by the plugin)
├─ token sidecar
└─ UI server
Data Plane ──> normal Runner path. Deployments that a plugin requests are
ordinary Deployments.
- The Control Plane hosts the plugin gateway. It reads the Plugin Manager state, answers discovery, enforces enablement and entitlement, and forwards registered operations to the plugin.
- The plugin runtime verifies the signed package, its SBOM, and its provenance attestation before anything runs, executes a sealed in-memory copy of the verified executable, and serves only digest-verified UI assets. It tells the plugin its installation UUID and the verified package digest, and the plugin reports both to Core, which activates the installation only if they match.
- IAM exchanges the plugin Pod’s projected Kubernetes ServiceAccount token for a short-lived token of the installation’s platform principal. See Plugin workload identity. IAM also decides which users operate the Plugin Manager.
- The Console shows a plugin’s pages in a sandboxed iframe from the plugin’s own origin.
- The Data Plane runs every Deployment through the normal Runner path. There is no second execution engine for plugins.
How a request reaches a plugin
- A client discovers plugins.
GET /orgs/{orgId}/pluginslists the plugins available to an organization with their state, mode, advertised operations, and UI contribution.GET /orgs/{orgId}/plugins/{pluginId}returns the state of one plugin, a remediation hint inadmin_action, and the license projection. An unknown plugin is reported asnot_installed. - The client invokes a registered operation with
POST /orgs/{orgId}/plugins/{pluginId}/invoke. The body names anoperation_idand can carrypath_parameters,query,body, and anidempotency_key. Mutating operations require an idempotency key of at most 128 characters. - The Control Plane rejects operations that the Plugin Manager does not
register, checks the plugin’s state and the license feature that the
operation requires, and signs a short-lived request context. The context binds
plugin, organization, acting user, operation, method, path, body digest, and
request ID. The Control Plane never forwards the caller’s cookies, bearer
token, or
Fromheader. - The plugin verifies the context and applies its own resource-level checks. When it calls Core, it authenticates as its platform principal, and Core checks the delegated user again where the contract requires end-user authority.
Plugin APIs are reached through these operation IDs. Arbitrary REST paths under
a plugin namespace are not part of v1alpha.
Boundaries
- Out of process. No plugin code runs in a Core Pod. The runtime never executes a file path; it executes the verified bytes from a sealed in-memory copy.
- No Core database access. A plugin owns its database and migrations. The migration runs in a separate Job with its own database principal. The runtime principal cannot migrate, and the package fetch step never receives database credentials.
- Namespaced identifiers. Plugin IAM capabilities have the form
plugin.<plugin-id>.<name>. Plugin events have the typeplugin.<plugin-id>.<event>. A plugin cannot register capabilities or events in another namespace. - One plugin, several organizations. One installation serves every organization that is enabled for it, with one runtime, one plugin database, and one platform principal. Core confines each effect of the plugin to one organization and checks that organization’s enablement inside the effect’s own transaction. The platform principal holds no IAM role in any organization. A compromised plugin reaches every enabled organization within the plugin boundary, and all organizations share the plugin’s database, so a plugin bug can mix their data there. See Limits.
- No implied Core rights. A plugin capability never grants a Core
permission. For example, a plugin capability that overrides a Pin for one
operation never grants
module.version.unpin. - Events without broker credentials. Plugins read Core resource events from an HTTP journal and publish their own events through the Core transactional outbox. They receive no NATS credentials.
- Isolated UI. Plugin pages run in a sandboxed cross-origin iframe on a separate hostname. They receive no Console cookies and no Core tokens. The Console renders a plugin only after Core confirms its enablement and entitlement. Hiding navigation is not authorization.
- Contained failures. If a plugin fails, its operations fail with
plugin_unavailable. Core APIs, ordinary Deployments, and Module Version Management keep working.
Access to a package, package trust, installation, organization enablement, license entitlement, plugin service grants, and user permissions are separate decisions. A valid signature or a license never makes a user an administrator, and a license never grants an IAM permission.
Availability states
Every plugin has one state per organization. Clients map each state to a stable error code.
| State | Stable code | Meaning | Mode |
|---|---|---|---|
available | none | Installed, enabled, and licensed where required | full |
not_installed | plugin_not_installed | No installation for this organization, or the server predates discovery | none |
not_enabled | plugin_not_enabled | Installed, but not enabled for this organization | safety_only |
not_entitled | plugin_not_entitled | No current license for a required feature | safety_only, or hidden without reads |
incompatible | plugin_incompatible | The installed version is not compatible with this Core or client | hidden |
unavailable | plugin_unavailable | Out of service, or Core cannot reach the plugin or evaluate its license | hidden |
In full mode, every advertised operation is available. In safety_only mode,
Core keeps advertising reads and safety operations, such as cancel and
rollback, and rejects new forward work. In hidden mode, no operation is
available. See
Plugin availability diagnostics
for the client behavior.
Scope of v1alpha
Supported
The following table describes the implemented mechanisms. The released set has passed native installation checks on Linux AMD64 and ARM64. Offline bundle verification and mirror support do not, by themselves, qualify every air-gapped topology. Validate your registry, network boundaries, and chosen infrastructure before using a disconnected installation in production.
| Area | v1alpha |
|---|---|
| Publishers | Stellwerk only. Trust configuration and data are scoped per publisher |
| Delivery | Native executable package, installed by the operator through Helm and the Core Plugin Manager API (octl plugin-manager) |
| Platforms | linux/amd64 and linux/arm64 |
| Workload identity | Projected Kubernetes ServiceAccount token exchanged for a short-lived token of the installation’s role-less platform principal, bound to installation, ServiceAccount, and namespace |
| Organizations | Every organization (organizations: all, including organizations created later, with opt-out by disabling the enablement) or a chosen set. One platform principal, one runtime, and one plugin database serve all of them |
| Plugin writer availability | Several replicas with exactly one active writer through a lease with a monotonic fencing token. A standby takes over after the lease expires. Read APIs and UI may run on every replica |
| Events | HTTP resource journal and the Core transactional outbox |
| Package trust | The managed-key-no-tlog policy for publishers pinned in an operator-supplied trust bootstrap, covering the package signature, SBOM, and provenance. An installed digest stays loadable after a key rotation; only an explicitly revoked key blocks it |
| Entitlements | Canonical CBOR in COSE_Sign1, bound to the installation UUID and either one organization UUID or the whole installation. A valid organization grant takes precedence; an expired or revoked one falls back to a valid installation grant. An optional soft limit counts all enabled organizations. Development trust is rejected in production mode |
| Air-gapped operation | Offline bundle and mirror-first installation into your own registry |
| UI | Sandboxed cross-origin iframe pages, navigation, and contextual actions |
| Operator view | A read-only Plugin Manager view in the Console for platform operators with plugin_manager_read. You change the state with octl or the API |
| Upgrade and removal | The Plugin Manager upgrades to a later package, and uninstalls, reinstalls, and purges an installation. It refuses a downgrade. Recovery is roll-forward, or plugin-off with schema-compatible Core images |
Later. These areas are not part of v1alpha. Stellwerk has not committed
to a design or a date for them.
- Partner publishers, partner license issuers, and a partner conformance target
- Generic NATS subscription provisioning for plugins
- A generic renderer for schema-defined tables and forms. Navigation, contextual actions, and isolated plugin pages are supported without it
- Keyless Sigstore publishers with transparency-log verification. Publishers without a supported policy fail closed
- Organization self-service, delegated plugin administration, a configuration
and secret broker, and quotas. In
v1alpha, the operator binds Kubernetes Secret references
Not supported
- Synchronous policy hooks
- Same-origin UI microfrontends
- A marketplace or catalogue, usage metering, and dependencies between plugins
- Migrating data from earlier integrated (in-process) builds. Install plugins fresh
Plugin data export is also deferred. The Plugin Manager supports uninstall with retained data and an explicit purge; it does not provide an export step.
Provenance profiles
Provenance records which source and build inputs produced an artifact. It is separate from the SBOM, which describes its contents, and the signature, which binds those bytes to a publisher.
The release uses distinct profiles:
- Explicit release attestations for the generic plugin runtime use the
SLSA Provenance v1 predicate,
https://slsa.dev/provenance/v1. - Image builds retain the original attestations emitted by the pinned BuildKit producer. Their format and subjects are not rewritten after the build.
- Native plugin packages carry the source provenance predicate
https://stellwerk.dev/attestation/source-provenance/v1. The runtime verifies this statement, the package signature, and the SBOM against the same trusted publisher and package digest. It is not a SLSA Provenance v1 predicate.
Artifact verification checks the source, actual builder or workflow, build
inputs, subject digest, and signatures for the relevant profile. An attestation
file alone is not proof that these checks passed. Neither a predicate format
nor BuildKit’s mode=max establishes a certified SLSA level; this release makes
no such assurance claim.
Next steps
- Core-only installations describes what changes when you install no plugin.
- Install and operate native plugins covers installation, upgrade, disable, and uninstall.
- Plugin recovery covers plugin-off operation and stuck Environments.