feat(attestation): add SAT-SPEC-0002 protobuf definitions

Add protobuf message definitions for the four-layer attestation
architecture defined in SAT-SPEC-0002. Package: substrate.attestation.v2

Files:
- common.proto: QmReceipt (hash-chained Ed25519-signed receipts)
- platform.proto: PlatformClaim, TpmQuoteBinding (L1 hardware identity)
- software.proto: SoftwareClaim, BuildProvenance (L2 image provenance)
- governance.proto: GovernanceClaim, AccordReference, DelegationReference (L3)
- session.proto: SessionClaim, ActorContext, PostureEvidence, PostureLevel (L4)
- sat.proto: SatBundle (composite, optional claim fields for has_*() codegen)

Also adds buf.yaml for lint/breaking-change checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tyler King 2026-02-28 09:12:59 -05:00
parent 9a4076df49
commit 98aa2b0ec7
8 changed files with 244 additions and 0 deletions

35
attestation/v2/README.md Normal file
View file

@ -0,0 +1,35 @@
# attestation/v2 — SAT-SPEC-0002 Protobuf Definitions
Protobuf message definitions for the Substrate Attestation Token v2 (SAT-SPEC-0002),
the four-layer attestation architecture for Substrate nodes.
## Package
`substrate.attestation.v2`
## Files
| File | Layer | Messages |
|------|-------|----------|
| `sat.proto` | — | `SatBundle` (top-level composite) |
| `platform.proto` | L1 Platform | `PlatformClaim`, `TpmQuoteBinding` |
| `software.proto` | L2 Software | `SoftwareClaim`, `BuildProvenance` |
| `governance.proto` | L3 Governance | `GovernanceClaim`, `AccordReference`, `DelegationReference` |
| `session.proto` | L4 Session | `SessionClaim`, `ActorContext`, `PostureEvidence`, `PostureLevel` (enum) |
| `common.proto` | — | `QmReceipt` |
## Layer Architecture
```
L4 Session ← actor identity + posture evaluation
L3 Governance ← accords, delegations, ceremonies
L2 Software ← image provenance + QM build receipt
L1 Platform ← TPM measurements + hardware identity
```
Each layer's claim hash binds to the layers below it (hash chaining).
The composite `sat_hash` in `SatBundle` covers all present layers.
## Spec
Canonical specification: `substrate/docs/specs/SAT-SPEC-0002.md`

View file

@ -0,0 +1,20 @@
syntax = "proto3";
package substrate.attestation.v2;
// QM Receipt
//
// The Quartermaster receipt is the fundamental trust primitive for
// non-hardware claims. Every QM receipt is hash-chained, self-contained,
// and verifiable with the QM's Ed25519 public key.
message QmReceipt {
string receipt_id = 1; // Unique identifier (UUIDv7)
bytes claim_hash = 2; // SHA-256 hash of the notarized claim
repeated bytes referenced_hashes = 3; // Previous layer claim hashes (binding chain)
string issuer = 4; // QM instance identity (SPIFFE ID)
string timestamp = 5; // ISO 8601, QM clock (authoritative)
uint64 chain_position = 6; // Position in the QM's hash chain (monotonic)
optional string previous_receipt = 7; // Receipt ID of previous chain entry
bytes signature = 8; // Ed25519 signature
}

View file

@ -0,0 +1,44 @@
syntax = "proto3";
package substrate.attestation.v2;
// Layer 3: Governance Claim
//
// Governance state binding: active accords, delegations, and ceremony
// state. Hash-chained via governance_epoch for tamper detection.
message GovernanceClaim {
uint32 layer = 1; // MUST be 3
bytes governance_state_hash = 2;
repeated AccordReference active_accords = 3;
repeated DelegationReference active_delegations = 4;
uint32 pending_ceremonies = 5;
uint64 governance_epoch = 6;
bytes previous_governance_hash = 7;
bytes platform_claim_hash = 8;
bytes software_claim_hash = 9;
bytes claim_hash = 10;
}
message AccordReference {
string accord_id = 1;
bytes accord_hash = 2;
string forge_repo = 3;
string forge_commit = 4;
repeated string parties = 5;
string scope = 6; // Summary
string activated_at = 7; // ISO 8601
optional string expires_at = 8;
string qm_receipt = 9;
}
message DelegationReference {
string delegation_id = 1;
string delegator = 2;
string delegate = 3;
repeated string scope = 4;
string delegator_accord = 5;
optional string ceremony_id = 6;
string qm_receipt = 7;
string expires_at = 8; // ISO 8601
}

View file

