# SPIRE Plugin Types This document describes the four SPIRE plugin interfaces implemented by this repository, their callback methods, invocation timing, and the Guildhouse plugin that implements each. ## Overview SPIRE's plugin architecture uses the go-plugin (hashicorp/go-plugin) framework. Plugins are compiled as separate binaries and loaded by the SPIRE Agent or Server at startup via the plugin configuration in `server.conf` or `agent.conf`. | Interface | Side | Guildhouse Plugin | Purpose | |-----------|------|-------------------|---------| | WorkloadAttestor | Agent | oidc-attestor | Identify workloads by OIDC token | | CredentialComposer | Server | ssh-credential-composer | Modify credentials during minting | | Notifier | Server | governance-notifier | React to SPIRE lifecycle events | | KeyManager | Server | substrate-keymanager | Manage server signing keys | ## WorkloadAttestor **Side:** SPIRE Agent **Plugin:** `cmd/oidc-attestor` **Interface methods:** ```go Attest(ctx context.Context, pid int32) ([]*common.Selector, error) ``` **When called:** Every time a workload calls the Workload API (typically `FetchX509SVID` or `FetchJWTSVID`). The agent identifies the calling process by PID, then passes that PID to all registered WorkloadAttestor plugins. Each plugin returns zero or more selectors. **What oidc-attestor does:** Given the workload PID, it discovers an OIDC token associated with that process (via a projected volume path or environment variable), validates the token signature against a JWKS endpoint, and returns selectors derived from token claims (issuer, subject, audience, custom claims). **Selector format:** ``` oidc_attestor:iss: oidc_attestor:sub: oidc_attestor:aud: oidc_attestor:claim:: ``` ## CredentialComposer **Side:** SPIRE Server **Plugin:** `cmd/ssh-credential-composer` **Interface methods:** ```go ComposeServerX509CA(ctx context.Context, attributes X509CAAttributes) (X509CAAttributes, error) ComposeServerX509SVID(ctx context.Context, attributes X509SVIDAttributes) (X509SVIDAttributes, error) ComposeAgentX509SVID(ctx context.Context, attributes X509SVIDAttributes) (X509SVIDAttributes, error) ComposeWorkloadX509SVID(ctx context.Context, attributes X509SVIDAttributes) (X509SVIDAttributes, error) ComposeWorkloadJWTSVID(ctx context.Context, attributes JWTSVIDAttributes) (JWTSVIDAttributes, error) ``` **When called:** During credential minting on the server. After the server decides to issue a credential (X.509 SVID, JWT SVID, or CA certificate), it passes the proposed attributes through all registered CredentialComposer plugins. Each plugin may modify the attributes before the credential is signed. **What ssh-credential-composer does:** Intercepts `ComposeWorkloadX509SVID` to handle SSH credential composition. Because SPIRE v1.9 does not define an SSH-specific CredentialComposer hook, the plugin dispatches on registration entry selectors or hints (e.g., a `ssh-svid: true` selector) to identify SSH-destined requests. It reads the SPIFFE ID and registration entry metadata, calls GovernanceService to create a MutationIntent for the issuance, constructs a MutationEnvelope, anchors it via NotaryService, then encodes the governance metadata as Shellstream extensions in the SSH certificate's critical options using the `pkg/shellstream` encoder. **Hook mapping detail:** The composer implements all 5 `CredentialComposer` methods but only performs governance logic in `ComposeWorkloadX509SVID` when the request matches SSH selectors. All other methods (`ComposeServerX509CA`, `ComposeServerX509SVID`, `ComposeAgentX509SVID`, `ComposeWorkloadJWTSVID`) return attributes unmodified. See `specs/spiffe-ssh-svid.md` Section 4, Step 4 for the normative hook mapping note. ## Notifier **Side:** SPIRE Server **Plugin:** `cmd/governance-notifier` **Interface methods:** ```go Notify(ctx context.Context, event *NotifyRequest) (*NotifyResponse, error) NotifyAndAdvise(ctx context.Context, event *NotifyAndAdviseRequest) (*NotifyAndAdviseResponse, error) ``` **When called:** - `Notify`: After a bundle update or registration entry change has been committed. This is fire-and-forget; the server does not block on the response. - `NotifyAndAdvise`: Before certain operations where the plugin can influence the outcome. The server waits for the response. **Event types the plugin handles:** - `BundleUpdated` — A trust bundle was rotated. - `EntryUpdated` — A registration entry was created, updated, or deleted. **What governance-notifier does:** On bundle rotation events, it creates a MutationIntent via GovernanceService to record the rotation as an auditable governance event. For sensitive entry changes (e.g., high-privilege SPIFFE IDs), it triggers a CeremonyService flow requiring multi-stakeholder approval. All events are anchored in the merkle tree via NotaryService. ## KeyManager **Side:** SPIRE Server **Plugin:** `cmd/substrate-keymanager` **Interface methods:** ```go GenerateKey(ctx context.Context, req *GenerateKeyRequest) (*GenerateKeyResponse, error) GetPublicKey(ctx context.Context, req *GetPublicKeyRequest) (*GetPublicKeyResponse, error) GetPublicKeys(ctx context.Context, req *GetPublicKeysRequest) (*GetPublicKeysResponse, error) SignData(ctx context.Context, req *SignDataRequest) (*SignDataResponse, error) ``` **When called:** - `GenerateKey`: When the server needs a new signing key (initial startup, key rotation). - `GetPublicKey`/`GetPublicKeys`: When the server needs to retrieve existing key material. - `SignData`: Every time the server signs a credential (SVID, CA cert). **What substrate-keymanager does:** Manages the SPIRE Server's signing keys with governance-aware lifecycle. Key generation and rotation events are recorded as MutationIntents. The plugin stores keys in a configured backend (filesystem, Kubernetes Secret, or remote KMS) and ensures every key operation is traceable through the governance audit trail. ## Plugin Loading Order SPIRE loads plugins in the order they appear in configuration. For correctness: 1. **substrate-keymanager** must load first (server needs signing keys before issuing anything). 2. **ssh-credential-composer** loads next (must be present before any credential minting). 3. **governance-notifier** loads last (reacts to events that other plugins may trigger). On the agent side, **oidc-attestor** is loaded alongside any other workload attestors (e.g., the built-in `k8s` attestor). Multiple attestors can coexist; their selectors are unioned.