@kya-os/mcp 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/AUDITABILITY.md +441 -0
  2. package/CHANGELOG.md +108 -0
  3. package/CONFORMANCE.md +600 -0
  4. package/README.md +69 -6
  5. package/SPEC.md +1820 -0
  6. package/dist/audit/adapters/legacy-audit-log.d.ts +20 -0
  7. package/dist/audit/adapters/legacy-audit-log.d.ts.map +1 -0
  8. package/dist/audit/adapters/legacy-audit-log.js +56 -0
  9. package/dist/audit/adapters/legacy-audit-log.js.map +1 -0
  10. package/dist/audit/adapters/mcp.d.ts +105 -0
  11. package/dist/audit/adapters/mcp.d.ts.map +1 -0
  12. package/dist/audit/adapters/mcp.js +213 -0
  13. package/dist/audit/adapters/mcp.js.map +1 -0
  14. package/dist/audit/assurance.d.ts +22 -0
  15. package/dist/audit/assurance.d.ts.map +1 -0
  16. package/dist/audit/assurance.js +39 -0
  17. package/dist/audit/assurance.js.map +1 -0
  18. package/dist/audit/checkpoint-coordinator.d.ts +51 -0
  19. package/dist/audit/checkpoint-coordinator.d.ts.map +1 -0
  20. package/dist/audit/checkpoint-coordinator.js +112 -0
  21. package/dist/audit/checkpoint-coordinator.js.map +1 -0
  22. package/dist/audit/checkpoint.d.ts +59 -0
  23. package/dist/audit/checkpoint.d.ts.map +1 -0
  24. package/dist/audit/checkpoint.js +219 -0
  25. package/dist/audit/checkpoint.js.map +1 -0
  26. package/dist/audit/cli.d.ts +8 -0
  27. package/dist/audit/cli.d.ts.map +1 -0
  28. package/dist/audit/cli.js +85 -0
  29. package/dist/audit/cli.js.map +1 -0
  30. package/dist/audit/crypto.d.ts +37 -0
  31. package/dist/audit/crypto.d.ts.map +1 -0
  32. package/dist/audit/crypto.js +68 -0
  33. package/dist/audit/crypto.js.map +1 -0
  34. package/dist/audit/errors.d.ts +29 -0
  35. package/dist/audit/errors.d.ts.map +1 -0
  36. package/dist/audit/errors.js +33 -0
  37. package/dist/audit/errors.js.map +1 -0
  38. package/dist/audit/evidence.d.ts +45 -0
  39. package/dist/audit/evidence.d.ts.map +1 -0
  40. package/dist/audit/evidence.js +151 -0
  41. package/dist/audit/evidence.js.map +1 -0
  42. package/dist/audit/index.d.ts +30 -0
  43. package/dist/audit/index.d.ts.map +1 -0
  44. package/dist/audit/index.js +30 -0
  45. package/dist/audit/index.js.map +1 -0
  46. package/dist/audit/integrity.d.ts +19 -0
  47. package/dist/audit/integrity.d.ts.map +1 -0
  48. package/dist/audit/integrity.js +64 -0
  49. package/dist/audit/integrity.js.map +1 -0
  50. package/dist/audit/merkle.d.ts +31 -0
  51. package/dist/audit/merkle.d.ts.map +1 -0
  52. package/dist/audit/merkle.js +195 -0
  53. package/dist/audit/merkle.js.map +1 -0
  54. package/dist/audit/mirror-service.d.ts +19 -0
  55. package/dist/audit/mirror-service.d.ts.map +1 -0
  56. package/dist/audit/mirror-service.js +54 -0
  57. package/dist/audit/mirror-service.js.map +1 -0
  58. package/dist/audit/projection.d.ts +90 -0
  59. package/dist/audit/projection.d.ts.map +1 -0
  60. package/dist/audit/projection.js +142 -0
  61. package/dist/audit/projection.js.map +1 -0
  62. package/dist/audit/providers/anchor.d.ts +23 -0
  63. package/dist/audit/providers/anchor.d.ts.map +1 -0
  64. package/dist/audit/providers/anchor.js +25 -0
  65. package/dist/audit/providers/anchor.js.map +1 -0
  66. package/dist/audit/providers/evidence.d.ts +36 -0
  67. package/dist/audit/providers/evidence.d.ts.map +1 -0
  68. package/dist/audit/providers/evidence.js +2 -0
  69. package/dist/audit/providers/evidence.js.map +1 -0
  70. package/dist/audit/providers/journal.d.ts +35 -0
  71. package/dist/audit/providers/journal.d.ts.map +1 -0
  72. package/dist/audit/providers/journal.js +2 -0
  73. package/dist/audit/providers/journal.js.map +1 -0
  74. package/dist/audit/providers/memory-journal.d.ts +24 -0
  75. package/dist/audit/providers/memory-journal.d.ts.map +1 -0
  76. package/dist/audit/providers/memory-journal.js +73 -0
  77. package/dist/audit/providers/memory-journal.js.map +1 -0
  78. package/dist/audit/providers/observer.d.ts +30 -0
  79. package/dist/audit/providers/observer.d.ts.map +1 -0
  80. package/dist/audit/providers/observer.js +100 -0
  81. package/dist/audit/providers/observer.js.map +1 -0
  82. package/dist/audit/providers/outbox.d.ts +37 -0
  83. package/dist/audit/providers/outbox.d.ts.map +1 -0
  84. package/dist/audit/providers/outbox.js +52 -0
  85. package/dist/audit/providers/outbox.js.map +1 -0
  86. package/dist/audit/providers/recorder-client.d.ts +22 -0
  87. package/dist/audit/providers/recorder-client.d.ts.map +1 -0
  88. package/dist/audit/providers/recorder-client.js +17 -0
  89. package/dist/audit/providers/recorder-client.js.map +1 -0
  90. package/dist/audit/providers/source-state.d.ts +37 -0
  91. package/dist/audit/providers/source-state.d.ts.map +1 -0
  92. package/dist/audit/providers/source-state.js +94 -0
  93. package/dist/audit/providers/source-state.js.map +1 -0
  94. package/dist/audit/read-contract.d.ts +445 -0
  95. package/dist/audit/read-contract.d.ts.map +1 -0
  96. package/dist/audit/read-contract.js +126 -0
  97. package/dist/audit/read-contract.js.map +1 -0
  98. package/dist/audit/read-service.d.ts +99 -0
  99. package/dist/audit/read-service.d.ts.map +1 -0
  100. package/dist/audit/read-service.js +124 -0
  101. package/dist/audit/read-service.js.map +1 -0
  102. package/dist/audit/recorder-service.d.ts +78 -0
  103. package/dist/audit/recorder-service.d.ts.map +1 -0
  104. package/dist/audit/recorder-service.js +331 -0
  105. package/dist/audit/recorder-service.js.map +1 -0
  106. package/dist/audit/reference-recorder.d.ts +43 -0
  107. package/dist/audit/reference-recorder.d.ts.map +1 -0
  108. package/dist/audit/reference-recorder.js +131 -0
  109. package/dist/audit/reference-recorder.js.map +1 -0
  110. package/dist/audit/replay-bundle.d.ts +52 -0
  111. package/dist/audit/replay-bundle.d.ts.map +1 -0
  112. package/dist/audit/replay-bundle.js +551 -0
  113. package/dist/audit/replay-bundle.js.map +1 -0
  114. package/dist/audit/schemas.d.ts +1145 -0
  115. package/dist/audit/schemas.d.ts.map +1 -0
  116. package/dist/audit/schemas.js +483 -0
  117. package/dist/audit/schemas.js.map +1 -0
  118. package/dist/audit/service.d.ts +91 -0
  119. package/dist/audit/service.d.ts.map +1 -0
  120. package/dist/audit/service.js +222 -0
  121. package/dist/audit/service.js.map +1 -0
  122. package/dist/audit/testing/index.d.ts +2 -0
  123. package/dist/audit/testing/index.d.ts.map +1 -0
  124. package/dist/audit/testing/index.js +2 -0
  125. package/dist/audit/testing/index.js.map +1 -0
  126. package/dist/audit/testing/provider-contracts.d.ts +51 -0
  127. package/dist/audit/testing/provider-contracts.d.ts.map +1 -0
  128. package/dist/audit/testing/provider-contracts.js +391 -0
  129. package/dist/audit/testing/provider-contracts.js.map +1 -0
  130. package/dist/audit/types.d.ts +356 -0
  131. package/dist/audit/types.d.ts.map +1 -0
  132. package/dist/audit/types.js +49 -0
  133. package/dist/audit/types.js.map +1 -0
  134. package/dist/audit/verifier.d.ts +73 -0
  135. package/dist/audit/verifier.d.ts.map +1 -0
  136. package/dist/audit/verifier.js +322 -0
  137. package/dist/audit/verifier.js.map +1 -0
  138. package/dist/card/builder.d.ts +1 -1
  139. package/dist/card/builder.js +1 -1
  140. package/dist/card/emit.d.ts +3 -3
  141. package/dist/card/emit.d.ts.map +1 -1
  142. package/dist/card/emit.js.map +1 -1
  143. package/dist/card/index.d.ts +1 -1
  144. package/dist/card/index.js +1 -1
  145. package/dist/card/middleware.d.ts +6 -4
  146. package/dist/card/middleware.d.ts.map +1 -1
  147. package/dist/card/middleware.js +11 -7
  148. package/dist/card/middleware.js.map +1 -1
  149. package/dist/card/proof/build.d.ts +3 -3
  150. package/dist/card/proof/build.js +3 -3
  151. package/dist/card/proof/canonical.d.ts.map +1 -1
  152. package/dist/card/proof/canonical.js +19 -1
  153. package/dist/card/proof/canonical.js.map +1 -1
  154. package/dist/card/proof/http-sig.d.ts +1 -1
  155. package/dist/card/proof/http-sig.js +1 -1
  156. package/dist/card/proof/index.d.ts +2 -2
  157. package/dist/card/proof/index.js +2 -2
  158. package/dist/card/proof/nonce-cache.d.ts +1 -1
  159. package/dist/card/proof/nonce-cache.js +1 -1
  160. package/dist/card/proof/types.d.ts +16 -7
  161. package/dist/card/proof/types.d.ts.map +1 -1
  162. package/dist/card/proof/types.js +16 -7
  163. package/dist/card/proof/types.js.map +1 -1
  164. package/dist/card/proof/verify.d.ts +2 -2
  165. package/dist/card/proof/verify.js +2 -2
  166. package/dist/card/schema.d.ts +14 -6
  167. package/dist/card/schema.d.ts.map +1 -1
  168. package/dist/card/schema.js +14 -3
  169. package/dist/card/schema.js.map +1 -1
  170. package/dist/card/verify.d.ts +1 -1
  171. package/dist/card/verify.d.ts.map +1 -1
  172. package/dist/delegation/delegation-graph.d.ts +4 -0
  173. package/dist/delegation/delegation-graph.d.ts.map +1 -1
  174. package/dist/delegation/delegation-graph.js +28 -2
  175. package/dist/delegation/delegation-graph.js.map +1 -1
  176. package/dist/delegation/statuslist-manager.d.ts +5 -0
  177. package/dist/delegation/statuslist-manager.d.ts.map +1 -1
  178. package/dist/delegation/statuslist-manager.js.map +1 -1
  179. package/dist/delegation/storage/memory-graph-storage.d.ts +4 -2
  180. package/dist/delegation/storage/memory-graph-storage.d.ts.map +1 -1
  181. package/dist/delegation/storage/memory-graph-storage.js +18 -0
  182. package/dist/delegation/storage/memory-graph-storage.js.map +1 -1
  183. package/dist/delegation/storage/memory-statuslist-storage.d.ts +5 -2
  184. package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +1 -1
  185. package/dist/delegation/storage/memory-statuslist-storage.js +21 -2
  186. package/dist/delegation/storage/memory-statuslist-storage.js.map +1 -1
  187. package/dist/delegation/utils.d.ts.map +1 -1
  188. package/dist/delegation/utils.js +2 -42
  189. package/dist/delegation/utils.js.map +1 -1
  190. package/dist/delegation/vc-verification-checks.d.ts.map +1 -1
  191. package/dist/delegation/vc-verification-checks.js +16 -1
  192. package/dist/delegation/vc-verification-checks.js.map +1 -1
  193. package/dist/delegation/vc-verifier.d.ts +4 -0
  194. package/dist/delegation/vc-verifier.d.ts.map +1 -1
  195. package/dist/delegation/vc-verifier.js +44 -13
  196. package/dist/delegation/vc-verifier.js.map +1 -1
  197. package/dist/errors.d.ts +1 -1
  198. package/dist/errors.js +1 -1
  199. package/dist/extension/declaration.d.ts +60 -0
  200. package/dist/extension/declaration.d.ts.map +1 -0
  201. package/dist/extension/declaration.js +75 -0
  202. package/dist/extension/declaration.js.map +1 -0
  203. package/dist/extension/gate.d.ts +110 -0
  204. package/dist/extension/gate.d.ts.map +1 -0
  205. package/dist/extension/gate.js +133 -0
  206. package/dist/extension/gate.js.map +1 -0
  207. package/dist/extension/index.d.ts +14 -0
  208. package/dist/extension/index.d.ts.map +1 -0
  209. package/dist/extension/index.js +14 -0
  210. package/dist/extension/index.js.map +1 -0
  211. package/dist/extension/settings.d.ts +70 -0
  212. package/dist/extension/settings.d.ts.map +1 -0
  213. package/dist/extension/settings.js +96 -0
  214. package/dist/extension/settings.js.map +1 -0
  215. package/dist/index.d.ts +4 -2
  216. package/dist/index.d.ts.map +1 -1
  217. package/dist/index.js +6 -2
  218. package/dist/index.js.map +1 -1
  219. package/dist/middleware/kya-os-transport.d.ts +1 -1
  220. package/dist/middleware/kya-os-transport.d.ts.map +1 -1
  221. package/dist/middleware/kya-os-transport.js +11 -4
  222. package/dist/middleware/kya-os-transport.js.map +1 -1
  223. package/dist/middleware/with-kya-os-server.d.ts +6 -0
  224. package/dist/middleware/with-kya-os-server.d.ts.map +1 -1
  225. package/dist/middleware/with-kya-os-server.js +2 -0
  226. package/dist/middleware/with-kya-os-server.js.map +1 -1
  227. package/dist/middleware/with-kya-os.config-types.d.ts +15 -0
  228. package/dist/middleware/with-kya-os.config-types.d.ts.map +1 -1
  229. package/dist/middleware/with-kya-os.config-types.js.map +1 -1
  230. package/dist/middleware/with-kya-os.d.ts.map +1 -1
  231. package/dist/middleware/with-kya-os.delegation-gate.d.ts.map +1 -1
  232. package/dist/middleware/with-kya-os.delegation-gate.js +96 -3
  233. package/dist/middleware/with-kya-os.delegation-gate.js.map +1 -1
  234. package/dist/middleware/with-kya-os.delegation-verify.d.ts.map +1 -1
  235. package/dist/middleware/with-kya-os.delegation-verify.js +2 -2
  236. package/dist/middleware/with-kya-os.delegation-verify.js.map +1 -1
  237. package/dist/middleware/with-kya-os.deps.d.ts +2 -0
  238. package/dist/middleware/with-kya-os.deps.d.ts.map +1 -1
  239. package/dist/middleware/with-kya-os.js +23 -0
  240. package/dist/middleware/with-kya-os.js.map +1 -1
  241. package/dist/middleware/with-kya-os.policy-gate.d.ts.map +1 -1
  242. package/dist/middleware/with-kya-os.policy-gate.js +29 -4
  243. package/dist/middleware/with-kya-os.policy-gate.js.map +1 -1
  244. package/dist/middleware/with-kya-os.protocol.d.ts.map +1 -1
  245. package/dist/middleware/with-kya-os.protocol.js +10 -0
  246. package/dist/middleware/with-kya-os.protocol.js.map +1 -1
  247. package/dist/middleware/with-kya-os.session.d.ts.map +1 -1
  248. package/dist/middleware/with-kya-os.session.js +208 -2
  249. package/dist/middleware/with-kya-os.session.js.map +1 -1
  250. package/dist/middleware/with-kya-os.types.d.ts +6 -0
  251. package/dist/middleware/with-kya-os.types.d.ts.map +1 -1
  252. package/dist/middleware/with-kya-os.types.js.map +1 -1
  253. package/dist/proof/generator.d.ts +14 -2
  254. package/dist/proof/generator.d.ts.map +1 -1
  255. package/dist/proof/generator.js +20 -7
  256. package/dist/proof/generator.js.map +1 -1
  257. package/dist/proof/index.d.ts +1 -1
  258. package/dist/proof/index.d.ts.map +1 -1
  259. package/dist/proof/index.js +1 -1
  260. package/dist/proof/index.js.map +1 -1
  261. package/dist/proof/verifier.d.ts +15 -2
  262. package/dist/proof/verifier.d.ts.map +1 -1
  263. package/dist/proof/verifier.js +48 -12
  264. package/dist/proof/verifier.js.map +1 -1
  265. package/dist/session/manager.js +1 -1
  266. package/dist/session/manager.js.map +1 -1
  267. package/dist/types/protocol.d.ts +22 -0
  268. package/dist/types/protocol.d.ts.map +1 -1
  269. package/dist/types/protocol.js +31 -5
  270. package/dist/types/protocol.js.map +1 -1
  271. package/dist/utils/canonical-json.d.ts +15 -0
  272. package/dist/utils/canonical-json.d.ts.map +1 -0
  273. package/dist/utils/canonical-json.js +109 -0
  274. package/dist/utils/canonical-json.js.map +1 -0
  275. package/dist/utils/crypto-service.d.ts.map +1 -1
  276. package/dist/utils/crypto-service.js +5 -0
  277. package/dist/utils/crypto-service.js.map +1 -1
  278. package/dist/utils/index.d.ts +1 -0
  279. package/dist/utils/index.d.ts.map +1 -1
  280. package/dist/utils/index.js +1 -0
  281. package/dist/utils/index.js.map +1 -1
  282. package/package.json +29 -11
  283. package/schemas/README.md +28 -1
  284. package/schemas/audit-anchor-receipt.schema.json +16 -0
  285. package/schemas/audit-bundle-manifest.schema.json +71 -0
  286. package/schemas/audit-checkpoint.schema.json +37 -0
  287. package/schemas/audit-consistency-proof.schema.json +17 -0
  288. package/schemas/audit-entry.schema.json +33 -0
  289. package/schemas/audit-event.schema.json +168 -0
  290. package/schemas/audit-inclusion-proof.schema.json +17 -0
  291. package/schemas/audit-observation.schema.json +40 -0
  292. package/schemas/audit-receipt.schema.json +32 -0
  293. package/schemas/audit-verification-policy.schema.json +71 -0
  294. package/schemas/audit-verification-report.schema.json +29 -0
  295. package/schemas/kya-os-card.schema.json +6 -6
  296. package/schemas/mcp-extension-settings.json +37 -0
  297. package/schemas/well-known-mcpi.json +17 -0
