Chio/Docs

BuildIdentity

Bind Workload Identity

Bind a capability to a SPIFFE workload: write the match policy, issue the token, and inspect the signed receipt.

You do not install SPIRE through Chio

Chio consumes SPIFFE identities; it does not issue them. This guide assumes your runtime already emits a SPIFFE ID, through an Envoy ext_authz principal, a SPIRE-managed SVID, an attestation JWT, or an explicit operator claim. Chio evaluates the policy, issues the capability, and verifies the receipt.

Prerequisites

  • A running chio kernel with a capability authority. See Installation.
  • A workload whose SPIFFE ID you know, for example spiffe://prod.chio/payments/worker.
  • One of the three delivery paths wired up (Envoy ext_authz, explicit attestation, or a verifier bridge). The policy and verification steps below are identical for all three.

Step 1: Write a WorkloadIdentityMatch Rule

Start from a rule that already enumerates the tools you want to gate, then add require_workload_identity. The match is additive: trust domain, path prefix, and credential kind all have to pass for admission.

policy.yaml
rules:
  payments_write:
    enabled: true
    default: block
    allow:
      - transfer_funds
      - reconcile_ledger
    require_workload_identity:
      scheme: spiffe
      trust_domain: prod.chio
      path_prefixes:
        - /payments/worker
        - /payments/reconciler
      credential_kinds:
        - x509_svid
        - jwt_svid
    require_runtime_assurance_tier: attested

Important field behavior:

  • Omitting credential_kinds accepts any of uri, x509_svid, and jwt_svid. Specify the accepted kinds to limit which credentials pass the rule.
  • path_prefixes is a list of prefix strings, not globs. A workload at /payments/worker/shard-3 matches /payments/worker.
  • Pairing require_workload_identity with require_runtime_assurance_tier gives you both "which workload" and "how strongly attested". The two requirements evaluate independently.

Soft match for gradual rollout

Use prefer_workload_identity instead of require_ during rollout. The kernel records a soft match on the receipt without denying the call, which lets you observe the identity population in your audit log before flipping to a hard requirement.

Step 2: Issue an Identity-Bound Capability

Issuance can carry a runtimeAttestation block. The caller supplies the already-normalized assurance tier as part of that evidence; the authority does not derive it from the other fields. The trust policy (trusted_verifiers and the runtime_assurance tiers) is what later decides whether the asserted tier is honored, rebound, or rejected, stamping the capability with a minimum runtime-assurance constraint. Governed execution then re-checks that the presented evidence still clears the stamped tier.

Identity-bound issuance uses this endpoint: POST /v1/capabilities/issue on a trust-control cluster. The JSON body is an IssueCapabilityRequest carrying the subject public key, the scope, a TTL in seconds, and the runtimeAttestation evidence.

bash
curl -X POST https://trust.example.com/v1/capabilities/issue \
  -H "Authorization: Bearer $CHIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d @issuance-request.json
issuance-request.json
{
  "subjectPublicKey": "7b0f6f63...",
  "scope": {
    "grants": [
      { "server_id": "payments", "tool_name": "transfer_funds", "operations": ["invoke"] }
    ]
  },
  "ttlSeconds": 300,
  "runtimeAttestation": {
    "schema": "chio.runtime-attestation.v1",
    "verifier": "spire-agent",
    "tier": "attested",
    "issued_at": 1744537800,
    "expires_at": 1744538100,
    "evidence_sha256": "…",
    "workload_identity": {
      "scheme": "spiffe",
      "credentialKind": "x509_svid",
      "uri": "spiffe://prod.chio/payments/worker",
      "trustDomain": "prod.chio",
      "path": "/payments/worker"
    }
  }
}

Fail-closed on conflict

If the request carries both an explicit workloadIdentity and a raw runtimeIdentity that do not agree, or if the SPIFFE URI is malformed, issuance fails. Do not wrap this in a retry. Fix the upstream and reissue.

Step 3: Verify the Binding on the Receipt

Every governed admission that clears a workload-identity check records the accepted workloadIdentity object on the signed receipt. A reviewer can inspect the signed receipt to see which workload made the call.

bash
chio --json receipt explain rct-01hxab...

