@kya-os/mcp 1.14.2 → 1.16.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 +95 -0
- package/CONFORMANCE.md +31 -4
- package/README.md +30 -160
- package/SPEC.md +53 -14
- package/dist/delegation/holder-binding.d.ts.map +1 -1
- package/dist/delegation/holder-binding.js +8 -1
- package/dist/delegation/holder-binding.js.map +1 -1
- package/dist/delegation/scope-matcher.d.ts +8 -3
- package/dist/delegation/scope-matcher.d.ts.map +1 -1
- package/dist/delegation/scope-matcher.js +15 -1
- package/dist/delegation/scope-matcher.js.map +1 -1
- package/dist/delegation/vc-verification-checks.d.ts.map +1 -1
- package/dist/delegation/vc-verification-checks.js +10 -2
- package/dist/delegation/vc-verification-checks.js.map +1 -1
- package/dist/delegation/vc-verifier.types.d.ts +4 -0
- package/dist/delegation/vc-verifier.types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware/with-kya-os.config-types.d.ts +22 -1
- package/dist/middleware/with-kya-os.config-types.d.ts.map +1 -1
- package/dist/middleware/with-kya-os.d.ts.map +1 -1
- package/dist/middleware/with-kya-os.deps.d.ts +7 -0
- package/dist/middleware/with-kya-os.deps.d.ts.map +1 -1
- package/dist/middleware/with-kya-os.js +6 -1
- package/dist/middleware/with-kya-os.js.map +1 -1
- package/dist/middleware/with-kya-os.session.d.ts.map +1 -1
- package/dist/middleware/with-kya-os.session.js +28 -6
- package/dist/middleware/with-kya-os.session.js.map +1 -1
- package/dist/proof/generator.d.ts +38 -5
- package/dist/proof/generator.d.ts.map +1 -1
- package/dist/proof/generator.js +95 -25
- 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 +6 -1
- package/dist/proof/verifier.d.ts.map +1 -1
- package/dist/proof/verifier.js +20 -27
- package/dist/proof/verifier.js.map +1 -1
- package/dist/types/protocol.d.ts +28 -1
- package/dist/types/protocol.d.ts.map +1 -1
- package/dist/types/protocol.js +31 -0
- package/dist/types/protocol.js.map +1 -1
- package/package.json +1 -1
- package/schemas/delegation-credential.json +1 -1
- package/schemas/detached-proof.json +6 -1
- package/schemas/well-known-mcpi.json +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,101 @@ Versioning: https://semver.org/spec/v2.0.0.html
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.16.0] - 2026-09-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`path-prefix` CRISP scope matcher.** `prefix` is character-level and right
|
|
15
|
+
for scope identifiers, but applied to a resource path it lets a grant for
|
|
16
|
+
`notes` authorize `notesx/secret.md`. `path-prefix` matches the path itself
|
|
17
|
+
or anything beneath it as a `/`-separated path, with an optional trailing `/`
|
|
18
|
+
or `/*`; an empty base grants nothing. Added to `matchScope`, the `CrispScope`
|
|
19
|
+
type, `schemas/delegation-credential.json`, and SPEC.md §6.3/§6.4.1.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **`DelegationVCVerificationResult.stage` names the failing check.** A failed
|
|
24
|
+
verdict from `verifyDelegationCredential` / `verifyDelegationJwt` reported
|
|
25
|
+
`stage: "complete"` once both the signature and status checks had run, leaving
|
|
26
|
+
only the `checks` flags to say which one failed. It now reports `"signature"` or
|
|
27
|
+
`"status"`; `"complete"` accompanies `valid: true` only.
|
|
28
|
+
- The revocation reason names the status method on the credential
|
|
29
|
+
(`Credential revoked via <credentialStatus.type> (…)`) instead of always
|
|
30
|
+
saying `StatusList2021`.
|
|
31
|
+
- `schemas/well-known-mcpi.json`: `version` accepts the three-part protocol
|
|
32
|
+
version the reference implementation advertises (`1.0.0`); `endpoints.handshake`
|
|
33
|
+
is no longer required — it belongs to the legacy 1.x session profile
|
|
34
|
+
(SPEC-MCP-EXTENSION.md §4).
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- SPEC.md §10: the discovery example now validates against the schema
|
|
39
|
+
(`serverDid`, not `did`; schema capability names; `statusListCredential`;
|
|
40
|
+
camelCase advisory fields) and says when `endpoints.handshake` applies.
|
|
41
|
+
- CONFORMANCE.md: names the three ladders (Core L1–L3, Card L1–L3, AAP-0–4) and
|
|
42
|
+
asks claims to say which; the Entity Card section uses the terminal `_meta`
|
|
43
|
+
key names (`org.kya-os/response-proof`, `org.kya-os/request-proof` carrying
|
|
44
|
+
`prf: "org.kya-os/proof.v1"`).
|
|
45
|
+
|
|
46
|
+
## [1.15.0] - 2026-08-25
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **Response-proof `envelope` profile** (`org.kya-os/response-proof.envelope`).
|
|
51
|
+
The `body` profile binds `responseHash` over the response body only, leaving
|
|
52
|
+
result members such as `structuredContent`, `isError` and `resultType`
|
|
53
|
+
unauthenticated - an in-path intermediary could rewrite them under a still
|
|
54
|
+
valid proof. The envelope profile covers the ENTIRE result object with the
|
|
55
|
+
top-level `_meta` member removed, mirroring the request side's
|
|
56
|
+
`{method, params minus _meta}` rule, so every present and future result
|
|
57
|
+
member is authenticated while `_meta` stays intermediary-mutable (which is
|
|
58
|
+
what keeps attach-after-sign sound). The profile is discriminated by a `prf`
|
|
59
|
+
claim COVERED by the JWS signature, so a proof cannot be silently downgraded
|
|
60
|
+
to body semantics, and `validateDetachedProof` rejects unknown `prf` values
|
|
61
|
+
fail-closed. Verification always derives the profile from the proof's own
|
|
62
|
+
claim, never from configuration, so one verifier accepts both.
|
|
63
|
+
- **Conformance vector-set immutability.** `conformance/SUITE-MANIFEST.json`
|
|
64
|
+
pins the vector set by hash, `scripts/suite-hash.mjs` computes and checks it,
|
|
65
|
+
and CI enforces it as its own line - a published vector set can no longer be
|
|
66
|
+
edited in place without the check failing. Suite is at 1.1.0.
|
|
67
|
+
- **`DEPRECATIONS.md`** - an internal ledger of deferred breaking changes.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- **Sessionless request proofs now bind.** `generateRequestProof` defaulted
|
|
72
|
+
`meta.sessionId` to `''` when the caller omitted it, but `sessionId` is a
|
|
73
|
+
required non-empty string in the proof meta schema, so the proof failed
|
|
74
|
+
validation as `INVALID_PROOF_STRUCTURE` and `assertHolderBinding` returned
|
|
75
|
+
`unbound` for the LEGITIMATE holder - indistinguishable from the thief the
|
|
76
|
+
gate exists to catch. `sessionId` is documented as optional and a request
|
|
77
|
+
proof routinely precedes any handshake on a stateless core, so this was the
|
|
78
|
+
normal path rather than an edge case. It failed closed (nothing was wrongly
|
|
79
|
+
admitted), but phase-1 holder binding was unusable without a `sessionId` the
|
|
80
|
+
signature never said was mandatory. A per-proof id is now minted when none is
|
|
81
|
+
supplied; verification, the schema and the conformance vectors are unchanged.
|
|
82
|
+
- The `prf` claim is derived only from the profile option, and the proof schema
|
|
83
|
+
id is bumped accordingly.
|
|
84
|
+
- A challenge proof that the degraded-audit `isError` flip would have broken is
|
|
85
|
+
now stripped rather than emitted.
|
|
86
|
+
- `suite-hash` hashes the committed blob bytes rather than the working tree, so
|
|
87
|
+
a dirty checkout cannot produce a passing hash.
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- **Response-proof profiles are named by mechanism, not sequence**:
|
|
92
|
+
`org.kya-os/response-proof.v2` -> `.envelope`, `.v1` -> `.body`, and
|
|
93
|
+
`RESPONSE_PROOF_PROFILE_V2`/`_V1` -> `_ENVELOPE`/`_BODY`. The sequence
|
|
94
|
+
framing was wrong on its own terms - original proofs carry no profile
|
|
95
|
+
identifier at all, so the first wire identifier would have debuted as `.v2`
|
|
96
|
+
naming a v1 that never existed. **No published API changes**: these
|
|
97
|
+
identifiers and constants are new in this release and never shipped in
|
|
98
|
+
1.14.2, so nothing downstream can be pinned to the old names. The
|
|
99
|
+
request-proof profile keeps its shipped `org.kya-os/proof.v1` name.
|
|
100
|
+
- Conformance vectors use semantic names and a more robust signature-tamper
|
|
101
|
+
case.
|
|
102
|
+
- Dependency bumps across the minor-and-patch group.
|
|
103
|
+
- README restructures `did:cheqd` into a hierarchical Integrations section.
|
|
104
|
+
|
|
10
105
|
## [1.14.2] - 2026-08-19
|
|
11
106
|
|
|
12
107
|
### Fixed
|
package/CONFORMANCE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Compliance levels for KYA-OS implementations**
|
|
4
4
|
|
|
5
|
-
Version: 1.
|
|
5
|
+
Version: 1.1.0
|
|
6
6
|
Status: Stable
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -11,6 +11,17 @@ Status: Stable
|
|
|
11
11
|
|
|
12
12
|
This document defines three compliance levels for KYA-OS implementations. Each level builds on the previous, with increasing capability requirements. Implementations MUST pass all tests for a given level to claim conformance at that level.
|
|
13
13
|
|
|
14
|
+
Three independent ladders live in this document, and two of them reuse the
|
|
15
|
+
names L1–L3. A conformance claim MUST name its ladder:
|
|
16
|
+
|
|
17
|
+
| Ladder | Levels | Measures |
|
|
18
|
+
|--------|--------|----------|
|
|
19
|
+
| Core (this section onward) | Core L1 / L2 / L3 | Crypto primitives, the legacy session-bound proof, W3C delegation credentials |
|
|
20
|
+
| Entity Card (§ Entity Card Conformance) | Card L1 / L2 / L3 | The typed card and the stateless `org.kya-os/proof.v1` request proof |
|
|
21
|
+
| Audit Assurance Profile (§ Audit Assurance Profile conformance) | AAP-0 … AAP-4 | Auditability mechanics an implementation can truthfully advertise |
|
|
22
|
+
|
|
23
|
+
A bare "L2" is ambiguous; write "Core L2", "Card L2", or "AAP-2".
|
|
24
|
+
|
|
14
25
|
---
|
|
15
26
|
|
|
16
27
|
## Level 1 — Core Crypto
|
|
@@ -411,7 +422,7 @@ so it is reproducible against any implementation without re-signing.
|
|
|
411
422
|
|
|
412
423
|
| File | Category | Positive | Negative |
|
|
413
424
|
|------|----------|----------|----------|
|
|
414
|
-
| `vectors/signed-proof.json` | Detached proof verification | valid signature + in-window ts | tampered signature, tampered meta, wrong key, timestamp skew exceeded |
|
|
425
|
+
| `vectors/signed-proof.json` | Detached proof verification | valid signature + in-window ts; envelope content binding (profile `org.kya-os/response-proof.envelope`) | tampered signature, tampered meta, wrong key, timestamp skew exceeded; tampered `structuredContent` under envelope coverage, `prf` stripped (downgrade), unknown profile |
|
|
415
426
|
| `vectors/delegation-chain.json` | Delegation chain verification | single-hop, two-hop attenuated | broken issuer↔subject linkage, scope widening, tampered signature, audience mismatch |
|
|
416
427
|
| `vectors/status-list.json` | StatusList2021 revocation | active (bit unset) | revoked (bit set) |
|
|
417
428
|
| `vectors/did-key-resolution.json` | did:key resolution | valid Ed25519 | malformed multibase, wrong method |
|
|
@@ -430,6 +441,11 @@ protocol, and `negotiation` exercises the MCP extension admission gate
|
|
|
430
441
|
A `fail` vector passes the suite only when the implementation correctly **rejects**
|
|
431
442
|
it. The runner exits non-zero on any mismatch.
|
|
432
443
|
|
|
444
|
+
**On the envelope-profile vectors (`signed-proof/envelope-*`, `prf-stripped-downgrade`, `unknown-profile`; suite ≥ 1.1.0).**
|
|
445
|
+
These exercise the `org.kya-os/response-proof.envelope` profile (SPEC.md §7.3): `responseHash` covers the full result envelope with the top-level `_meta` member removed, discriminated by a signature-covered `prf` claim.
|
|
446
|
+
Their inputs carry an OPTIONAL `expected` member (`{ request, response }`) — the content the verifier "received" — which a conformant verifier re-hashes under the proof's own `prf` profile and reconciles against the bound hashes.
|
|
447
|
+
Vectors without `expected` remain pure signature/skew checks.
|
|
448
|
+
|
|
433
449
|
**On `signed-proof/tampered-meta`.**
|
|
434
450
|
The proof's `meta` block mirrors the claims signed inside the JWS, but it is a convenience for consumers that read the proof without decoding the token, not an authoritative copy.
|
|
435
451
|
This vector leaves the JWS byte-identical to `valid-basic` and alters only the mirrored `meta.requestHash`, so the JWS signature still verifies.
|
|
@@ -457,6 +473,16 @@ pnpm run conformance:generate
|
|
|
457
473
|
|
|
458
474
|
Re-running mints fresh keys but preserves every positive/negative relationship by
|
|
459
475
|
construction.
|
|
476
|
+
Because fresh keys change the committed bytes, regenerated output MUST NOT be committed over an existing `suiteVersion` (see [Suite versioning and immutability](#suite-versioning-and-immutability)).
|
|
477
|
+
|
|
478
|
+
### Suite versioning and immutability
|
|
479
|
+
|
|
480
|
+
The committed vector set is immutable at a given `suiteVersion`: the vector bytes may only change together with a `suiteVersion` bump.
|
|
481
|
+
`conformance/SUITE-MANIFEST.json` pins the current set (`suiteVersion`, `vectorSetHash`, `vectorCount`, per-file hashes) and is the anchor CI verifies against.
|
|
482
|
+
The hash recipe: SHA-256 each vector file's raw committed bytes, sort the `[filename, hex]` pairs by filename, canonicalize the array with RFC 8785 (JCS), SHA-256 that, prefix with `sha256:`.
|
|
483
|
+
CI enforces the invariant twice: a vitest guard (`conformance/__tests__/suite-immutability.test.ts`) and a dedicated step running `node conformance/suite-hash.mjs --check`.
|
|
484
|
+
To change vectors intentionally, bump `suiteVersion` (including the in-file `version` fields), regenerate the manifest with `node conformance/suite-hash.mjs --json`, and keep `pinnedAt` current.
|
|
485
|
+
The SIGNED public suite manifest published by the Conformance Attestation Program is a separate artifact built on this committed one.
|
|
460
486
|
|
|
461
487
|
### Running the harness against YOUR implementation
|
|
462
488
|
|
|
@@ -521,8 +547,9 @@ deployment satisfies its operational durability or independence claims.
|
|
|
521
547
|
The Entity Card is a **distinct, newer layer** on top of the Level 1–3 ladder above:
|
|
522
548
|
a typed, DID-anchored card plus a stateless, sender-constrained per-request proof.
|
|
523
549
|
It is orthogonal to the legacy session-bound proof — the two coexist, each under
|
|
524
|
-
its OWN distinct `_meta` key (`_meta[
|
|
525
|
-
session-bound proof, `_meta[
|
|
550
|
+
its OWN distinct `_meta` key (`_meta["org.kya-os/response-proof"]` for the legacy
|
|
551
|
+
session-bound proof, historically `org.kya-os/proof`; `_meta["org.kya-os/request-proof"]`
|
|
552
|
+
for the stateless card proof, whose object carries `prf: "org.kya-os/proof.v1"`),
|
|
526
553
|
and each verifier reads its own key and ignores the other. Its
|
|
527
554
|
conformance vectors live in the SAME harness under two categories, wired to the two
|
|
528
555
|
adapter methods `verifyCardProof` and `verifyEntityCard`.
|
package/README.md
CHANGED
|
@@ -265,182 +265,52 @@ flows, and sessions are shared across instances and survive restarts.
|
|
|
265
265
|
|
|
266
266
|
---
|
|
267
267
|
|
|
268
|
-
##
|
|
268
|
+
## Integrations
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
KYA-OS reaches the outside world two ways: shipped **modules** for specific
|
|
271
|
+
systems and standards, and typed **adapter seams** with in-memory defaults you
|
|
272
|
+
swap out. Adding one is "drop a folder under `src/integrations/`" or "implement
|
|
273
|
+
this interface" — contributions welcome.
|
|
273
274
|
|
|
274
|
-
###
|
|
275
|
+
### Modules
|
|
276
|
+
|
|
277
|
+
| Module | Adds | Docs |
|
|
278
|
+
| --- | --- | --- |
|
|
279
|
+
| **cheqd** (on-chain) | `did:cheqd` resolution, StatusList2021 revocation, DID-Linked Resources + registrar | [src/integrations/cheqd](./src/integrations/cheqd/README.md) |
|
|
280
|
+
| **OAuth / OIDC** | PKCE + protected-resource metadata; generic-OIDC reference adapter | [`@kya-os/mcp/authz`](./src/authz/) · [example](./examples/authz-inspector/) |
|
|
281
|
+
| **Verifiable audit** | RFC 9162 ledger; local or Checkpoint recorders, on-chain anchoring | [AUDITABILITY.md](./AUDITABILITY.md) |
|
|
282
|
+
|
|
283
|
+
Enabling a module is a small config change. cheqd, for example, is a resolver
|
|
284
|
+
you register under `didResolvers`:
|
|
275
285
|
|
|
276
286
|
```typescript
|
|
277
|
-
import { RuntimeFetchProvider, withKyaOs, NodeCryptoProvider } from '@kya-os/mcp';
|
|
278
287
|
import { cheqdResolver } from '@kya-os/mcp/cheqd';
|
|
279
288
|
|
|
280
|
-
const crypto = new NodeCryptoProvider();
|
|
281
|
-
const fetchProvider = new RuntimeFetchProvider();
|
|
282
|
-
const didResolvers = {
|
|
283
|
-
cheqd: cheqdResolver({ resolverUrl: 'https://resolver.cheqd.net' }),
|
|
284
|
-
};
|
|
285
|
-
|
|
286
289
|
await withKyaOs(server, {
|
|
287
290
|
crypto,
|
|
288
291
|
delegation: {
|
|
289
|
-
|
|
290
|
-
didResolvers,
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
`did:cheqd` is resolved only when a resolver for the `cheqd` DID method is
|
|
296
|
-
explicitly supplied. The core middleware and fetch provider use the generic
|
|
297
|
-
`didResolvers` registry; cheqd-specific URL/cache/header options live in the
|
|
298
|
-
`cheqdResolver()` factory from `@kya-os/mcp/cheqd`.
|
|
299
|
-
`RuntimeFetchProvider` accepts the same registry when it is used directly by a
|
|
300
|
-
standalone verifier or operator script.
|
|
301
|
-
Unsupported methods, malformed DIDs, fetch failures, invalid JSON, malformed DID
|
|
302
|
-
Documents, and DID id mismatches fail closed by returning `null`. The resolver
|
|
303
|
-
accepts both raw DID Documents and Universal Resolver-style DID Resolution
|
|
304
|
-
Results (`{ didDocument: ... }`).
|
|
305
|
-
|
|
306
|
-
### Registrar writes
|
|
307
|
-
|
|
308
|
-
Registrar writes are explicit operator/admin actions. The package exposes
|
|
309
|
-
`CheqdDidRegistrarClient` for cheqd DID Registrar `/create`, `/update`, and
|
|
310
|
-
`/{did}/create-resource` flows, using cheqd's client-managed-secret pattern:
|
|
311
|
-
the registrar returns a serialized payload, your signer signs it, and the
|
|
312
|
-
signature is submitted back. Private keys are not sent to the registrar.
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
import {
|
|
316
|
-
CheqdDidRegistrarClient,
|
|
317
|
-
createLocalEd25519CheqdRegistrarSigner,
|
|
318
|
-
} from '@kya-os/mcp/cheqd';
|
|
319
|
-
import {
|
|
320
|
-
NodeCryptoProvider,
|
|
321
|
-
} from '@kya-os/mcp';
|
|
322
|
-
|
|
323
|
-
const crypto = new NodeCryptoProvider();
|
|
324
|
-
const registrar = new CheqdDidRegistrarClient({
|
|
325
|
-
registrarUrl: 'https://did-registrar-staging.cheqd.net/1.0',
|
|
326
|
-
fetchProvider,
|
|
327
|
-
// Optional static or async auth headers for private registrar deployments.
|
|
328
|
-
// headers: async () => ({ Authorization: `Bearer ${token}` }),
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
const signer = createLocalEd25519CheqdRegistrarSigner({
|
|
332
|
-
cryptoProvider: crypto,
|
|
333
|
-
privateKey: process.env.CHEQD_DID_PRIVATE_KEY_BASE64!,
|
|
334
|
-
verificationMethodId: 'did:cheqd:testnet:...#key-1',
|
|
335
|
-
signatureEncoding: 'base64url',
|
|
336
|
-
});
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
For mainnet, run or contract against your own fee-payer registrar deployment and
|
|
340
|
-
provide the signer hook from your KMS/HSM boundary. The local Ed25519 helper is
|
|
341
|
-
for simple controlled deployments and tests; it still signs locally and sends
|
|
342
|
-
only signatures to the registrar.
|
|
343
|
-
|
|
344
|
-
Runtime proof generation does not perform registrar writes. Create/update/DLR
|
|
345
|
-
publishing should be triggered by an explicit operator workflow, deployment
|
|
346
|
-
step, or admin tool.
|
|
347
|
-
|
|
348
|
-
### DID linkage
|
|
349
|
-
|
|
350
|
-
For `did:web` <-> `did:cheqd` binding, publish reciprocal `alsoKnownAs` values
|
|
351
|
-
and verify both DID Documents with `verifyDidLinkage()`. `buildDidWebDocument`
|
|
352
|
-
can include the `did:cheqd` reference on the `did:web` side, while
|
|
353
|
-
`updateCheqdAlsoKnownAs()` updates the cheqd side through the registrar.
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
import { verifyDidLinkage } from '@kya-os/mcp';
|
|
357
|
-
import { updateCheqdAlsoKnownAs } from '@kya-os/mcp/cheqd';
|
|
358
|
-
|
|
359
|
-
await updateCheqdAlsoKnownAs({
|
|
360
|
-
didWeb: 'did:web:agent.example.com',
|
|
361
|
-
didCheqd: 'did:cheqd:testnet:...',
|
|
362
|
-
resolver: cheqdResolver,
|
|
363
|
-
registrar,
|
|
364
|
-
signer,
|
|
365
|
-
verificationMethodId: 'did:cheqd:testnet:...#key-1',
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
const linkage = verifyDidLinkage({
|
|
369
|
-
primaryDid: 'did:web:agent.example.com',
|
|
370
|
-
secondaryDid: 'did:cheqd:testnet:...',
|
|
371
|
-
primaryDidDocument: didWebDocument,
|
|
372
|
-
secondaryDidDocument: didCheqdDocument,
|
|
373
|
-
});
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### DID-Linked Resource helpers
|
|
377
|
-
|
|
378
|
-
DID-Linked Resource helpers are intended for durable manifests only. Supported
|
|
379
|
-
artifact types are:
|
|
380
|
-
|
|
381
|
-
- `CapabilityManifest`
|
|
382
|
-
- `ConformanceManifest`
|
|
383
|
-
- `AccessHashManifest`
|
|
384
|
-
- `TrustConfigManifest`
|
|
385
|
-
|
|
386
|
-
`prepareCheqdDlrResource()` validates the artifact, canonicalizes its `content`
|
|
387
|
-
with JSON Canonicalization Scheme, computes or validates a `sha256:<64 hex>`
|
|
388
|
-
content hash, and returns a registrar resource body. Updates are modeled as new
|
|
389
|
-
resource versions under the same resource `name` and `type`; prior resources are
|
|
390
|
-
not overwritten. Do not write high-volume tool calls, raw operational logs, or
|
|
391
|
-
normal runtime proof events to cheqd; keep those in your normal audit/hash
|
|
392
|
-
stores.
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
import { prepareCheqdDlrResource } from '@kya-os/mcp/cheqd';
|
|
396
|
-
|
|
397
|
-
const prepared = await prepareCheqdDlrResource({
|
|
398
|
-
type: 'TrustConfigManifest',
|
|
399
|
-
subjectDid: 'did:cheqd:testnet:...',
|
|
400
|
-
name: 'agent-trust-config',
|
|
401
|
-
resourceType: 'TrustConfigManifest',
|
|
402
|
-
version: '2026-06-01',
|
|
403
|
-
content: {
|
|
404
|
-
acceptedDidMethods: ['did:web', 'did:key', 'did:cheqd'],
|
|
405
|
-
requiredLinkage: { type: 'alsoKnownAs', bidirectional: true },
|
|
292
|
+
didResolvers: { cheqd: cheqdResolver({ resolverUrl: 'https://resolver.cheqd.net' }) },
|
|
406
293
|
},
|
|
407
|
-
}, crypto);
|
|
408
|
-
|
|
409
|
-
await registrar.createResource({
|
|
410
|
-
did: 'did:cheqd:testnet:...',
|
|
411
|
-
resource: prepared.resource,
|
|
412
|
-
signer,
|
|
413
|
-
verificationMethodId: 'did:cheqd:testnet:...#key-1',
|
|
414
294
|
});
|
|
415
295
|
```
|
|
416
296
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
Live registrar coverage is opt-in because it performs real writes to cheqd
|
|
422
|
-
testnet. The default endpoint is the cheqd-published testnet staging registrar;
|
|
423
|
-
override it only with another testnet registrar. The live test creates a testnet
|
|
424
|
-
DID, adds a realistic `did:web` alias via `alsoKnownAs`, then publishes one
|
|
425
|
-
resource for each supported KYA DLR artifact type.
|
|
426
|
-
|
|
427
|
-
```powershell
|
|
428
|
-
$env:KYA_OS_CHEQD_E2E = '1'
|
|
429
|
-
npm run test:e2e:cheqd:testnet
|
|
430
|
-
Remove-Item Env:\KYA_OS_CHEQD_E2E
|
|
431
|
-
```
|
|
297
|
+
Its full reference (registrar writes, `did:web` <-> `did:cheqd` linkage,
|
|
298
|
+
DID-Linked Resource helpers, live testnet E2E) lives in
|
|
299
|
+
[src/integrations/cheqd/README.md](./src/integrations/cheqd/README.md); see
|
|
300
|
+
[examples/cheqd-dlr](./examples/cheqd-dlr/) for a complete operator flow.
|
|
432
301
|
|
|
433
|
-
|
|
302
|
+
### Adapter seams (bring your own)
|
|
434
303
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
| `KYA_OS_CHEQD_TESTNET_REGISTRAR_URL` | `https://did-registrar-staging.cheqd.net/1.0` |
|
|
438
|
-
| `KYA_OS_CHEQD_TESTNET_RESOLVER_URL` | `https://resolver.cheqd.net` |
|
|
439
|
-
| `KYA_OS_CHEQD_E2E_TIMEOUT_MS` | `180000` |
|
|
304
|
+
Every external dependency is a typed interface with an in-memory default, so you
|
|
305
|
+
can swap in your own backend without touching the middleware.
|
|
440
306
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
307
|
+
| Seam | Default | Swap in |
|
|
308
|
+
| --- | --- | --- |
|
|
309
|
+
| DID resolution — `DIDResolver` | `did:key`, `did:web` | `did:cheqd`, custom |
|
|
310
|
+
| Revocation — `StatusListResolver` | none | cheqd StatusList2021 |
|
|
311
|
+
| State — `GrantStore`, `SessionStore`, `NonceCacheProvider`, `PendingFlowStore` | in-memory | Redis, DynamoDB, KV, Durable Objects, DB ([multi-instance](#multi-instance-deployments)) |
|
|
312
|
+
| Crypto — `CryptoProvider` | Node, WebCrypto | your KMS / HSM |
|
|
313
|
+
| Policy — `PolicyEngine` | default | custom |
|
|
444
314
|
|
|
445
315
|
## Links
|
|
446
316
|
|
package/SPEC.md
CHANGED
|
@@ -548,13 +548,26 @@ interface DelegationConstraints {
|
|
|
548
548
|
};
|
|
549
549
|
scopes: Array<{
|
|
550
550
|
resource: string;
|
|
551
|
-
matcher: 'exact' | 'prefix' | 'regex';
|
|
551
|
+
matcher: 'exact' | 'prefix' | 'path-prefix' | 'regex';
|
|
552
552
|
constraints?: Record<string, unknown>;
|
|
553
553
|
}>;
|
|
554
554
|
};
|
|
555
555
|
}
|
|
556
556
|
```
|
|
557
557
|
|
|
558
|
+
#### CRISP scope matchers
|
|
559
|
+
|
|
560
|
+
| `matcher` | Semantics | Intended value |
|
|
561
|
+
|-----------|-----------|----------------|
|
|
562
|
+
| `exact` | Strict string equality. | Any. Preferred for sensitive resources (§11.4). |
|
|
563
|
+
| `prefix` | Character-level: the value starts with the pattern; one trailing `*` is optional sugar. An empty base grants nothing. | Scope **identifiers** (`repo:` matches `repo:write`). |
|
|
564
|
+
| `path-prefix` | Path-level: the value equals the pattern or lies beneath it as a `/`-separated path. `notes` matches `notes` and `notes/2026/plan.md`, never `notesx/secret.md`. A trailing `/` or `/*` is optional sugar; an empty base grants nothing. | Resource **paths** (files, folders, URL paths). |
|
|
565
|
+
| `regex` | Anchored full-string match; invalid patterns never match. Verifiers bound pattern and value length and reject nested-quantifier patterns. | Any; issuers accepting patterns from untrusted parties SHOULD prefer the other three. |
|
|
566
|
+
|
|
567
|
+
Verifiers MUST treat `prefix` and `path-prefix` as distinct: applying `prefix`
|
|
568
|
+
to a resource path lets a grant for `notes` authorize `notesx/secret.md`, which
|
|
569
|
+
is a boundary escape, not a match.
|
|
570
|
+
|
|
558
571
|
### 6.4 Delegation Graph
|
|
559
572
|
|
|
560
573
|
Delegations form a directed acyclic graph (DAG):
|
|
@@ -582,7 +595,7 @@ Delegations form a directed acyclic graph (DAG):
|
|
|
582
595
|
|
|
583
596
|
A `DelegationCredential` may authorize multiple resources, either via a list
|
|
584
597
|
of explicit scope strings (`constraints.scopes[]`) or via pattern matchers
|
|
585
|
-
(`constraints.crisp.scopes[].matcher`: `'exact' | 'prefix' | 'regex'`). When
|
|
598
|
+
(`constraints.crisp.scopes[].matcher`: `'exact' | 'prefix' | 'path-prefix' | 'regex'`). When
|
|
586
599
|
the credential is used to invoke a specific tool or resource, the invocation
|
|
587
600
|
MUST designate the specific scope being exercised, and verifiers MUST confirm
|
|
588
601
|
that the designated scope is a member of (or matches a pattern in) the
|
|
@@ -878,6 +891,11 @@ interface ProofMeta {
|
|
|
878
891
|
outcome?: 'allowed' | 'denied' | 'step_up_required' | 'needs_authorization';
|
|
879
892
|
// Authorization outcome; ABSENT ⇒ implicitly 'allowed' (success)
|
|
880
893
|
reason?: string; // Human-readable reason on a non-'allowed' outcome
|
|
894
|
+
prf?: 'org.kya-os/response-proof.envelope';
|
|
895
|
+
// Response-proof profile discriminator, COVERED by the
|
|
896
|
+
// JWS signature. ABSENT ⇒ the body profile (body-only
|
|
897
|
+
// responseHash, the original wire shape). Verifiers
|
|
898
|
+
// MUST reject any other value (fail-closed; §7.3).
|
|
881
899
|
}
|
|
882
900
|
```
|
|
883
901
|
|
|
@@ -895,7 +913,16 @@ Request canonicalization includes:
|
|
|
895
913
|
}
|
|
896
914
|
```
|
|
897
915
|
|
|
898
|
-
Response canonicalization is the
|
|
916
|
+
Response canonicalization is selected by the proof's response-proof profile, named by the signature-covered `prf` claim (§7.2):
|
|
917
|
+
|
|
918
|
+
- **body** (`prf` ABSENT — the original profile): the `data` field only (excludes `_meta`).
|
|
919
|
+
Under MCP carriage, `data` is the result's `content` array; result members outside it are NOT covered.
|
|
920
|
+
- **envelope** (`prf: "org.kya-os/response-proof.envelope"`): the ENTIRE result object with the top-level `_meta` member removed, mirroring the request side's `{method, params minus _meta}` rule.
|
|
921
|
+
This authenticates result members such as `structuredContent`, `isError`, and `resultType` that the body profile leaves uncovered, while `_meta` stays intermediary-mutable — which is what lets the proof itself be attached there after signing.
|
|
922
|
+
|
|
923
|
+
A verifier derives the profile from the received proof's own `prf` claim, never from configuration, and MUST reject an unrecognized `prf` outright.
|
|
924
|
+
Because `prf` is covered by the signature, stripping it from an envelope-profile proof breaks verification — a downgrade to body-only semantics is a hard failure, not a silent fallback.
|
|
925
|
+
Producers default to the body profile (wire compatibility with existing verifiers); the envelope profile is an explicit opt-in (`responseProofProfile` in the middleware configuration).
|
|
899
926
|
|
|
900
927
|
### 7.4 JWS Compact Serialization
|
|
901
928
|
|
|
@@ -927,6 +954,9 @@ BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)
|
|
|
927
954
|
}
|
|
928
955
|
```
|
|
929
956
|
|
|
957
|
+
An envelope-profile proof (§7.3) additionally carries its `prf` claim in this payload — profile selection is signed, never advisory.
|
|
958
|
+
Every optional `ProofMeta` member present on the proof (`scopeId`, `delegationRef`, `clientDid`, `outcome`, `reason`, `prf`) appears in the payload; the mirrored `meta` block and the decoded payload MUST reconcile exactly.
|
|
959
|
+
|
|
930
960
|
**Denial** and **step-up** proofs have no response body, so `responseHash` is
|
|
931
961
|
omitted and `outcome` (plus an optional `reason`) is included instead. Keys
|
|
932
962
|
remain RFC 8785-sorted:
|
|
@@ -1040,8 +1070,9 @@ verifier treats `_meta` keys that are *not* KYA-OS's proof key:
|
|
|
1040
1070
|
`_meta` keys to the application layer rather than discarding them.
|
|
1041
1071
|
|
|
1042
1072
|
Under no policy does a verifier include any non-proof `_meta` key in a hash or
|
|
1043
|
-
signature computation. The signature covers the
|
|
1044
|
-
|
|
1073
|
+
signature computation. The signature covers the profile-selected response
|
|
1074
|
+
material only — the `data` body under the body profile, the result envelope minus
|
|
1075
|
+
its top-level `_meta` under the envelope profile — and never covers `_meta` itself (§7.3).
|
|
1045
1076
|
|
|
1046
1077
|
> **Note.** The proof object's shape is normatively defined by
|
|
1047
1078
|
> `schemas/detached-proof.json` (`$schema: draft/2020-12`). The *placement* key
|
|
@@ -1275,27 +1306,35 @@ sufficient on its own: the resulting DelegationCredential is still verified per
|
|
|
1275
1306
|
|
|
1276
1307
|
## 10. Well-Known Endpoint
|
|
1277
1308
|
|
|
1278
|
-
KYA-OS servers SHOULD expose `/.well-known/mcp
|
|
1309
|
+
KYA-OS servers SHOULD expose `/.well-known/mcp`. The normative shape is
|
|
1310
|
+
`schemas/well-known-mcpi.json`; the example below validates against it:
|
|
1279
1311
|
|
|
1280
1312
|
```json
|
|
1281
1313
|
{
|
|
1282
|
-
"did": "did:web:mcp-server.example.com",
|
|
1283
1314
|
"version": "1.0.0",
|
|
1315
|
+
"serverDid": "did:web:mcp-server.example.com",
|
|
1284
1316
|
"capabilities": {
|
|
1317
|
+
"proofs": true,
|
|
1285
1318
|
"delegation": true,
|
|
1286
|
-
"
|
|
1287
|
-
"
|
|
1319
|
+
"statusList": true,
|
|
1320
|
+
"crisp": true
|
|
1288
1321
|
},
|
|
1289
|
-
"supported_did_methods": ["did:key", "did:web", "did:cheqd"],
|
|
1290
|
-
"proof_algorithms": ["EdDSA"],
|
|
1291
1322
|
"clockSkewSeconds": 120,
|
|
1292
1323
|
"endpoints": {
|
|
1293
|
-
"
|
|
1294
|
-
|
|
1295
|
-
|
|
1324
|
+
"tools": "/mcp"
|
|
1325
|
+
},
|
|
1326
|
+
"statusListCredential": "https://mcp-server.example.com/.well-known/status/1",
|
|
1327
|
+
"supportedDidMethods": ["did:key", "did:web", "did:cheqd"],
|
|
1328
|
+
"proofAlgorithms": ["EdDSA"]
|
|
1296
1329
|
}
|
|
1297
1330
|
```
|
|
1298
1331
|
|
|
1332
|
+
`serverDid` is the server's own DID, the audience a client binds proofs to.
|
|
1333
|
+
`supportedDidMethods` and `proofAlgorithms` are advisory additional properties
|
|
1334
|
+
(the schema permits them; it does not require them). Servers running the legacy
|
|
1335
|
+
1.x session profile additionally list `endpoints.handshake`
|
|
1336
|
+
(SPEC-MCP-EXTENSION.md §4); servers on the stateless card-proof profile omit it.
|
|
1337
|
+
|
|
1299
1338
|
Servers SHOULD list `did:cheqd` only when cheqd resolution has been explicitly
|
|
1300
1339
|
configured for that deployment.
|
|
1301
1340
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"holder-binding.d.ts","sourceRoot":"","sources":["../../src/delegation/holder-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,WAAW,CAMb;AAED,MAAM,WAAW,yBAAyB;IACxC,uEAAuE;IACvE,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,sEAAsE;IACtE,MAAM,EAAE,cAAc,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"holder-binding.d.ts","sourceRoot":"","sources":["../../src/delegation/holder-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,WAAW,CAMb;AAED,MAAM,WAAW,yBAAyB;IACxC,uEAAuE;IACvE,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,sEAAsE;IACtE,MAAM,EAAE,cAAc,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,aAAa,CAAC,CAuBxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAEzE;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAErE;AAED,MAAM,WAAW,wBAAwB;IACvC,8DAA8D;IAC9D,KAAK,EAAE,aAAa,CAAC;IACrB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,uFAAuF;IACvF,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACxC,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CA4E9B"}
|
|
@@ -69,8 +69,15 @@ export async function generateRequestProof(input) {
|
|
|
69
69
|
const request = toHolderBindingRequest(toolName, args);
|
|
70
70
|
const nonce = base64urlEncodeFromBytes(await crypto.randomBytes(16));
|
|
71
71
|
const now = Math.floor(Date.now() / 1000);
|
|
72
|
+
// `sessionId` is optional on the way in (a request proof can precede any
|
|
73
|
+
// handshake), but `meta.sessionId` is a REQUIRED non-empty string in the
|
|
74
|
+
// proof schema. Defaulting to '' therefore minted a proof that could never
|
|
75
|
+
// verify: `assertHolderBinding` rejected the LEGITIMATE holder as
|
|
76
|
+
// INVALID_PROOF_STRUCTURE, indistinguishably from a thief. Mint a per-proof
|
|
77
|
+
// id instead, so the sessionless case (the stateless MCP core) works.
|
|
78
|
+
const boundSessionId = sessionId ?? `req-${base64urlEncodeFromBytes(await crypto.randomBytes(9))}`;
|
|
72
79
|
return generator.generateProof(request, undefined, {
|
|
73
|
-
sessionId:
|
|
80
|
+
sessionId: boundSessionId,
|
|
74
81
|
audience,
|
|
75
82
|
nonce,
|
|
76
83
|
timestamp: now,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"holder-binding.js","sourceRoot":"","sources":["../../src/delegation/holder-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAO9D;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,IAA6B;IAE7B,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC;AAiBD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAAgC;IAEhC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxE,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,wBAAwB,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,OAAO,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE;QACjD,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"holder-binding.js","sourceRoot":"","sources":["../../src/delegation/holder-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { DelegationCredential } from '../types/protocol.js';
|
|
2
|
-
export type ScopeMatcher = 'exact' | 'prefix' | 'regex';
|
|
2
|
+
export type ScopeMatcher = 'exact' | 'prefix' | 'path-prefix' | 'regex';
|
|
3
3
|
/**
|
|
4
4
|
* Match a single requested scope/resource value against a pattern by matcher kind.
|
|
5
5
|
*
|
|
6
6
|
* - `exact` : strict string equality.
|
|
7
7
|
* - `prefix` : value starts with the pattern (a single trailing `*` is optional sugar).
|
|
8
8
|
* An empty base ('' or lone '*') matches nothing — it will NOT grant
|
|
9
|
-
* universal scope.
|
|
9
|
+
* universal scope. Character-level: `notes` matches `notesx/secret.md`.
|
|
10
|
+
* Use it for scope ids, where the value is an identifier, not a path.
|
|
11
|
+
* - `path-prefix` : the value IS the pattern, or lies under it as a `/`-separated
|
|
12
|
+
* path (`notes` matches `notes` and `notes/a/b.md`, never
|
|
13
|
+
* `notesx/secret.md`). Trailing `/` or `/*` on the pattern is optional
|
|
14
|
+
* sugar; an empty base matches nothing. Use it for resource paths.
|
|
10
15
|
* - `regex` : anchored full-string match; never throws (invalid patterns → false).
|
|
11
16
|
*
|
|
12
17
|
* SECURITY: the regex pattern is supplied by the credential ISSUER. JS `RegExp`
|
|
@@ -20,7 +25,7 @@ export declare function matchScope(pattern: string, matcher: ScopeMatcher, value
|
|
|
20
25
|
export interface ScopeSatisfaction {
|
|
21
26
|
satisfied: boolean;
|
|
22
27
|
/**
|
|
23
|
-
* True when satisfaction came via a non-exact (prefix|regex) CrispScope matcher.
|
|
28
|
+
* True when satisfaction came via a non-exact (prefix|path-prefix|regex) CrispScope matcher.
|
|
24
29
|
* Callers should surface a warning — non-exact matchers widen effective authority.
|
|
25
30
|
*/
|
|
26
31
|
usedNonExactMatcher: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-matcher.d.ts","sourceRoot":"","sources":["../../src/delegation/scope-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAc,MAAM,sBAAsB,CAAC;AAY7E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-matcher.d.ts","sourceRoot":"","sources":["../../src/delegation/scope-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAc,MAAM,sBAAsB,CAAC;AAY7E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,GAAG,OAAO,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CA6BzF;AAkBD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,oBAAoB,GAC/B,iBAAiB,CAUnB"}
|
|
@@ -12,7 +12,12 @@ const NESTED_QUANTIFIER = /\([^()]*[+*?{][^()]*\)\s*[+*{]/;
|
|
|
12
12
|
* - `exact` : strict string equality.
|
|
13
13
|
* - `prefix` : value starts with the pattern (a single trailing `*` is optional sugar).
|
|
14
14
|
* An empty base ('' or lone '*') matches nothing — it will NOT grant
|
|
15
|
-
* universal scope.
|
|
15
|
+
* universal scope. Character-level: `notes` matches `notesx/secret.md`.
|
|
16
|
+
* Use it for scope ids, where the value is an identifier, not a path.
|
|
17
|
+
* - `path-prefix` : the value IS the pattern, or lies under it as a `/`-separated
|
|
18
|
+
* path (`notes` matches `notes` and `notes/a/b.md`, never
|
|
19
|
+
* `notesx/secret.md`). Trailing `/` or `/*` on the pattern is optional
|
|
20
|
+
* sugar; an empty base matches nothing. Use it for resource paths.
|
|
16
21
|
* - `regex` : anchored full-string match; never throws (invalid patterns → false).
|
|
17
22
|
*
|
|
18
23
|
* SECURITY: the regex pattern is supplied by the credential ISSUER. JS `RegExp`
|
|
@@ -33,6 +38,15 @@ export function matchScope(pattern, matcher, value) {
|
|
|
33
38
|
return false;
|
|
34
39
|
return value.startsWith(base);
|
|
35
40
|
}
|
|
41
|
+
case 'path-prefix': {
|
|
42
|
+
let base = pattern.endsWith('*') ? pattern.slice(0, -1) : pattern;
|
|
43
|
+
while (base.endsWith('/'))
|
|
44
|
+
base = base.slice(0, -1);
|
|
45
|
+
// Refuse to grant universal scope via an empty/`*`-only prefix.
|
|
46
|
+
if (base.length === 0)
|
|
47
|
+
return false;
|
|
48
|
+
return value === base || value.startsWith(`${base}/`);
|
|
49
|
+
}
|
|
36
50
|
case 'regex': {
|
|
37
51
|
if (pattern.length > MAX_REGEX_LEN || value.length > MAX_VALUE_LEN)
|
|
38
52
|
return false;
|