Architecture
Big picture
SPIRE is two binaries. spire-server is the certificate authority for one trust domain: it attests nodes, holds the registration entries, and signs SVIDs. spire-agent runs on each node, gets its own identity from the server through node attestation, then serves identities to local workloads over a Unix domain socket. Both binaries push almost every function through a plugin catalog (pkg/common/catalog/), so attestation, key management, and upstream authorities are swappable. The agent caches and rotates SVIDs ahead of time, so a workload's request is served from a local cache rather than a round trip to the server.
Components
spire-server
The trust-domain CA. Its entry point is cmd/spire-server/main.go, which delegates to the CLI through entrypoint.NewEntryPoint(new(cli.CLI).Run).Main(). It attests nodes, stores registration entries in a datastore, and signs workload SVIDs. The CA signing surface is pkg/server/ca/ca.go:335 (SignWorkloadX509SVID). Server-side plugin families live under pkg/server/plugin/: nodeattestor, upstreamauthority, keymanager, bundlepublisher, credentialcomposer, and notifier.
spire-agent
Runs on each node. Its entry point is cmd/spire-agent/main.go, the same thin CLI delegation. It performs node attestation to obtain its own SVID, then exposes the Workload API on a Unix domain socket. The agent's manager subscribes the cache to workload updates (pkg/agent/manager/manager.go:258) and an SVID rotator renews certificates through the server client (pkg/agent/svid/rotator.go, RenewSVID). Agent-side plugin families live under pkg/agent/plugin/: nodeattestor, keymanager, svidstore, and workloadattestor.
Plugin catalog
All functionality is pluginized. The catalog implementation is pkg/common/catalog/ (catalog.go, builtin.go, bind.go). Built-in plugins and external plugins (loaded through HashiCorp go-plugin) implement the same catalog interface, so the loading path is uniform.
How a request flows
This traces a workload fetching an X509-SVID, end to end.
- The workload calls the streaming RPC
FetchX509SVIDover the UDS. The handler ispkg/agent/endpoints/workload/handler.go:251. The request body is empty and carries no credential. - Workload attestation. The handler calls
h.c.Attestor.Attest(ctx)athandler.go:256. The caller's PID comes from the connection itself: SPIRE reads the UDS peer credential from the kernel, usingunix.GetsockoptUcredwithSO_PEERCREDon Linux (pkg/common/peertracker/uds_linux.go:10) andLOCAL_PEERPIDon BSD/macOS (pkg/common/peertracker/uds_bsd.go:13). - The attestor body is
pkg/agent/attestor/workload/workload.go:49(Attest(ctx, pid)). It pulls the workload attestor plugins from the catalog and runs each in a goroutine to gather selectors from the PID (workload.go:55-87). It only returns an error if every plugin failed (workload.go:89-91). - Rate limiting.
h.rateLimit(ctx, MethodFetchX509SVID, selectors)athandler.go:262. The agent's own calls (such as the health check) are exempt viaisAgent(ctx)athandler.go:86. - Cache subscription.
h.c.Manager.SubscribeToCacheChanges(ctx, selectors)athandler.go:266, which the manager maps tom.cache.SubscribeToWorkloadUpdates(pkg/agent/manager/manager.go:258). The SVID is not fetched from the server per request; the manager has already cached and rotated it. - The streaming loop (
handler.go:273-283) receives cache updates onsubscriber.Updates(), keeps only the identities for this caller withfilterIdentities, and writes the chain and key withsendX509SVIDResponse. It exits onctx.Done(). When an SVID rotates, the new one is pushed onto the open stream rather than polled.
Server-side signing happens outside this loop. The agent's rotator generates the key pair locally and sends a CSR to the server (pkg/agent/svid/rotator.go). The server CA receives only the public key, builds a template, signs it, and validates the resulting SPIFFE ID (pkg/server/ca/ca.go:341-358).
Key design decisions
- No credential from the workload. Identity is derived only from kernel-verified process metadata (the PID from
SO_PEERCRED, and the uid/gid/container ID derived from it). There is no bootstrap secret, so the secret distribution and rotation problem disappears. - Private keys never leave the node. The agent (or a workload via an SVID store) generates the key pair locally and sends only a CSR. The server CA references only
params.PublicKeywhen signing (pkg/server/ca/ca.go:347). The server never holds the workload's private key. - Two-factor attestation. Node attestation (the agent proving itself with platform evidence) plus workload attestation (process attributes) form a hierarchy, where an entry's
ParentIdmaps to the node andSelectorsmap to the workload. - Cache and push, not poll. The agent rotates SVIDs ahead of expiry and pushes updates to open streams, so workloads avoid a per-request round trip to the server.
Extension points
The plugin families are the extension surface. On the server: nodeattestor, upstreamauthority, keymanager, bundlepublisher, credentialcomposer, notifier (pkg/server/plugin/). On the agent: nodeattestor, keymanager, svidstore, workloadattestor (pkg/agent/plugin/). Plugins can be compiled in as built-ins or loaded as external processes through the same catalog interface (pkg/common/catalog/).