Adapstory Docs
Platform

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:

  1. Verifies the image — Cosign signature must match the public key bound to your publisher account. Kyverno rejects anything else at admission.
  2. Creates a namespaceplugin-{tenant-id}-{plugin-slug} under the plugins AppProject.
  3. Provisions secrets — every entry under spec.secrets in plugin.yaml gets resolved through External Secrets Operator. Vault paths are namespaced: adapstory/plugins/{plugin-name}/{tenant-id}/....
  4. Applies limits — CPU, memory, storage, LLM token budgets, request rate.
  5. Wires network policy — egress only to the explicitly declared targets (Plugin Gateway, Kafka, Data Model Engine). No LAN probing.
  6. Schedules the pod — with pod-security.kubernetes.io/enforce: restricted, runAsNonRoot: true, and allowPrivilegeEscalation: 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:

AgentWhat it collectsWhere it ships
OTEL Collector sidecarTraces + metrics (OTLP)Tempo / Prometheus
Grafana Alloy (DaemonSet)Continuous CPU profiles (eBPF)Pyroscope
Fluent BitStructured JSON logsLoki

Your code just needs to:

  • Emit OpenTelemetry spans (most SDKs do this for free).
  • Log JSON (log-format=json).
  • Include tenant.id as a span attribute on every call.

Failure modes

  • Out-of-budget LLM call — BC-01 returns HTTP 429 with X-LLM-Budget-Reset header. Your plugin must degrade gracefully (cache, fallback, queue).
  • Kafka consumer lagconsumer_lag alerts fire at p99 > 60s for 5m. The runtime will not auto-scale you; you tune maxReplicas in 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.

On this page