Skip to content

Identity & network isolation (Cilium + SPIFFE)

Every workload and every person on the platform has a cryptographic identity, and every network decision is made on that identity — never on an IP address. This is what keeps one customer's silo sealed off from every other, and it is the single idea behind all the rules on this page.

See also: Networking & isolation — the two-plane cluster model and the public edge this identity layer sits behind; read that first for the overall shape. Identity & connection auth — how a person signs in (Zitadel OIDC) and reaches their own assistant. Silo deployment model — how each customer's silo is installed as its own release. ADR 0003 — Cilium + SPIFFE identity substrate — the decision behind this model, and why it supersedes the earlier Linkerd choice.


Two kinds of identity

There are exactly two kinds of principal on the platform, and each carries a cryptographic identity that the network can check.

PrincipalIdentityIssued byUsed for
A person (owner, admin, member)Zitadel OIDC session, scoped to their orgZitadel (one org per customer)Signing in to the control plane and reaching their own assistant
A workload (control plane, operator, LiteLLM, Obot, Cognee, an assistant pod)SPIFFE SVIDspiffe://opencrane/ct/<org>/<workload>SPIRE, from the pod's Kubernetes ServiceAccountProving which workload is calling, over mutual TLS

People and workloads meet only at the control plane, and only over an OIDC-guarded hop. A person never gets a workload identity, and a workload never gets a browser credential.

Why identity, not IP

Pods come and go; IP addresses churn and can be reused. A SPIFFE identity is bound to the workload's ServiceAccount, is short-lived, and rotates automatically — so a rule written against it keeps meaning the same thing no matter where the pod lands.


One rule: default-deny, opened by identity

Each customer org is a silo — its own namespace, its own runtime planes. The rule in every silo is the same: deny everything, then re-open only for named identities.

┌───────────────────────────────────────────────────────────────┐
│  SILO  (org "acme")                                            │
│                                                                │
│   default: DENY all traffic, in and out                        │
│                                                                │
│   allow ▸ acme workloads  ⇄  acme workloads     (same silo)    │
│   allow ▸ super-admin identity  →  acme         (control plane) │
│   allow ▸ acme workloads  →  DNS                               │
│   allow ▸ acme workloads  →  approved hostnames only  (egress) │
│                                                                │
│   there is NO rule that names another silo — so:               │
│        acme  ✗  bravo         bravo  ✗  acme                    │
└───────────────────────────────────────────────────────────────┘

Because no rule ever names another silo, there is no silo-to-silo path to exploit — cross-silo traffic isn't blocked by a clever filter, it simply has nothing that permits it. The only identity admitted into a silo from outside is the super-admin (the control plane / operator), which brokers the person's connection to their pod.


Who can talk to whom

These are the network-level authorisation rules, expressed as CiliumNetworkPolicy and keyed on workload identity. Every allow below is an identity match; everything not listed is denied.

From (identity)To (identity)Allowed?Why
An assistant pod in silo acmeAnother pod in silo acmeIntra-silo — one org's workloads collaborate
An assistant pod in silo acmeA pod in silo bravoNo rule names another silo — cross-silo is impossible
The super-admin (control plane / operator)Any siloThe only cross-silo identity; it brokers connections and reconciles the silo
Any siloThe super-admin control plane✅, /api/* onlySilos re-pull their effective contract and mint model keys
An assistant podLiteLLM / Obot / Cognee in its own siloThe runtime planes are dedicated per silo
An assistant podAn external hostname on the egress allow-liste.g. the model provider — see egress
An assistant podAny other external hostnameEgress is default-deny by hostname
A browserAn assistant pod directlyBrowsers never reach a pod directly — only through the identity-routing proxy (connection auth)

How a workload gets its identity

The chain is automatic — no secrets to distribute, nothing for an operator to rotate by hand.

Kubernetes ServiceAccount           the pod's declared identity


SPIRE issues a SPIFFE SVID          spiffe://opencrane/ct/acme/openclaw
        │  (short-lived, auto-rotating X.509)

Cilium mutual authentication        every silo-to-silo call is mTLS,
        │                           proven on both ends by SVID

CiliumNetworkPolicy decision        allow / deny keyed on the SVID identity

Each workload's SVID is derived from its ServiceAccount, so "who is calling" is a cryptographic fact, not a header a caller can spoof. Calls between workloads are wrapped in mutual TLS, so both ends prove their identity before a byte of application data flows.


The super-admin is the only cross-silo identity

The control plane and operator run as the super-admin identity — and it is the only principal that may cross a silo boundary. It uses that reach to broker a person's connection to their own assistant and to reconcile the silo's resources. Nothing else — no assistant, no plane, no person — can address a workload in a silo it doesn't belong to.

That makes the super-admin's identity the crown jewel: its issuance, rotation, and audit are the most load-bearing security tasks on the platform. Every other identity is scoped to a single silo by construction.


Egress is an allow-list too

Isolation isn't only about east-west (silo-to-silo) traffic — it also bounds what a silo can reach on the outside. Egress is default-deny by hostname: a silo can resolve DNS and reach only the external endpoints on its allow-list.

assistant pod ──► DNS (cluster)                         ✅ always
assistant pod ──► api.openai.com (approved provider)    ✅ on the allow-list
assistant pod ──► anything else on the internet         ✗ denied

CiliumNetworkPolicy toFQDN rules express this per silo, so a compromised assistant cannot exfiltrate to an arbitrary host — it can only reach the model and tool endpoints the org has approved.


Three layers, all keyed on identity

The isolation is defence-in-depth: a connection between two silos has to defeat all three layers, and each is keyed on the same workload identity.

LayerWhat it doesCross-silo verdict
L3/L4 — CiliumNetworkPolicyPacket-level allow-list, keyed on Cilium security identityDrops the packet — the source identity isn't in any allow rule
L7 — CiliumNetworkPolicy (per-route)Which identities may call which routes/methodsRejects the request — the caller identity isn't authorised for that route
mTLS — SPIFFE mutual authBoth ends prove their SVID before data flowsFails the handshake — no trusted SVID for a foreign silo

A gap in any one layer doesn't open the silo, because the other two still have to pass. And because all three read the same identity, they can't disagree about who the caller is.

The floor never disappears

The portable, standard-NetworkPolicy default-deny floor from the silo baseline stays in place underneath — Cilium enforces it too. The identity layers add to it; they never relax it.


See also

Released under the AGPL-3.0-or-later License.