@ -0,0 +1,31 @@
syntax = "proto3";
package substrate.attestation.v2;
// Layer 1: Platform Claim
//
// Hardware-rooted identity and integrity measurements. Anchored to TPM
// Endorsement Key (EK) and backed by TPM Quote over PCR digest.
message PlatformClaim {
uint32 sat_version = 1; // MUST be 2
uint32 layer = 2; // MUST be 1
string machine_id = 3; // H(TPM EK public key)
bytes tpm_ek_public = 4;
bytes tpm_ak_cert = 5;
bytes pcr_digest = 6;
string pcr_bank = 7; // "sha256" or "sha384"
repeated uint32 pcr_selection = 8;
optional bytes ima_log_hash = 9;
bool uefi_secureboot = 10;
uint64 boot_timestamp = 11; // TPM monotonic counter
bytes nonce = 12;
bytes claim_hash = 13; // Computed, not serialized for hashing
TpmQuoteBinding tpm_binding = 14;
}
message TpmQuoteBinding {
bytes quoted = 1;
bytes signature = 2;
string signature_algorithm = 3; // "ECDSA-P256"
}

26
attestation/v2/sat.proto Normal file
View file

@ -0,0 +1,26 @@
syntax = "proto3";
package substrate.attestation.v2;
import "attestation/v2/platform.proto";
import "attestation/v2/software.proto";
import "attestation/v2/governance.proto";
import "attestation/v2/session.proto";
// Top-level SAT Bundle
//
// Composite attestation bundle containing all four layers.
// Layers are optional a minimal bundle may contain only L1 + L4.
// The explicit `optional` keyword generates has_*() accessors in
// codegen (Rust/prost, Python/protobuf) to distinguish absent layers
// from present-but-empty layers.
message SatBundle {
uint32 sat_version = 1; // MUST be 2
optional PlatformClaim platform_claim = 2;
optional SoftwareClaim software_claim = 3;
optional GovernanceClaim governance_claim = 4;
optional SessionClaim session_claim = 5;
bytes sat_hash = 6; // H(L1 || L2 || L3 || L4)
string qm_receipt_id = 7; // Top-level QM receipt
}

View file

@ -0,0 +1,50 @@
syntax = "proto3";
package substrate.attestation.v2;
// Layer 4: Session Claim
//
// Per-session attestation binding actor identity to platform, software,
// and governance state. Posture is verifier-computed, not self-asserted.
message SessionClaim {
uint32 layer = 1; // MUST be 4
string session_id = 2; // UUIDv7
ActorContext actor = 3;
PostureEvidence posture_evidence = 4;
PostureLevel posture_level = 5;
string timestamp = 6; // ISO 8601
bytes nonce = 7;
bytes platform_claim_hash = 8;
bytes software_claim_hash = 9;
bytes governance_claim_hash = 10;
bytes claim_hash = 11;
}
message ActorContext {
string actor_id = 1; // OIDC sub or SPIFFE ID
string actor_type = 2; // "human" | "agent" | "system" | "node"
string auth_method = 3; // "oidc" | "oidc+entra" | "spiffe" | "service_account"
optional string delegated_by = 4;
optional string delegation_id = 5;
}
message PostureEvidence {
bool platform_attested = 1;
string platform_method = 2;
bool software_verified = 3;
string software_method = 4;
bool governance_bound = 5;
string governance_method = 6;
bool identity_verified = 7;
string identity_method = 8;
}
enum PostureLevel {
POSTURE_LEVEL_UNSPECIFIED = 0;
POSTURE_LEVEL_NONE = 1;
POSTURE_LEVEL_LOCAL = 2;
POSTURE_LEVEL_VERIFIED = 3;
POSTURE_LEVEL_GOVERNED = 4;
POSTURE_LEVEL_ATTESTED = 5;
}

View file

@ -0,0 +1,29 @@
syntax = "proto3";
package substrate.attestation.v2;
// Layer 2: Software Claim
//
// Software provenance and capability declaration. Binds the running
// image to its build pipeline via QM-notarized build receipts.
message SoftwareClaim {
uint32 layer = 1; // MUST be 2
string image_id = 2;
bytes image_hash = 3;
bytes capability_manifest = 4; // H(manifest JSON)
repeated string capabilities = 5; // Informational
BuildProvenance build_provenance = 6;
string qm_build_receipt = 7;
bytes platform_claim_hash = 8; // L1 binding
bytes claim_hash = 9;
}
message BuildProvenance {
string source_repo = 1;
string source_commit = 2;
string builder_identity = 3; // SPIFFE ID or OIDC sub
string build_timestamp = 4; // ISO 8601
bool build_reproducible = 5;
uint32 builder_count = 6;
}

9
buf.yaml Normal file
View file

@ -0,0 +1,9 @@
version: v2
modules:
- path: .
lint:
use:
- STANDARD
breaking:
use:
- FILE