guildhouse-spire-plugins/specs/shellstream-extensions.md
Tyler King 6321037ac1 Add network-policy extension and network governance lifecycle events
New shellstream extension §10.6 network-policy@guildhouse.dev carrying
GovernedNetworkPolicy hash in SSH certificates. New §8.7 in upper layers
spec documenting network governance lifecycle events (attach, detach,
flow policy, route announce/withdraw) emitted by governance-notifier
using the tiered consent transport model.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:38:13 -05:00

23 KiB

Shellstream SSH Certificate Extensions

1. Abstract

Shellstream extensions are a set of SSH certificate extensions using the @guildhouse.dev vendor suffix that carry structured governance metadata within SSH certificates issued by SPIRE. These extensions encode authorization scope, tenant context, governance ceremony references, and merkle audit proofs, enabling SSH servers to make fine-grained authorization decisions based on Guildhouse platform state without requiring separate API calls at connection time.

2. Status

Draft Specification -- This document is a working draft and subject to change. It defines the normative behavior that conforming implementations MUST follow once finalized.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

3. Terminology

Shellstream Extension
An SSH certificate extension whose name uses the @guildhouse.dev vendor suffix and whose value carries Guildhouse governance metadata as defined in this specification.
SSH Certificate Extension
A key-value string pair embedded in an OpenSSH certificate as defined in the OpenSSH PROTOCOL.certkeys document. Extensions are advisory and do not restrict the certificate holder; they convey additional information to the server.
Vendor Suffix
The @domain portion of an extension name that designates the namespace owner, following the OpenSSH convention for avoiding collisions between independently defined extensions.
SAT (Substrate Attestation Token)
A signed token issued by the Quartermaster credential service that binds a SPIFFE identity to a set of permitted operations on a specific registry type. The SAT is the primary authorization artifact in the Guildhouse platform.
SatScope
A structured object within a SAT that defines the registry type, permitted verbs, and resource pattern for which the token grants access.
Governance Ceremony
A structured approval workflow managed by the Bascule CeremonyService that authorizes privilege elevation. Ceremony types range from self-granted to quorum-based approval.
Merkle Anchor
A cryptographic commitment (merkle tree root hash) representing the complete governance state at a point in time. Each governance mutation is recorded as a leaf in the tree.
MutationEnvelope
A signed wrapper around a governance state change that is recorded as a leaf in the governance merkle tree.
Accord Policy
A tenant-scoped authorization policy evaluated by the Accord policy engine (backed by OPA) that defines roles, permissions, and ceremony requirements for a tenant.
Trust Domain
A SPIFFE trust domain (e.g., guildhouse.dev) that defines the boundary within which SPIFFE identities and their associated attestations are valid.

4. Introduction

SSH certificates, as defined by OpenSSH's PROTOCOL.certkeys, support arbitrary extensions as key-value string pairs. By convention, vendor- specific extensions use the name@domain format to avoid collisions with extensions defined by other parties or by future OpenSSH releases.

Shellstream extensions embed Guildhouse governance metadata into SSH certificates so that SSH servers can make authorization decisions based on platform state without issuing separate API calls. When SPIRE issues an SSH certificate for a Guildhouse workload or user, the certificate may include Shellstream extensions that describe:

  • What the holder is authorized to do (SAT scope).
  • Who the holder is acting as (tenant identity, roles).
  • Why elevated access was granted (ceremony reference).
  • When the governance state was captured (merkle root, epoch).

This decoupling of authorization metadata from runtime API lookups enables offline authorization decisions, improves latency, and creates a cryptographically verifiable audit trail linking SSH sessions to governance state.

5. Extension Format

5.1 Naming Convention

All Shellstream extensions use the @guildhouse.dev vendor suffix. Extension names MUST be lowercase, hyphen-separated identifiers followed by the suffix:

<name>@guildhouse.dev

where <name> matches the regular expression [a-z][a-z0-9]*(-[a-z0-9]+)*.

