Provisioning Lifecycle Profile
The Provisioning Lifecycle profile (org.peacprotocol/provisioning-lifecycle) provides observer-scope signed records for infrastructure provisioning events: credential issuance, secret rotation, service authorization, key lifecycle, and access management. Introduced in v0.14.2 as the 16th PEAC extension group.
Package: @peac/schema (validator) — use with @peac/protocol to issue records.
Observer scope
Provisioning lifecycle records describe what provisioning systems reported. PEAC does not issue credentials, rotate secrets, or authorize services. Your provisioning infrastructure owns those decisions. PEAC provides portable, offline-verifiable signed evidence of what was reported at each provisioning boundary.
Install
pnpm add @peac/schema @peac/protocol
Type URIs
10 *-observed type URIs are registered under the org.peacprotocol/provisioning-lifecycle extension group (Section 31, PROV-LIFE-001..010):
| Type URI | Description |
|---|---|
org.peacprotocol/provisioning-credential-issuance-observed | A credential was issued by an external provider |
org.peacprotocol/provisioning-credential-rotation-observed | An existing credential was rotated |
org.peacprotocol/provisioning-secret-rotation-observed | A secret was rotated in a secrets manager |
org.peacprotocol/provisioning-service-authorization-observed | A service was authorized to access another service |
org.peacprotocol/provisioning-access-grant-observed | An access grant was issued |
org.peacprotocol/provisioning-access-revocation-observed | An access grant was revoked |
org.peacprotocol/provisioning-key-creation-observed | A cryptographic key was created |
org.peacprotocol/provisioning-key-deletion-observed | A cryptographic key was deleted or archived |
org.peacprotocol/provisioning-service-binding-observed | A service binding was created (e.g., database credential injection) |
org.peacprotocol/provisioning-lifecycle-complete-observed | A provisioning lifecycle workflow completed |
Quick start
import { validateProvisioningLifecycle } from '@peac/schema';
import { issue } from '@peac/protocol';
// Build the extension object
const provisioningExt = {
'org.peacprotocol/provisioning-lifecycle': {
event_kind: 'credential-issuance',
provider_ref: 'urn:provider:vault.corp.example.com',
credential_ref: 'ref:credential/db-svc-account',
storage_surface: {
kind: 'external_secret_store',
provider_ref: 'ref:provider/hashicorp-vault',
surface_ref: 'ref:path/secret/data/myapp/db',
},
observed_at: new Date().toISOString(),
},
};
// Validate before issuing
const result = validateProvisioningLifecycle(provisioningExt);
if (!result.success) {
console.error(result.error);
process.exit(1);
}
// Issue the signed record
const jws = await issue({
sub: 'ref:credential/db-svc-account',
iss: 'https://provisioning.corp.example.com',
type: 'org.peacprotocol/provisioning-credential-issuance-observed',
extensions: provisioningExt,
}, signingKey);
console.log(jws); // Compact JWS -- verifiable offline
Core fields
| Field | Required | Type | Description |
|---|---|---|---|
event_kind | Yes | string | Event kind identifier (see type URIs above) |
provider_ref | Yes | opaque ref | Reference to the provisioning provider |
observed_at | Yes | ISO 8601 | When the provisioning event was observed |
credential_ref | No | opaque ref | Reference to the credential being issued/rotated |
storage_surface | No | object | Where the credential is stored (see below) |
authorization_ref | No | opaque ref | Reference to the authorization grant |
expires_at | No | ISO 8601 | When the provisioned credential expires |
sub_event | No | string | Sub-state within the event kind (e.g., requested, granted) |
Opaque reference grammar
All *_ref fields use the opaque reference grammar. Raw values (API keys, tokens, emails, paths) are not allowed inline. Use one of these prefixes:
| Prefix | Use |
|---|---|
urn: | URN-style identifier |
ref: | Logical path reference |
did: | DID identifier |
sha256: | SHA-256 content digest |
https: | URL reference |
// Correct: opaque reference
provider_ref: 'ref:provider/hashicorp-vault'
provider_ref: 'urn:provider:vault.corp.example.com'
provider_ref: 'did:web:vault.corp.example.com'
// Incorrect: inline value (rejected)
provider_ref: 'hvs.CAESIJ...' // token value -- rejected
provider_ref: '/secret/data/myapp' // raw path -- rejected
Storage surface object
The storage_surface object describes where a credential is stored without exposing the credential itself:
storage_surface: {
kind: 'external_secret_store', // see kind values below
provider_ref: 'ref:provider/hashicorp-vault', // opaque ref
surface_ref: 'ref:path/secret/data/myapp/db', // opaque ref
material_redaction: 'sha256_ref', // how credential material is redacted
}
Storage surface kind values:
| Kind | Description |
|---|---|
external_secret_store | External secrets manager (Vault, AWS Secrets Manager, etc.) |
local_encrypted_file | Locally encrypted credential file |
local_plaintext_file | Local plaintext file (flag for audit) |
environment_file | .env or environment variable file |
runtime_secret_binding | Injected at runtime (e.g., Kubernetes secret mount) |
none | No persistent storage |
unknown | Storage surface not determinable |
Credential material scanner
validateProvisioningLifecycle() includes a recursive credential material scanner that rejects records containing inline secrets. The scanner checks all string values for patterns matching JWTs, Bearer tokens, PEM private keys, .env-style assignments, and connection strings with credentials.
This is a hard gate: if your extension object contains inline credential material in any nested field, validation will fail with provisioning.token_material_blocked or provisioning.inline_credential_blocked.
// This will fail validation
const bad = {
'org.peacprotocol/provisioning-lifecycle': {
event_kind: 'credential-issuance',
provider_ref: 'ref:provider/vault',
// Do NOT put actual token values in any field
note: 'eyJhbGciOiJIUzI1NiIs...', // JWT -- blocked
observed_at: new Date().toISOString(),
},
};
const result = validateProvisioningLifecycle(bad);
// result.success === false
// result.error.issues[0].code === 'provisioning.token_material_blocked'
Error codes (21 stable codes)
| Code | Meaning |
|---|---|
provisioning.inline_credential_blocked | Forbidden top-level key with inline credential value |
provisioning.token_material_blocked | Inline token material detected in a string field |
provisioning.forbidden_key_name | Field name is on the forbidden key list |
provisioning.field_too_large | Field value exceeds byte limit |
provisioning.replacement_character_in_string | String contains Unicode replacement character |
provisioning.structure_too_deep | Nesting depth exceeds maximum |
provisioning.structure_too_large | Total node count exceeds maximum |
provisioning.opaque_ref_grammar_violation | Reference field does not match opaque ref grammar |
provisioning.invalid_storage_surface | storage_surface.kind is not a valid enum value |
provisioning.invalid_material_redaction | material_redaction is not a valid value |
provisioning.invalid_event_kind | Top-level event_kind is not a known value |
provisioning.invalid_sub_event | sub_event is not valid for this event_kind |
provisioning.invalid_scheme_id | scheme_id violates the bounded grammar |
provisioning.invalid_amount_minor | amount_minor is not a valid non-negative decimal string |
provisioning.invalid_observed_at | observed_at is not a valid ISO 8601 timestamp |
provisioning.invalid_retrieved_at | retrieved_at is not a valid ISO 8601 timestamp |
provisioning.invalid_expires_at | expires_at is not a valid ISO 8601 timestamp |
provisioning.invalid_currency | currency is not a valid ISO 4217 code |
provisioning.unrecognized_field | Unknown field in the extension object |
provisioning.missing_required_field | Required field is absent |
provisioning.invalid_utf8 | String contains invalid UTF-8 sequences (fixture loader) |
Conformance
Section 31 (PROV-LIFE-001..010) covers the 10 normative conformance requirements for provisioning lifecycle records. These are part of the 290 conformance requirements across 32 sections in PEAC v0.14.4.
Non-goals
Provisioning lifecycle records do not:
- Issue or provision credentials themselves
- Rotate secrets on your behalf
- Connect to or authenticate against any secrets manager
- Guarantee that the reported provisioning event occurred correctly
- Replace audit logs from your provisioning infrastructure
Related
- Runtime Governance Profile: Policy evaluation and safety check records
- CLI Reference:
peac record commandfor issuing records from CI pipelines - Wire Format: Interaction Record format (stable)