SPIFFE / mTLS
Service-to-service authentication in Scrydon — Dapr-native SPIFFE identity, mTLS, and the four-layer trust chain.
All internal service-to-service communication in Scrydon is mutually authenticated via SPIFFE identity over mTLS. There are no shared bearer tokens for service callers, and there is no fallback path that bypasses identity verification.
The four-layer trust chain
Each internal call passes through four independent checks. Any failure denies the request.
| Layer | What it checks |
|---|---|
| Transport | Dapr mTLS between sidecars. Certificates issued by the Dapr Sentry CA. |
| Network | Dapr access-control list (defaultAction: deny). Only explicitly allowed app IDs reach a given route. |
| Application | The receiving service validates the caller's app ID, namespace, and required capability. |
| Provenance | The receiver verifies the request traversed the Dapr sidecar by inspecting a sidecar-issued token. |
Fail-closed: if any layer fails, the request is denied with no fallback.
Identity verification
Every internal route resolves the caller through a single helper:
- Extract the caller identity — app ID, namespace, sidecar token.
- Look up the required capability for the route.
- Match the identity against the route's allowlist.
- Match the capability against the caller's grant set.
- Return decision — authorised, or denied with a structured error.
The decision is recorded in the audit log as an AUTH_INTERNAL_* event.
Capabilities
Internal calls don't get a blank cheque. Each receiver declares which capabilities it accepts, and each caller is granted only the capabilities it needs. Examples:
| Capability | Granted to | Purpose |
|---|---|---|
llm:complete | The LLM gateway | Permission to invoke an LLM model |
managed-table:read | The analytics service, the ontology service | Read managed-table rows |
managed-table:agent-create | The workflow runtime | Create agent-managed tables |
kb:read | The retrieval surface, the chat surface | Read knowledge-base documents at the caller's clearance |
secrets:internal | The workflow runtime | Fetch decrypted credentials for the active execution grant |
Capabilities are checked per request. There is no notion of "this caller is admin so anything goes."
Dev mode
Local development without a Dapr control plane runs in a degraded mode: a short-lived dev identity marker substitutes for SPIFFE identity. This mode is rejected outright in production. The receiver inspects the deployment mode and refuses to honour a dev marker if it's not running in a dev environment.
What this gives you
- Spoofing protection. A request claiming to come from an internal service must actually traverse a Dapr sidecar with a valid mTLS certificate. Hand-crafted requests with forged headers are rejected at the transport layer.
- Lateral-movement containment. A compromised pod can only call services its app ID is explicitly allowed to reach. There is no "internal trust zone" that grants everyone access to everything.
- Audit clarity. Every internal call is identified by the caller's app ID, not by an opaque service-account token. The audit log can answer "which service called this endpoint" deterministically.
The Dapr control plane runs in your cluster. Scrydon does not have access to your service mesh identity material — the Sentry CA is yours, and its private key never leaves your cluster.
Related
- Network boundary — the wider perimeter around the mesh.
- Ingress hardening — how external requests are scrubbed before they reach the mesh.
- Authorization — how user-driven authorisation interacts with service-to-service identity.