guildhouse-spire-plugins/docs/architecture.md
Tyler King 420a4e2ea0 Remediate all 17 audit findings from AUDIT.md
Critical fixes:
- F-01: SatScope array form support (single pointer → slice with polymorphic JSON)
- F-02: Add governance-intent@guildhouse.dev as 10th Shellstream extension
- F-06: Replace os.Exit(1) stubs with go-plugin Serve() boilerplate in all cmd/
- F-13: Validate SatScope.ResourcePattern is non-empty

High priority:
- F-03: Add normative Accord policy syntax note to credential-governance.md §8.2
- F-04: Replace OID XXXXX placeholder with explicit PEN reference and IANA TODO
- F-05: Document CredentialComposer hook mapping in spec and plugin-types.md
- F-07/F-08: Commit CI pipeline (.github/workflows/ci.yaml)
- F-09: Add hashicorp/go-plugin v1.6.3 to go.mod

Medium priority:
- F-10: Wire sample-ssh-cert-extensions.json fixture into shellstream tests
- F-11: Cross-reference merkle proof depth limit (256 leaves) in governance spec
- F-12: Add YAML format clarification headers to deploy configs
- F-14: Expand README with project status, docs links, and quick-start

Low priority:
- F-15: Standardize "SSH SVID" → "SSH-SVID" terminology across docs
- F-16: Add GovernanceEpochSeconds to PluginConfig and deploy configs
- F-17: Add troubleshooting section to deployment.md, error handling to OIDC docs

Global: Rename all extension keys from @guildhouse.io to @guildhouse.dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:45:33 -05:00

6.5 KiB

Architecture Overview

This document describes how the Guildhouse SPIRE plugins integrate SPIFFE workload identity with Guildhouse's governance platform.

System Diagram

+----------------------------------------------------------+
|                     Kubernetes Cluster                    |
|                                                          |
|  +--------------------+     +-------------------------+  |
|  |    SPIRE Agent     |     |     SPIRE Server        |  |
|  |                    |     |                         |  |
|  | +----------------+ |     | +---------------------+ |  |
|  | | oidc-attestor  | |     | | ssh-credential-     | |  |
|  | | (WorkloadAttr) | |     | | composer (CredComp) | |  |
|  | +----------------+ |     | +---------------------+ |  |
|  |                    |     |                         |  |
|  +--------|-----------+     | +---------------------+ |  |
|           |                 | | governance-notifier  | |  |
|           |                 | | (Notifier)           | |  |
|           |                 | +---------------------+ |  |
|           |                 |                         |  |
|  +--------v-----------+     | +---------------------+ |  |
|  |    Workloads       |     | | substrate-keymanager| |  |
|  | (present OIDC      |     | | (KeyManager)        | |  |
|  |  tokens for        |     | +---------------------+ |  |
|  |  attestation)      |     +------|--------|----------+  |
|  +--------------------+            |        |             |
|                               gRPC |   gRPC |             |
|                          (mTLS)    |        |  (mTLS)     |
|  +-----------------------------+   |   +------------------+
|  |   Quartermaster             |<--+   |  Bascule        |
|  |                             |       |                  |
|  | - GovernanceService         |       | - CeremonyServ  |
|  |   (MutationIntents)         |       |   (multi-party  |
|  | - NotaryService             |       |    approval)     |
|  |   (Merkle anchoring)       |       |                  |
|  +-----------------------------+       +------------------+
+----------------------------------------------------------+

Components

SPIRE Agent Plugins

oidc-attestor (WorkloadAttestor) runs inside the SPIRE Agent. When a workload calls the Workload API to request an SVID, the agent invokes oidc-attestor to identify the workload. The plugin discovers an OIDC token from the workload's environment (file path or environment variable), verifies it against a JWKS endpoint, and returns selectors derived from token claims.

SPIRE Server Plugins

Three plugins run inside the SPIRE Server:

  • ssh-credential-composer (CredentialComposer) intercepts credential minting. When the server issues an SSH-SVID, this plugin encodes Shellstream extensions into the SSH certificate's critical options, embedding governance metadata (intent ID, ceremony outcome, SAT hash) into the cert itself.

  • governance-notifier (Notifier) receives SPIRE lifecycle events (bundle updates, registration entry changes, SVID rotations). On relevant events, it calls GovernanceService to create or update MutationIntents, and may trigger CeremonyService flows for operations requiring multi-stakeholder approval.

  • substrate-keymanager (KeyManager) manages the signing keys used by the SPIRE Server. It stores key material with governance-aware lifecycle management, ensuring key rotations are captured as auditable intents.

Guildhouse gRPC Services

All plugin-to-service communication uses mTLS via SPIFFE SVIDs. The plugins themselves hold SPIFFE identities and authenticate to Guildhouse services through mutual TLS.

Service Package Plugin Consumers
GovernanceService quartermaster.v1 governance-notifier, ssh-credential-composer
NotaryService quartermaster.v1 governance-notifier, ssh-credential-composer
CeremonyService bascule.v1 governance-notifier

Data Flow: SSH Certificate Issuance

  1. A workload calls the SPIRE Workload API requesting an SSH-SVID.
  2. The SPIRE Agent invokes oidc-attestor, which discovers and verifies the workload's OIDC token and returns selectors.
  3. The agent matches selectors against registration entries and forwards the request to the SPIRE Server.
  4. The server invokes ssh-credential-composer during credential minting.
  5. The composer calls GovernanceService.CreateIntent to record the SSH cert issuance as a MutationIntent.
  6. If the intent requires approval, CeremonyService.CreateCeremony is called and the flow blocks until approval.
  7. The composer constructs a MutationEnvelope (RFC 8785 JCS canonicalization, domain-separated SHA-256 hash), which becomes a merkle leaf.
  8. NotaryService.CreateAnchor anchors the envelope hash into the merkle tree.
  9. The composer encodes Shellstream extensions into the SSH certificate's critical options (intent ID, SAT hash, ceremony result).
  10. The signed SSH certificate is returned to the workload through the agent.

Package Map

guildhouse-spire-plugins/
  cmd/
    oidc-attestor/          # WorkloadAttestor plugin binary
    ssh-credential-composer/ # CredentialComposer plugin binary
    governance-notifier/     # Notifier plugin binary
    substrate-keymanager/    # KeyManager plugin binary
  pkg/
    shellstream/            # Encode/decode SSH cert Shellstream extensions
    oidc/                   # OIDC token discovery and verification
    governance/             # GovernanceService + CeremonyService client
    sshcert/                # SSH certificate construction helpers
    config/                 # Shared plugin configuration loading
  proto/
    quartermaster/v1/       # governance.proto, notary.proto, credentials.proto
    bascule/v1/             # ceremony.proto
  specs/                    # Formal specifications (read-only reference)
  deploy/                   # Kubernetes manifests and Helm values
  test/                     # Integration tests and fixtures

Proto Dependencies

Proto File Service Package
governance.proto GovernanceService quartermaster.v1
notary.proto NotaryService quartermaster.v1
credentials.proto Credential types quartermaster.v1
ceremony.proto CeremonyService bascule.v1

Generated Go code from these protos lives alongside the .proto files or in a generated output directory. The pkg/governance package wraps the raw gRPC stubs with higher-level client logic (retries, circuit breaking, intent lifecycle management).