guildhouse-spire-plugins/docs/governance-integration.md

4.4 KiB

Governance Integration

How plugins interact with GovernanceService, CeremonyService, and NotaryService.

Services Overview

Service Proto Package Purpose
GovernanceService quartermaster.v1 MutationIntent lifecycle (create, redeem, revoke)
CeremonyService bascule.v1 Multi-stakeholder approval workflows
NotaryService quartermaster.v1 Merkle tree anchoring for audit

All communication uses gRPC with mTLS via SPIFFE SVIDs.

Intent Lifecycle

Every credential operation follows the intent lifecycle:

CreateIntent ──> [authorized] ──> RedeemIntent ──> SAT ──> Operation ──> Anchor
                      |
                      v
               [ceremony_pending] ──> CeremonyService ──> [approved] ──> RedeemIntent
                                                    |
                                                    v
                                               [denied] ──> Abort

CreateIntent

rpc CreateIntent(CreateIntentRequest) returns (CreateIntentResponse);

Fields:

  • registry_type: "credential" for all credential operations
  • verb: "issue", "rotate", or "revoke"
  • artifact_scope: JSON description of the credential parameters
  • tenant_id: Owning tenant UUID
  • identity_claim: OIDC token or external event claim
  • ttl_seconds: Intent lifetime (default 300)
  • max_redemptions: Always 1 for credential operations
  • idempotency_key: "{registry_type}:{verb}:{credential_id}"

Response includes:

  • intent_id: Unique identifier for the intent
  • ceremony_id: Non-empty if a governance ceremony is required
  • denied: True if the Accord policy immediately rejects the operation

RedeemIntent

Called after the intent is authorized (immediately or after ceremony approval):

rpc RedeemIntent(RedeemIntentRequest) returns (RedeemIntentResponse);

Returns a SatToken containing:

  • sat_hash: Cryptographic binding of the SAT contents
  • bearer_svid: SPIFFE ID authorized to perform the operation
  • scopes: SatScopeMsg entries defining permitted operations
  • issued_at / expires_at: SAT validity window

Ceremony Flow

When CreateIntentResponse.ceremony_id is non-empty:

  1. Plugin monitors ceremony status via CeremonyService.GetCeremony
  2. Approvers use ApproveCeremony or DenyCeremony (via Bascule shell or API)
  3. On approval, GovernanceService transitions the intent to authorized
  4. Plugin proceeds with RedeemIntent

Ceremony types (from Accord policy):

  • Autonomous: No approval needed, intent authorized immediately
  • SelfGrant: Requestor self-approves
  • SingleApproval: One external approver required
  • QuorumApproval: Multiple approvers required (configurable quorum)
  • EmergencyBreakGlass: Proceed immediately, require post-hoc approval

MutationEnvelope Construction

After the credential operation succeeds, the plugin constructs a MutationEnvelope for audit anchoring:

Step 1: Canonicalize Payload

Serialize the credential event payload using RFC 8785 JSON Canonicalization Scheme (JCS):

  • Sorted keys
  • No whitespace
  • Deterministic number formatting

Step 2: Domain-Separated Hash

payload_hash = SHA-256("guildhouse.credential.v1:" + jcs_bytes)

The domain prefix prevents cross-protocol hash collisions.

Step 3: Build Envelope

{
  "domain": "guildhouse.credential.v1",
  "payload_hash": "<hex-sha256>",
  "timestamp": "<rfc3339>",
  "actor_svid": "<spiffe-id>",
  "tenant_id": "<uuid>",
  "event_type": "issue",
  "intent_id": "<intent-id>",
  "sat_hash": "<hex-sha256>"
}

Step 4: Anchor

JCS-canonicalize the envelope, SHA-256 hash it, submit as a leaf to NotaryService.CreateAnchor.

Error Handling

Failure Behavior
GovernanceService unreachable Fail closed — credential operation fails
Ceremony timeout Treat as denial, abort operation
NotaryService unreachable Credential operation proceeds, queue leaf for retry
No Accord policy match Default to SingleApproval (fail safe)

Plugin Configuration

All plugins share governance connection configuration:

plugin_data {
    governance_addr = "governance.quartermaster.svc.cluster.local:50051"
    ceremony_addr = "ceremony.bascule.svc.cluster.local:50052"
    notary_addr = "notary.quartermaster.svc.cluster.local:50051"
    trust_domain = "guildhouse.example.org"
    cluster_id = "guildhouse-prod"
}