package/SPEC.md ADDED
@@ -0,0 +1,1820 @@
1
+ # KYA-OS Protocol Specification
2
+
3
+ **Cryptographic identity, delegation, and proof for Model Context Protocol servers**
4
+
5
+ Version: 1.0.0
6
+ Status: Stable
7
+ Editors: KYA-OS Working Group
8
+ Repository: https://github.com/decentralized-identity/kya-os-mcp
9
+
10
+ ---
11
+
12
+ ## Abstract
13
+
14
+ KYA-OS (Know Your Agent Operating System) is a protocol extension for the Model Context Protocol (MCP) that adds cryptographic identity, delegation chains, and non-repudiation proofs to AI agent interactions. KYA-OS enables MCP servers to verify *who* is calling (agent DID), *what authority they hold* (a delegation chain rooted at a Responsible Party, expressed as W3C Verifiable Credentials), and *what was done* (signed proof for audit trails). The protocol uses Decentralized Identifiers (DIDs) for agent identity, W3C Verifiable Credentials for delegation, Ed25519 signatures for cryptographic operations, and StatusList2021 for revocation.
15
+
16
+ ---
17
+
18
+ ## Conformance Keywords
19
+
20
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119].
21
+
22
+ ---
23
+
24
+ ## Status
25
+
26
+ **Stable 1.0.0** — Donated to DIF TAAWG (Decentralized Identity Foundation, Trusted AI Agents Working Group). Under review for ratification as a DIF standard. The wire format is stable: any future change that breaks compatibility with this version will require a major version bump.
27
+
28
+ ---
29
+
30
+ ## 1. Motivation
31
+
32
+ The Model Context Protocol (MCP) defines a standard interface for AI agents to interact with tools and resources. However, MCP lacks an identity layer, creating several security vulnerabilities:
33
+
34
+ 1. **Agent Impersonation**: Without identity verification, any process can claim to be a particular agent. Malicious actors can impersonate trusted agents to gain unauthorized access to sensitive tools.
35
+
36
+ 2. **Prompt Injection with Forged Identity**: Attackers can inject prompts that include fabricated identity claims. Without cryptographic verification, servers cannot distinguish legitimate identity assertions from forged ones.
37
+
38
+ 3. **No Delegation Chain**: When Agent A delegates to Agent B, there is no standard mechanism to prove the delegation occurred or verify the delegation constraints. This prevents secure hierarchical agent architectures.
39
+
40
+ 4. **No Audit Trail**: Tool calls leave no cryptographic evidence of what was requested, what was returned, or who was responsible. This makes compliance, debugging, and incident response difficult.
41
+
42
+ 5. **Replay Attacks**: Without nonce-based session establishment, captured tool call payloads can be replayed by attackers.
43
+
44
+ DIDs and Verifiable Credentials are the right fit for this problem:
45
+
46
+ - **DIDs** provide decentralized, cryptographically-verifiable identifiers that agents control. They do not require a central authority and support multiple resolution methods (did:key for ephemeral, did:web for persistent organizational identity).
47
+
48
+ - **Verifiable Credentials** provide a W3C standard for expressing cryptographically-signed claims. Delegation credentials can express scope constraints, temporal bounds, budget limits, and revocation status in an interoperable format.
49
+
50
+ - **EdDSA/Ed25519** provides high-performance, deterministic signatures suitable for high-throughput agent interactions with no reliance on hash function collision resistance.
51
+
52
+ ---
53
+
54
+ ## 2. Terminology
55
+
56
+ | Term | Definition |
57
+ |------|------------|
58
+ | **Agent DID** | A Decentralized Identifier (DID) that uniquely identifies an AI agent. KYA-OS supports `did:key` (self-certifying, ephemeral) and `did:web` (organization-hosted, persistent). |
59
+ | **Principal** | The entity that immediately delegates authority to an agent — typically the human operator who launches the agent for a task. The Principal MAY or MAY NOT be the Responsible Party. In personal-use scenarios they are usually the same; in organizational scenarios they differ. |
60
+ | **Responsible Party** | The entity ultimately accountable for the actions of an agent operating under a delegation chain. The Responsible Party is the root issuer of the chain (`issuerDid` of the root `DelegationCredential`). In personal use, the Responsible Party equals the Principal. In organizational use, the Responsible Party is the employing organization or parent entity while the Principal is the immediate human delegator within that organization. Reputation and accountability signals are scoped primarily to the Responsible Party rather than to an agent's ephemeral identity — "can this be trusted?" is ultimately a question about the accountable party. |
61
+ | **Delegation Chain** | An ordered sequence of Delegation Credentials from a root delegator (the Responsible Party) to the current agent, where each credential's subject is the next credential's issuer. |
62
+ | **Delegation Credential** | A W3C Verifiable Credential that grants specific permissions from an issuer (delegator) to a subject (delegate). Contains CRISP constraints defining allowed operations. |
63
+ | **Detached Proof** | A JWS (JSON Web Signature) that cryptographically binds a tool request and response together, enabling non-repudiation and audit. Attached to responses under the reverse-DNS key `org.kya-os/response-proof` within the MCP `_meta` field (prior keys `org.kya-os/proof` and bare `proof` accepted for backward compatibility; see §7.6). |
64
+ | **CRISP Constraints** | **C**onstraints, **R**esources, **I**dentity, **S**cope, **P**olicy — a structured envelope defining what operations a delegation permits: allowed scopes, budget caps, temporal bounds, and audience restrictions. |
65
+ | **Session** | An OPTIONAL, validated, time-bounded convenience context established via the `_kyaos_handshake` tool. Sessions aid replay prevention and provide a stable context for proof generation, but are **not** the durable authority — the per-request detached-JWS proof (§7) and DID-anchored grant (§6) are (see §5 preamble). KYA-OS sessions are independent of MCP's session, which was removed in MCP 2026-07-28 (SEP-2567/SEP-2575). |
66
+ | **Handshake Nonce** | A cryptographically random value provided by the client during session establishment. Used once; prevents replay attacks. |
67
+ | **Audience** | The intended recipient of a credential or proof, typically the MCP server's DID or domain. Prevents credential/proof misuse across different servers. |
68
+
69
+ ---
70
+
71
+ ## 3. Protocol Overview
72
+
73
+ The following diagram illustrates the KYA-OS protocol flow:
74
+
75
+ ```
76
+ ┌─────────────────┐ ┌─────────────────┐
77
+ │ Client Agent │ │ MCP Server │
78
+ │ (did:key:z...) │ │ (did:web:srv) │
79
+ └────────┬────────┘ └────────┬────────┘
80
+ │ │
81
+ │ ┌──────────────────────────────────────────┐ │
82
+ │ │ 1. HANDSHAKE REQUEST │ │
83
+ │ │ - nonce: <22-char base64url> │ │
84
+ │ │ - audience: "did:web:srv" │ │
85
+ │ │ - timestamp: <unix epoch seconds> │ │
86
+ │ │ - agentDid: "did:key:z6Mk..." │ │
87
+ ├──┴──────────────────────────────────────────┴──►
88
+ │ │
89
+ │ ┌─ Validate: │
90
+ │ │ • timestamp within ±120s │
91
+ │ │ • nonce not seen before │
92
+ │ │ • audience matches server DID │
93
+ │ └──────────────────────────────────────┤
94
+ │ │
95
+ │ ┌──────────────────────────────────────────┐ │
96
+ │ │ 2. HANDSHAKE RESPONSE │ │
97
+ │ │ - sessionId: "kyaos_<uuid>" │ │
98
+ │ │ - serverDid: "did:web:srv" │ │
99
+ │ │ - ttlMinutes: 30 │ │
100
+ ◄──┴──────────────────────────────────────────┴──┤
101
+ │ │
102
+ │ ┌──────────────────────────────────────────┐ │
103
+ │ │ 3. TOOL CALL REQUEST │ │
104
+ │ │ method: "tools/call" │ │
105
+ │ │ params: │ │
106
+ │ │ name: "read_file" │ │
107
+ │ │ arguments: { path: "/etc/hosts" } │ │
108
+ │ │ sessionId: "kyaos_..." │ │
109
+ │ │ delegation: <DelegationCredential> │◄── Optional
110
+ ├──┴──────────────────────────────────────────┴──►
111
+ │ │
112
+ │ ┌─ Process: │
113
+ │ │ • Validate session │
114
+ │ │ • Verify delegation (if present) │
115
+ │ │ • Execute tool │
116
+ │ │ • Generate detached proof │
117
+ │ └──────────────────────────────────────┤
118
+ │ │
119
+ │ ┌──────────────────────────────────────────┐ │
120
+ │ │ 4. TOOL CALL RESPONSE │ │
121
+ │ │ content: [ { type: "text", ... } ] │ │
122
+ │ │ _meta: │ │
123
+ │ │ org.kya-os/response-proof: │ │
124
+ │ │ jws: "eyJhbGciOiJFZERTQSI..." │ │
125
+ │ │ meta: │ │
126
+ │ │ did: "did:web:srv" │ │
127
+ │ │ kid: "did:web:srv#key-1" │ │
128
+ │ │ ts: 1710288000 │ │
129
+ │ │ nonce: "..." │ │
130
+ │ │ requestHash: "sha256:..." │ │
131
+ │ │ responseHash: "sha256:..." │ │
132
+ ◄──┴──────────────────────────────────────────┴──┤
133
+ │ │
134
+ ▼ ▼
135
+ ```
136
+
137
+ ---
138
+
139
+ ## 4. Agent Identity
140
+
141
+ ### 4.1 Supported DID Methods
142
+
143
+ KYA-OS implementations MUST support:
144
+
145
+ | Method | Use Case | Resolution |
146
+ |--------|----------|------------|
147
+ | `did:key` | Ephemeral agents, development, testing | Local derivation from public key bytes |
148
+ | `did:web` | Production servers, organizational identity | HTTPS fetch of `/.well-known/did.json` or path-based document |
149
+
150
+ Implementations MAY support additional DID methods. `did:cheqd` support, when
151
+ implemented, MUST be additive: it MUST NOT replace or weaken existing `did:key`
152
+ or `did:web` behavior, and it MUST NOT make cheqd resolution mandatory for
153
+ deployments that have not configured it.
154
+
155
+ ### 4.2 Key Material
156
+
157
+ KYA-OS uses Ed25519 (EdDSA over Curve25519) for all cryptographic operations:
158
+
159
+ - **Key Size**: 32-byte private seed, 32-byte public key
160
+ - **Signature Size**: 64 bytes
161
+ - **Algorithm Identifier**: `EdDSA` (in JWS headers)
162
+ - **JWK Key Type**: `OKP` (Octet Key Pair)
163
+ - **JWK Curve**: `Ed25519`
164
+
165
+ > **Terminology.** This specification uses _secret key_ for the private half of a
166
+ > key pair, following current cryptographic convention. _Private key_ is synonymous
167
+ > and is retained where referenced standards use it (e.g. PKCS#8, JWK).
168
+
169
+ An agent's key pair MUST be generated by the agent or its designated custodian.
170
+ The secret key MUST NOT be generated by, transmitted to, or escrowed with any
171
+ registration, DID, directory, or reputation service; such services receive only
172
+ the public key (as the DID document or an indexing record). For `did:key` this
173
+ holds by construction — the DID is derived locally from the public key (§4.3) and
174
+ no service participates in creation. For `did:web`, the controller publishes only
175
+ the public key in the DID document (§4.4). A key-management proxy or HSM that
176
+ holds the secret key on the agent's behalf (key custody, §11.0) is part of the
177
+ agent's own trust boundary and is distinct from the shared services this
178
+ requirement constrains.
179
+
180
+ ### 4.3 did:key Derivation
181
+
182
+ A `did:key` DID is derived from an Ed25519 public key as follows:
183
+
184
+ 1. Prepend the Ed25519 multicodec prefix `0xed01` to the 32-byte public key
185
+ 2. Encode the result using base58btc
186
+ 3. Prepend the multibase prefix `z`
187
+ 4. Construct the DID: `did:key:z<base58btc-encoded-bytes>`
188
+
189
+ Example:
190
+ ```
191
+ Public Key (hex): 8076ee2cfc1a...32 bytes...
192
+ Multicodec: ed01 + 8076ee2cfc1a...
193
+ Base58btc: 6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
194
+ DID: did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
195
+ ```
196
+
197
+ The verification method ID is `<did>#keys-1`.
198
+
199
+ ### 4.4 did:web Resolution
200
+
201
+ A `did:web` DID resolves to a DID Document via HTTPS:
202
+
203
+ | DID | Resolution URL |
204
+ |-----|---------------|
205
+ | `did:web:example.com` | `https://example.com/.well-known/did.json` |
206
+ | `did:web:example.com:agents:bot1` | `https://example.com/agents/bot1/did.json` |
207
+
208
+ The DID Document MUST contain at least one verification method with `publicKeyJwk` in Ed25519 format:
209
+
210
+ ```json
211
+ {
212
+ "id": "did:web:example.com",
213
+ "verificationMethod": [{
214
+ "id": "did:web:example.com#key-1",
215
+ "type": "Ed25519VerificationKey2020",
216
+ "controller": "did:web:example.com",
217
+ "publicKeyJwk": {
218
+ "kty": "OKP",
219
+ "crv": "Ed25519",
220
+ "x": "<base64url-encoded-32-byte-public-key>"
221
+ }
222
+ }],
223
+ "authentication": ["did:web:example.com#key-1"],
224
+ "assertionMethod": ["did:web:example.com#key-1"]
225
+ }
226
+ ```
227
+
228
+ ### 4.4.1 Optional did:cheqd Resolution and Registrar Writes
229
+
230
+ A KYA-OS implementation MAY support `did:cheqd` as an additional resolvable DID
231
+ method. `did:cheqd` resolution MUST be explicit opt-in through a configured
232
+ resolver endpoint, such as `https://resolver.cheqd.net/1.0/identifiers/{did}`.
233
+ Resolution failures MUST fail closed and MUST NOT fall back to accepting an
234
+ unresolved key.
235
+
236
+ Resolvers SHOULD accept both raw DID Documents and Universal Resolver-style DID
237
+ Resolution Results containing `didDocument`. They MUST reject malformed
238
+ `did:cheqd` values, HTTP or transport failures, invalid JSON, malformed DID
239
+ Documents, and DID Document `id` mismatches by returning no document.
240
+
241
+ KYA-OS deployments that expose cheqd support SHOULD use the generic DID method
242
+ resolver registry rather than a vendor-specific core config block:
243
+
244
+ ```ts
245
+ didResolvers?: Record<
246
+ string,
247
+ DIDResolver | ((fetchProvider: FetchProvider) => DIDResolver)
248
+ >;
249
+
250
+ didResolvers: {
251
+ cheqd: cheqdResolver({ resolverUrl: 'https://resolver.cheqd.net' }),
252
+ }
253
+ ```
254
+
255
+ cheqd-specific resolver URL, cache, and header settings live in the cheqd
256
+ resolver factory or integration module, not in core middleware/provider
257
+ configuration. Registrar URLs are for explicit operator/admin write flows only.
258
+ Runtime proof generation and normal MCP tool-call handling MUST NOT create,
259
+ update, or publish cheqd ledger entries.
260
+
261
+ Updates to cheqd DID Documents and DID-Linked Resources SHOULD use cheqd DID
262
+ Registrar's client-managed-secret flow for `/create`, `/update`, and
263
+ `/{did}/create-resource`: the registrar returns a `jobId` and serialized
264
+ payload, the controller signs the serialized payload locally, then the client
265
+ submits `secret.signingResponse` back to the registrar. Secret keys MUST NOT be
266
+ transmitted to the registrar. Signing MAY be delegated to a KMS/HSM-backed
267
+ signer hook. Local Ed25519 helpers MUST keep key material inside the caller's
268
+ trust boundary and send only signatures to the registrar.
269
+
270
+ Mainnet deployments SHOULD run or contract against their own fee-payer
271
+ registrar deployment. Public or staging registrar endpoints SHOULD be treated as
272
+ testnet-only unless the operator has an explicit mainnet service agreement.
273
+
274
+ When linking an existing `did:web` identity to a `did:cheqd` identity, verifiers
275
+ SHOULD require bidirectional `alsoKnownAs` linkage:
276
+
277
+ 1. Resolve the `did:web` DID Document.
278
+ 2. Resolve the `did:cheqd` DID Document.
279
+ 3. Confirm the `did:web` document lists the `did:cheqd` DID in `alsoKnownAs`.
280
+ 4. Confirm the `did:cheqd` document lists the `did:web` DID in `alsoKnownAs`.
281
+
282
+ `did:web` remains canonical unless an operator explicitly publishes reciprocal
283
+ linkage. `alsoKnownAs` values on DID Documents MUST be strings. Resource metadata
284
+ aliases, where supported by a registrar, use a different resource-specific shape
285
+ and MUST NOT be accepted as DID Document `alsoKnownAs` entries.
286
+
287
+ Selected durable artifacts MAY be anchored as cheqd DID-Linked Resources under
288
+ the `did:cheqd` subject. Supported KYA-OS DLR artifact categories are:
289
+
290
+ | Artifact | Purpose |
291
+ |----------|---------|
292
+ | `CapabilityManifest` | Durable description of agent/tool capabilities and outputs |
293
+ | `ConformanceManifest` | Durable statement of protocol profiles and checks satisfied |
294
+ | `AccessHashManifest` | Durable hashes of off-chain policy/access artifacts |
295
+ | `TrustConfigManifest` | Durable trust policy, accepted DID methods, and linkage requirements |
296
+
297
+ DLR content SHOULD be canonicalized before hashing, and content hashes SHOULD
298
+ use `sha256:<64 lowercase hex characters>`. Updating a DLR means publishing a
299
+ new resource version in the same resource collection/name/type; prior content is
300
+ not overwritten. High-volume runtime events, raw operational logs, normal
301
+ tool-call proofs, and frequently changing metadata MUST remain off-chain.
302
+
303
+ Out of scope for this specification section: external registry changes,
304
+ reputation VC snapshot publication, status-list backend hosting, KYC/KYB
305
+ issuance, and replacing `did:web` as the canonical identity.
306
+
307
+ ### 4.5 Agent-Controlled Key Generation
308
+
309
+ An agent MUST generate its own keypair locally. No other party generates,
310
+ holds, escrows, or otherwise gains access to an agent's secret key at any
311
+ point:
312
+
313
+ 1. The agent generates an Ed25519 keypair (§4.2) using a local CSPRNG.
314
+ 2. The agent derives its DID from the public key (`did:key`, §4.3) or publishes
315
+ a DID document referencing the public key (`did:web`, §4.4).
316
+ 3. Where a DID document is registered or published through a service, the agent
317
+ signs the registration request with its secret key.
318
+ 4. The service verifies the signature and publishes the document. It receives
319
+ the public key only.
320
+
321
+ A registry (§6.8), verifier, or any registration or provisioning service MUST
322
+ NOT generate, escrow, or otherwise gain access to an agent's secret key. This
323
+ invariant keeps identity infrastructure from becoming a centralized key escrow:
324
+ a compromise of a registry or verifier MUST NOT yield agent secret keys (see
325
+ §11.11).
326
+
327
+ Implementations that generate or hold agent secret keys on the agent's behalf
328
+ are non-conformant.
329
+
330
+ ---
331
+
332
+ ## 5. Session Lifecycle
333
+
334
+ > **Normative framing (MCP 2026-07-28).** The KYA-OS session described in this
335
+ > section is an **OPTIONAL convenience layer**, not the protocol's durable source
336
+ > of authority. The **normative, durable authority** for every KYA-OS interaction
337
+ > is (a) the per-request **holder-of-key detached-JWS proof** (§7), which is
338
+ > self-contained and replay-bound on its own, and (b) the **DID-anchored
339
+ > delegation grant** (§6). A conformant verifier MUST be able to authorize and
340
+ > audit a request from the proof and grant alone, with no server-side session
341
+ > state.
342
+ >
343
+ > This independence is deliberate. MCP 2026-07-28 removed `initialize`/
344
+ > `initialized` (SEP-2575) and the `Mcp-Session-Id` header (SEP-2567), making the
345
+ > MCP core stateless: every request is self-contained, and client info, version,
346
+ > and capabilities ride in `_meta` per request. KYA-OS sessions are **KYA-OS's
347
+ > own layer** — established via the `_kyaos_handshake` tool (§14), independent of
348
+ > MCP's now-removed session — and exist only as a **transport/UX convenience**
349
+ > (e.g. for wallet-less clients, nonce bookkeeping, and a stable context for proof
350
+ > generation). They are an *application-state handle* in the SEP-2575 sense:
351
+ > explicit and model-visible, never hidden server state on which authorization
352
+ > silently depends.
353
+ >
354
+ > The `sessionId`/`nonce` carried in a proof (§7.2) therefore bind a proof to its
355
+ > originating handshake for replay defense; they are **not** a substitute for
356
+ > verifying the proof signature and the delegation chain. The mechanics below
357
+ > remain valid where a session is used.
358
+
359
+ ### 5.1 Handshake Request
360
+
361
+ To establish a session, the client sends a handshake request:
362
+
363
+ ```typescript
364
+ interface HandshakeRequest {
365
+ nonce: string; // 16 bytes, base64url-encoded (22 chars, no padding)
366
+ audience: string; // Server DID or domain
367
+ timestamp: number; // Unix epoch seconds
368
+ agentDid?: string; // Client's DID (optional for anonymous sessions)
369
+ clientInfo?: { // Optional client metadata
370
+ name: string;
371
+ version?: string;
372
+ platform?: string;
373
+ };
374
+ }
375
+ ```
376
+
377
+ ### 5.2 Validation Rules
378
+
379
+ The server MUST validate the handshake request:
380
+
381
+ 1. **Timestamp Skew**: `|server_time - request.timestamp| <= 120 seconds`
382
+ - Reject if outside this window
383
+ - Remediation: "Check NTP sync on client and server"
384
+
385
+ 2. **Nonce Uniqueness**: The (nonce, agentDid) pair MUST NOT have been seen before
386
+ - Reject with "Nonce already used (replay attack prevention)"
387
+ - Nonces MUST be cached for at least `sessionTtlMinutes + 1 minute`
388
+ - When `agentDid` is omitted, the dedupe key is `nonce` alone. Servers MUST use a shorter cache TTL for anonymous nonces (recommended: 60 seconds, vs. the default 120 seconds for authenticated handshakes)
389
+
390
+ 3. **Audience Match**: `request.audience` MUST match the server's DID or expected domain
391
+ - Prevents credential forwarding attacks
392
+
393
+ ### 5.3 Session Creation
394
+
395
+ On successful validation, the server creates a session:
396
+
397
+ ```typescript
398
+ interface SessionContext {
399
+ sessionId: string; // Format: "kyaos_<uuid-v4>"
400
+ audience: string; // Echoed from request
401
+ nonce: string; // Echoed from request
402
+ timestamp: number; // Request timestamp
403
+ createdAt: number; // Server timestamp at creation
404
+ lastActivity: number; // Updated on each request
405
+ ttlMinutes: number; // Default: 30
406
+ agentDid?: string; // Client DID if provided
407
+ serverDid?: string; // Server's DID
408
+ identityState: 'anonymous' | 'authenticated';
409
+ }
410
+ ```
411
+
412
+ ### 5.4 Session TTL and Expiry
413
+
414
+ - **Idle Timeout**: Sessions expire after `ttlMinutes` of inactivity
415
+ - **Absolute Lifetime**: Implementations MAY enforce a maximum session lifetime
416
+ - **Activity Update**: `lastActivity` is updated on each valid request
417
+
418
+ ### 5.5 Replay Prevention
419
+
420
+ The nonce cache implementation MUST:
421
+ - Store (nonce, agentDid, expiry) tuples
422
+ - Support TTL-based automatic expiry
423
+ - Be atomic to prevent race conditions in concurrent environments
424
+ - For distributed deployments: use Redis, DynamoDB, or Cloudflare KV (not in-memory)
425
+
426
+ Nonce lifetime MUST exceed the session TTL. Servers MUST NOT drop nonces before the lifetime expires, to prevent replay attacks via early eviction.
427
+
428
+ ---
429
+
430
+ ## 6. Delegation
431
+
432
+ ### 6.1 DelegationRecord Structure
433
+
434
+ Internal representation of a delegation:
435
+
436
+ ```typescript
437
+ interface DelegationRecord {
438
+ id: string; // Unique delegation identifier
439
+ issuerDid: string; // DID of the delegator
440
+ subjectDid: string; // DID of the delegate
441
+ controller?: string; // DID that can revoke this delegation
442
+ vcId: string; // URN of the Verifiable Credential
443
+ parentId?: string; // Parent delegation ID (for chains)
444
+ constraints: DelegationConstraints;
445
+ signature: string; // Extracted from VC proof
446
+ status: 'active' | 'revoked' | 'expired';
447
+ createdAt?: number;
448
+ revokedAt?: number;
449
+ revokedReason?: string;
450
+ metadata?: Record<string, unknown>;
451
+ }
452
+ ```
453
+
454
+ ### 6.2 DelegationCredential as W3C VC
455
+
456
+ > **Note:** A Delegation credential is itself a W3C Verifiable Credential. The Delegation Registry is a specialized index of delegations, which are VCs.
457
+
458
+ Delegations are issued as W3C Verifiable Credentials:
459
+
460
+ ```json
461
+ {
462
+ "@context": [
463
+ "https://www.w3.org/2018/credentials/v1",
464
+ "https://schema.kya-os.org/v1/protocol/delegation/context/v1.0.0"
465
+ ],
466
+ "id": "urn:uuid:d7f8a9b0-1234-5678-9abc-def012345678",
467
+ "type": ["VerifiableCredential", "DelegationCredential"],
468
+ "issuer": "did:key:z6MkIssuer...",
469
+ "issuanceDate": "2024-03-01T12:00:00Z",
470
+ "expirationDate": "2024-03-02T12:00:00Z",
471
+ "credentialSubject": {
472
+ "id": "did:key:z6MkDelegate...",
473
+ "delegation": {
474
+ "id": "del-001",
475
+ "issuerDid": "did:key:z6MkIssuer...",
476
+ "subjectDid": "did:key:z6MkDelegate...",
477
+ "constraints": {
478
+ "scopes": ["tool:read_file", "tool:list_directory"],
479
+ "notBefore": 1709294400,
480
+ "notAfter": 1709380800,
481
+ "audience": "did:web:mcp-server.example.com"
482
+ },
483
+ "status": "active"
484
+ }
485
+ },
486
+ "credentialStatus": {
487
+ "id": "https://example.com/status/1#42",
488
+ "type": "StatusList2021Entry",
489
+ "statusPurpose": "revocation",
490
+ "statusListIndex": "42",
491
+ "statusListCredential": "https://example.com/status/1"
492
+ },
493
+ "proof": {
494
+ "type": "Ed25519Signature2020",
495
+ "created": "2024-03-01T12:00:00Z",
496
+ "verificationMethod": "did:key:z6MkIssuer...#keys-1",
497
+ "proofPurpose": "assertionMethod",
498
+ "proofValue": "<base64url-encoded-signature>"
499
+ }
500
+ }
501
+ ```
502
+
503
+ A `DelegationCredential` carries a _permission_, not a _claim_. To keep claim
504
+ semantics out of an authorization decision — a confused-deputy vector (§11.6) —
505
+ the `credentialSubject` MUST contain only two properties: `id` (the delegate's
506
+ DID) and `delegation` (the permission payload). A verifier MUST reject a
507
+ `DelegationCredential` whose `credentialSubject` carries any other property, and
508
+ MUST reject a credential whose `type` array does not include both
509
+ `VerifiableCredential` and `DelegationCredential`.
510
+
511
+ Implementations MAY offer an explicit, audited opt-out of the `credentialSubject`
512
+ shape check to bridge non-conformant issuers. Such an opt-out MUST default to off
513
+ and MUST emit a one-time warning when enabled, so the relaxation is visible in
514
+ operational logs.
515
+
516
+ > **Schema dialect (MCP 2026-07-28).** All KYA-OS JSON Schemas in this
517
+ > specification — and the normative copies in `schemas/*.json` — are authored
518
+ > against **JSON Schema 2020-12** and MUST declare
519
+ > `"$schema": "https://json-schema.org/draft/2020-12/schema"`. KYA-OS MCP tool
520
+ > definitions MUST likewise express their `inputSchema` and `outputSchema` in
521
+ > JSON Schema 2020-12 (SEP-2106). Consistent with the RC, a tool's
522
+ > `structuredContent` MAY be any JSON value permitted by its `outputSchema`, not
523
+ > only a JSON object.
524
+
525
+ ### 6.3 CRISP Constraint Envelope
526
+
527
+ ```typescript
528
+ interface DelegationConstraints {
529
+ // Temporal bounds (Unix epoch seconds)
530
+ notBefore?: number;
531
+ notAfter?: number;
532
+
533
+ // Simple scope list (tool names, resource patterns)
534
+ scopes?: string[];
535
+
536
+ // Audience restriction (server DID or domain)
537
+ audience?: string | string[];
538
+
539
+ // Extended CRISP constraints
540
+ crisp?: {
541
+ budget?: {
542
+ unit: 'USD' | 'ops' | 'points';
543
+ cap: number;
544
+ window?: {
545
+ kind: 'rolling' | 'fixed';
546
+ durationSec: number;
547
+ };
548
+ };
549
+ scopes: Array<{
550
+ resource: string;
551
+ matcher: 'exact' | 'prefix' | 'regex';
552
+ constraints?: Record<string, unknown>;
553
+ }>;
554
+ };
555
+ }
556
+ ```
557
+
558
+ ### 6.4 Delegation Graph
559
+
560
+ Delegations form a directed acyclic graph (DAG):
561
+
562
+ ```
563
+ [Root: Responsible Party → Principal]
564
+ │
565
+ [Principal → Agent A]
566
+ │
567
+ ┌──────┴──────┐
568
+ ▼ ▼
569
+ [A → B] [A → C]
570
+ │
571
+ ▼
572
+ [B → D]
573
+ ```
574
+
575
+ - Each node is a `DelegationCredential`
576
+ - `parentId` links to the parent delegation
577
+ - Child delegation's `issuerDid` MUST equal parent's `subjectDid`
578
+ - Scope constraints MUST be equal to or narrower than parent's constraints
579
+ - Every chain MUST terminate at a Responsible Party. The Responsible Party's DID is the `issuerDid` of the root `DelegationCredential` and is the entity ultimately accountable for actions taken under any descendant delegation. In personal use the Responsible Party equals the Principal; in organizational use the Responsible Party is the parent organization while the Principal is the immediate human delegator (see §2).
580
+
581
+ ### 6.4.1 Designation Invariant
582
+
583
+ A `DelegationCredential` may authorize multiple resources, either via a list
584
+ of explicit scope strings (`constraints.scopes[]`) or via pattern matchers
585
+ (`constraints.crisp.scopes[].matcher`: `'exact' | 'prefix' | 'regex'`). When
586
+ the credential is used to invoke a specific tool or resource, the invocation
587
+ MUST designate the specific scope being exercised, and verifiers MUST confirm
588
+ that the designated scope is a member of (or matches a pattern in) the
589
+ delegation's authorized scopes.
590
+
591
+ In other words: multi-resource delegations are valid as authority grants
592
+ but cannot be used in invocations without designation. An invocation that
593
+ omits the specific resource designation MUST be rejected, even if the
594
+ delegation would have authorized it under any matching scope.
595
+
596
+ This is the designation/authorization separation: without it, an attacker
597
+ who acquires a multi-resource delegation can use it against resources the
598
+ original delegator did not anticipate. Together with the audience-on-
599
+ re-delegation requirement (§11.6), this closes the confused-deputy class
600
+ on the invocation path.
601
+
602
+ Reference implementation: each tool wrapped via `wrapWithDelegation` is
603
+ registered with a fixed `scopeId`; invocations carry the wrapped tool's
604
+ name and the verifier requires the delegation's scopes to contain that
605
+ exact scopeId before the handler runs.
606
+
607
+ ### 6.5 Cascading Revocation
608
+
609
+ When a delegation is revoked:
610
+
611
+ 1. Mark the delegation as `revoked` in the delegation graph
612
+ 2. Recursively mark all descendant delegations as `revoked`
613
+ 3. Update StatusList2021 credential for each revoked delegation
614
+ 4. Emit revocation events for audit logging
615
+
616
+ #### 6.5.1 Revocation Rights
617
+
618
+ For v1.0, the parties authorized to revoke a delegation are:
619
+
620
+ 1. **The direct issuer of the delegation.** The party that signed the
621
+ `DelegationCredential` MAY revoke it at any time.
622
+ 2. **Any ancestor issuer in the delegation chain.** A party that issued
623
+ an upstream delegation MAY revoke any descendant delegation; cascading
624
+ revocation propagates downward through the graph (§6.5).
625
+ 3. **The Responsible Party at the root of the chain.** The root issuer
626
+ MAY revoke the entire chain.
627
+ 4. **The subject of a delegation MAY NOT revoke it.** Revocation requires
628
+ authority from above; an agent cannot unilaterally invalidate a
629
+ delegation it received.
630
+
631
+ Revocation requests SHOULD be signed by the revoking party's key, and
632
+ verifiers SHOULD validate the revoker's identity against the chain before
633
+ honoring the revocation.
634
+
635
+ Future versions may adopt an explicit "revocation as a delegatable
636
+ permission" model (UCAN-style), allowing the responsible party to grant
637
+ revocation authority to other parties (e.g. a security operations team)
638
+ without those parties being in the original chain. Tracked for v1.1.
639
+
640
+ #### 6.5.2 Concurrency and the Revocation Race
641
+
642
+ Revocation and invocation are concurrent operations in the Lamport sense.
643
+ An invocation that arrives at a verifier between the moment a revocation
644
+ is issued and the moment that revocation propagates to the verifier will
645
+ succeed despite the revocation being logically prior. This race is
646
+ unavoidable in any distributed system; implementations can bound the
647
+ window but cannot eliminate it.
648
+
649
+ Implementations MUST:
650
+
651
+ - Propagate revocations to all known verifiers as quickly as possible,
652
+ including immediate StatusList2021 updates and cache invalidation.
653
+ - Cache status-list lookups with a TTL no longer than the revocation
654
+ propagation target. For high-privilege scopes, ≤ 60 seconds is
655
+ recommended.
656
+ - Honor explicit revocation signals (e.g., a side-channel notification
657
+ from a trusted source) immediately, ahead of the next status-list
658
+ refresh.
659
+
660
+ Operators of high-stakes resources SHOULD reduce the race window by
661
+ issuing short-lived delegations (so revocation by expiration is the
662
+ primary mechanism) and polling status lists frequently. The unavoidable
663
+ race is the same trade-off familiar from PKI: short certificate lifetimes
664
+ reduce the window during which a compromised credential can be used.
665
+
666
+ ### 6.6 StatusList2021 Revocation
667
+
668
+ KYA-OS uses the W3C StatusList2021 specification for revocation:
669
+
670
+ ```json
671
+ {
672
+ "@context": [
673
+ "https://www.w3.org/2018/credentials/v1",
674
+ "https://w3id.org/vc/status-list/2021/v1"
675
+ ],
676
+ "id": "https://example.com/status/1",
677
+ "type": ["VerifiableCredential", "StatusList2021Credential"],
678
+ "issuer": "did:web:example.com",
679
+ "issuanceDate": "2024-03-01T00:00:00Z",
680
+ "credentialSubject": {
681
+ "id": "https://example.com/status/1#list",
682
+ "type": "StatusList2021",
683
+ "statusPurpose": "revocation",
684
+ "encodedList": "<gzip-compressed-base64-bitstring>"
685
+ }
686
+ }
687
+ ```
688
+
689
+ L1 implementations do not require revocation support; revocation checking is available at L3 only.
690
+
691
+ Revocation does not require a globally accessible list. A verifier only needs to
692
+ know whether a delegation has been revoked **for the resources it gates**, so the
693
+ revocation state MAY be held as verifier-local state rather than fetched from an
694
+ external URL. `StatusList2021` is the interoperable wire format for _publishing_
695
+ revocation when the delegator and verifier are operationally decoupled; it is not
696
+ a requirement to operate a public, globally-readable certificate-revocation list.
697
+ A verifier that issues and gates its own delegations MAY check revocation entirely
698
+ from local state.
699
+
700
+ ### 6.7 Orchestration & Extensibility
701
+
702
+ Orchestration scope is service-local; directory federation is out of scope for this version. Delegation graphs and revocation state are managed within a single service boundary and are not synchronized across independent KYA-OS deployments or external agent directories (e.g. NANDA).
703
+
704
+ - Each delegation is assigned a `statusListIndex` (0 to 131071)
705
+ - Bit at index is 0 = valid, 1 = revoked
706
+ - Bitstring is gzip-compressed and base64-encoded
707
+
708
+ ### 6.8 Protocol Registry
709
+
710
+ The registry indexes anything addressable by DID — agents, MCP servers, services, and delegation policies.
711
+
712
+ Three registry types are defined within the protocol:
713
+
714
+ - **Delegation Registry** — stores `DelegationCredential` objects and supports querying by issuer, subject, scope, and revocation status.
715
+ - **Credential Registry** — stores all W3C Verifiable Credentials issued within the ecosystem, providing a general-purpose VC index.
716
+ - **Trust Registry** — indexes entities by DID for discovery, enabling participants to resolve trust relationships and verify standing.
717
+
718
+ `DelegationCredential` IS a Verifiable Credential; Delegation Registry is a specialized Credential Registry view.
719
+
720
+ Trust-registry rejection is advisory, not blocking. The cryptographic
721
+ delegation check is the load-bearing gate (§11.0); registry standing is a
722
+ secondary signal. Services SHOULD honor well-formed delegations from
723
+ low-standing delegates but MAY apply rate limits, additional verification, or
724
+ scope reduction. Outright rejection of well-formed delegations pushes the same
725
+ authority through worse channels — credential sharing, request proxying — which
726
+ reduces auditability rather than improving security.
727
+
728
+ ### 6.9 Ephemeral Delegate Keys (Non-Normative)
729
+
730
+ A delegator MAY subject a `DelegationCredential` to a one-off ephemeral public
731
+ key (typically a `did:key`) instead of a long-lived agent DID. This
732
+ privacy-preserving pattern lets a chain authorize an action without correlating
733
+ the delegate across unrelated delegations (§12.1).
734
+
735
+ ```json
736
+ {
737
+ "credentialSubject": {
738
+ "id": "did:key:z6Mk-ephemeral-...",
739
+ "delegation": {
740
+ "id": "del-ephemeral-001",
741
+ "subjectDid": "did:key:z6Mk-ephemeral-...",
742
+ "parentId": "urn:uuid:...",
743
+ "constraints": { "scopes": ["files:read"] },
744
+ "status": "active"
745
+ }
746
+ }
747
+ }
748
+ ```
749
+
750
+ The ephemeral key signs presentations of this credential and is discarded once
751
+ the delegation expires. The `credentialSubject` carries only `id` and
752
+ `delegation`, so the example remains conformant with the subject-shape rule in
753
+ §6.2. Implementations MAY support this pattern; it is not required for any
754
+ conformance level.
755
+
756
+ ### 6.10 Card-era profile: VC 2.0 + ZCAP-LD delegation
757
+
758
+ The Entity Card profile ([SPEC-ENTITY-CARD.md](./SPEC-ENTITY-CARD.md) §10) uses a newer credential
759
+ shape than §6.2: a W3C Verifiable Credentials 2.0 credential whose `credentialSubject` IS an
760
+ attenuated ZCAP-LD capability, revoked via W3C Bitstring Status List v1.0 (the successor to the
761
+ StatusList2021 mechanism of §6.6). The delegation model of this chapter - one credential per hop,
762
+ subset discipline at every link, cascading revocation (§6.5), the designation invariant (§6.4.1),
763
+ and the per-hop `audience` rule (§11.6) - is unchanged; only the wire shape and the revocation
764
+ list format differ. Both shapes remain valid for the 1.x line: §6.2 is what the legacy session
765
+ profile issues and verifies, this profile is what the Entity Card path issues and verifies, and an
766
+ implementation encountering one never needs to accept the other in its place. The JSON Schema is
767
+ published at `schemas/card-delegation-credential.json`.
768
+
769
+ **Credential shape.** One `DelegationCredential` per delegation **hop**; a chain runs `root → … → leaf`. A
770
+ `DelegationCredential` is a VC 2.0 whose `credentialSubject` IS an attenuated ZCAP-LD capability:
771
+
772
+ ```jsonc
773
+ {
774
+ "@context": [ "https://www.w3.org/ns/credentials/v2",
775
+ "https://w3id.org/security/zcap/v1",
776
+ "https://kya-os.org/ns/delegation/v1" ],
777
+ "type": [ "VerifiableCredential", "DelegationCredential" ],
778
+ "issuer": "<delegator DID>",
779
+ "validUntil": "2027-01-01T00:00:00Z",
780
+ "credentialSubject": {
781
+ "id": "urn:zcap:del_123",
782
+ "invoker": "<delegate DID>", // or "controller" (§2.3)
783
+ "parentCapability": "<parent id | root invocationTarget>",
784
+ "invocationTarget": "<resource DID>",
785
+ "allowedAction": [ "payments.transfer" ],
786
+ "caveats": [ { "type": "ValidUntil", "date": "…" },
787
+ { "type": "MaxAmount", "limit": "500.00", "currency": "USD" } ]
788
+ },
789
+ "credentialStatus": { "type": "BitstringStatusListEntry",
790
+ "statusPurpose": "revocation", "statusListIndex": "42",
791
+ "statusListCredential": "https://…/status/delegations" },
792
+ "proof": { "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022" }
793
+ }
794
+ ```
795
+
796
+ Per-hop signature verification (the `DataIntegrityProof`, `eddsa-jcs-2022`) is a SEPARATE injected
797
+ concern; this profile specifies the attenuation + continuity recompute.
798
+
799
+ **The delegate.** The delegate is `credentialSubject.invoker`, falling back to `credentialSubject.controller`; a
800
+ capability MUST name one of them.
801
+
802
+ **Attenuation invariants (fail-closed, on resolve).** Note: earlier drafts labelled these "CRISP
803
+ attenuation"; they are the chain-attenuation rules and are distinct from the §6.3 CRISP constraint
804
+ envelope, whose subset discipline they generalize to ZCAP chains. A chain is valid only if EVERY hop attenuates its parent. Any broadening hop invalidates the whole
805
+ chain:
806
+
807
+ - **action subset** - child `allowedAction ⊆ parent`;
808
+ - **monotone caveats** - no parent caveat may be silently dropped; for a shared caveat type, child
809
+ `MaxAmount ≤ parent` (same currency; compared as fixed-point decimals scaled to 6 places) and
810
+ child `ValidUntil ≤ parent`; an unknown caveat type MUST be replicated **verbatim**;
811
+ - **top-level `validUntil` narrowing** - child `validUntil ≤ parent`;
812
+ - **continuity** - the parent's delegate (`invoker`) MUST equal the child's `issuer` (you may only
813
+ re-delegate what was delegated to YOUR key), and child `parentCapability` MUST reference the
814
+ parent capability `id`;
815
+ - **constant `invocationTarget`** along the chain;
816
+ - **depth** MUST NOT exceed **10** hops (`MAX_DELEGATION_DEPTH`);
817
+ - **root** - root `parentCapability` MUST equal its `invocationTarget` (the resource); when a
818
+ resource owner / resource is asserted, root `issuer` MUST equal the resource owner and root
819
+ `invocationTarget` MUST equal the resource.
820
+
821
+ A verifying resource SHOULD assert its own identity as the expected resource when it evaluates a
822
+ chain (the `resource` context of `evaluateDelegationChain`), so that a chain minted for a
823
+ different resource fails at the root check instead of being accepted by an unrelated Verifier.
824
+
825
+ **Revocation.** Revocation uses **W3C Bitstring Status List v1.0** [BITSTRING-STATUS-LIST]
826
+ (`BitstringStatusListEntry`) - the **successor to** the deprecated StatusList2021 - behind a
827
+ pluggable `RevocationChecker` seam so status-list churn never reaches callers. The default checker
828
+ resolves `statusListCredential` via SafeFetch (§6.6), inflates the multibase (`u` = base64url) +
829
+ GZIP `encodedList`, and reads the bit at `statusListIndex` MSB-first within each byte. It is
830
+ **fail-closed**: an unreachable list, malformed credential, mismatched `statusPurpose`, or
831
+ out-of-range index all resolve to `{ revoked: true }`. Each verdict also reports `fresh` - `true`
832
+ only when read from a live, in-validity-window (`validFrom`/`validUntil`, or
833
+ `issuanceDate`/`expirationDate`) status list. The chain walk is **cascading**: root→leaf,
834
+ short-circuiting on the first revoked/unresolvable hop (a revoked ancestor invalidates the subtree),
835
+ with `fresh` the AND of every checked hop. L2 accepts a non-revoked chain (cached / offline
836
+ acceptable); L3 additionally requires `fresh` (a live check).
837
+
838
+ **KYC/KYB.** KYC/KYB rides the same rail as an `IdentityVerificationCredential` whose `credentialSubject` is the
839
+ `responsibleParty` and which asserts the verification **fact + level** (`verificationLevel ∈
840
+ { basic, enhanced, loa3 }`), never raw PII. It is verified via the same trusted-issuer + signature +
841
+ expiry path as an L2 capability attestation and surfaced through the Card's `attestations[]`.
842
+
843
+ ---
844
+
845
+ The equalities a Verifier recomputes from a resolved chain, and their place in the fail-closed
846
+ verification order, are specified in SPEC-ENTITY-CARD.md §10.2 and §11.
847
+
848
+ ---
849
+
850
+ ## 7. Proof Generation
851
+
852
+ ### 7.1 Purpose
853
+
854
+ Detached proofs provide:
855
+
856
+ - **Non-repudiation**: Cryptographic evidence that the server processed the request
857
+ - **Integrity**: Hashes bind the exact request and response together
858
+ - **Audit Trail**: Proofs can be stored and verified offline
859
+
860
+ ### 7.2 ProofMeta Fields
861
+
862
+ ```typescript
863
+ interface ProofMeta {
864
+ did: string; // Server's DID (signer)
865
+ kid: string; // Key ID used for signing
866
+ ts: number; // Unix epoch seconds when proof was generated
867
+ nonce: string; // Session nonce (prevents cross-session replay)
868
+ audience: string; // Session audience
869
+ sessionId: string; // Session identifier
870
+ requestHash: string; // SHA-256 of canonicalized request
871
+ responseHash?: string; // SHA-256 of canonicalized response. Present on success
872
+ // AND on needs_authorization challenges (binds the
873
+ // challenge content, incl. authorizationUrl); ABSENT on
874
+ // denial / step-up proofs (no response body).
875
+ scopeId?: string; // Scope under which the call was made
876
+ delegationRef?: string; // Reference to delegation credential
877
+ clientDid?: string; // Client's DID if authenticated
878
+ outcome?: 'allowed' | 'denied' | 'step_up_required' | 'needs_authorization';
879
+ // Authorization outcome; ABSENT ⇒ implicitly 'allowed' (success)
880
+ reason?: string; // Human-readable reason on a non-'allowed' outcome
881
+ }
882
+ ```
883
+
884
+ ### 7.3 Hash Generation
885
+
886
+ 1. **Canonicalize**: Convert request/response to RFC 8785 (JCS) canonical JSON
887
+ 2. **Hash**: Compute SHA-256 of the canonical JSON bytes (UTF-8 encoded)
888
+ 3. **Format**: `sha256:<64-char-lowercase-hex>`
889
+
890
+ Request canonicalization includes:
891
+ ```json
892
+ {
893
+ "method": "tools/call",
894
+ "params": { /* sorted keys, no whitespace */ }
895
+ }
896
+ ```
897
+
898
+ Response canonicalization is the `data` field only (excludes `_meta`).
899
+
900
+ ### 7.4 JWS Compact Serialization
901
+
902
+ The proof JWS is generated as:
903
+
904
+ ```
905
+ BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)
906
+ ```
907
+
908
+ **Header**:
909
+ ```json
910
+ {
911
+ "alg": "EdDSA",
912
+ "kid": "did:web:server.example.com#key-1"
913
+ }
914
+ ```
915
+
916
+ **Payload** (canonicalized):
917
+ ```json
918
+ {
919
+ "aud": "did:web:server.example.com",
920
+ "iss": "did:web:server.example.com",
921
+ "nonce": "...",
922
+ "requestHash": "sha256:...",
923
+ "responseHash": "sha256:...",
924
+ "sessionId": "kyaos_...",
925
+ "sub": "did:web:server.example.com",
926
+ "ts": 1710288000
927
+ }
928
+ ```
929
+
930
+ **Denial** and **step-up** proofs have no response body, so `responseHash` is
931
+ omitted and `outcome` (plus an optional `reason`) is included instead. Keys
932
+ remain RFC 8785-sorted:
933
+ ```json
934
+ {
935
+ "aud": "did:web:server.example.com",
936
+ "iss": "did:web:server.example.com",
937
+ "nonce": "...",
938
+ "outcome": "denied",
939
+ "reason": "insufficient_scope",
940
+ "requestHash": "sha256:...",
941
+ "sessionId": "kyaos_...",
942
+ "sub": "did:web:server.example.com",
943
+ "ts": 1710288000
944
+ }
945
+ ```
946
+
947
+ A **needs_authorization** challenge *does* carry a response body (the consent
948
+ challenge, including the `authorizationUrl`), so its proof includes **both** a
949
+ `responseHash` over that body — binding the URL against tampering / MITM
950
+ substitution — and `outcome: "needs_authorization"`:
951
+ ```json
952
+ {
953
+ "aud": "did:web:server.example.com",
954
+ "iss": "did:web:server.example.com",
955
+ "nonce": "...",
956
+ "outcome": "needs_authorization",
957
+ "reason": "requires delegation with scope: greeting:restricted",
958
+ "requestHash": "sha256:...",
959
+ "responseHash": "sha256:...",
960
+ "sessionId": "kyaos_...",
961
+ "sub": "did:web:server.example.com",
962
+ "ts": 1710288000
963
+ }
964
+ ```
965
+
966
+ ### 7.5 _meta Attachment
967
+
968
+ The proof is attached to tool responses under the reverse-DNS key
969
+ `org.kya-os/response-proof` inside the standard MCP `_meta` field. Other `_meta` keys
970
+ (e.g. `io.modelcontextprotocol/*`, `traceparent`) MAY coexist and are ignored by
971
+ the verifier (§7.6):
972
+
973
+ ```json
974
+ {
975
+ "content": [{ "type": "text", "text": "File contents..." }],
976
+ "_meta": {
977
+ "org.kya-os/response-proof": {
978
+ "jws": "eyJhbGciOiJFZERTQSIsImtpZCI6Ii4uLiJ9.eyJhdWQiOi4uLn0.c2ln...",
979
+ "meta": {
980
+ "did": "did:web:server.example.com",
981
+ "kid": "did:web:server.example.com#key-1",
982
+ "ts": 1710288000,
983
+ "nonce": "abc123...",
984
+ "audience": "did:web:server.example.com",
985
+ "sessionId": "kyaos_d7f8a9b0-...",
986
+ "requestHash": "sha256:a1b2c3...",
987
+ "responseHash": "sha256:d4e5f6..."
988
+ }
989
+ },
990
+ "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
991
+ }
992
+ }
993
+ ```
994
+
995
+ The `traceparent` key above illustrates a coexisting reserved key; it is not part
996
+ of the proof and is never hashed or trusted (§7.6).
997
+
998
+ ### 7.6 _meta Namespacing and Hash Exclusion
999
+
1000
+ The response hash is computed over the response object with `_meta` removed (see
1001
+ §7.3). Implementations MUST NOT rely on signature coverage of any `_meta` field.
1002
+
1003
+ `_meta` is the Model Context Protocol per-request metadata channel and is shared
1004
+ real estate: under MCP 2026-07-28 it legitimately carries reverse-DNS–namespaced
1005
+ keys reserved by the MCP maintainers (`io.modelcontextprotocol/*`) and W3C Trace
1006
+ Context propagation keys (`traceparent`, `tracestate`, `baggage`). KYA-OS
1007
+ therefore namespaces its own payload and MUST NOT assume exclusive ownership of
1008
+ `_meta`.
1009
+
1010
+ **Canonical key.** KYA-OS attaches its detached response proof under the
1011
+ reverse-DNS key `org.kya-os/response-proof` (the `proofMetaKey`). The key is
1012
+ named for its role, pairing with the request proof's `org.kya-os/request-proof`
1013
+ (SPEC-ENTITY-CARD §8); the two keys carry different objects, and neither carries
1014
+ a version - the request proof's profile version lives inside the object (`prf`). This key SHOULD be configurable (e.g., a
1015
+ single build-time constant rather than a value scattered through the code), so
1016
+ that if and when KYA-OS is registered as an MCP Extension (SEP-2133), its
1017
+ reverse-DNS extension id — and hence this key — can be re-pointed without a wire
1018
+ change.
1019
+
1020
+ **Backward compatibility.** Two prior keys carried this proof: bare `proof`
1021
+ (pre-1.1) and `org.kya-os/proof` (1.1 until the role-named rename). For one
1022
+ major version, verifiers MUST also accept a proof published under either prior
1023
+ key. Producers emit `org.kya-os/response-proof` and MAY additionally mirror the
1024
+ proof under a prior key when targeting older verifiers. When several keys are
1025
+ present, the newest canonical form wins (`org.kya-os/response-proof`, then
1026
+ `org.kya-os/proof`, then `proof`).
1027
+
1028
+ **`metaPolicy` semantics.** The `session.metaPolicy` setting governs how a
1029
+ verifier treats `_meta` keys that are *not* KYA-OS's proof key:
1030
+
1031
+ - `strict` (the default) — the verifier processes **only** the KYA-OS proof key
1032
+ (`org.kya-os/response-proof`, or a prior key `org.kya-os/proof` / `proof`). All other keys are **ignored**: never
1033
+ hashed, never trusted, and — critically — **never a cause for rejection**. In
1034
+ particular a `strict` verifier MUST NOT reject a response merely because
1035
+ `_meta` also carries `io.modelcontextprotocol/*`, `traceparent`, `tracestate`,
1036
+ or `baggage`; these reserved/standard keys are explicitly allowlisted and pass
1037
+ through untouched.
1038
+ - `allow-extensions` — identical trust boundary (still only the KYA-OS proof key
1039
+ is hashed or trusted), but the verifier additionally surfaces non-KYA-OS
1040
+ `_meta` keys to the application layer rather than discarding them.
1041
+
1042
+ Under no policy does a verifier include any non-proof `_meta` key in a hash or
1043
+ signature computation. The signature covers the `data`/response body only; it
1044
+ never covers `_meta` (§7.3).
1045
+
1046
+ > **Note.** The proof object's shape is normatively defined by
1047
+ > `schemas/detached-proof.json` (`$schema: draft/2020-12`). The *placement* key
1048
+ > inside `_meta` is `org.kya-os/response-proof`; the schema describes the value
1049
+ > (`{ jws, meta }`), not the key.
1050
+
1051
+ ### 7.7 Delegation Chain Audit Example
1052
+
1053
+ The following shows how audit records link across a 3-level delegation chain: User → Agent A → Agent B → tool call.
1054
+
1055
+ **Step 1 — User delegates to Agent A**
1056
+
1057
+ ```json
1058
+ {
1059
+ "id": "urn:uuid:1111-aaaa",
1060
+ "type": "DelegationCredential",
1061
+ "issuer": "did:web:user.example.com",
1062
+ "credentialSubject": {
1063
+ "id": "did:web:agent-a.example.com",
1064
+ "parentDelegation": null,
1065
+ "scope": "files:read files:write"
1066
+ }
1067
+ }
1068
+ ```
1069
+
1070
+ **Step 2 — Agent A delegates to Agent B**
1071
+
1072
+ ```json
1073
+ {
1074
+ "id": "urn:uuid:2222-bbbb",
1075
+ "type": "DelegationCredential",
1076
+ "issuer": "did:web:agent-a.example.com",
1077
+ "credentialSubject": {
1078
+ "id": "did:web:agent-b.example.com",
1079
+ "parentDelegation": "urn:uuid:1111-aaaa",
1080
+ "scope": "files:read"
1081
+ }
1082
+ }
1083
+ ```
1084
+
1085
+ The `parentDelegation` field links this record back to the User → Agent A credential, forming a verifiable chain.
1086
+
1087
+ **Step 3 — Agent B calls a tool; the server generates an audit record**
1088
+
1089
+ ```json
1090
+ {
1091
+ "content": [{ "type": "text", "text": "file contents..." }],
1092
+ "_meta": {
1093
+ "org.kya-os/response-proof": {
1094
+ "jws": "eyJhbGciOiJFZERTQSJ9...",
1095
+ "meta": {
1096
+ "did": "did:web:tool-server.example.com",
1097
+ "kid": "did:web:tool-server.example.com#key-1",
1098
+ "ts": 1710288120,
1099
+ "nonce": "xyz789",
1100
+ "audience": "did:web:tool-server.example.com",
1101
+ "sessionId": "kyaos_f1e2d3c4-...",
1102
+ "requestHash": "sha256:aabbcc...",
1103
+ "responseHash": "sha256:ddeeff...",
1104
+ "delegationRef": "urn:uuid:2222-bbbb",
1105
+ "clientDid": "did:web:agent-b.example.com"
1106
+ }
1107
+ }
1108
+ }
1109
+ }
1110
+ ```
1111
+
1112
+ The `delegationRef` in the proof points to Agent B's delegation credential (`urn:uuid:2222-bbbb`), which in turn carries `parentDelegation: urn:uuid:1111-aaaa` back to the original user grant. An auditor can reconstruct the full chain — User → Agent A → Agent B → tool call — by following `delegationRef` → `parentDelegation` links. All three records are independently verifiable offline using the signers' DID documents.
1113
+
1114
+ ---
1115
+
1116
+ ## 8. Outbound Delegation Propagation
1117
+
1118
+ When an MCP server calls downstream services (APIs, other MCP servers), it MUST forward delegation context.
1119
+
1120
+ ### 8.1 HTTP Headers
1121
+
1122
+ | Header | Disposition | Value |
1123
+ |--------|-------------|-------|
1124
+ | `KYA-OS-Delegation-Credential` | **AUTHORITATIVE** | The delegation Verifiable Credential (VC-JWT). The verifier derives granted scopes and the delegation chain from it (its embedded scopes, its chain to a trusted root, its StatusList revocation state). |
1125
+ | `KYA-OS-Delegation-Proof` | **AUTHORITATIVE** | The holder-of-key delegation proof JWT (EdDSA-signed, `aud`-bound to the request authority, `sub` == the Layer-1 signature DID); see §8.2. |
1126
+ | `KYA-OS-Agent-DID` | **AUTHORITATIVE** | The agent's DID. A conformant verifier MUST check it equals the Layer-1 signature's resolved DID. |
1127
+ | `KYA-OS-Session-Id` | Anchor | The KYA-OS session ID, used for grant read-back. An anchor, not an authorization input. |
1128
+ | `KYA-OS-Delegation-Chain` | OPTIONAL (advisory) | A transport hint for routing/observability/debugging. **MUST NOT** be used for any authorization decision; ignored when it disagrees with the credential. |
1129
+ | `KYA-OS-Granted-Scopes` | OPTIONAL (advisory) | A transport hint for routing/observability/debugging. **MUST NOT** be used for any authorization decision; ignored when it disagrees with the credential. |
1130
+
1131
+ **The Verifiable Credential is authoritative.** Granted scopes and the delegation
1132
+ chain are derived from cryptographically-signed artifacts — the
1133
+ `KYA-OS-Delegation-Credential` (VC-JWT) and the `KYA-OS-Delegation-Proof` JWT —
1134
+ never from advisory transport headers. A conformant verifier MUST derive
1135
+ authorization from the credential (its embedded scopes, its chain to a trusted
1136
+ root, its revocation state) and MUST check that `KYA-OS-Agent-DID` equals the
1137
+ Layer-1 signature's resolved DID. `KYA-OS-Delegation-Chain` and
1138
+ `KYA-OS-Granted-Scopes` are advisory only: a conformant verifier MUST NOT trust
1139
+ them for any authorization decision and MUST ignore them when they disagree with
1140
+ the credential.
1141
+
1142
+ **Corollary (covered components).** Because authority is derived only from signed
1143
+ artifacts — the VC and the proof JWT, each with independent integrity — the
1144
+ Layer-2 headers are **NOT required** to be RFC 9421 covered components. Signing
1145
+ them is permitted as defense-in-depth but is not a conformance requirement. A
1146
+ tampered `KYA-OS-Granted-Scopes` or `KYA-OS-Delegation-Chain` is therefore not a
1147
+ vulnerability: it is ignored by a conformant verifier.
1148
+
1149
+ ### 8.2 Delegation Proof JWT
1150
+
1151
+ ```typescript
1152
+ interface DelegationProofJWT {
1153
+ // Standard JWT claims
1154
+ iss: string; // Server DID (JWT issuer)
1155
+ sub: string; // Responsible Party DID (the root issuer of the delegation chain)
1156
+ aud: string; // Target service hostname
1157
+ iat: number; // Issued at (Unix epoch)
1158
+ exp: number; // Expires at (iat + 60 seconds)
1159
+ jti: string; // Unique JWT ID (UUID)
1160
+
1161
+ // KYA-OS claims
1162
+ delegation_id: string; // Current delegation ID
1163
+ delegation_chain: string; // Chain path (vcId>delegationId)
1164
+ scope: string; // Comma-separated scopes
1165
+ }
1166
+ ```
1167
+
1168
+ The JWT is signed with EdDSA using the server's secret key.
1169
+
1170
+ ### 8.3 Chain String Format
1171
+
1172
+ ```
1173
+ <vcId>><delegationId>
1174
+ ```
1175
+
1176
+ Example: `urn:uuid:d7f8a9b0-1234-5678-9abc-def012345678>del-001`
1177
+
1178
+ ### 8.4 Gateway Routing Without Body Inspection (MCP 2026-07-28)
1179
+
1180
+ A policy enforcement point (PEP) or gateway in front of a KYA-OS server can make
1181
+ routing and coarse policy decisions from headers alone, without parsing the JSON
1182
+ body. Under MCP 2026-07-28 (SEP-2243), the request carries the routing headers
1183
+ `Mcp-Method` (the JSON-RPC method, e.g. `tools/call`) and `Mcp-Name` (the target,
1184
+ e.g. the tool name). A KYA-OS-aware gateway MAY:
1185
+
1186
+ - route on `Mcp-Method` / `Mcp-Name`; and
1187
+ - authenticate the caller and check coarse authority from the KYA-OS delegation
1188
+ headers (§8.1) and the detached `KYA-OS-Delegation-Proof` (§8.2),
1189
+
1190
+ all before the body is read. This is an optimization only: the origin server MUST
1191
+ still perform full delegation-chain and detached-proof verification (§6, §7) on
1192
+ the request body. Routing headers are advisory and MUST NOT be trusted as
1193
+ authority.
1194
+
1195
+ ---
1196
+
1197
+ ## 9. Authorization Flow
1198
+
1199
+ ### 9.1 verifyOrHints Pattern
1200
+
1201
+ When a tool call requires authorization:
1202
+
1203
+ 1. Server checks for valid delegation in the request
1204
+ 2. If delegation is valid and covers required scopes: proceed
1205
+ 3. If delegation is missing or insufficient: return `needs_authorization` error
1206
+
1207
+ ### 9.2 needs_authorization Error Structure
1208
+
1209
+ ```typescript
1210
+ interface NeedsAuthorizationError {
1211
+ error: 'needs_authorization';
1212
+ message: string;
1213
+ authorizationUrl: string; // URL where user can grant authorization
1214
+ resumeToken: string; // Token to resume flow after authorization
1215
+ expiresAt: number; // Unix epoch when resumeToken expires
1216
+ scopes: string[]; // Scopes being requested
1217
+ display?: {
1218
+ title?: string;
1219
+ hint?: Array<'link' | 'qr' | 'code'>;
1220
+ authorizationCode?: string;
1221
+ qrUrl?: string;
1222
+ };
1223
+ }
1224
+ ```
1225
+
1226
+ The `needs_authorization` response is itself **signed**: it carries a detached-JWS
1227
+ proof in `_meta` (`outcome: 'needs_authorization'`) whose `responseHash` binds the
1228
+ challenge content, including `authorizationUrl` (see §7.4). A client detects a
1229
+ substituted consent URL by verifying that proof against the resource's DID and
1230
+ recomputing `responseHash` over the challenge it actually received
1231
+ (`ProofVerifier.verifyProof(proof, jwk, { request, response })`) — see §11.1.
1232
+
1233
+ ### 9.3 Resume Flow
1234
+
1235
+ 1. Client receives `needs_authorization` error
1236
+ 2. Client verifies the challenge proof against the resource's DID — recomputing
1237
+ `responseHash` over the received content — before trusting `authorizationUrl`
1238
+ (detects a substituted consent URL; §11.1)
1239
+ 3. Client directs user to `authorizationUrl`
1240
+ 4. User authenticates and grants authorization
1241
+ 5. Authorization service issues DelegationCredential
1242
+ 6. Client retries request with `resumeToken` and new delegation
1243
+ 7. Server validates delegation and processes request
1244
+
1245
+ ### 9.4 OAuth/OIDC Hardening Alignment (MCP 2026-07-28)
1246
+
1247
+ Where the authorization service in §9.3 is an OAuth 2.0 / OIDC authorization
1248
+ server, the KYA-OS profile aligns with the MCP 2026-07-28 OAuth hardening SEPs.
1249
+ These are *additive* hardening requirements; they do not change the KYA-OS trust
1250
+ model (§15.1), in which authority rides on a verified delegation chain, not on a
1251
+ bearer token.
1252
+
1253
+ 1. **Issuer validation (RFC 9207 / SEP-2468).** On the authorization callback /
1254
+ resume step, the client MUST validate the `iss` authorization-response
1255
+ parameter against the expected issuer and MUST reject a response whose `iss`
1256
+ does not exactly match. This is verified *in addition to* recomputing
1257
+ `responseHash` over the challenge (§9.3 step 2).
1258
+ 2. **Credential↔issuer binding (SEP-2352).** Any token or credential obtained in
1259
+ step 5 MUST be bound to, and accepted only for, the issuer that minted it; a
1260
+ credential MUST NOT be replayed against a different issuer or resource.
1261
+ 3. **Dynamic Client Registration `application_type` (SEP-837).** Clients
1262
+ performing DCR MUST declare `application_type` and MUST NOT use a redirect URI
1263
+ inconsistent with it.
1264
+ 4. **Refresh & step-up (SEP-2207 / SEP-2350).** Refresh tokens follow SEP-2207;
1265
+ a `step_up_required` outcome (§7.2) accumulates scope per SEP-2350 rather than
1266
+ discarding previously granted scope.
1267
+ 5. **Metadata discovery (SEP-2351).** `.well-known` authorization-server and
1268
+ protected-resource metadata use the SEP-2351 path-suffix form.
1269
+
1270
+ A KYA-OS server MUST NOT treat successful completion of this OAuth flow as
1271
+ sufficient on its own: the resulting DelegationCredential is still verified per
1272
+ §6 and §7 on the retried request (§9.3 step 7).
1273
+
1274
+ ---
1275
+
1276
+ ## 10. Well-Known Endpoint
1277
+
1278
+ KYA-OS servers SHOULD expose `/.well-known/mcp`:
1279
+
1280
+ ```json
1281
+ {
1282
+ "did": "did:web:mcp-server.example.com",
1283
+ "version": "1.0.0",
1284
+ "capabilities": {
1285
+ "delegation": true,
1286
+ "proof": true,
1287
+ "revocation": true
1288
+ },
1289
+ "supported_did_methods": ["did:key", "did:web", "did:cheqd"],
1290
+ "proof_algorithms": ["EdDSA"],
1291
+ "clockSkewSeconds": 120,
1292
+ "endpoints": {
1293
+ "handshake": "/_kya-os/handshake",
1294
+ "status_list": "/.well-known/status/1"
1295
+ }
1296
+ }
1297
+ ```
1298
+
1299
+ Servers SHOULD list `did:cheqd` only when cheqd resolution has been explicitly
1300
+ configured for that deployment.
1301
+
1302
+ ### 10.1 Clock Skew Negotiation
1303
+
1304
+ Servers MAY advertise a `clockSkewSeconds` field (type: number, default: 120, minimum: 30, maximum: 600) indicating the tolerance window for timestamp validation. Clients SHOULD read this value on first contact and use it for their own clock-skew tolerance when validating server timestamps, falling back to the hardcoded default (120 seconds) if the field is absent or out of range.
1305
+
1306
+ ---
1307
+
1308
+ ## 11. Security Considerations
1309
+
1310
+ ### 11.0 Trust Model
1311
+
1312
+ KYA-OS distributes verification work across three trust boundaries. Operators
1313
+ deploying the protocol need to know which component is trusted with what.
1314
+
1315
+ **1. The agent process itself.** Every agent holds its own Ed25519 secret key
1316
+ and is trusted to use it only on operations the agent has been authorized to
1317
+ perform. Compromise here yields full compromise of every chain the agent can
1318
+ sign under. Mitigations are platform-level: software-only identity (default),
1319
+ proxy-managed identity (key held by a trusted local process), or
1320
+ hardware-attested identity (TEE-bound — SEV-SNP, TDX, Nitro Enclaves). The
1321
+ spec is signature-algorithm-agnostic; operators choose key custody to fit
1322
+ their threat model.
1323
+
1324
+ **2. The verifier.** At Conformance Level 1 (edge-proxy verification), the
1325
+ verifier sits in front of legacy MCP servers that don't natively understand
1326
+ KYA-OS. In this deployment the verifier is part of the trusted computing
1327
+ base for any resource it gates — a compromised L1 verifier can forge accepts
1328
+ on requests the resource owner would have rejected. **Operators relying on
1329
+ L1 SHOULD treat the verifier as a TCB component**, deploy it with the same
1330
+ isolation as other TCB infrastructure, and minimize its blast radius via
1331
+ per-service or per-tenant verifier instances rather than a single global
1332
+ edge gate.
1333
+
1334
+ At Conformance Level 2+, verification moves into the resource boundary
1335
+ (native server-side verification). This shrinks the TCB: the edge verifier
1336
+ becomes defense-in-depth rather than the sole gate. **Services protecting
1337
+ sensitive resources SHOULD adopt L2+ to remove the edge verifier from their
1338
+ TCB.**
1339
+
1340
+ **3. The service / resource owner.** The service decides what scopes it
1341
+ accepts, who its trust registry trusts, and which `did:web` documents it
1342
+ will fetch. Trust-registry decisions are advisory: an adversarial agent
1343
+ can route around registry rejection via credential sharing or request
1344
+ proxying. The cryptographic delegation check is the load-bearing gate; the
1345
+ registry is a signal that raises the friction of adversarial behavior.
1346
+
1347
+ **Mutual authentication.** Services SHOULD be addressable by a DID
1348
+ (typically `did:web`), and agents performing verification SHOULD authenticate
1349
+ the service identity before sending sensitive payloads. This prevents an
1350
+ agent's delegation from being harvested by a service masquerading as the
1351
+ intended audience.
1352
+
1353
+ **L2+ is not OAuth-style client registration.** Direct server-side verification
1354
+ (Conformance Level 2+) authenticates the agent's identity, but the access decision
1355
+ still rests on the **delegation chain the agent presents** — the delegated
1356
+ authority rooted at a Responsible Party — not on the agent's identity alone. An
1357
+ authenticated agent holding no valid delegation for the requested resource is
1358
+ denied. This inverts the OAuth Dynamic Client Registration pattern, where a
1359
+ registered client gains access by virtue of being a recognized client: here, being
1360
+ a recognized agent grants nothing without a credential that designates the specific
1361
+ resource (§6.4.1).
1362
+
1363
+ ### 11.1 Threat Model Summary
1364
+
1365
+ The table below names each protocol-level threat, the protocol's mitigation,
1366
+ and the residual risk an operator carries.
1367
+
1368
+ | Threat | Mitigation | Residual risk |
1369
+ |---|---|---|
1370
+ | **Impersonation** — a process claims to be an agent it is not. | Cryptographic identity (Ed25519 + DID). Every tool response carries a detached JWS proof; verifiers check the agent's DID document. | Compromise of an agent's secret key, or compromise of an L1 verifier (see §11.0). |
1371
+ | **Replay** — capturing a valid signed request and re-sending it. | Per-handshake nonces with bounded lifetime, dedupe cache on `(nonce, agentDid)`, ±120s clock skew. | Race within the dedupe cache window. Distributed deployments need atomic check-and-set on the cache. |
1372
+ | **Scope escalation** — a delegated agent invokes outside the delegator's intent. | Scope subset enforcement at every chain link; CRISP `matcher: 'exact'` for sensitive resources. See §11.4. | Implementation bugs in scope-subset checks. |
1373
+ | **Confused deputy** — a multi-resource delegation is repurposed for resources the delegator did not anticipate. | Designation invariant (§6.4.1) + audience-on-redelegation default `true` (§11.6). | Implementation bugs in the designation check. |
1374
+ | **Credential theft** — a `DelegationCredential` is intercepted and used by an attacker. | `audience` constraint; short `notAfter`; session binding at high-security tiers (§11.8); StatusList2021 revocation. | Window between theft and revocation. |
1375
+ | **Agent abuse** — a legitimate agent exceeds expected behavior. | Audit log of every signed tool call (§7); reputation tracking on both agent and Responsible Party (§11.12); revocation. | Detection latency; damage done before revocation propagates. |
1376
+ | **Key compromise** — an agent's secret key is exfiltrated. | Short-lived delegations; explicit revocation; key rotation via DID document update. | Window between compromise and detection. See §11.5. |
1377
+ | **Revocation race** — an invocation arrives while a revocation is propagating. | Status-list TTL bounded ≤ 60s for high-privilege scopes; side-channel revocation signals honored immediately; expiry as primary revocation mechanism. See §6.5.2. | Unavoidable Lamport-concurrent race. Operators bound the window; they cannot eliminate it. |
1378
+ | **Downgrade** — a client strips KYA-OS headers entirely. | Fail-closed: servers requiring identity MUST reject sessionless calls; `/.well-known/mcp` advertises requirements. See §11.7. | A non-compliant client that the operator has not configured to require identity. |
1379
+ | **Consent-URL substitution** — a malicious in-path intermediary swaps the `authorizationUrl` in a `needs_authorization` challenge to phish the user toward an attacker-controlled consent page. | The challenge is signed (`outcome: 'needs_authorization'`); its `responseHash` binds the challenge content incl. the URL (§7.4, §9.2). A client recomputes `responseHash` over the received challenge and verifies the proof against the resource DID, detecting the swap. | A client that does not verify the challenge proof / recompute the hash — the check is the verifier's responsibility (a §11.7-style downgrade). TLS protects the URL in transit; this adds defense against a malicious in-path component. |
1380
+ | **Denial of service** | Per-session and per-handshake rate limiting; session caps; external session storage with TTL eviction. See §11.9. | Standard transport-layer DoS exposure (DDoS) is out of scope — operators rely on conventional network defenses. |
1381
+
1382
+ ### 11.2 Nonce Replay Attacks
1383
+
1384
+ - Nonces MUST be cryptographically random (16 bytes minimum entropy)
1385
+ - Nonce cache MUST persist across server restarts (use external storage)
1386
+ - Nonce cache TTL MUST exceed session TTL
1387
+ - Distributed deployments MUST use atomic check-and-set operations
1388
+
1389
+ ### 11.3 Timestamp Skew Attacks
1390
+
1391
+ - Default skew tolerance is 120 seconds
1392
+ - Servers MAY reduce this for high-security deployments
1393
+ - Servers SHOULD use NTP for time synchronization
1394
+ - Timestamps in proofs allow detection of delayed replay attempts
1395
+
1396
+ ### 11.4 Delegation Scope Escalation
1397
+
1398
+ - Child delegations MUST NOT exceed parent scope
1399
+ - Servers MUST validate entire delegation chain, not just leaf
1400
+ - Scope comparison MUST be performed at each chain link
1401
+ - Use CRISP `matcher: 'exact'` for sensitive resources
1402
+
1403
+ ### 11.5 Key Rotation
1404
+
1405
+ - did:key: Generate new DID (no rotation mechanism)
1406
+ - did:web: Update `did.json` with new verification method
1407
+ - Old keys SHOULD remain in `did.json` for proof verification
1408
+ - Delegation credentials reference specific key IDs; reissue after rotation
1409
+ - Key custody options include software-only (default), proxy-managed, and hardware-attested (TEE). See §11.0.
1410
+
1411
+ ### 11.6 Confused Deputy Attacks
1412
+
1413
+ A confused deputy attack occurs when Agent B receives a valid delegation from Agent A, then uses it to invoke tools that Agent A did not intend. Because delegation scopes may be coarse-grained (e.g., `tool:*`), the delegated agent may access resources beyond the delegator's intent.
1414
+
1415
+ - Servers SHOULD enforce the principle of least privilege when issuing delegations
1416
+ - Delegation scopes SHOULD be as narrow as possible (prefer `tool:read_file` over `tool:*`)
1417
+ - CRISP `matcher: 'exact'` SHOULD be used for sensitive resources
1418
+ - Servers MAY implement per-delegation call logging to detect misuse
1419
+
1420
+ Two normative invariants close the confused-deputy class on the invocation path:
1421
+
1422
+ 1. **Designation invariant (§6.4.1).** Invocations MUST designate the specific
1423
+ resource being exercised; verifiers MUST reject invocations that rely on a
1424
+ multi-resource delegation without designation. The reference implementation
1425
+ enforces this by requiring each tool's registered `scopeId` to be present
1426
+ in the delegation's authorized scopes before the handler runs.
1427
+ 2. **Audience-on-redelegation default (§this section).** Non-root credentials
1428
+ in a delegation chain MUST carry an `audience` constraint binding them to a
1429
+ specific verifying server. This default is `true` as of v1.3.x; opt-out is
1430
+ available with a one-time per-process warning for legacy integrations.
1431
+
1432
+ ### 11.7 Downgrade Attacks
1433
+
1434
+ An adversary or non-compliant client may omit KYA-OS headers entirely, bypassing identity and delegation checks. Because KYA-OS is an extension to MCP, a server cannot distinguish between a client that does not support KYA-OS and one that is deliberately stripping identity headers.
1435
+
1436
+ - Servers that require identity MUST reject tool calls without a valid session (fail-closed)
1437
+ - Servers SHOULD NOT fall back to unauthenticated mode for tools that require delegation
1438
+ - The `/.well-known/mcp` endpoint allows clients to discover server requirements before connecting
1439
+
1440
+ ### 11.8 Delegation Credential Theft
1441
+
1442
+ If a DelegationCredential is intercepted (e.g., via a compromised transport or log exposure), an attacker may present it to other servers. The `audience` constraint in the credential limits where it can be used, but does not bind it to a specific session or transport channel.
1443
+
1444
+ - Delegations SHOULD include an `audience` constraint limiting them to the intended server DID
1445
+ - Delegations SHOULD use short `notAfter` expiration windows
1446
+ - Servers SHOULD bind delegation context to the session that presented it
1447
+ - High-security deployments SHOULD use session-scoped delegations (include `sessionId` in constraints)
1448
+ - Revocation via StatusList2021 provides a mitigation path for stolen credentials
1449
+
1450
+ ### 11.9 Session Exhaustion (Denial of Service)
1451
+
1452
+ An attacker may send a large volume of valid handshake requests to exhaust server memory.
1453
+
1454
+ - Implementations MUST enforce a maximum number of concurrent sessions
1455
+ - Implementations SHOULD evict the oldest sessions when the limit is reached
1456
+ - Rate limiting on the handshake endpoint is RECOMMENDED
1457
+ - Distributed deployments SHOULD use external session storage with built-in TTL eviction
1458
+
1459
+ ### 11.10 Revocation Freshness
1460
+
1461
+ The Lamport-concurrent revocation race is documented in §6.5.2. The
1462
+ deployment-level controls are:
1463
+
1464
+ - StatusList2021 credentials have cache-control considerations
1465
+ - Servers SHOULD set appropriate `Cache-Control` headers
1466
+ - High-security deployments MAY require real-time revocation checks
1467
+ - Cascading revocation MUST be atomic
1468
+ - Status-list cache TTL SHOULD be ≤ 60 seconds for high-privilege scopes
1469
+
1470
+ ### 11.11 Key Custody and Escrow Exclusion
1471
+
1472
+ KYA-OS excludes centralized agent-key generation as a failure mode. If a single
1473
+ service generated agent keypairs — even transiently — that service would hold
1474
+ the secret material for every identity it provisioned, recreating the escrow
1475
+ weakness of identity-based-encryption-style key-generation centers: one
1476
+ compromise yields every dependent secret key. Agent-controlled key generation
1477
+ (§4.5) removes that single point of total compromise. A registry or verifier
1478
+ that is breached leaks public material and metadata, never agent secret keys,
1479
+ because no protocol component ever holds them. Operators MUST NOT introduce a
1480
+ server-side key-generation or escrow step as a convenience; doing so is
1481
+ non-conformant (§4.5).
1482
+
1483
+ ### 11.12 Reputation Scope
1484
+
1485
+ Reputation is tracked primarily on the Responsible Party — the root issuer
1486
+ accountable for a delegation chain (§2). Agent-level reputation, where
1487
+ surfaced, is a secondary signal scoped to a specific binding rather than a
1488
+ free-floating per-agent score. A newly minted agent inheriting authority from a
1489
+ high-reputation Responsible Party MUST be treated as carrying that party's
1490
+ standing for capability decisions, modulo binding-specific risk signals. This
1491
+ closes reputation laundering in both directions: a bad actor cannot shed a poor
1492
+ Responsible-Party history by spawning fresh agents, and a new agent under a
1493
+ trusted party is not penalized for lacking its own history.
1494
+
1495
+ ---
1496
+
1497
+ ## 12. Privacy Considerations
1498
+
1499
+ ### 12.1 DID Correlation
1500
+
1501
+ A persistent `did:key` or `did:web` identifier acts as a pseudonym. Using the same DID across multiple MCP servers enables cross-server activity correlation. Implementations SHOULD consider per-server DID rotation for privacy-sensitive deployments.
1502
+
1503
+ ### 12.2 Session Linkability
1504
+
1505
+ Session IDs (`kyaos_*`) appear in detached proofs. Within a session, all tool calls are linkable. Implementations SHOULD use short session TTLs and avoid logging session IDs alongside PII.
1506
+
1507
+ ### 12.3 Delegation Chain Disclosure
1508
+
1509
+ Outbound `KYA-OS-Agent-DID` and `KYA-OS-Delegation-Chain` headers reveal the agent's identity and delegation provenance to downstream services. Implementations SHOULD only propagate delegation headers to trusted downstream services.
1510
+
1511
+ ### 12.4 Proof Retention and Right to Erasure
1512
+
1513
+ Detached proofs are audit records containing DIDs and session identifiers. Operators retaining proof logs SHOULD consider applicable data protection regulations (GDPR Art. 17, CCPA) and implement appropriate retention policies.
1514
+
1515
+ ### 12.5 Per-Delegation Keys (Delegate Unlinkability)
1516
+
1517
+ A delegate that does not wish to link its activity across delegations MAY present a fresh, single-purpose public key for each delegation it receives, rather than its long-term DID. The delegator issues the `DelegationCredential` to that one-off key, and the delegate proves control of it for that delegation only. This keeps the delegate's primary identity unlinkable across delegations and prevents a delegator — or an observer who sees multiple delegations — from correlating them through a shared subject DID.
1518
+
1519
+ _Non-normative example:_ an organization delegating read access to an external auditing agent issues each engagement's delegation to a per-engagement key the auditor generates, so separate engagements cannot be cross-correlated via a common subject identifier.
1520
+
1521
+ ---
1522
+
1523
+ ## 13. Protocol Versioning
1524
+
1525
+ The current protocol version is `1.0.0`.
1526
+
1527
+ Handshake requests SHOULD include `clientProtocolVersion: "1.0.0"`.
1528
+ Handshake responses MUST include `protocolVersion: "1.0.0"`.
1529
+
1530
+ Servers MUST reject clients with an incompatible major version (e.g., a `2.x` server MUST reject a `1.x` client).
1531
+ Minor version differences SHOULD be handled gracefully — servers SHOULD implement backward compatibility within a major version.
1532
+
1533
+ ---
1534
+
1535
+ ## 14. Transport Binding
1536
+
1537
+ KYA-OS is transport-agnostic. The handshake and proofs use standard MCP mechanisms:
1538
+
1539
+ **Handshake**: Implemented as an MCP tool named `_kyaos_handshake`. This is compatible with all MCP transports (stdio, SSE, HTTP Streamable) without modification.
1540
+
1541
+ **Proof attachment**: Detached proofs are attached to tool responses in the standard MCP `_meta` field, which is transported transparently by all MCP transport implementations.
1542
+
1543
+ **Outbound delegation headers**: When an MCP server makes outbound HTTP calls (not MCP calls), delegation context is propagated via HTTP headers as defined in §8. For MCP-to-MCP calls, delegation context SHOULD be passed via the `_kyaos_handshake` flow.
1544
+
1545
+ **Body-free routing**: On HTTP transports, intermediaries MAY route on the MCP
1546
+ 2026-07-28 routing headers `Mcp-Method` and `Mcp-Name` (SEP-2243) and verify the
1547
+ KYA-OS delegation headers (§8.1) and detached proof (§8.2) without inspecting the
1548
+ request body. See §8.4. The origin server remains responsible for full
1549
+ verification.
1550
+
1551
+ ---
1552
+
1553
+ ## 15. Conformance
1554
+
1555
+ Implementation conformance requirements are defined in `CONFORMANCE.md`. Three compliance levels are specified:
1556
+
1557
+ - **Level 1 — Core Crypto**: Basic key generation, signing, verification, and well-known endpoint
1558
+ - **Level 2 — Full Session**: Session handshake, nonce replay prevention, proof generation
1559
+ - **Level 3 — Full Delegation**: VC issuance/verification, delegation graphs, revocation, outbound propagation
1560
+
1561
+ See `CONFORMANCE.md` for detailed requirements and test references.
1562
+
1563
+ ### 15.1 Relationship to OAuth Dynamic Client Registration
1564
+
1565
+ KYA-OS L2+ is not OAuth Dynamic Client Registration restated. OAuth-DCR binds a
1566
+ client by a `client_id`/`client_secret` registered with an authorization server,
1567
+ after which authority rides on a bearer token. KYA-OS binds an agent by its DID
1568
+ and requires the delegation chain to be presented and cryptographically verified
1569
+ at every invocation (§6, §7). The delegation chain remains required and
1570
+ verifiable at Level 2 and Level 3 regardless of whether or how the agent's
1571
+ identity is recorded in a trust registry (§6.8). Registry presence is discovery
1572
+ and a trust signal (§11.0), never a substitute for chain verification.
1573
+
1574
+ This is complementary to, not in tension with, MCP 2026-07-28's OAuth hardening.
1575
+ Where KYA-OS interoperates with an OAuth/OIDC authorization server (§9.4), it
1576
+ adopts that server-side hardening — `iss` validation per RFC 9207 (SEP-2468),
1577
+ credential↔issuer binding (SEP-2352), DCR `application_type` (SEP-837) — as the
1578
+ *token-acquisition* layer, while the delegation chain (§6, §7) remains the
1579
+ *authority* layer verified at every invocation. OAuth hardening strengthens how a
1580
+ credential is obtained; it never becomes the basis on which a KYA-OS request is
1581
+ authorized.
1582
+
1583
+ ### 15.2 MCP 2026-07-28 Compatibility
1584
+
1585
+ KYA-OS is designed to layer cleanly on the MCP 2026-07-28 Release Candidate.
1586
+
1587
+ - **Stateless core.** MCP removed `initialize`/`initialized` (SEP-2575) and the
1588
+ `Mcp-Session-Id` header (SEP-2567). KYA-OS does not depend on either: every
1589
+ KYA-OS request is authorized from its self-contained detached-JWS proof (§7)
1590
+ and DID-anchored grant (§6). The KYA-OS session (§5) is an optional KYA-OS-owned
1591
+ convenience layer delivered via the `_kyaos_handshake` tool (§14) and is
1592
+ independent of MCP's removed session.
1593
+ - **`_meta` reserved-namespace coexistence.** KYA-OS publishes its response proof
1594
+ under the reverse-DNS key `org.kya-os/response-proof` and ignores all other `_meta` keys, including
1595
+ the MCP-reserved `io.modelcontextprotocol/*` and W3C Trace Context keys
1596
+ `traceparent`/`tracestate`/`baggage` (SEP-414). See §7.6.
1597
+ - **Routing headers.** KYA-OS gateways MAY use `Mcp-Method`/`Mcp-Name` (SEP-2243)
1598
+ for body-free routing (§8.4).
1599
+ - **JSON Schema 2020-12.** Tool `inputSchema`/`outputSchema` and KYA-OS schemas use
1600
+ JSON Schema 2020-12 (SEP-2106); see §6.2.
1601
+ - **Extensions Track intent (SEP-2133).** KYA-OS intends to register as an MCP
1602
+ Extension under the proposed reverse-DNS extension id `org.kya-os/decentralized-authority`,
1603
+ with an `ext-*` repository, independent versioning, and negotiation via the
1604
+ `extensions` capability map. Once registered, the canonical proof key (§7.6
1605
+ `proofMetaKey`) and capability advertisement (§10) will use that extension id;
1606
+ the key is configurable for this reason. KYA-OS targets the SEP-2133
1607
+ Standards-Track path.
1608
+ - **Deprecations are zero-impact.** SEP-2577 deprecates MCP **Roots**, **Sampling**,
1609
+ and **Logging** on 12-month windows. KYA-OS uses **none** of these features, so
1610
+ the deprecations have **no impact** on KYA-OS implementations.
1611
+
1612
+ ### 15.3 Auditability Protocol
1613
+
1614
+ An implementation that advertises a KYA-OS Audit Assurance Profile (AAP) MUST
1615
+ use the versioned audit schemas under `schemas/audit-*.schema.json` and MUST NOT
1616
+ infer a stronger claim from detached proofs alone.
1617
+
1618
+ The audit protocol separates three authorities:
1619
+
1620
+ 1. The producer emits a strict, privacy-minimal event with a stable event ID,
1621
+ source ID, source sequence, and optional source-predecessor digest.
1622
+ 2. Exactly one authoritative recorder for a `(ledgerId, ledgerEpochId)` assigns
1623
+ recorder time, a monotonically increasing decimal sequence, the previous
1624
+ entry digest, and a signed append receipt through an atomic compare-and-append
1625
+ operation.
1626
+ 3. An independent observer MAY retain and compare signed RFC 9162 checkpoints.
1627
+ WORM, RFC 3161, and SCITT receipts are supporting anchors and MUST NOT be
1628
+ represented as independent split-view detection.
1629
+
1630
+ The producer event, entry, receipt, checkpoint, observation, and bundle manifest
1631
+ MUST be strictly schema-valid before RFC 8785 canonicalization. Integrity hashes
1632
+ MUST use the domain-separated construction
1633
+ `SHA-256(UTF8(domain) || 0x00 || JCS(value))`. Entries are immutable: correction,
1634
+ redaction, disposal, retention, legal-hold, key transition, and epoch transition
1635
+ are represented by new events.
1636
+
1637
+ Redelivery of the same authenticated producer/source/event identity and the same
1638
+ frozen producer bytes MUST return the original stored receipt. Reuse with
1639
+ different bytes MUST fail. This idempotency namespace spans retained epochs of
1640
+ the logical ledger. A new epoch starts at sequence zero only through a signed
1641
+ genesis that commits the prior epoch and its terminal checkpoint.
1642
+
1643
+ Replay-bundle verification MUST receive recorder/observer/exporter trust and
1644
+ purpose/scope authorization out of band. Bundle contents cannot authorize their
1645
+ own signer or bootstrap their own trust root. A verifier MUST distinguish
1646
+ `valid`, `invalid`, and `indeterminate`, MUST keep authorization-as-observed
1647
+ separate from current authorization/revocation, and MUST NOT consume a live
1648
+ nonce cache when verifying historical proof artifacts.
1649
+
1650
+ Sensitive payloads SHOULD be encrypted in a separate evidence provider and
1651
+ referenced by opaque randomized identifiers and ciphertext commitments. The
1652
+ integrity ledger MUST NOT contain raw tool arguments, responses, access tokens,
1653
+ or free-form exception text by default. Evidence disposal MUST NOT mutate the
1654
+ retained integrity history.
1655
+
1656
+ The AAP levels are cumulative:
1657
+
1658
+ - **AAP-0:** no audit assurance.
1659
+ - **AAP-1 Recorded:** structured capture of delivered instrumented events.
1660
+ - **AAP-2 Chained:** durable atomic accepted history with non-best-effort
1661
+ delivery.
1662
+ - **AAP-3 Transparent:** AAP-2 plus durable source high-water reconciliation and
1663
+ signed RFC 9162 checkpoints/proofs.
1664
+ - **AAP-4 Observed:** AAP-3 plus independently administered checkpoint
1665
+ observation and authenticated view comparison, with supporting external
1666
+ receipt evidence.
1667
+
1668
+ Implementations MUST advertise capabilities truthfully and MUST reject startup
1669
+ when configured mechanics cannot satisfy the selected profile. Full event
1670
+ semantics, provider contracts, failure behavior, privacy guidance, replay-bundle
1671
+ rules, and operational requirements are specified in [`AUDITABILITY.md`](./AUDITABILITY.md).
1672
+
1673
+ ---
1674
+
1675
+ ## 16. References
1676
+
1677
+ ### Normative References
1678
+
1679
+ - **[RFC 2119]** IETF. *Key words for use in RFCs to Indicate Requirement Levels*. https://datatracker.ietf.org/doc/html/rfc2119
1680
+ - **[DID-CORE]** W3C. *Decentralized Identifiers (DIDs) v1.0*. https://www.w3.org/TR/did-core/
1681
+ - **[VC-DATA-MODEL]** W3C. *Verifiable Credentials Data Model v1.1*. https://www.w3.org/TR/vc-data-model/
1682
+ - **[DID-KEY]** W3C CCG. *did:key Method Specification*. https://w3c-ccg.github.io/did-method-key/
1683
+ - **[DID-WEB]** W3C CCG. *did:web Method Specification*. https://w3c-ccg.github.io/did-method-web/
1684
+ - **[STATUS-LIST-2021]** W3C CCG. *Status List 2021*. https://w3c-ccg.github.io/vc-status-list-2021/
1685
+ - **[RFC8037]** IETF. *CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JOSE*. https://datatracker.ietf.org/doc/html/rfc8037
1686
+ - **[RFC7517]** IETF. *JSON Web Key (JWK)*. https://datatracker.ietf.org/doc/html/rfc7517
1687
+ - **[RFC7515]** IETF. *JSON Web Signature (JWS)*. https://datatracker.ietf.org/doc/html/rfc7515
1688
+ - **[RFC8785]** IETF. *JSON Canonicalization Scheme (JCS)*. https://datatracker.ietf.org/doc/html/rfc8785
1689
+
1690
+ ### Informative References
1691
+
1692
+ - **[MCP]** Anthropic. *Model Context Protocol Specification*. https://spec.modelcontextprotocol.io/
1693
+ - **[MULTICODEC]** Multiformats. *Multicodec Table*. https://github.com/multiformats/multicodec
1694
+ - **[MULTIBASE]** Multiformats. *Multibase Specification*. https://github.com/multiformats/multibase
1695
+
1696
+ ---
1697
+
1698
+ ## Appendix A: Error Codes
1699
+
1700
+ | Code | Description |
1701
+ |------|-------------|
1702
+ | `handshake_failed` | Handshake validation failed (timestamp, nonce, or audience) |
1703
+ | `session_expired` | Session not found or expired |
1704
+ | `delegation_invalid` | Delegation verification failed |
1705
+ | `insufficient_scope` | Requested operation outside delegated scope |
1706
+ | `delegation_revoked` | Delegation has been revoked |
1707
+ | `invalid_proof` | Proof verification failed |
1708
+ | `did_not_found` | DID resolution failed |
1709
+
1710
+ ---
1711
+
1712
+ ## Appendix B: Base58btc Alphabet
1713
+
1714
+ ```
1715
+ 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
1716
+ ```
1717
+
1718
+ Note: Excludes `0`, `O`, `I`, `l` to avoid visual ambiguity.
1719
+
1720
+ ---
1721
+
1722
+ ## Appendix C: Test Vectors
1723
+
1724
+ These test vectors enable interoperability testing across implementations.
1725
+
1726
+ ### C.1 SHA-256 Canonical Hash
1727
+
1728
+ **Input JSON:**
1729
+ ```json
1730
+ {"method":"tools/call","params":{"name":"echo","arguments":{}}}
1731
+ ```
1732
+
1733
+ **JCS Canonicalized (RFC 8785):**
1734
+ ```json
1735
+ {"method":"tools/call","params":{"arguments":{},"name":"echo"}}
1736
+ ```
1737
+
1738
+ **SHA-256 (hex):**
1739
+ ```
1740
+ 5057521f310b536837b619f0ac040ef8064f8c597da8ec22a56801b435744033
1741
+ ```
1742
+
1743
+ **KYA-OS Format:**
1744
+ ```
1745
+ sha256:5057521f310b536837b619f0ac040ef8064f8c597da8ec22a56801b435744033
1746
+ ```
1747
+
1748
+ ### C.2 did:key Derivation
1749
+
1750
+ **Ed25519 Public Key (32 bytes, hex):**
1751
+ ```
1752
+ 8076ee2cfc1acdd3f8f4e38c665a0a3e6ad6e06dc05b4f6ec9c5b1ae7c81c9a2
1753
+ ```
1754
+
1755
+ **Steps:**
1756
+ 1. Prepend multicodec prefix `0xed01` (Ed25519 public key)
1757
+ 2. Encode with base58btc
1758
+ 3. Prepend multibase prefix `z`
1759
+
1760
+ **Multicodec + Key (hex):**
1761
+ ```
1762
+ ed018076ee2cfc1acdd3f8f4e38c665a0a3e6ad6e06dc05b4f6ec9c5b1ae7c81c9a2
1763
+ ```
1764
+
1765
+ **Base58btc Encoded:**
1766
+ ```
1767
+ 6Mko6jQvza2BSKRcrbJwgwbL9KYDn1isCUV5Lnq7gSTTKJq
1768
+ ```
1769
+
1770
+ **did:key:**
1771
+ ```
1772
+ did:key:z6Mko6jQvza2BSKRcrbJwgwbL9KYDn1isCUV5Lnq7gSTTKJq
1773
+ ```
1774
+
1775
+ **Verification Method ID:**
1776
+ ```
1777
+ did:key:z6Mko6jQvza2BSKRcrbJwgwbL9KYDn1isCUV5Lnq7gSTTKJq#keys-1
1778
+ ```
1779
+
1780
+ ### C.3 JWS Structure
1781
+
1782
+ A valid detached proof JWS has the following structure:
1783
+
1784
+ **Protected Header (decoded):**
1785
+ ```json
1786
+ {
1787
+ "alg": "EdDSA",
1788
+ "kid": "did:key:z6Mk...#keys-1"
1789
+ }
1790
+ ```
1791
+
1792
+ **Payload (decoded, canonicalized):**
1793
+ ```json
1794
+ {
1795
+ "aud": "did:web:server.example.com",
1796
+ "iss": "did:web:server.example.com",
1797
+ "nonce": "abc123...",
1798
+ "requestHash": "sha256:5057521f...",
1799
+ "responseHash": "sha256:d4e5f6...",
1800
+ "sessionId": "kyaos_d7f8a9b0-...",
1801
+ "sub": "did:web:server.example.com",
1802
+ "ts": 1710288000
1803
+ }
1804
+ ```
1805
+
1806
+ **Signature:**
1807
+ ```
1808
+ Ed25519Sign(privateKey, BASE64URL(header) || "." || BASE64URL(canonicalize(payload)))
1809
+ ```
1810
+
1811
+ **Compact JWS:**
1812
+ ```
1813
+ <base64url-header>.<base64url-payload>.<base64url-signature>
1814
+ ```
1815
+
1816
+ Note: Actual signature values are key-dependent. Implementers should verify the structure and use the test key material from the reference implementation's test suite for bit-exact validation.
1817
+
1818
+ ---
1819
+
1820
+ *End of Specification*