Runtime architecture
How the Multi-Tenant Runtime schedules, isolates, and observes plugins.
The Multi-Tenant Runtime (BC-19) is where your plugin runs. This page is about what it actually does to your container at install-time and runtime.
Install flow
When a tenant installs your plugin, BC-02 (Lifecycle) hands the manifest to BC-19 (Runtime). BC-19:
- Verifies the image — Cosign signature must match the public key bound to your publisher account. Kyverno rejects anything else at admission.
- Creates a namespace —
plugin-{tenant-id}-{plugin-slug}under thepluginsAppProject. - Provisions secrets — every entry under
spec.secretsinplugin.yamlgets resolved through External Secrets Operator. Vault paths are namespaced:adapstory/plugins/{plugin-name}/{tenant-id}/.... - Applies limits — CPU, memory, storage, LLM token budgets, request rate.
- Wires network policy — egress only to the explicitly declared targets (Plugin Gateway, Kafka, Data Model Engine). No LAN probing.
- Schedules the pod — with
pod-security.kubernetes.io/enforce: restricted,runAsNonRoot: true, andallowPrivilegeEscalation: false.
Your image must run as non-root. If it hardcodes USER root the admission controller will reject it.
Per-tenant isolation
Each plugin-tenant pair gets its own namespace and its own database schema. The runtime:
- Injects a short-lived JWT with
sub=plugin:{name}:{tenant}— use it to call core APIs. - Binds a tenant-scoped PostgreSQL user (
plugin_{name}_{tenant_id}) with permissions only on its own schema. - Routes Kafka traffic through ACLs keyed by
(tenant-id, plugin-name). You only see events published to or for your tenant.
Cross-tenant access is a contract violation and is detected by audit logs + per-namespace NetworkPolicies.
Observability
Every plugin pod gets three agents injected automatically:
| Agent | What it collects | Where it ships |
|---|---|---|
| OTEL Collector sidecar | Traces + metrics (OTLP) | Tempo / Prometheus |
| Grafana Alloy (DaemonSet) | Continuous CPU profiles (eBPF) | Pyroscope |
| Fluent Bit | Structured JSON logs | Loki |
Your code just needs to:
- Emit OpenTelemetry spans (most SDKs do this for free).
- Log JSON (
log-format=json). - Include
tenant.idas a span attribute on every call.
Failure modes
- Out-of-budget LLM call — BC-01 returns HTTP 429 with
X-LLM-Budget-Resetheader. Your plugin must degrade gracefully (cache, fallback, queue). - Kafka consumer lag —
consumer_lagalerts fire at p99 > 60s for 5m. The runtime will not auto-scale you; you tunemaxReplicasin the manifest. - Guardrail violation — BC-01 returns HTTP 403 with
X-Guardrail-Rule. Do not retry — fix the prompt or the content. - Signature invalid — install fails fast with
ImagePullBackOff+ Kyverno audit event. Re-sign and republish.
Rollback
Rollbacks are deterministic: bd plugin rollback <name> --tenant <id> (or via the admin UI) calls BC-02, which re-applies the last known-good manifest and drains traffic to the old version.
- DB schema migrations are forward-compatible — if your v1.3 added a column, v1.2 can still run (it just ignores the column).
- If a migration is not forward-compatible, BC-15 will reject the manifest at install. Split the migration across two versions.
Continue: Plugin quickstart.