# Prints the allow/deny reasoning and lineage for one receipt, including
# the recorded runtime_assurance block:
# {
#   "receiptId": "rct-01hxab…",
#   "verdict": "allow",
#   "capabilityId": "cap-7b0f…",
#   "runtime_assurance": {
#     "schema": "chio.runtime-attestation.spiffe.x509-svid.v1",
#     "tier": "attested",
#     "verifier": "spire-agent",
#     "evidenceSha256": "…",
#     "workloadIdentity": {
#       "scheme": "spiffe",
#       "credentialKind": "x509_svid",
#       "uri": "spiffe://prod.chio/payments/worker",
#       "trustDomain": "prod.chio",
#       "path": "/payments/worker"
#     }
#   },
#   "signature": "ed25519:…"
# }

If you run an auditor pipeline, filter receipts by runtime_assurance.workloadIdentity.uri to get the exact set of admissions a particular workload made. The receipt query API exposes this filter.


Runtime-Attestation Appraisal

Raw runtimeAttestation evidence rides on the governed and issuance requests. A separate, adapter-facing contract normalizes it into a runtime-attestation appraisal. It carries the verifier family, an evidence descriptor, normalized assertions, and reason codes for cross-organization verifiers and auditors.

Export an appraisal locally from an evidence payload, optionally passing a HushSpec policy to evaluate its policy-visible outcomes:

bash
chio trust appraisal export \
  --input runtime-attestation.json \
  --policy-file policy.yaml

Remote deployments produce the same report over HTTP at POST /v1/reports/runtime-attestation-appraisal. A signed appraisal result can be exported for a partner (chio trust appraisal export-result or POST /v1/reports/runtime-attestation-appraisal-result) and evaluated on import against local policy (chio trust appraisal import or POST /v1/reports/runtime-attestation-appraisal/import). Imported results are defended by signature and freshness today; there is no replay registry for them yet.


Rebinding to verified via Trusted Verifiers

Raw attestation evidence lands at the attested tier by default. To let a rule require verified, add an explicit trusted_verifiers entry that binds a {schema, verifier} pair to an effective tier. This keeps verifier trust in a policy extension instead of individual rules.

policy.yaml (extensions)
extensions:
  runtime_assurance:
    tiers:
      verified:
        minimum_attestation_tier: verified
        max_scope:
          operations: ["invoke"]
          ttl_seconds: 300
    trusted_verifiers:
      spire_prod:
        schema: chio.runtime-attestation.spiffe.x509-svid.v1
        verifier: https://spire.prod.internal
        verifier_family: spiffe
        effective_tier: verified
        max_evidence_age_seconds: 120
        allowed_attestation_types: [x509_svid]

With this in place, a rule that sets require_runtime_assurance_tier: verified admits only calls whose attestation matches a trusted-verifier rule and satisfies its freshness and claim constraints.


Fail-Closed Conditions

The kernel denies admission, not warns, when any of the following hold. Matching operator recovery guidance lives in WORKLOAD_IDENTITY_RUNBOOK in the reference tree.

ConditionWhat to do
Explicit workloadIdentity conflicts with raw runtimeIdentityFix the upstream so both agree; do not mask the conflict at the kernel.
SPIFFE URI is malformed (missing trust domain, empty path)Regenerate the SVID from SPIRE or your attestation source; inspect the exact string.
Presented identity fails require_workload_identityEither add the workload to the allowed prefixes, or accept the deny. Do not relax the match to get past one call.
Evidence older than max_evidence_age_secondsRefresh the upstream attestation and resend. Do not extend the age ceiling to bypass freshness.
Verifier bridge projects a non-SPIFFE identifierFix the projection rule on the bridge. Chio will not invent a typed identity from an opaque string.

Summary

  1. Write the rule. Add require_workload_identity with an explicit trust domain, path prefix set, and credential kind set.
  2. Issue with attestation. Pass runtimeAttestation on issuance so the capability is stamped with the minimum runtime-assurance tier.
  3. Verify on the receipt. The accepted workloadIdentity appears under the kernel signature; audit pipelines query on it directly.
  4. Rebind to verified when needed. Use trusted_verifiers to promote raw attestation into the verified tier under explicit operator policy.

Next Steps