5.2 Value Encoding

Extension values are UTF-8 strings, as constrained by the SSH certificate extension format (RFC 4251 string type). All values MUST be valid UTF-8.

Binary data MUST be encoded as follows:

  • Cryptographic hashes: lowercase hexadecimal encoding.
  • Proofs and opaque binary payloads: base64 encoding using the standard alphabet with padding (RFC 4648 Section 4).

JSON-structured values MUST use compact encoding with no unnecessary whitespace (no spaces after colons or commas, no newlines or indentation).

6. Extension Registry

This section defines each Shellstream extension. Extensions marked REQUIRED MUST be present in every Shellstream-bearing SSH certificate. Extensions marked OPTIONAL MAY be omitted.

6.1 sat-scope@guildhouse.dev

Presence: REQUIRED when a SAT is associated with the certificate.

Value: A JSON object (or JSON array of objects when multiple scopes exist) with the following structure:

{"registry_type":"<type>","verbs":["<verb>",...],"resource_pattern":"<pattern>"}

Fields:

Field Type Description
registry_type string Identifies the registry kind. Values include "oci", "helm", "git", "database".
verbs array of string Permitted operations. Examples: ["read","write"], ["push","pull"].
resource_pattern string Glob pattern for resource matching. Examples: "tenant-a/*", "ns/repo:*".

When a single SAT scope exists, the value MUST be a single JSON object. When multiple SAT scopes exist, the value MUST be a JSON array of scope objects. Implementations MUST accept both forms.

Example (single scope):

sat-scope@guildhouse.dev = {"registry_type":"oci","verbs":["push","pull"],"resource_pattern":"acme-corp/*"}

Example (multiple scopes):

sat-scope@guildhouse.dev = [{"registry_type":"oci","verbs":["pull"],"resource_pattern":"acme-corp/*"},{"registry_type":"helm","verbs":["read"],"resource_pattern":"charts/*"}]

6.2 sat-hash@guildhouse.dev

Presence: REQUIRED when a SAT is associated with the certificate.

Value: Lowercase hex-encoded SHA-256 hash of the raw SAT bytes.

The value MUST be exactly 64 hexadecimal characters ([0-9a-f]{64}). This hash is used for audit correlation -- it links the SSH session to the SAT without embedding the SAT itself in the certificate.

Example:

sat-hash@guildhouse.dev = a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

6.3 tenant-id@guildhouse.dev

Presence: REQUIRED.

Value: UUID string formatted per RFC 4122, using lowercase hexadecimal digits with hyphens in the 8-4-4-4-12 grouping.

The value MUST match the regular expression:

[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}

This extension identifies the tenant context for the SSH session. All authorization decisions MUST be scoped to this tenant.

Example:

tenant-id@guildhouse.dev = 7b2a91c4-3f8e-4d12-b5a6-9c0e1d2f3a4b

6.4 roles@guildhouse.dev

Presence: REQUIRED.

Value: Comma-separated list of role names with no whitespace. Each role name MUST match [a-z][a-z0-9_]*.

Roles are defined by the tenant's Accord policy and represent the set of permissions the certificate holder has been granted for this session.

Example:

roles@guildhouse.dev = analyst,viewer
roles@guildhouse.dev = administrator

6.5 ceremony-id@guildhouse.dev

Presence: OPTIONAL -- present only for elevated sessions that were authorized through a governance ceremony.

Value: UUID string formatted per RFC 4122, using the same format specified in Section 6.3.

This extension references the governance ceremony that authorized the privilege elevation for this session.

Example:

ceremony-id@guildhouse.dev = e4f5a6b7-8c9d-0e1f-2a3b-4c5d6e7f8a9b

6.6 ceremony-type@guildhouse.dev

Presence: OPTIONAL -- MUST be present when ceremony-id@guildhouse.dev is present. MUST NOT be present when ceremony-id@guildhouse.dev is absent.

