Delegation Layer
Learn how authority is securely delegated in KYA-OS using verifiable credentials.
Key Takeaways
- Understand how users delegate authority to agents using Verifiable Credentials (VCs)
- Learn how delegation chains work and how they’re validated
- Discover how revocation and credential rotation affect delegation
- Know which components participate in delegation flows, and where enforcement occurs
Why Delegation Matters
Without delegation, AI agents would need full access to a user's account—posing risks for security, privacy, and compliance.
Delegation in KYA-OS solves this by using VCs to:
- Scope authority to only what’s necessary
- Prove user consent cryptographically
- Support revocation and expiration
- Allow audit of each request
Delegation Credential Structure
Delegation is encoded inside a VC that includes:
- The issuer (user/delegator DID)
- The subject (agent/delegatee DID)
- The scope of allowed actions and constraints
- The expiration timestamp
- A cryptographic proof signed by the issuer
→ See Credential Models for the full schema
Delegation Process
The delegation process typically follows these steps:
Delegation Lifecycle Diagram
The following diagram shows how a credential is issued, optionally rotated, and eventually revoked:
- A credential may be valid until expiration or actively revoked
- Rotation means a new VC replaces the old one with updated scope or subject
- Verifiers must reject expired or revoked credentials
Delegation Chain
KYA-OS supports chained delegation:
- User → Agent A → Agent B
- Each link in the chain is a separate credential
- The verifier must validate each link on the chain recursively
When verifying a delegation chain:
- Start with the leaf credential (the one presented by the requesting agent)
- Verify each credential in the chain recursively
- Ensure each delegation's scope is a subset of its parent
- Check for circular dependencies
- Verify no credential in the chain is expired or revoked
Each VC must:
- Be valid (not expired or revoked)
- Be signed by the correct issuer
- Match the scope requested
- Link to previous subject DID
Delegation Validation Failures
Delegation fails if:
- A link is missing (e.g., broken chain)
- A VC is revoked or expired
- Signature is invalid
- Scope is insufficient or fails constraint expressions
The protocol error codes for these failures (SPEC.md Appendix A) are:
| Code | Meaning |
|---|---|
delegation_invalid | Delegation verification failed (structure, signature, chain, or constraints) |
delegation_revoked | A credential in the chain has been revoked |
insufficient_scope | The requested operation is outside the delegated scope |
needs_authorization | No sufficient delegation was presented; the response carries a signed consent challenge (SPEC.md §9) |
Card-era chain evaluation additionally returns fail-closed, human-readable reasons[] describing exactly which invariant broke (for example a broadening hop, broken continuity, invocationTarget drift, or excessive depth) — an empty reasons array is the only success state.
→ These checks run inside the resource server's own verifier on every request — verification is in-process, not delegated to a fronting proxy
Scope Definition
Scopes in KYA-OS follow a standardized format to ensure clear and consistent authorization:
action:resource[/subresource][#instance]
Examples:
read:email- Permission to read emailswrite:calendar/events- Permission to create calendar eventstransfer:finance#account123- Permission to transfer funds from account123
The CRISP Constraint Envelope
Scopes live inside the CRISP constraint envelope (SPEC.md §6.3), which also carries temporal bounds, audience restrictions, and extended constraints:
interface DelegationConstraints {
notBefore?: number; // Unix epoch seconds
notAfter?: number;
scopes?: string[]; // simple scope list
audience?: string | string[]; // server DID or domain
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>;
}>;
};
}
Two rules govern how constraints behave across a chain:
- Attenuation — a child delegation's constraints must be equal to or narrower than its parent's; any broadening hop invalidates the whole chain
- Designation — a multi-scope delegation authorizes, but an invocation must designate the specific scope being exercised, and the verifier confirms the designated scope is a member of the delegation's authorized scopes (SPEC.md §6.4.1)
Scope Best Practices
Define scopes that are specific enough to limit agent authority but not so granular that they become unmanageable. Group related permissions into logical scopes to simplify management.
Revocation
KYA-OS requires implementation of credential revocation to enable withdrawal of delegation. Revocation is two-track, matching the two schema eras:
Track 1 — StatusList2021 (legacy VC 1.0 era)
Legacy-era credentials carry a StatusList2021Entry:
{
"credentialStatus": {
"id": "https://example.com/status/123#94",
"type": "StatusList2021Entry",
"statusPurpose": "revocation",
"statusListIndex": "94",
"statusListCredential": "https://example.com/status/123"
}
}
Track 2 — Bitstring Status List v1.0 (card era)
Card-era credentials use the W3C Bitstring Status List v1.0 — the successor format — via a BitstringStatusListEntry.
The checker is fail-closed: an unreachable list, malformed credential, mismatched statusPurpose, or out-of-range index all resolve to revoked.
In both tracks each bit in a gzip-compressed bitstring represents one credential's status, and the status list does not have to be public: a verifier that issues and gates its own delegations may check revocation entirely from local state.
For on-chain publication, CheqdStatusListResolver (@kya-os/mcp/cheqd) resolves and verifies status lists anchored as cheqd DID-Linked Resources, pinning the expected issuer and verifying the list's own signature.
Revocation Semantics
- Evaluated on every verification — revocation status and expiry are checked on every call; verdicts are never cached (the v1.13.0 security fix)
- Cascading — revoking a delegation revokes every descendant delegation in the graph
- Authorized revokers — the direct issuer, any ancestor issuer in the chain, or the root Responsible Party; a delegate cannot revoke its own delegation
Revocation Process
Revocation Latency
Revocation and invocation are concurrent: a call that lands between a
revocation being issued and the verifier reading the updated status list will
still succeed. Bound the window with short-lived delegations and tight
status-list staleness (withStatusCache(resolver, { maxStalenessMs })) — for
high-privilege scopes, 60 seconds or less is recommended.