Credential Models
One delegation credential model, two schema eras: the legacy VC 1.0 shape and the card-era VC 2.0 + ZCAP-LD profile
One credential, two schema eras
KYA-OS defines exactly one delegation credential model: a W3C Verifiable
Credential of type DelegationCredential. The 1.x line carries it in two
wire shapes — the legacy VC 1.0 era and the card-era VC 2.0 + ZCAP-LD
profile. The delegation semantics are identical in both; only the wire shape
and the revocation list format differ.
Credentials
A delegation in KYA-OS is issued as a W3C Verifiable Credential whose type array includes both VerifiableCredential and DelegationCredential.
A DelegationCredential carries a permission, not a claim: it says what the delegate may do, on whose authority, within which constraints.
Chains of these credentials — one credential per hop — connect every action back to a Responsible Party.
The 1.x protocol line carries this one model in two schema eras (SPEC.md §6):
| Era | Wire shape | Revocation | Proof suite | Defined in |
|---|---|---|---|---|
| Legacy VC 1.0 era | VC 1.0; credentialSubject carries only id + delegation | StatusList2021 | Ed25519Signature2020 | SPEC.md §6.2 |
| Card era | VC 2.0; credentialSubject IS an attenuated ZCAP-LD capability | W3C Bitstring Status List v1.0 | DataIntegrityProof (eddsa-jcs-2022) | SPEC.md §6.10 |
Both shapes remain valid for the entire 1.x line. The legacy session profile issues and verifies the VC 1.0 shape; the Entity Card path issues and verifies the VC 2.0 + ZCAP-LD shape. An implementation encountering one never needs to accept the other in its place.
The CRISP constraint envelope
Constraints on a delegation use the CRISP envelope defined in SPEC.md §6.3:
interface DelegationConstraints {
// Temporal bounds (Unix epoch seconds)
notBefore?: number;
notAfter?: number;
// Simple scope list (tool names, resource patterns)
scopes?: string[];
// Audience restriction (server DID or domain)
audience?: string | string[];
// Extended CRISP constraints
crisp?: {
budget?: {
unit: "USD" | "ops" | "points";
cap: number;
window?: { kind: "rolling" | "fixed"; durationSec: number };
};
scopes: Array<{
resource: string;
matcher: "exact" | "prefix" | "regex";
constraints?: Record<string, unknown>;
}>;
};
}
In the card era the same discipline is expressed as ZCAP-LD caveats on the capability (for example ValidUntil, MaxAmount), governed by the attenuation invariants below.
Chain attenuation invariants
Delegations form chains: root → … → leaf, one credential per hop, and every chain terminates at a Responsible Party.
A chain is valid only if every hop attenuates its parent — any broadening hop invalidates the whole chain, fail-closed:
- Action subset — a child's allowed actions must be a subset of its parent's
- Monotone caveats — no parent caveat may be silently dropped; shared caveat types must narrow (child
MaxAmount ≤parent, childValidUntil ≤parent); unknown caveat types are replicated verbatim - Validity narrowing — child
validUntil ≤parent - Continuity — the parent's delegate must equal the child's issuer (you may only re-delegate what was delegated to your key), and the child references its parent by identifier
- Constant target — the
invocationTargetstays constant along the chain - Bounded depth — chains must not exceed 10 hops (
MAX_DELEGATION_DEPTH) - Root rule — the root credential is issued by the Responsible Party and anchors the resource it governs
See Chained Credential for how hops reference each other and how a verifier recomputes the chain.
Issuing a delegation credential
The reference implementation issues legacy-era credentials via @kya-os/mcp/delegation:
import { createDelegationIssuer } from "@kya-os/mcp/delegation";
const issuer = createDelegationIssuer(identity, signVc);
const credential = await issuer.createAndIssueDelegation({
id: "del-001",
issuerDid: "did:web:principal.example.com",
subjectDid: "did:key:z6MkDelegate...",
constraints: {
scopes: ["tool:read_file", "tool:list_directory"],
notAfter: Math.floor(Date.now() / 1000) + 86_400,
audience: "did:web:mcp-server.example.com",
},
});
Card-era chains are built and evaluated with the @kya-os/mcp/card delegation helpers (validateDelegationChain, evaluateDelegationChain), which enforce the attenuation invariants and Bitstring revocation on every verification.
Revocation is two-track
Legacy-era credentials revoke via StatusList2021; card-era credentials revoke via W3C Bitstring Status List v1.0 (the successor format). Both are evaluated on every verification — verdicts are never cached. Cascading revocation invalidates every descendant of a revoked hop.
Credential Structures
- Standard Credential (legacy VC 1.0 era) - The legacy-era wire shape issued by the session profile
- Chained Credential - How multi-hop chains reference, attenuate, and verify