Value: One of the following string literals, corresponding to the CeremonyType enum from the Bascule CeremonyService:

Value Description
self_grant The holder approved their own elevation (permitted by policy).
single_approval A single designated approver authorized the elevation.
quorum_approval A quorum of approvers collectively authorized the elevation.
emergency_break_glass Emergency access granted outside normal approval flow, with enhanced audit.

Example:

ceremony-type@guildhouse.dev = quorum_approval

6.7 merkle-root@guildhouse.dev

Presence: OPTIONAL.

Value: Lowercase hex-encoded SHA-256 merkle root hash. The value MUST be exactly 64 hexadecimal characters ([0-9a-f]{64}).

This is the root of the governance merkle tree at the time the certificate was issued. It allows offline verification that the certificate was issued during a known governance state.

Example:

merkle-root@guildhouse.dev = 4d7a9c2e1f3b5a8d0e6c4b2a9f7e5d3c1b0a8f6e4d2c0b9a7f5e3d1c0b8a7f

6.8 merkle-proof@guildhouse.dev

Presence: OPTIONAL -- MUST be present only when merkle-root@guildhouse.dev is present. MAY be omitted even when merkle-root is present (the root alone is useful for epoch pinning).

Value: Base64-encoded (standard alphabet with padding, per RFC 4648 Section 4) serialized inclusion proof.

Proof wire format: The proof is a byte sequence consisting of concatenated 32-byte SHA-256 sibling hashes followed by a single final byte encoding the direction bits. Each bit in the direction byte indicates whether the corresponding sibling is on the left (0) or right (1) of the path, with the least-significant bit corresponding to the first sibling. This limits proofs to trees of depth 8 (256 leaves). For deeper trees, a multi-byte direction encoding will be specified in a future revision.

Example:

merkle-proof@guildhouse.dev = QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ehQ=

6.9 governance-epoch@guildhouse.dev

Presence: OPTIONAL.

Value: Decimal string representation of an unsigned 64-bit integer. The value MUST match [1-9][0-9]*|0 (no leading zeros except for the value zero itself).

The governance epoch is a monotonically increasing counter that increments with each governance state change. It enables stale-state detection without requiring full merkle verification: if a server's last-known epoch is greater than the certificate's epoch, the certificate was issued against stale governance state.

Example:

governance-epoch@guildhouse.dev = 42

6.10 governance-intent@guildhouse.dev

Presence: OPTIONAL -- present when the certificate was issued through the governance flow and has an associated MutationIntent.

Value: UUID string formatted per RFC 4122, using the same format specified in Section 6.3.

This extension references the MutationIntent that authorized the credential issuance. It enables audit correlation between the SSH certificate and the governance audit trail maintained by the GovernanceService.

Security: The governance-intent value MUST be set by the SSH Credential Composer from the actual CreateIntentResponse.intent_id, NOT from external input (e.g., requester-supplied headers). Verifiers SHOULD cross-check this value against the GovernanceService to confirm that the referenced intent exists, is in redeemed status, and corresponds to this credential's issuance. A syntactically valid but fabricated intent UUID would pass format validation but fail this cross-check.

Example:

governance-intent@guildhouse.dev = c8d9e0f1-2a3b-4c5d-6e7f-8a9b0c1d2e3f

7. Encoding Rules

The following encoding rules apply to all Shellstream extension values:

  1. All values MUST be valid UTF-8 (RFC 3629).
  2. Hash values (in sat-hash, merkle-root) MUST be lowercase hexadecimal ([0-9a-f]+). Uppercase hex digits MUST be rejected.
  3. Base64 values (in merkle-proof) MUST use the standard alphabet (A-Z, a-z, 0-9, +, /) with = padding as specified in RFC 4648 Section 4. URL-safe base64 MUST NOT be used.
  4. JSON values (in sat-scope) MUST use compact encoding: no spaces after : or ,, no newlines, no indentation. Parsers SHOULD accept non-compact JSON for robustness but generators MUST produce compact JSON.
  5. UUID values (in tenant-id, ceremony-id, governance-intent) MUST be lowercase with hyphens in 8-4-4-4-12 format. Uppercase UUID strings MUST be rejected.
  6. Numeric values (in governance-epoch) MUST be decimal strings without leading zeros (except for the literal "0").
  7. Comma-separated values (in roles) MUST NOT contain whitespace between items.

