@kya-os/mcp 1.6.1 → 1.9.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.
- package/CHANGELOG.md +117 -0
- package/README.md +228 -3
- package/dist/authz/index.d.ts +1 -0
- package/dist/authz/index.d.ts.map +1 -1
- package/dist/authz/index.js +1 -0
- package/dist/authz/index.js.map +1 -1
- package/dist/authz/oidc/oidc-adapter.d.ts +10 -2
- package/dist/authz/oidc/oidc-adapter.d.ts.map +1 -1
- package/dist/authz/oidc/oidc-adapter.js +18 -9
- package/dist/authz/oidc/oidc-adapter.js.map +1 -1
- package/dist/authz/oidc/pending-flow-store.d.ts +80 -0
- package/dist/authz/oidc/pending-flow-store.d.ts.map +1 -0
- package/dist/authz/oidc/pending-flow-store.js +82 -0
- package/dist/authz/oidc/pending-flow-store.js.map +1 -0
- package/dist/card/build.d.ts +41 -0
- package/dist/card/build.d.ts.map +1 -0
- package/dist/card/build.js +47 -0
- package/dist/card/build.js.map +1 -0
- package/dist/card/builder.d.ts +99 -0
- package/dist/card/builder.d.ts.map +1 -0
- package/dist/card/builder.js +147 -0
- package/dist/card/builder.js.map +1 -0
- package/dist/card/cimd.d.ts +92 -0
- package/dist/card/cimd.d.ts.map +1 -0
- package/dist/card/cimd.js +225 -0
- package/dist/card/cimd.js.map +1 -0
- package/dist/card/delegation.d.ts +145 -0
- package/dist/card/delegation.d.ts.map +1 -0
- package/dist/card/delegation.js +293 -0
- package/dist/card/delegation.js.map +1 -0
- package/dist/card/emit.d.ts +133 -0
- package/dist/card/emit.d.ts.map +1 -0
- package/dist/card/emit.js +127 -0
- package/dist/card/emit.js.map +1 -0
- package/dist/card/index.d.ts +43 -0
- package/dist/card/index.d.ts.map +1 -0
- package/dist/card/index.js +46 -0
- package/dist/card/index.js.map +1 -0
- package/dist/card/middleware.d.ts +112 -0
- package/dist/card/middleware.d.ts.map +1 -0
- package/dist/card/middleware.js +121 -0
- package/dist/card/middleware.js.map +1 -0
- package/dist/card/proof/build.d.ts +22 -0
- package/dist/card/proof/build.d.ts.map +1 -0
- package/dist/card/proof/build.js +55 -0
- package/dist/card/proof/build.js.map +1 -0
- package/dist/card/proof/canonical.d.ts +66 -0
- package/dist/card/proof/canonical.d.ts.map +1 -0
- package/dist/card/proof/canonical.js +131 -0
- package/dist/card/proof/canonical.js.map +1 -0
- package/dist/card/proof/http-sig.d.ts +69 -0
- package/dist/card/proof/http-sig.d.ts.map +1 -0
- package/dist/card/proof/http-sig.js +101 -0
- package/dist/card/proof/http-sig.js.map +1 -0
- package/dist/card/proof/index.d.ts +25 -0
- package/dist/card/proof/index.d.ts.map +1 -0
- package/dist/card/proof/index.js +25 -0
- package/dist/card/proof/index.js.map +1 -0
- package/dist/card/proof/nonce-cache.d.ts +67 -0
- package/dist/card/proof/nonce-cache.d.ts.map +1 -0
- package/dist/card/proof/nonce-cache.js +88 -0
- package/dist/card/proof/nonce-cache.js.map +1 -0
- package/dist/card/proof/signer.d.ts +32 -0
- package/dist/card/proof/signer.d.ts.map +1 -0
- package/dist/card/proof/signer.js +59 -0
- package/dist/card/proof/signer.js.map +1 -0
- package/dist/card/proof/types.d.ts +219 -0
- package/dist/card/proof/types.d.ts.map +1 -0
- package/dist/card/proof/types.js +87 -0
- package/dist/card/proof/types.js.map +1 -0
- package/dist/card/proof/verify.d.ts +27 -0
- package/dist/card/proof/verify.d.ts.map +1 -0
- package/dist/card/proof/verify.js +236 -0
- package/dist/card/proof/verify.js.map +1 -0
- package/dist/card/resolve.d.ts +97 -0
- package/dist/card/resolve.d.ts.map +1 -0
- package/dist/card/resolve.js +223 -0
- package/dist/card/resolve.js.map +1 -0
- package/dist/card/revocation.d.ts +96 -0
- package/dist/card/revocation.d.ts.map +1 -0
- package/dist/card/revocation.js +190 -0
- package/dist/card/revocation.js.map +1 -0
- package/dist/card/schema.d.ts +177 -0
- package/dist/card/schema.d.ts.map +1 -0
- package/dist/card/schema.js +119 -0
- package/dist/card/schema.js.map +1 -0
- package/dist/card/verify.d.ts +144 -0
- package/dist/card/verify.d.ts.map +1 -0
- package/dist/card/verify.js +132 -0
- package/dist/card/verify.js.map +1 -0
- package/dist/delegation/bitstring.d.ts +9 -7
- package/dist/delegation/bitstring.d.ts.map +1 -1
- package/dist/delegation/bitstring.js +70 -37
- package/dist/delegation/bitstring.js.map +1 -1
- package/dist/delegation/did-linkage.d.ts +23 -0
- package/dist/delegation/did-linkage.d.ts.map +1 -0
- package/dist/delegation/did-linkage.js +57 -0
- package/dist/delegation/did-linkage.js.map +1 -0
- package/dist/delegation/did-resolver-registry.d.ts +7 -0
- package/dist/delegation/did-resolver-registry.d.ts.map +1 -0
- package/dist/delegation/did-resolver-registry.js +20 -0
- package/dist/delegation/did-resolver-registry.js.map +1 -0
- package/dist/delegation/did-web-resolver.d.ts +29 -18
- package/dist/delegation/did-web-resolver.d.ts.map +1 -1
- package/dist/delegation/did-web-resolver.js +65 -35
- package/dist/delegation/did-web-resolver.js.map +1 -1
- package/dist/delegation/index.d.ts +3 -0
- package/dist/delegation/index.d.ts.map +1 -1
- package/dist/delegation/index.js +3 -0
- package/dist/delegation/index.js.map +1 -1
- package/dist/delegation/statuslist-manager.d.ts.map +1 -1
- package/dist/delegation/statuslist-manager.js +16 -1
- package/dist/delegation/statuslist-manager.js.map +1 -1
- package/dist/delegation/vc-jwt-verify.d.ts +61 -0
- package/dist/delegation/vc-jwt-verify.d.ts.map +1 -0
- package/dist/delegation/vc-jwt-verify.js +131 -0
- package/dist/delegation/vc-jwt-verify.js.map +1 -0
- package/dist/delegation/vc-verification-checks.d.ts +50 -0
- package/dist/delegation/vc-verification-checks.d.ts.map +1 -0
- package/dist/delegation/vc-verification-checks.js +212 -0
- package/dist/delegation/vc-verification-checks.js.map +1 -0
- package/dist/delegation/vc-verifier.d.ts +24 -61
- package/dist/delegation/vc-verifier.d.ts.map +1 -1
- package/dist/delegation/vc-verifier.js +57 -180
- package/dist/delegation/vc-verifier.js.map +1 -1
- package/dist/delegation/vc-verifier.types.d.ts +88 -0
- package/dist/delegation/vc-verifier.types.d.ts.map +1 -0
- package/dist/delegation/vc-verifier.types.js +9 -0
- package/dist/delegation/vc-verifier.types.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations/cheqd/dlr.d.ts +44 -0
- package/dist/integrations/cheqd/dlr.d.ts.map +1 -0
- package/dist/integrations/cheqd/dlr.js +86 -0
- package/dist/integrations/cheqd/dlr.js.map +1 -0
- package/dist/integrations/cheqd/index.d.ts +5 -0
- package/dist/integrations/cheqd/index.d.ts.map +1 -0
- package/dist/integrations/cheqd/index.js +5 -0
- package/dist/integrations/cheqd/index.js.map +1 -0
- package/dist/integrations/cheqd/linkage.d.ts +20 -0
- package/dist/integrations/cheqd/linkage.d.ts.map +1 -0
- package/dist/integrations/cheqd/linkage.js +32 -0
- package/dist/integrations/cheqd/linkage.js.map +1 -0
- package/dist/integrations/cheqd/registrar.d.ts +85 -0
- package/dist/integrations/cheqd/registrar.d.ts.map +1 -0
- package/dist/integrations/cheqd/registrar.js +304 -0
- package/dist/integrations/cheqd/registrar.js.map +1 -0
- package/dist/integrations/cheqd/resolver.d.ts +38 -0
- package/dist/integrations/cheqd/resolver.d.ts.map +1 -0
- package/dist/integrations/cheqd/resolver.js +156 -0
- package/dist/integrations/cheqd/resolver.js.map +1 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +5 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/kya-os-transport.d.ts +2 -1
- package/dist/middleware/kya-os-transport.d.ts.map +1 -1
- package/dist/middleware/kya-os-transport.js +2 -1
- package/dist/middleware/kya-os-transport.js.map +1 -1
- package/dist/middleware/with-kya-os-server.d.ts +21 -4
- package/dist/middleware/with-kya-os-server.d.ts.map +1 -1
- package/dist/middleware/with-kya-os-server.js +4 -0
- package/dist/middleware/with-kya-os-server.js.map +1 -1
- package/dist/middleware/with-kya-os.config-types.d.ts +139 -0
- package/dist/middleware/with-kya-os.config-types.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.config-types.js +9 -0
- package/dist/middleware/with-kya-os.config-types.js.map +1 -0
- package/dist/middleware/with-kya-os.d.ts +10 -238
- package/dist/middleware/with-kya-os.d.ts.map +1 -1
- package/dist/middleware/with-kya-os.delegation-gate.d.ts +19 -0
- package/dist/middleware/with-kya-os.delegation-gate.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.delegation-gate.js +175 -0
- package/dist/middleware/with-kya-os.delegation-gate.js.map +1 -0
- package/dist/middleware/with-kya-os.delegation-verify.d.ts +51 -0
- package/dist/middleware/with-kya-os.delegation-verify.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.delegation-verify.js +165 -0
- package/dist/middleware/with-kya-os.delegation-verify.js.map +1 -0
- package/dist/middleware/with-kya-os.deps.d.ts +40 -0
- package/dist/middleware/with-kya-os.deps.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.deps.js +9 -0
- package/dist/middleware/with-kya-os.deps.js.map +1 -0
- package/dist/middleware/with-kya-os.grants.d.ts +30 -0
- package/dist/middleware/with-kya-os.grants.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.grants.js +145 -0
- package/dist/middleware/with-kya-os.grants.js.map +1 -0
- package/dist/middleware/with-kya-os.helpers.d.ts +24 -0
- package/dist/middleware/with-kya-os.helpers.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.helpers.js +57 -0
- package/dist/middleware/with-kya-os.helpers.js.map +1 -0
- package/dist/middleware/with-kya-os.js +65 -703
- package/dist/middleware/with-kya-os.js.map +1 -1
- package/dist/middleware/with-kya-os.policy-gate.d.ts +16 -0
- package/dist/middleware/with-kya-os.policy-gate.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.policy-gate.js +98 -0
- package/dist/middleware/with-kya-os.policy-gate.js.map +1 -0
- package/dist/middleware/with-kya-os.protocol.d.ts +29 -0
- package/dist/middleware/with-kya-os.protocol.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.protocol.js +121 -0
- package/dist/middleware/with-kya-os.protocol.js.map +1 -0
- package/dist/middleware/with-kya-os.session.d.ts +32 -0
- package/dist/middleware/with-kya-os.session.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.session.js +201 -0
- package/dist/middleware/with-kya-os.session.js.map +1 -0
- package/dist/middleware/with-kya-os.types.d.ts +178 -0
- package/dist/middleware/with-kya-os.types.d.ts.map +1 -0
- package/dist/middleware/with-kya-os.types.js +13 -0
- package/dist/middleware/with-kya-os.types.js.map +1 -0
- package/dist/proof/generator.d.ts +21 -0
- package/dist/proof/generator.d.ts.map +1 -1
- package/dist/proof/generator.js +21 -0
- package/dist/proof/generator.js.map +1 -1
- package/dist/proof/index.d.ts +1 -1
- package/dist/proof/index.d.ts.map +1 -1
- package/dist/proof/index.js +1 -1
- package/dist/proof/index.js.map +1 -1
- package/dist/proof/verifier.d.ts +26 -3
- package/dist/proof/verifier.d.ts.map +1 -1
- package/dist/proof/verifier.js +54 -24
- package/dist/proof/verifier.js.map +1 -1
- package/dist/providers/grant-store.d.ts +10 -1
- package/dist/providers/grant-store.d.ts.map +1 -1
- package/dist/providers/grant-store.js.map +1 -1
- package/dist/providers/runtime-fetch.d.ts +8 -0
- package/dist/providers/runtime-fetch.d.ts.map +1 -1
- package/dist/providers/runtime-fetch.js +17 -0
- package/dist/providers/runtime-fetch.js.map +1 -1
- package/dist/providers/web-crypto.d.ts.map +1 -1
- package/dist/providers/web-crypto.js +15 -7
- package/dist/providers/web-crypto.js.map +1 -1
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +1 -0
- package/dist/session/index.js.map +1 -1
- package/dist/session/manager.d.ts +17 -6
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +16 -26
- package/dist/session/manager.js.map +1 -1
- package/dist/session/session-store.d.ts +78 -0
- package/dist/session/session-store.d.ts.map +1 -0
- package/dist/session/session-store.js +79 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/types/protocol.d.ts +9 -3
- package/dist/types/protocol.d.ts.map +1 -1
- package/dist/types/protocol.js.map +1 -1
- package/dist/utils/guards.d.ts +2 -0
- package/dist/utils/guards.d.ts.map +1 -0
- package/dist/utils/guards.js +4 -0
- package/dist/utils/guards.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/ip-classifier.d.ts +12 -0
- package/dist/utils/ip-classifier.d.ts.map +1 -0
- package/dist/utils/ip-classifier.js +153 -0
- package/dist/utils/ip-classifier.js.map +1 -0
- package/dist/utils/safe-fetch-transports.d.ts +40 -0
- package/dist/utils/safe-fetch-transports.d.ts.map +1 -0
- package/dist/utils/safe-fetch-transports.js +121 -0
- package/dist/utils/safe-fetch-transports.js.map +1 -0
- package/dist/utils/safe-fetch-types.d.ts +66 -0
- package/dist/utils/safe-fetch-types.d.ts.map +1 -0
- package/dist/utils/safe-fetch-types.js +10 -0
- package/dist/utils/safe-fetch-types.js.map +1 -0
- package/dist/utils/safe-fetch.d.ts +40 -0
- package/dist/utils/safe-fetch.d.ts.map +1 -0
- package/dist/utils/safe-fetch.js +188 -0
- package/dist/utils/safe-fetch.js.map +1 -0
- package/dist/utils/statuslist-purpose.d.ts +12 -0
- package/dist/utils/statuslist-purpose.d.ts.map +1 -0
- package/dist/utils/statuslist-purpose.js +19 -0
- package/dist/utils/statuslist-purpose.js.map +1 -0
- package/dist/utils/url.d.ts +2 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +8 -0
- package/dist/utils/url.js.map +1 -0
- package/package.json +25 -5
- package/schemas/README.md +2 -1
- package/schemas/card-delegation-credential.json +239 -0
- package/schemas/kya-os-card.schema.json +284 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF-hardened outbound fetch (safe-fetch) — the policy orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* `resolveCard` — and, later, JWKS + status-list resolution — follows URLs an attacker can
|
|
5
|
+
* influence across four discovery surfaces, so every outbound request goes through this
|
|
6
|
+
* guard. `createSafeFetch` returns a fetcher structurally compatible with the card's
|
|
7
|
+
* `FetchLike` ({ ok, status, json }) that enforces, fail-closed:
|
|
8
|
+
*
|
|
9
|
+
* - https-only (no http:, file:, data:, …);
|
|
10
|
+
* - the connection IP is a PUBLIC UNICAST address (RFC1918, loopback, link-local incl. the
|
|
11
|
+
* 169.254.169.254 cloud-metadata endpoint, IPv6 ULA/link-local, NAT64/6to4/Teredo transition
|
|
12
|
+
* ranges are denied — see `./ip-classifier`). Each HOP is resolved ONCE and its connection is
|
|
13
|
+
* PINNED to that validated address, closing the DNS-rebinding (TOCTOU) window within the hop; a
|
|
14
|
+
* same-origin redirect to a different host triggers a fresh resolve-and-pin cycle for that hop;
|
|
15
|
+
* - cross-origin redirects are blocked (manual handling; a 3xx to a different origin is
|
|
16
|
+
* rejected, same-origin redirects are followed up to a small cap);
|
|
17
|
+
* - a response size cap and an AbortSignal-backed timeout bound resource use.
|
|
18
|
+
*
|
|
19
|
+
* The two Node transports live in `./safe-fetch-transports`; the seam vocabulary in
|
|
20
|
+
* `./safe-fetch-types`; the address policy in `./ip-classifier`. This module owns only the
|
|
21
|
+
* hop-by-hop POLICY (scheme, resolve-and-pin, redirect, size cap, timeout, first-party escape
|
|
22
|
+
* hatch) and re-exports the transports + classifier + types so the public surface is one import.
|
|
23
|
+
*
|
|
24
|
+
* First-party escape hatch: `allowOrigins` / `firstPartyOrigin` list origins a caller owns. A
|
|
25
|
+
* request whose origin equals a configured one is fetched plain (origin-equality is not
|
|
26
|
+
* attacker-routable); every other origin still takes the guarded path, fail-closed.
|
|
27
|
+
*
|
|
28
|
+
* All I/O is injected via pluggable seams (`lookup`, `transport`) so the policy is unit tested
|
|
29
|
+
* with mocked DNS + transport. No crypto here — no mcp-i-core dependency leaks in.
|
|
30
|
+
*/
|
|
31
|
+
import type { SafeFetch, SafeFetchOptions } from './safe-fetch-types.js';
|
|
32
|
+
export * from './safe-fetch-types.js';
|
|
33
|
+
export { isBlockedAddress } from './ip-classifier.js';
|
|
34
|
+
export { defaultLookup, fetchTransport, nodeHttpsTransport, selectDefaultTransport, } from './safe-fetch-transports.js';
|
|
35
|
+
/**
|
|
36
|
+
* Build an SSRF-hardened fetcher. The returned function is structurally compatible with the
|
|
37
|
+
* card's `FetchLike`: `(url) => Promise<{ ok, status, json }>`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createSafeFetch(options?: SafeFetchOptions): SafeFetch;
|
|
40
|
+
//# sourceMappingURL=safe-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/safe-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH,OAAO,KAAK,EAIV,SAAS,EACT,gBAAgB,EAGjB,MAAM,uBAAuB,CAAC;AAG/B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AA6EpC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAuBzE"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF-hardened outbound fetch (safe-fetch) — the policy orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* `resolveCard` — and, later, JWKS + status-list resolution — follows URLs an attacker can
|
|
5
|
+
* influence across four discovery surfaces, so every outbound request goes through this
|
|
6
|
+
* guard. `createSafeFetch` returns a fetcher structurally compatible with the card's
|
|
7
|
+
* `FetchLike` ({ ok, status, json }) that enforces, fail-closed:
|
|
8
|
+
*
|
|
9
|
+
* - https-only (no http:, file:, data:, …);
|
|
10
|
+
* - the connection IP is a PUBLIC UNICAST address (RFC1918, loopback, link-local incl. the
|
|
11
|
+
* 169.254.169.254 cloud-metadata endpoint, IPv6 ULA/link-local, NAT64/6to4/Teredo transition
|
|
12
|
+
* ranges are denied — see `./ip-classifier`). Each HOP is resolved ONCE and its connection is
|
|
13
|
+
* PINNED to that validated address, closing the DNS-rebinding (TOCTOU) window within the hop; a
|
|
14
|
+
* same-origin redirect to a different host triggers a fresh resolve-and-pin cycle for that hop;
|
|
15
|
+
* - cross-origin redirects are blocked (manual handling; a 3xx to a different origin is
|
|
16
|
+
* rejected, same-origin redirects are followed up to a small cap);
|
|
17
|
+
* - a response size cap and an AbortSignal-backed timeout bound resource use.
|
|
18
|
+
*
|
|
19
|
+
* The two Node transports live in `./safe-fetch-transports`; the seam vocabulary in
|
|
20
|
+
* `./safe-fetch-types`; the address policy in `./ip-classifier`. This module owns only the
|
|
21
|
+
* hop-by-hop POLICY (scheme, resolve-and-pin, redirect, size cap, timeout, first-party escape
|
|
22
|
+
* hatch) and re-exports the transports + classifier + types so the public surface is one import.
|
|
23
|
+
*
|
|
24
|
+
* First-party escape hatch: `allowOrigins` / `firstPartyOrigin` list origins a caller owns. A
|
|
25
|
+
* request whose origin equals a configured one is fetched plain (origin-equality is not
|
|
26
|
+
* attacker-routable); every other origin still takes the guarded path, fail-closed.
|
|
27
|
+
*
|
|
28
|
+
* All I/O is injected via pluggable seams (`lookup`, `transport`) so the policy is unit tested
|
|
29
|
+
* with mocked DNS + transport. No crypto here — no mcp-i-core dependency leaks in.
|
|
30
|
+
*/
|
|
31
|
+
import { isBlockedAddress } from './ip-classifier.js';
|
|
32
|
+
import { defaultLookup, fetchTransport, selectDefaultTransport, } from './safe-fetch-transports.js';
|
|
33
|
+
// Re-export the full public surface so a consumer needs only `./safe-fetch` (barrels unchanged).
|
|
34
|
+
export * from './safe-fetch-types.js';
|
|
35
|
+
export { isBlockedAddress } from './ip-classifier.js';
|
|
36
|
+
export { defaultLookup, fetchTransport, nodeHttpsTransport, selectDefaultTransport, } from './safe-fetch-transports.js';
|
|
37
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
38
|
+
const DEFAULT_MAX_BYTES = 1_048_576;
|
|
39
|
+
const DEFAULT_MAX_REDIRECTS = 3;
|
|
40
|
+
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
41
|
+
/** Parse + require https; returns the normalized href. */
|
|
42
|
+
function assertHttpsUrl(raw) {
|
|
43
|
+
let url;
|
|
44
|
+
try {
|
|
45
|
+
url = new URL(raw);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error(`safe-fetch: invalid URL "${raw}"`);
|
|
49
|
+
}
|
|
50
|
+
if (url.protocol !== 'https:') {
|
|
51
|
+
throw new Error(`safe-fetch: only https is allowed (got "${url.protocol}")`);
|
|
52
|
+
}
|
|
53
|
+
return url.href;
|
|
54
|
+
}
|
|
55
|
+
/** Resolve the host ONCE, reject if ANY resolved address is non-public, and pin the first. */
|
|
56
|
+
async function resolveAndPin(hostname, lookup) {
|
|
57
|
+
const addresses = await lookup(hostname);
|
|
58
|
+
const [first] = addresses;
|
|
59
|
+
if (!first) {
|
|
60
|
+
throw new Error(`safe-fetch: DNS lookup for "${hostname}" returned no addresses`);
|
|
61
|
+
}
|
|
62
|
+
for (const candidate of addresses) {
|
|
63
|
+
if (isBlockedAddress(candidate.address)) {
|
|
64
|
+
throw new Error(`safe-fetch: refusing non-public address ${candidate.address} for host "${hostname}"`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return first;
|
|
68
|
+
}
|
|
69
|
+
/** Resolve a 3xx into the next URL, rejecting cross-origin / non-https / missing-Location. */
|
|
70
|
+
function nextRedirectUrl(currentUrl, raw) {
|
|
71
|
+
const location = raw.headers.get('location');
|
|
72
|
+
if (!location) {
|
|
73
|
+
throw new Error(`safe-fetch: ${raw.status} redirect without a Location header`);
|
|
74
|
+
}
|
|
75
|
+
let target;
|
|
76
|
+
try {
|
|
77
|
+
target = new URL(location, currentUrl);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Keep the module's fail-closed convention: a malformed Location surfaces a `safe-fetch:`-
|
|
81
|
+
// prefixed error rather than a raw TypeError leaking out of the URL constructor.
|
|
82
|
+
throw new Error(`safe-fetch: redirect location is not a valid URL "${location}"`);
|
|
83
|
+
}
|
|
84
|
+
if (target.protocol !== 'https:') {
|
|
85
|
+
throw new Error(`safe-fetch: redirect to non-https target "${target.protocol}" blocked`);
|
|
86
|
+
}
|
|
87
|
+
const origin = new URL(currentUrl).origin;
|
|
88
|
+
if (target.origin !== origin) {
|
|
89
|
+
throw new Error(`safe-fetch: cross-origin redirect blocked (${origin} → ${target.origin})`);
|
|
90
|
+
}
|
|
91
|
+
return target.href;
|
|
92
|
+
}
|
|
93
|
+
/** Enforce the size cap and project a RawResponse into the FetchLike-compatible shape. */
|
|
94
|
+
async function finalize(raw, maxBytes) {
|
|
95
|
+
const declared = Number(raw.headers.get('content-length'));
|
|
96
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
97
|
+
throw new Error(`safe-fetch: declared content-length ${declared} exceeds cap ${maxBytes}`);
|
|
98
|
+
}
|
|
99
|
+
const body = await raw.text();
|
|
100
|
+
if (Buffer.byteLength(body, 'utf8') > maxBytes) {
|
|
101
|
+
throw new Error(`safe-fetch: response body exceeds size cap ${maxBytes}`);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
ok: raw.status >= 200 && raw.status < 300,
|
|
105
|
+
status: raw.status,
|
|
106
|
+
json: () => Promise.resolve(JSON.parse(body)),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Build an SSRF-hardened fetcher. The returned function is structurally compatible with the
|
|
111
|
+
* card's `FetchLike`: `(url) => Promise<{ ok, status, json }>`.
|
|
112
|
+
*/
|
|
113
|
+
export function createSafeFetch(options = {}) {
|
|
114
|
+
const cfg = {
|
|
115
|
+
lookup: options.lookup ?? defaultLookup,
|
|
116
|
+
transport: options.transport ?? selectDefaultTransport(),
|
|
117
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
118
|
+
maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES,
|
|
119
|
+
trustedOrigins: normalizeTrustedOrigins(options),
|
|
120
|
+
};
|
|
121
|
+
const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
122
|
+
return async function safeFetch(initialUrl) {
|
|
123
|
+
let url = assertHttpsUrl(initialUrl);
|
|
124
|
+
for (let hop = 0;; hop++) {
|
|
125
|
+
const raw = await requestOnce(url, cfg);
|
|
126
|
+
if (!REDIRECT_STATUSES.has(raw.status)) {
|
|
127
|
+
return finalize(raw, cfg.maxBytes);
|
|
128
|
+
}
|
|
129
|
+
if (hop >= maxRedirects) {
|
|
130
|
+
throw new Error(`safe-fetch: too many redirects (> ${maxRedirects})`);
|
|
131
|
+
}
|
|
132
|
+
url = nextRedirectUrl(url, raw);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Perform ONE hop. First-party origins skip the resolve-and-pin SSRF screen (origin-equality is
|
|
138
|
+
* not attacker-routable) and are fetched plain by name; every other origin is resolved, screened,
|
|
139
|
+
* and pinned before the configured transport runs. Both branches are timeout-bounded.
|
|
140
|
+
*/
|
|
141
|
+
async function requestOnce(url, cfg) {
|
|
142
|
+
const { hostname, origin } = new URL(url);
|
|
143
|
+
if (cfg.trustedOrigins.has(origin)) {
|
|
144
|
+
return withTimeout(hostname, cfg.timeoutMs, (signal) => fetchTransport(url, { signal, hostname, pinnedAddress: '', family: 4, maxBytes: cfg.maxBytes }));
|
|
145
|
+
}
|
|
146
|
+
const pinned = await resolveAndPin(hostname, cfg.lookup);
|
|
147
|
+
return withTimeout(hostname, cfg.timeoutMs, (signal) => cfg.transport(url, {
|
|
148
|
+
signal,
|
|
149
|
+
hostname,
|
|
150
|
+
pinnedAddress: pinned.address,
|
|
151
|
+
family: pinned.family,
|
|
152
|
+
maxBytes: cfg.maxBytes,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
/** Run a transport call under an AbortSignal that fires after `timeoutMs`. */
|
|
156
|
+
async function withTimeout(hostname, timeoutMs, run) {
|
|
157
|
+
const controller = new AbortController();
|
|
158
|
+
const timer = setTimeout(() => controller.abort(new Error(`safe-fetch: request to ${hostname} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
159
|
+
try {
|
|
160
|
+
return await run(controller.signal);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Build the set of first-party origins that bypass the SSRF screen. Each `allowOrigins` /
|
|
168
|
+
* `firstPartyOrigin` entry MUST parse as an https origin — fail-closed (throw) otherwise.
|
|
169
|
+
*/
|
|
170
|
+
function normalizeTrustedOrigins(options) {
|
|
171
|
+
const raw = [...(options.allowOrigins ?? []), ...(options.firstPartyOrigin ? [options.firstPartyOrigin] : [])];
|
|
172
|
+
const origins = new Set();
|
|
173
|
+
for (const entry of raw) {
|
|
174
|
+
let url;
|
|
175
|
+
try {
|
|
176
|
+
url = new URL(entry);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
throw new Error(`safe-fetch: invalid first-party origin "${entry}"`);
|
|
180
|
+
}
|
|
181
|
+
if (url.protocol !== 'https:') {
|
|
182
|
+
throw new Error(`safe-fetch: first-party origin must be https (got "${entry}")`);
|
|
183
|
+
}
|
|
184
|
+
origins.add(url.origin);
|
|
185
|
+
}
|
|
186
|
+
return origins;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=safe-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/utils/safe-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fail-closed status-list `statusPurpose` check — the SINGLE source of truth for the
|
|
3
|
+
* invariant both the card revocation reader (`src/card/revocation.ts`) and the delegation status
|
|
4
|
+
* manager (`src/delegation/statuslist-manager.ts`) enforce, so the two readers cannot drift.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Fail CLOSED unless the resolved status list's `statusPurpose` is present AND equals `wanted`. A
|
|
8
|
+
* list of a DIFFERENT kind (e.g. a `suspension` list read as `revocation`) must not be accepted —
|
|
9
|
+
* its clear bit would otherwise report "not revoked" from the wrong list, a fail-open bypass.
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertStatusPurpose(listPurpose: unknown, wanted: string): void;
|
|
12
|
+
//# sourceMappingURL=statuslist-purpose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statuslist-purpose.d.ts","sourceRoot":"","sources":["../../src/utils/statuslist-purpose.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAO9E"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fail-closed status-list `statusPurpose` check — the SINGLE source of truth for the
|
|
3
|
+
* invariant both the card revocation reader (`src/card/revocation.ts`) and the delegation status
|
|
4
|
+
* manager (`src/delegation/statuslist-manager.ts`) enforce, so the two readers cannot drift.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Fail CLOSED unless the resolved status list's `statusPurpose` is present AND equals `wanted`. A
|
|
8
|
+
* list of a DIFFERENT kind (e.g. a `suspension` list read as `revocation`) must not be accepted —
|
|
9
|
+
* its clear bit would otherwise report "not revoked" from the wrong list, a fail-open bypass.
|
|
10
|
+
*/
|
|
11
|
+
export function assertStatusPurpose(listPurpose, wanted) {
|
|
12
|
+
if (typeof listPurpose !== 'string') {
|
|
13
|
+
throw new Error(`status list has no statusPurpose (expected "${wanted}") — fail-closed`);
|
|
14
|
+
}
|
|
15
|
+
if (listPurpose !== wanted) {
|
|
16
|
+
throw new Error(`status list statusPurpose mismatch: "${listPurpose}" ≠ "${wanted}"`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=statuslist-purpose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statuslist-purpose.js","sourceRoot":"","sources":["../../src/utils/statuslist-purpose.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAoB,EAAE,MAAc;IACtE,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,+CAA+C,MAAM,kBAAkB,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wCAAwC,WAAW,QAAQ,MAAM,GAAG,CAAC,CAAC;IACxF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QACnD,GAAG,EAAE,CAAC;IACR,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kya-os/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Reference implementation of the KYA-OS protocol for Model Context Protocol servers: delegation, proof, and session primitives",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
7
8
|
"main": "dist/index.js",
|
|
8
9
|
"types": "dist/index.d.ts",
|
|
9
10
|
"exports": {
|
|
@@ -11,6 +12,10 @@
|
|
|
11
12
|
"types": "./dist/index.d.ts",
|
|
12
13
|
"default": "./dist/index.js"
|
|
13
14
|
},
|
|
15
|
+
"./card": {
|
|
16
|
+
"types": "./dist/card/index.d.ts",
|
|
17
|
+
"default": "./dist/card/index.js"
|
|
18
|
+
},
|
|
14
19
|
"./delegation": {
|
|
15
20
|
"types": "./dist/delegation/index.d.ts",
|
|
16
21
|
"default": "./dist/delegation/index.js"
|
|
@@ -51,6 +56,10 @@
|
|
|
51
56
|
"types": "./dist/authz/index.d.ts",
|
|
52
57
|
"default": "./dist/authz/index.js"
|
|
53
58
|
},
|
|
59
|
+
"./cheqd": {
|
|
60
|
+
"types": "./dist/integrations/cheqd/index.d.ts",
|
|
61
|
+
"default": "./dist/integrations/cheqd/index.js"
|
|
62
|
+
},
|
|
54
63
|
"./schemas/*.json": "./schemas/*.json",
|
|
55
64
|
"./package.json": "./package.json"
|
|
56
65
|
},
|
|
@@ -64,9 +73,15 @@
|
|
|
64
73
|
"scripts": {
|
|
65
74
|
"build": "tsc",
|
|
66
75
|
"test": "vitest run",
|
|
76
|
+
"test:e2e:cheqd:testnet": "vitest run src/integrations/cheqd/__tests__/registrar.live.test.ts",
|
|
67
77
|
"test:watch": "vitest",
|
|
68
78
|
"test:coverage": "vitest run --coverage",
|
|
69
79
|
"typecheck": "tsc --noEmit",
|
|
80
|
+
"conformance": "tsx conformance/bin.ts",
|
|
81
|
+
"conformance:generate": "tsx conformance/generate-vectors.ts",
|
|
82
|
+
"conformance:generate:card": "tsx conformance/card-vectors.ts",
|
|
83
|
+
"conformance:verify:crosslang": "python3 conformance/verify.py",
|
|
84
|
+
"conformance:typecheck": "tsc --noEmit -p conformance/tsconfig.json",
|
|
70
85
|
"lint": "eslint src --ext .ts",
|
|
71
86
|
"clean": "rm -rf dist",
|
|
72
87
|
"prepublishOnly": "npm run clean && npm run build && npm run test",
|
|
@@ -75,13 +90,17 @@
|
|
|
75
90
|
"example:inspector": "npx @modelcontextprotocol/inspector npx tsx examples/node-server/server.ts --stdio",
|
|
76
91
|
"example:verify-proof": "npx tsx examples/verify-proof/verify.ts",
|
|
77
92
|
"example:anti-mitm": "npx tsx examples/verify-proof/anti-mitm-demo.ts",
|
|
93
|
+
"example:entity-card": "npx tsx examples/entity-card/walkthrough.ts",
|
|
94
|
+
"example:entity-card:server": "npx tsx examples/entity-card/server.ts",
|
|
95
|
+
"example:embedded-agent": "npx tsx examples/embedded-agent/walkthrough.ts",
|
|
78
96
|
"example:authz-inspector": "npx @modelcontextprotocol/inspector npx tsx examples/authz-inspector/src/stdio.ts",
|
|
79
97
|
"example:authz-inspector:http": "npx tsx examples/authz-inspector/src/inspector-http.ts",
|
|
80
98
|
"example:authz-inspector:http:server": "npx tsx examples/authz-inspector/src/http.ts",
|
|
81
|
-
"example:authz-inspector:stdio:server": "npx tsx examples/authz-inspector/src/stdio.ts"
|
|
99
|
+
"example:authz-inspector:stdio:server": "npx tsx examples/authz-inspector/src/stdio.ts",
|
|
100
|
+
"example:cheqd-dlr": "npx tsx examples/cheqd-dlr/operator-flow.ts"
|
|
82
101
|
},
|
|
83
102
|
"dependencies": {
|
|
84
|
-
"jose": "^
|
|
103
|
+
"jose": "^6.2.3",
|
|
85
104
|
"json-canonicalize": "^2.0.0",
|
|
86
105
|
"zod": "^4.3.6"
|
|
87
106
|
},
|
|
@@ -98,13 +117,14 @@
|
|
|
98
117
|
"@kya-os/consent": "^0.1.37",
|
|
99
118
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
100
119
|
"@types/express": "^5.0.6",
|
|
101
|
-
"@types/node": "^
|
|
120
|
+
"@types/node": "^26.0.1",
|
|
102
121
|
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
103
122
|
"@typescript-eslint/parser": "^8.57.0",
|
|
104
123
|
"@vitest/coverage-v8": "^2.0.0",
|
|
105
|
-
"commander": "^
|
|
124
|
+
"commander": "^15.0.0",
|
|
106
125
|
"eslint": "^10.0.3",
|
|
107
126
|
"express": "^5.2.1",
|
|
127
|
+
"tsx": "^4.22.4",
|
|
108
128
|
"typescript": "^5.5.3",
|
|
109
129
|
"typescript-eslint": "^8.57.0",
|
|
110
130
|
"vitest": "^2.0.0"
|
package/schemas/README.md
CHANGED
|
@@ -8,7 +8,8 @@ This directory contains JSON Schema definitions for the core KYA-OS (Model Conte
|
|
|
8
8
|
|--------|-------------|
|
|
9
9
|
| [handshake-request.json](./handshake-request.json) | Client-initiated session establishment request |
|
|
10
10
|
| [handshake-response.json](./handshake-response.json) | Server response with session context |
|
|
11
|
-
| [delegation-credential.json](./delegation-credential.json) | W3C Verifiable Credential for delegations |
|
|
11
|
+
| [delegation-credential.json](./delegation-credential.json) | W3C Verifiable Credential for delegations (legacy VC 1.0 shape) |
|
|
12
|
+
| [card-delegation-credential.json](./card-delegation-credential.json) | W3C VC 2.0 + ZCAP-LD delegation profile (Entity Card) |
|
|
12
13
|
| [detached-proof.json](./detached-proof.json) | Cryptographic proof for tool request/response |
|
|
13
14
|
| [well-known-kyaos.json](./well-known-kyaos.json) | Service discovery document |
|
|
14
15
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schema.kya-os.org/v1/protocol/delegation/credential/v1.1.0",
|
|
4
|
+
"title": "KYA-OS Delegation Credential (W3C VC 2.0 + ZCAP-LD profile)",
|
|
5
|
+
"description": "A W3C Verifiable Credential 2.0 whose credentialSubject IS an attenuated ZCAP-LD capability. One credential per delegation HOP; a chain runs root -> ... -> leaf. CRISP attenuation is enforced on resolve, fail-closed: a child's allowedAction MUST be a subset of its parent's; caveats are monotone-narrowing (child MaxAmount <= parent, child ValidUntil <= parent, and a parent caveat may never be silently dropped); the parent's delegate (invoker/controller) MUST be the child's issuer; child.parentCapability MUST reference the parent capability id; the invocationTarget is constant along the chain; depth <= 10; and the ROOT's parentCapability equals its invocationTarget with issuer/invocationTarget = the resource owner / resource. responsibleParty is recomputed as the issuer of the root credential; the leaf invoker is asserted equal to the per-request proof key. This schema is a re-profile of the prior draft, not a new artifact.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["@context", "type", "issuer", "credentialSubject"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"@context": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"items": {
|
|
12
|
+
"oneOf": [
|
|
13
|
+
{ "type": "string", "format": "uri" },
|
|
14
|
+
{ "type": "object" }
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"minItems": 2,
|
|
18
|
+
"description": "JSON-LD context. MUST include the W3C VC 2.0 context 'https://www.w3.org/ns/credentials/v2' as the first element, the ZCAP-LD context 'https://w3id.org/security/zcap/v1', and the KYA-OS delegation namespace 'https://kya-os.org/ns/delegation/v1'."
|
|
19
|
+
},
|
|
20
|
+
"id": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"format": "uri",
|
|
23
|
+
"description": "Unique credential identifier (URN or URL)."
|
|
24
|
+
},
|
|
25
|
+
"type": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": { "type": "string" },
|
|
28
|
+
"contains": { "const": "VerifiableCredential" },
|
|
29
|
+
"minItems": 2,
|
|
30
|
+
"description": "Credential types. MUST include 'VerifiableCredential' and 'DelegationCredential'."
|
|
31
|
+
},
|
|
32
|
+
"issuer": {
|
|
33
|
+
"oneOf": [
|
|
34
|
+
{ "type": "string", "pattern": "^did:.+$" },
|
|
35
|
+
{
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["id"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"id": { "type": "string", "pattern": "^did:.+$" }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"description": "Delegator DID (or an issuer object carrying id). For the ROOT credential this is the resource owner and the recomputed responsibleParty."
|
|
44
|
+
},
|
|
45
|
+
"validFrom": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"format": "date-time",
|
|
48
|
+
"description": "VC 2.0 validity start (replaces the legacy issuanceDate)."
|
|
49
|
+
},
|
|
50
|
+
"validUntil": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"format": "date-time",
|
|
53
|
+
"description": "VC 2.0 validity end (replaces the legacy expirationDate). A child's validUntil MUST be no later than its parent's (monotone-narrowing)."
|
|
54
|
+
},
|
|
55
|
+
"credentialSubject": {
|
|
56
|
+
"$ref": "#/$defs/ZcapCapability"
|
|
57
|
+
},
|
|
58
|
+
"credentialStatus": {
|
|
59
|
+
"$ref": "#/$defs/CredentialStatus"
|
|
60
|
+
},
|
|
61
|
+
"proof": {
|
|
62
|
+
"$ref": "#/$defs/Proof"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"additionalProperties": true,
|
|
66
|
+
"$defs": {
|
|
67
|
+
"ZcapCapability": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"description": "The attenuated ZCAP-LD capability carried as the VC credentialSubject.",
|
|
70
|
+
"required": ["id", "parentCapability", "invocationTarget", "allowedAction"],
|
|
71
|
+
"anyOf": [
|
|
72
|
+
{ "required": ["controller"] },
|
|
73
|
+
{ "required": ["invoker"] }
|
|
74
|
+
],
|
|
75
|
+
"properties": {
|
|
76
|
+
"id": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "Capability identifier, typically a 'urn:zcap:*' URN. A child's parentCapability MUST reference this value."
|
|
79
|
+
},
|
|
80
|
+
"controller": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"pattern": "^did:.+$",
|
|
83
|
+
"description": "The delegate DID that controls this capability (synonym of invoker)."
|
|
84
|
+
},
|
|
85
|
+
"invoker": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^did:.+$",
|
|
88
|
+
"description": "The delegate DID authorized to invoke this capability. For the leaf hop this is asserted equal to the per-request proof key (proof.did)."
|
|
89
|
+
},
|
|
90
|
+
"parentCapability": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "The parent capability id. For the ROOT hop this MUST equal invocationTarget (the resource itself)."
|
|
93
|
+
},
|
|
94
|
+
"invocationTarget": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "The resource DID/URI this capability authorizes. Constant along the whole chain."
|
|
97
|
+
},
|
|
98
|
+
"allowedAction": {
|
|
99
|
+
"type": "array",
|
|
100
|
+
"items": { "type": "string" },
|
|
101
|
+
"description": "Permitted action strings. A child's allowedAction MUST be a subset of its parent's (no escalation)."
|
|
102
|
+
},
|
|
103
|
+
"caveats": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"items": { "$ref": "#/$defs/Caveat" },
|
|
106
|
+
"description": "Monotone-narrowing constraints accumulated down the chain."
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"additionalProperties": true
|
|
110
|
+
},
|
|
111
|
+
"Caveat": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"description": "A monotone-narrowing constraint. A child MUST carry, for every parent caveat, a caveat of the same type that is at least as narrow; unknown caveat types MUST be replicated verbatim (fail-closed).",
|
|
114
|
+
"required": ["type"],
|
|
115
|
+
"properties": {
|
|
116
|
+
"type": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "Caveat type, e.g. 'ValidUntil' or 'MaxAmount'."
|
|
119
|
+
},
|
|
120
|
+
"date": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"format": "date-time",
|
|
123
|
+
"description": "ValidUntil caveat: the expiry; a child's date MUST be <= its parent's."
|
|
124
|
+
},
|
|
125
|
+
"limit": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"description": "MaxAmount caveat: a decimal cap; a child's limit MUST be <= its parent's for the same currency."
|
|
128
|
+
},
|
|
129
|
+
"currency": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "MaxAmount caveat: the ISO currency code (e.g. 'USD')."
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"additionalProperties": true
|
|
135
|
+
},
|
|
136
|
+
"CredentialStatus": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"description": "W3C Bitstring Status List v1.0 entry (the StatusList2021Entry successor), evaluated through the injected RevocationChecker seam; a revoked ancestor cascades to invalidate the subtree.",
|
|
139
|
+
"required": ["type", "statusListIndex", "statusListCredential"],
|
|
140
|
+
"properties": {
|
|
141
|
+
"id": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"format": "uri",
|
|
144
|
+
"description": "Status entry identifier."
|
|
145
|
+
},
|
|
146
|
+
"type": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"const": "BitstringStatusListEntry"
|
|
149
|
+
},
|
|
150
|
+
"statusPurpose": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"enum": ["revocation", "suspension"]
|
|
153
|
+
},
|
|
154
|
+
"statusListIndex": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"pattern": "^[0-9]+$",
|
|
157
|
+
"description": "Index in the status-list bitstring."
|
|
158
|
+
},
|
|
159
|
+
"statusListCredential": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"format": "uri",
|
|
162
|
+
"description": "URL of the Bitstring Status List credential."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"Proof": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"description": "Data Integrity proof over the credential.",
|
|
169
|
+
"required": ["type", "cryptosuite"],
|
|
170
|
+
"properties": {
|
|
171
|
+
"type": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"const": "DataIntegrityProof"
|
|
174
|
+
},
|
|
175
|
+
"cryptosuite": {
|
|
176
|
+
"type": "string",
|
|
177
|
+
"const": "eddsa-jcs-2022"
|
|
178
|
+
},
|
|
179
|
+
"created": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"format": "date-time"
|
|
182
|
+
},
|
|
183
|
+
"verificationMethod": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": "DID URL of the verification key."
|
|
186
|
+
},
|
|
187
|
+
"proofPurpose": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"description": "Purpose of the proof (e.g., 'assertionMethod')."
|
|
190
|
+
},
|
|
191
|
+
"proofValue": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"description": "Multibase-encoded signature value."
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"additionalProperties": true
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"examples": [
|
|
200
|
+
{
|
|
201
|
+
"@context": [
|
|
202
|
+
"https://www.w3.org/ns/credentials/v2",
|
|
203
|
+
"https://w3id.org/security/zcap/v1",
|
|
204
|
+
"https://kya-os.org/ns/delegation/v1"
|
|
205
|
+
],
|
|
206
|
+
"id": "urn:uuid:12345678-1234-1234-1234-123456789abc",
|
|
207
|
+
"type": ["VerifiableCredential", "DelegationCredential"],
|
|
208
|
+
"issuer": "did:web:agent-a.example",
|
|
209
|
+
"validFrom": "2026-03-12T00:00:00Z",
|
|
210
|
+
"validUntil": "2026-06-01T00:00:00Z",
|
|
211
|
+
"credentialSubject": {
|
|
212
|
+
"id": "urn:zcap:del_123",
|
|
213
|
+
"invoker": "did:web:agent-b.example",
|
|
214
|
+
"parentCapability": "urn:zcap:root",
|
|
215
|
+
"invocationTarget": "did:web:api.example:payments",
|
|
216
|
+
"allowedAction": ["payments.transfer"],
|
|
217
|
+
"caveats": [
|
|
218
|
+
{ "type": "ValidUntil", "date": "2026-06-01T00:00:00Z" },
|
|
219
|
+
{ "type": "MaxAmount", "limit": "500.00", "currency": "USD" }
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
"credentialStatus": {
|
|
223
|
+
"id": "https://issuer.example/status/1#94567",
|
|
224
|
+
"type": "BitstringStatusListEntry",
|
|
225
|
+
"statusPurpose": "revocation",
|
|
226
|
+
"statusListIndex": "94567",
|
|
227
|
+
"statusListCredential": "https://issuer.example/status/1"
|
|
228
|
+
},
|
|
229
|
+
"proof": {
|
|
230
|
+
"type": "DataIntegrityProof",
|
|
231
|
+
"cryptosuite": "eddsa-jcs-2022",
|
|
232
|
+
"created": "2026-03-12T00:00:01Z",
|
|
233
|
+
"verificationMethod": "did:web:agent-a.example#key-1",
|
|
234
|
+
"proofPurpose": "assertionMethod",
|
|
235
|
+
"proofValue": "z3FXQ..."
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
}
|