8. Validation Requirements

Implementations receiving SSH certificates with Shellstream extensions MUST validate the following constraints before using extension values for authorization or audit purposes:

8.1 Structural Validation

  • Each extension value MUST conform to the format specified in its registry entry (Section 6).
  • Values that fail format validation MUST be treated as absent. The server MAY log a warning but MUST NOT terminate the SSH connection solely due to a malformed extension value.

8.2 Co-occurrence Constraints

The following co-occurrence rules MUST be enforced:

If present Then MUST also be present
sat-scope@guildhouse.dev sat-hash@guildhouse.dev
sat-hash@guildhouse.dev sat-scope@guildhouse.dev
ceremony-id@guildhouse.dev ceremony-type@guildhouse.dev
ceremony-type@guildhouse.dev ceremony-id@guildhouse.dev
merkle-proof@guildhouse.dev merkle-root@guildhouse.dev

Note: merkle-root@guildhouse.dev MAY be present without merkle-proof@guildhouse.dev (root-only pinning).

Note: governance-intent@guildhouse.dev has no co-occurrence constraints. It MAY appear independently or alongside merkle extensions.

8.3 Required Extensions

The following extensions MUST always be present in a Shellstream-bearing SSH certificate:

  • tenant-id@guildhouse.dev
  • roles@guildhouse.dev

A certificate that contains any @guildhouse.dev extension but is missing either tenant-id or roles MUST be treated as invalid. The server SHOULD reject the session.

8.4 Forward Compatibility

Unknown extensions bearing the @guildhouse.dev suffix MUST be ignored. Implementations MUST NOT reject a certificate solely because it contains unrecognized @guildhouse.dev extensions. This ensures that new extensions can be introduced without breaking existing deployments.

9. Security Considerations

9.1 Extension Visibility

SSH certificate extensions are not encrypted. Any entity that can read the certificate (including intermediate proxies, logging infrastructure, and the SSH server) can read extension values. Accordingly:

  • Implementations MUST NOT embed secrets, bearer tokens, private keys, or other sensitive credentials in extension values.
  • The sat-hash extension contains a SHA-256 hash of the SAT, not the SAT itself. The SAT MUST be validated through a separate, secure channel.

9.2 Authorization Boundary

  • Merkle proofs provide auditability, not authorization. A valid merkle inclusion proof means the credential issuance event was recorded in the governance tree; it does not assert that the event was correct or authorized. Authorization decisions MUST be based on SAT scope and Accord policy, not on merkle proof validity alone.
  • The governance-epoch extension enables stale-state detection but MUST NOT be used as the sole authorization signal. A stale epoch indicates that governance state may have changed since issuance; the server SHOULD require re-attestation in this case.

9.3 Parsing Safety

Extension values are strings, not executable code. Implementations MUST NOT evaluate extension values as code, shell commands, SQL, or any other executable format. JSON values MUST be parsed with a standard JSON parser; hand-rolled parsers are strongly discouraged.

9.4 Size Constraints

The total size of all Shellstream extension values (keys and values combined) SHOULD NOT exceed 4096 bytes (4 KB). Exceeding this limit may cause compatibility issues with certain SSH implementations that impose limits on certificate size. Implementations MAY reject certificates whose total @guildhouse.dev extension payload exceeds this threshold.

9.5 Replay and Freshness

SSH certificates have their own validity period (valid-after to valid-before). Shellstream extensions inherit this validity window. Implementations SHOULD treat extension values as valid only within the certificate's validity period. The governance-epoch extension provides an additional freshness signal beyond the certificate's temporal validity.

10. Compatibility

10.1 SSH Protocol Compatibility

All Shellstream extensions use the standard SSH certificate extension format: a string key mapping to a string value. This is fully compatible with OpenSSH and any SSH implementation that conforms to the PROTOCOL.certkeys specification.

10.2 Transparent Degradation

SSH servers that do not understand Shellstream extensions will ignore them entirely. This is standard OpenSSH behavior for unrecognized extensions. Certificates bearing Shellstream extensions remain valid SSH certificates and can be used for authentication on non-Guildhouse SSH servers (though governance-based authorization will not be available).

10.3 Additive Evolution

Extensions are additive. New @guildhouse.dev extensions can be introduced in future revisions of this specification without breaking existing parsers, per the forward compatibility rule in Section 8.4.

10.4 Version Negotiation

Version negotiation is implicit via extension presence. There is no explicit version field. If a future revision deprecates or changes the semantics of an extension, it MUST do so under a new extension name. The original extension name retains its original semantics indefinitely.

Presence: OPTIONAL.

Value: Comma-separated list of available consent transport channel identifiers with no whitespace. Each channel identifier MUST match [a-z][a-z0-9]*(-[a-z0-9]+)*.

Valid channel identifiers (corresponding to substrate:consent/channel.channel-kind):

Identifier Tier Description
local-tty 0 Synchronous, local terminal prompt
unix-socket 0-1 Synchronous or async via Unix domain socket
dbus 1 Asynchronous, local D-Bus notification
http-webhook 2 Asynchronous, network HTTP webhook
message-queue 2 Asynchronous, network message queue
store-forward 3 Store-and-forward, delivered on reconnect

This extension advertises the consent transport channels available on the host that issued the certificate. Client modules use this information to determine which consent tier is available for governance operations requiring human or policy-automated approval. See GH-DESIGN-0005 §5 for the tiered consent transport model.

The value parallels the consent_channels field in HostCapabilities (SAT-SPEC-0001 §3.9). When both are present, they SHOULD contain the same set of channels. If they differ, the SAT's HostCapabilities is authoritative.

Example:

consent-channels@guildhouse.dev = local-tty,unix-socket,http-webhook

Example (air-gapped host):

consent-channels@guildhouse.dev = local-tty

10.6 network-policy@guildhouse.dev

Presence: OPTIONAL.

Value: SHA-256 hash (lowercase hexadecimal, exactly 64 characters) of the GovernedNetworkPolicy applied to this workload. The hash is computed over the canonical (RFC 8785 JCS) JSON serialization of the policy object.

This extension binds network governance policy to workload identity through the SPIRE credential chain. The ssh-credential-composer embeds the policy hash when SPIRE issues an SVID for a pod. At CNI ADD time, Kedge reads the policy hash from the workload's SVID and passes it to the host function layer via substrate:network/workload-network.attach-workload() in the governance-context field. The host function layer resolves the hash to a full GovernedNetworkPolicy and installs identity-based flow rules in the eBPF enforcement plane.

The trust chain:

  1. SPIRE attests workload identity
  2. ssh-credential-composer embeds network-policy@guildhouse.dev hash
  3. Kedge CNI reads the hash during CNI ADD
  4. substrate:network WIT host functions install governed flow policy
  5. eBPF in the VPP processing graph enforces at wire speed
  6. governance-notifier records the network attach event in Chronicle

See GH-DESIGN-0005 §11 for the GovernedNetworkPolicy schema and the network-as-API model.

Example:

network-policy@guildhouse.dev = a1b2c3d4e5f6...64 hex chars...

Co-occurrence: This extension MAY appear alongside sat-hash@guildhouse.dev. When both are present, the SAT's HostCapabilities.wit_packages SHOULD include substrate:network@0.1 to indicate that the host supports network governance host functions.

11. References