@agenticprimitives/content-primitives 1.0.0-alpha.10
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/CLAUDE.md +62 -0
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/descriptor.d.ts +67 -0
- package/dist/descriptor.d.ts.map +1 -0
- package/dist/descriptor.js +128 -0
- package/dist/descriptor.js.map +1 -0
- package/dist/entitlement.d.ts +64 -0
- package/dist/entitlement.d.ts.map +1 -0
- package/dist/entitlement.js +64 -0
- package/dist/entitlement.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/jcs.d.ts +4 -0
- package/dist/jcs.d.ts.map +1 -0
- package/dist/jcs.js +25 -0
- package/dist/jcs.js.map +1 -0
- package/dist/merkle.d.ts +29 -0
- package/dist/merkle.d.ts.map +1 -0
- package/dist/merkle.js +74 -0
- package/dist/merkle.js.map +1 -0
- package/dist/reference.d.ts +28 -0
- package/dist/reference.d.ts.map +1 -0
- package/dist/reference.js +35 -0
- package/dist/reference.js.map +1 -0
- package/dist/reserved.d.ts +12 -0
- package/dist/reserved.d.ts.map +1 -0
- package/dist/reserved.js +19 -0
- package/dist/reserved.js.map +1 -0
- package/dist/resolution.d.ts +41 -0
- package/dist/resolution.d.ts.map +1 -0
- package/dist/resolution.js +35 -0
- package/dist/resolution.js.map +1 -0
- package/dist/types.d.ts +194 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/spec.md +16 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @agenticprimitives/content-primitives — Claude guide
|
|
2
|
+
|
|
3
|
+
Name → resolve → commit → entitlement-gate → cite, for content that lives
|
|
4
|
+
off-platform and is controlled by third-party rights holders. **Bible verses are
|
|
5
|
+
its first usage domain (spec 267), not its vocabulary.**
|
|
6
|
+
|
|
7
|
+
## The one idea
|
|
8
|
+
|
|
9
|
+
**Content is not an Agent.** A `CanonicalLocus` (a passage address) is
|
|
10
|
+
**scheme-anchored** — a deterministic `locusId`, never registered, never an SA
|
|
11
|
+
facet. Only *issuers/corpora* and *parties* are Smart Agents (ADR-0010 still
|
|
12
|
+
governs them via `agent-naming`). This is the FRBR Work/Item split and the reason
|
|
13
|
+
this is a new substrate, not an `agent-naming` record type. See ADR-0033.
|
|
14
|
+
|
|
15
|
+
## What this package owns (FRBR)
|
|
16
|
+
|
|
17
|
+
- `CanonicalLocus` (Work) — `computeLocusId(scheme, normalizedPath)`.
|
|
18
|
+
- `CorpusManifest` (Manifestation) — issuer-owned, Merkle `corpusRoot`.
|
|
19
|
+
- `ContentDescriptor` (Item) — the deliverable leaf; `commitment` +
|
|
20
|
+
`retrievalPointer` + issuer signature. **Never the text.**
|
|
21
|
+
- `Entitlement` / `CitationAssertion` — VC 2.0 subjects (envelope from
|
|
22
|
+
`verifiable-credentials`).
|
|
23
|
+
- Merkle helpers (`/merkle` subpath), commitment + descriptor verification,
|
|
24
|
+
fail-closed `evaluateEntitlement`.
|
|
25
|
+
|
|
26
|
+
## Hard rules (ADR-0033 — do not break)
|
|
27
|
+
|
|
28
|
+
- **R3 — no rendering text** in any descriptor, a commitment preimage we store,
|
|
29
|
+
or on-chain. Descriptors carry a `retrievalPointer`.
|
|
30
|
+
- **R1 — no licensed/copyrighted content** in src/tests/fixtures (CI:
|
|
31
|
+
`check:no-licensed-content`).
|
|
32
|
+
- **R4 — content-agnostic.** No faith/vertical vocabulary; `contentType` + the
|
|
33
|
+
`ReferenceScheme` are app-injected (CI: `check:no-domain-in-packages`).
|
|
34
|
+
- **R5 — trust = issuer signature + access policy**, never a platform claim.
|
|
35
|
+
|
|
36
|
+
## Boundary
|
|
37
|
+
|
|
38
|
+
Allowed imports: `@agenticprimitives/types`, `@agenticprimitives/verifiable-credentials`,
|
|
39
|
+
`viem`. ERC-1271 verification is **injected** (`SignatureVerifier`, ADR-0006) — do
|
|
40
|
+
NOT import `agent-account`/`agent-naming`; apps compose them.
|
|
41
|
+
|
|
42
|
+
## Drift triggers — STOP
|
|
43
|
+
|
|
44
|
+
- "Register a locus / give a verse an SA / put it in `agent-naming`" — **STOP.**
|
|
45
|
+
Loci are scheme-anchored content (ADR-0033).
|
|
46
|
+
- "Store the verse text / put text in the commitment we keep / on-chain" — **STOP** (R3).
|
|
47
|
+
- "Hardcode a translation, book table, OSIS grammar, or `'bible-verse'`" — **STOP.**
|
|
48
|
+
App layer (spec 267); inject a `ReferenceScheme`.
|
|
49
|
+
- "Implement the ZK/payment reserved fns" — that's Phase 4/5 (spec 266 §6).
|
|
50
|
+
|
|
51
|
+
## Read first
|
|
52
|
+
|
|
53
|
+
`../../specs/266-verifiable-content-substrate.md` ·
|
|
54
|
+
`../../docs/architecture/decisions/0033-content-agnostic-verifiable-content-firewall.md`
|
|
55
|
+
· then `src/index.ts`.
|
|
56
|
+
|
|
57
|
+
## Validate
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm --filter @agenticprimitives/content-primitives typecheck
|
|
61
|
+
pnpm --filter @agenticprimitives/content-primitives test
|
|
62
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agentic Trust Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @agenticprimitives/content-primitives
|
|
2
|
+
|
|
3
|
+
The verifiable content substrate — name, resolve, commit, entitlement-gate, and cite content that lives off-platform and is controlled by third-party rights holders.
|
|
4
|
+
|
|
5
|
+
Agents increasingly act on content they do not own: quoting a licensed text, citing a canonical passage, gating access to a rights-held corpus. The naive approaches all fail — copy the text and you violate the license; trust the platform and the citation is unverifiable; mint every passage an identity and the identity layer drowns. This package takes the third path: content is **never an agent and never stored**. A unit of content gets a deterministic, scheme-anchored address; an issuer (who IS a Smart Agent) signs a commitment to it; readers verify the signature, evaluate the entitlement, and cite the result — all without the text ever entering the substrate ([ADR-0033](../../docs/architecture/decisions/0033-content-agnostic-verifiable-content-firewall.md)).
|
|
6
|
+
|
|
7
|
+
The package is strictly content-agnostic: no rendering text in any descriptor or commitment we store (R3), no licensed material in the repo (R1), and the reference grammar is app-injected, never hardcoded (R4). Trust derives from the issuer's signature and access policy, never a platform claim (R5).
|
|
8
|
+
|
|
9
|
+
Part of [agenticprimitives](../../README.md) — the trust substrate for the agent economy: one canonical Smart Agent identity with custody, delegation, naming, credentials, and audit evidence designed as one system.
|
|
10
|
+
|
|
11
|
+
## Model (FRBR)
|
|
12
|
+
|
|
13
|
+
| FRBR | Type | Notes |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Work | `CanonicalLocus` | scheme-anchored deterministic `locusId`; never registered |
|
|
16
|
+
| Manifestation | `CorpusManifest` | issuer-owned, Merkle `corpusRoot`, signed |
|
|
17
|
+
| Item | `ContentDescriptor` | commitment + `retrievalPointer` + issuer signature — **never text** |
|
|
18
|
+
| — | `Entitlement` / `CitationAssertion` | W3C VC 2.0 subjects |
|
|
19
|
+
|
|
20
|
+
The FRBR Work/Item split is why this is a new substrate and not another naming-record type: a content locus is an address computed from a reference scheme, while only issuers, corpora owners, and parties are Smart Agents (ADR-0010 still governs *them*).
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
buildCanonicalLocus, corpusRef, contentCommitment,
|
|
27
|
+
buildContentDescriptor, verifyContentDescriptor,
|
|
28
|
+
evaluateEntitlement, buildCitationAssertion,
|
|
29
|
+
} from '@agenticprimitives/content-primitives';
|
|
30
|
+
|
|
31
|
+
// 1. App supplies the reference grammar (kept out of the package — R4).
|
|
32
|
+
const clauseScheme = { id: 'standard-clause', normalize: (p: string) => /* canonical form */ p };
|
|
33
|
+
const locus = buildCanonicalLocus(clauseScheme, 'Part 4, Section 2'); // -> { locusId, path: 'Part4.Section2' }
|
|
34
|
+
|
|
35
|
+
// 2. Issuer commits to off-platform text + signs a descriptor (no text on-chain — R3).
|
|
36
|
+
const commitment = contentCommitment(renderingText); // keccak256, off-platform
|
|
37
|
+
const descriptor = await buildContentDescriptor(
|
|
38
|
+
{ locusId: locus.locusId, corpusRef: ref, contentType: 'standard-clause',
|
|
39
|
+
commitment, proofPolicy: 'signature', accessPolicy: 'public',
|
|
40
|
+
retrievalPointer: 'content://standard-clause/v3/Part4.Section2', issuer },
|
|
41
|
+
issuerSign, // signs descriptorHash via the issuer SA (ERC-1271)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// 3. Reader verifies (signature injected — ADR-0006) + gates + cites.
|
|
45
|
+
const ok = await verifyContentDescriptor(descriptor, { verifySignature });
|
|
46
|
+
const gate = evaluateEntitlement(descriptor.accessPolicy, descriptor.corpusRef);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Entitlement evaluation is fail-closed; Merkle helpers ship under the `/merkle` subpath; ERC-1271 signature verification is dependency-injected so the package stays at the base of the graph.
|
|
50
|
+
|
|
51
|
+
## Status
|
|
52
|
+
|
|
53
|
+
Phases 1–3 (naming, commitment, descriptor verification, entitlement gating, citation) are implemented and tested. **ZK proofs and paid access are reserved (Phase 4/5) and throw until implemented** — the full SDK surface and phased roadmap live in [spec 266](../../specs/266-verifiable-content-substrate.md) (see [`spec.md`](./spec.md)).
|
|
54
|
+
|
|
55
|
+
Testnet/pilot-ready. Production launch is gated on the public checklist in the root README — including third-party contract audit and governance key rotation. Track every security finding live in [`docs/audits/findings.yaml`](../../docs/audits/findings.yaml).
|
|
56
|
+
|
|
57
|
+
## Build
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm --filter @agenticprimitives/content-primitives typecheck
|
|
61
|
+
pnpm --filter @agenticprimitives/content-primitives test
|
|
62
|
+
pnpm --filter @agenticprimitives/content-primitives build
|
|
63
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
import type { BuildDescriptorInput, ContentCommitment, ContentDescriptor, DelegatingSigner, DelegatedAuthorityVerifier, SignatureVerifier } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The frozen Phase-1 normalization profile id (spec 266 §7). Rules of
|
|
5
|
+
* `canonical-text-v1`: NFC; trim leading/trailing whitespace; collapse internal
|
|
6
|
+
* whitespace runs to a single space; text body only (verse numbers, labels,
|
|
7
|
+
* footnotes, headings, cross-refs, formatting are excluded by the caller before
|
|
8
|
+
* commitment); digest SHA-256 over UTF-8. Bump the version to change any rule.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NORMALIZATION_V1 = "ap:normalization:canonical-text-v1";
|
|
11
|
+
/** Apply the canonical-text-v1 normalization to a rendering body. */
|
|
12
|
+
export declare function canonicalizeRendering(text: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Structured commitment to an off-platform rendering (spec 266 §7):
|
|
15
|
+
* SHA-256 of the canonical-text-v1-normalized body. SHA-256 (not keccak) keeps
|
|
16
|
+
* the content commitment portable/standard; it is verified off-chain and never
|
|
17
|
+
* stored on-chain (ADR-0033 R3). Binds a descriptor to a rendering WITHOUT
|
|
18
|
+
* revealing it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function contentCommitment(text: string): ContentCommitment;
|
|
21
|
+
/** True iff `text` canonicalizes+hashes to the commitment value. */
|
|
22
|
+
export declare function verifyCommitment(text: string, commitment: ContentCommitment): boolean;
|
|
23
|
+
/** Throwing variant of {@link verifyCommitment}. */
|
|
24
|
+
export declare function assertCommitment(text: string, commitment: ContentCommitment): void;
|
|
25
|
+
/**
|
|
26
|
+
* Deterministic, domain-separated hash of the unsigned descriptor — both its
|
|
27
|
+
* cryptographic identity (ContentDescriptorId) AND the ERC-1271 signing
|
|
28
|
+
* preimage. `keccak256("ap:content-descriptor:v1\0" || JCS(unsigned))`. The
|
|
29
|
+
* domain separator keeps it from ever colliding with a canonical-locus id.
|
|
30
|
+
*/
|
|
31
|
+
export declare function descriptorHash(d: BuildDescriptorInput): Hex;
|
|
32
|
+
/**
|
|
33
|
+
* Build a signed {@link ContentDescriptor}. `sign` signs {@link descriptorHash} — either as the issuer's
|
|
34
|
+
* own Smart Agent (ERC-1271), or, when `delegatingSigner` is given, as the issuer-AUTHORIZED operational
|
|
35
|
+
* key (e.g. Cloud KMS). The attached `delegatingSigner` lets a verifier root trust in the issuer SA while
|
|
36
|
+
* the day-to-day signing key is delegated/rotatable (no issuer custodian key held).
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildContentDescriptor(input: BuildDescriptorInput, sign: (hash: Hex) => Promise<Hex> | Hex, delegatingSigner?: DelegatingSigner): Promise<ContentDescriptor>;
|
|
39
|
+
export interface VerifyDescriptorOpts {
|
|
40
|
+
/** Verifies an ERC-1271/ECDSA signature by a signer (injected; ADR-0006). */
|
|
41
|
+
verifySignature: SignatureVerifier;
|
|
42
|
+
/** Verifies an issuer→delegate authorization leaf — REQUIRED when the descriptor carries
|
|
43
|
+
* `delegatingSigner` (else delegate-signed descriptors fail closed). Injected (ADR-0006). */
|
|
44
|
+
verifyDelegatedAuthority?: DelegatedAuthorityVerifier;
|
|
45
|
+
/** Required when proofPolicy is `merkle-membership-v1`. */
|
|
46
|
+
corpusRoot?: Hex;
|
|
47
|
+
inclusionProof?: Hex[];
|
|
48
|
+
/** Current time (seconds) for validity/status checks; defaults to now. */
|
|
49
|
+
nowSeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
/** Result of {@link verifyContentDescriptor} — an auditable VerificationResult. */
|
|
52
|
+
export interface VerificationResult {
|
|
53
|
+
ok: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
signatureVerified: boolean;
|
|
56
|
+
statusOk: boolean;
|
|
57
|
+
withinValidity: boolean;
|
|
58
|
+
inclusionVerified?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Verify a descriptor fail-closed (spec 266 §7): status must be `active`, the
|
|
62
|
+
* validity window must contain `now`, the issuer ERC-1271 signature over the
|
|
63
|
+
* canonical descriptor hash must verify, and — for `merkle-membership-v1` — the
|
|
64
|
+
* commitment must prove under `corpusRoot`. Reserved proof policies are rejected.
|
|
65
|
+
*/
|
|
66
|
+
export declare function verifyContentDescriptor(d: ContentDescriptor, opts: VerifyDescriptorOpts): Promise<VerificationResult>;
|
|
67
|
+
//# sourceMappingURL=descriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../src/descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,GAAG,EAAE,MAAM,MAAM,CAAC;AAEpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAI9J;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,uCAAuC,CAAC;AAErE,qEAAqE;AACrE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAOjE;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAGrF;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAIlF;AAKD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,GAAG,CAE3D;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EACvC,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,eAAe,EAAE,iBAAiB,CAAC;IACnC;kGAC8F;IAC9F,wBAAwB,CAAC,EAAE,0BAA0B,CAAC;IACtD,2DAA2D;IAC3D,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC;IACvB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,CAAC,EAAE,iBAAiB,EACpB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAiD7B"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { keccak256, sha256, concat, toBytes } from 'viem';
|
|
2
|
+
import { jcsCanonicalize } from '@agenticprimitives/verifiable-credentials';
|
|
3
|
+
import { leafHash, verifyInclusion } from './merkle.js';
|
|
4
|
+
import { CommitmentMismatchError } from './errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* The frozen Phase-1 normalization profile id (spec 266 §7). Rules of
|
|
7
|
+
* `canonical-text-v1`: NFC; trim leading/trailing whitespace; collapse internal
|
|
8
|
+
* whitespace runs to a single space; text body only (verse numbers, labels,
|
|
9
|
+
* footnotes, headings, cross-refs, formatting are excluded by the caller before
|
|
10
|
+
* commitment); digest SHA-256 over UTF-8. Bump the version to change any rule.
|
|
11
|
+
*/
|
|
12
|
+
export const NORMALIZATION_V1 = 'ap:normalization:canonical-text-v1';
|
|
13
|
+
/** Apply the canonical-text-v1 normalization to a rendering body. */
|
|
14
|
+
export function canonicalizeRendering(text) {
|
|
15
|
+
return text.normalize('NFC').replace(/\s+/g, ' ').trim();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Structured commitment to an off-platform rendering (spec 266 §7):
|
|
19
|
+
* SHA-256 of the canonical-text-v1-normalized body. SHA-256 (not keccak) keeps
|
|
20
|
+
* the content commitment portable/standard; it is verified off-chain and never
|
|
21
|
+
* stored on-chain (ADR-0033 R3). Binds a descriptor to a rendering WITHOUT
|
|
22
|
+
* revealing it.
|
|
23
|
+
*/
|
|
24
|
+
export function contentCommitment(text) {
|
|
25
|
+
return {
|
|
26
|
+
type: 'canonicalTextCommitment',
|
|
27
|
+
normalization: NORMALIZATION_V1,
|
|
28
|
+
algorithm: 'sha-256',
|
|
29
|
+
value: sha256(toBytes(canonicalizeRendering(text))),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** True iff `text` canonicalizes+hashes to the commitment value. */
|
|
33
|
+
export function verifyCommitment(text, commitment) {
|
|
34
|
+
if (commitment.algorithm !== 'sha-256' || commitment.normalization !== NORMALIZATION_V1)
|
|
35
|
+
return false;
|
|
36
|
+
return contentCommitment(text).value.toLowerCase() === commitment.value.toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
/** Throwing variant of {@link verifyCommitment}. */
|
|
39
|
+
export function assertCommitment(text, commitment) {
|
|
40
|
+
if (!verifyCommitment(text, commitment)) {
|
|
41
|
+
throw new CommitmentMismatchError('rendering does not match descriptor commitment');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Domain separator for the descriptor identity (distinct from the locus id). */
|
|
45
|
+
const DESCRIPTOR_DOMAIN_SEP = 'ap:content-descriptor:v1\0';
|
|
46
|
+
/**
|
|
47
|
+
* Deterministic, domain-separated hash of the unsigned descriptor — both its
|
|
48
|
+
* cryptographic identity (ContentDescriptorId) AND the ERC-1271 signing
|
|
49
|
+
* preimage. `keccak256("ap:content-descriptor:v1\0" || JCS(unsigned))`. The
|
|
50
|
+
* domain separator keeps it from ever colliding with a canonical-locus id.
|
|
51
|
+
*/
|
|
52
|
+
export function descriptorHash(d) {
|
|
53
|
+
return keccak256(concat([toBytes(DESCRIPTOR_DOMAIN_SEP), toBytes(jcsCanonicalize(d))]));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a signed {@link ContentDescriptor}. `sign` signs {@link descriptorHash} — either as the issuer's
|
|
57
|
+
* own Smart Agent (ERC-1271), or, when `delegatingSigner` is given, as the issuer-AUTHORIZED operational
|
|
58
|
+
* key (e.g. Cloud KMS). The attached `delegatingSigner` lets a verifier root trust in the issuer SA while
|
|
59
|
+
* the day-to-day signing key is delegated/rotatable (no issuer custodian key held).
|
|
60
|
+
*/
|
|
61
|
+
export async function buildContentDescriptor(input, sign, delegatingSigner) {
|
|
62
|
+
const signature = await sign(descriptorHash(input));
|
|
63
|
+
return { ...input, signature, ...(delegatingSigner ? { delegatingSigner } : {}) };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Verify a descriptor fail-closed (spec 266 §7): status must be `active`, the
|
|
67
|
+
* validity window must contain `now`, the issuer ERC-1271 signature over the
|
|
68
|
+
* canonical descriptor hash must verify, and — for `merkle-membership-v1` — the
|
|
69
|
+
* commitment must prove under `corpusRoot`. Reserved proof policies are rejected.
|
|
70
|
+
*/
|
|
71
|
+
export async function verifyContentDescriptor(d, opts) {
|
|
72
|
+
const now = opts.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
73
|
+
const statusOk = d.status === 'active';
|
|
74
|
+
const fromOk = !d.validFrom || Math.floor(new Date(d.validFrom).getTime() / 1000) <= now;
|
|
75
|
+
const untilOk = !d.validUntil || Math.floor(new Date(d.validUntil).getTime() / 1000) >= now;
|
|
76
|
+
const withinValidity = fromOk && untilOk;
|
|
77
|
+
const { signature, delegatingSigner, ...unsigned } = d;
|
|
78
|
+
const hash = descriptorHash(unsigned);
|
|
79
|
+
// Trust ALWAYS roots in the issuer SA. Either the issuer signed directly, or it authorized a delegate
|
|
80
|
+
// key (proven by the leaf) that signed — and even then the descriptor's issuer.address must equal the
|
|
81
|
+
// delegating issuer, so a delegate can never re-attribute a descriptor to a different issuer.
|
|
82
|
+
let signatureVerified;
|
|
83
|
+
let sigReason = 'issuer signature did not verify';
|
|
84
|
+
if (delegatingSigner) {
|
|
85
|
+
if (!opts.verifyDelegatedAuthority) {
|
|
86
|
+
signatureVerified = false;
|
|
87
|
+
sigReason = 'descriptor is delegate-signed but no delegated-authority verifier was provided';
|
|
88
|
+
}
|
|
89
|
+
else if (d.issuer.address.toLowerCase() !== delegatingSigner.delegatorIssuer.toLowerCase()) {
|
|
90
|
+
signatureVerified = false;
|
|
91
|
+
sigReason = 'delegatingSigner.delegatorIssuer ≠ descriptor issuer';
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const authOk = await opts.verifyDelegatedAuthority(delegatingSigner);
|
|
95
|
+
const sigOk = authOk && (await opts.verifySignature({ signer: delegatingSigner.delegateKey, hash, signature }));
|
|
96
|
+
signatureVerified = sigOk;
|
|
97
|
+
if (!authOk)
|
|
98
|
+
sigReason = 'issuer did not authorize the delegate signing key';
|
|
99
|
+
else if (!sigOk)
|
|
100
|
+
sigReason = 'delegate-key signature did not verify';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
signatureVerified = await opts.verifySignature({ signer: d.issuer.address, hash, signature });
|
|
105
|
+
}
|
|
106
|
+
const base = { ok: false, signatureVerified, statusOk, withinValidity };
|
|
107
|
+
if (!statusOk)
|
|
108
|
+
return { ...base, reason: `descriptor status is ${d.status}` };
|
|
109
|
+
if (!withinValidity)
|
|
110
|
+
return { ...base, reason: 'descriptor outside its validity window' };
|
|
111
|
+
if (!signatureVerified)
|
|
112
|
+
return { ...base, reason: sigReason };
|
|
113
|
+
switch (d.proofPolicy) {
|
|
114
|
+
case 'issuer-signature-v1':
|
|
115
|
+
case 'issuer-signature-and-hash-v1':
|
|
116
|
+
return { ...base, ok: true };
|
|
117
|
+
case 'merkle-membership-v1': {
|
|
118
|
+
if (!d.commitment || !opts.corpusRoot || !opts.inclusionProof) {
|
|
119
|
+
return { ...base, reason: 'merkle-membership-v1 requires commitment + corpusRoot + inclusionProof' };
|
|
120
|
+
}
|
|
121
|
+
const inclusionVerified = verifyInclusion(leafHash(d.commitment.value), opts.inclusionProof, opts.corpusRoot);
|
|
122
|
+
return { ...base, ok: inclusionVerified, inclusionVerified, reason: inclusionVerified ? undefined : 'commitment not included under corpusRoot' };
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
return { ...base, reason: `proofPolicy ${d.proofPolicy} is reserved (spec 266 §6/§8)` };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=descriptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.js","sourceRoot":"","sources":["../src/descriptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { UnsignedCredential } from '@agenticprimitives/verifiable-credentials';
|
|
2
|
+
import type { Address, Hex } from '@agenticprimitives/types';
|
|
3
|
+
import type { AccessPolicy, ContentCommitment, Entitlement } from './types.js';
|
|
4
|
+
export interface EntitlementDecision {
|
|
5
|
+
decision: 'allow' | 'deny';
|
|
6
|
+
reason?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Gate retrieval against a corpus's access policy (spec 266 §3 step 5).
|
|
10
|
+
* Deterministic, fail-closed (spec 266 §7). An accessPolicy is a POLICY INPUT,
|
|
11
|
+
* not a legal grant (ADR-0033 R5): the platform still decides here.
|
|
12
|
+
* - `public` → allow.
|
|
13
|
+
* - `licensed`/`private` → require a presented {@link Entitlement} whose subject
|
|
14
|
+
* corpusRef matches and which has not expired.
|
|
15
|
+
* - unknown policy → deny.
|
|
16
|
+
*
|
|
17
|
+
* Cryptographic verification of the entitlement VC (issuer signature,
|
|
18
|
+
* revocation) is the caller's job via the `verifiable-credentials` verifier —
|
|
19
|
+
* pass an already-verified credential (ADR-0013: one mechanism, no fallback).
|
|
20
|
+
*/
|
|
21
|
+
export declare function evaluateEntitlement(accessPolicy: AccessPolicy, corpusRef: Hex, entitlement?: Entitlement, nowSeconds?: number): EntitlementDecision;
|
|
22
|
+
export interface CitationInput {
|
|
23
|
+
/** Issuer of the citation (the resolving agent's SA, CAIP-10 or DID). */
|
|
24
|
+
issuer: string;
|
|
25
|
+
subjectId: string;
|
|
26
|
+
canonicalId: Hex;
|
|
27
|
+
descriptorId: string;
|
|
28
|
+
contentType: string;
|
|
29
|
+
citationKind: 'quote' | 'reference' | 'summary' | 'paraphrase';
|
|
30
|
+
commitment?: ContentCommitment;
|
|
31
|
+
commitmentVerified: boolean;
|
|
32
|
+
contentIssuer: Address;
|
|
33
|
+
validFrom: string;
|
|
34
|
+
/** Agentic provenance (which run/output produced the citation). */
|
|
35
|
+
agentRunId?: string;
|
|
36
|
+
outputId?: string;
|
|
37
|
+
/** Optional hash of the specific quoted span + the normalization spec used. */
|
|
38
|
+
quoteSpanHash?: Hex;
|
|
39
|
+
normalizationSpec?: string;
|
|
40
|
+
underEntitlement?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build an UNSIGNED citation credential — the AI-safe citation record. The
|
|
44
|
+
* caller signs it via `verifiable-credentials.signCredential` and emits it to
|
|
45
|
+
* the audit/provenance package (spec 266 §audit; do NOT invent a parallel audit
|
|
46
|
+
* model). Carries the commitment + verification result + agentic provenance,
|
|
47
|
+
* NEVER the rendering text (ADR-0033 R3).
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildCitationAssertion(input: CitationInput): UnsignedCredential<{
|
|
50
|
+
id: string;
|
|
51
|
+
agentRunId?: string;
|
|
52
|
+
outputId?: string;
|
|
53
|
+
citationKind: 'quote' | 'reference' | 'summary' | 'paraphrase';
|
|
54
|
+
canonicalId: Hex;
|
|
55
|
+
descriptorId: string;
|
|
56
|
+
contentType: string;
|
|
57
|
+
commitment?: ContentCommitment;
|
|
58
|
+
commitmentVerified: boolean;
|
|
59
|
+
quoteSpanHash?: Hex;
|
|
60
|
+
normalizationSpec?: string;
|
|
61
|
+
issuer: Address;
|
|
62
|
+
underEntitlement?: string;
|
|
63
|
+
}>;
|
|
64
|
+
//# sourceMappingURL=entitlement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement.d.ts","sourceRoot":"","sources":["../src/entitlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,GAAG,EACd,WAAW,CAAC,EAAE,WAAW,EACzB,UAAU,GAAE,MAAsC,GACjD,mBAAmB,CAkBrB;AAED,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;IAC/D,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,kBAAkB,CAAC;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;IAC/D,WAAW,EAAE,GAAG,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC,CAsBD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate retrieval against a corpus's access policy (spec 266 §3 step 5).
|
|
3
|
+
* Deterministic, fail-closed (spec 266 §7). An accessPolicy is a POLICY INPUT,
|
|
4
|
+
* not a legal grant (ADR-0033 R5): the platform still decides here.
|
|
5
|
+
* - `public` → allow.
|
|
6
|
+
* - `licensed`/`private` → require a presented {@link Entitlement} whose subject
|
|
7
|
+
* corpusRef matches and which has not expired.
|
|
8
|
+
* - unknown policy → deny.
|
|
9
|
+
*
|
|
10
|
+
* Cryptographic verification of the entitlement VC (issuer signature,
|
|
11
|
+
* revocation) is the caller's job via the `verifiable-credentials` verifier —
|
|
12
|
+
* pass an already-verified credential (ADR-0013: one mechanism, no fallback).
|
|
13
|
+
*/
|
|
14
|
+
export function evaluateEntitlement(accessPolicy, corpusRef, entitlement, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
15
|
+
if (accessPolicy === 'public')
|
|
16
|
+
return { decision: 'allow' };
|
|
17
|
+
if (accessPolicy !== 'licensed' && accessPolicy !== 'private') {
|
|
18
|
+
return { decision: 'deny', reason: `unknown accessPolicy: ${String(accessPolicy)}` };
|
|
19
|
+
}
|
|
20
|
+
if (!entitlement) {
|
|
21
|
+
return { decision: 'deny', reason: `${accessPolicy} corpus requires an entitlement` };
|
|
22
|
+
}
|
|
23
|
+
if (entitlement.credentialSubject.corpusRef.toLowerCase() !== corpusRef.toLowerCase()) {
|
|
24
|
+
return { decision: 'deny', reason: 'entitlement is for a different corpus' };
|
|
25
|
+
}
|
|
26
|
+
if (entitlement.validUntil) {
|
|
27
|
+
const exp = Math.floor(new Date(entitlement.validUntil).getTime() / 1000);
|
|
28
|
+
if (Number.isFinite(exp) && exp < nowSeconds) {
|
|
29
|
+
return { decision: 'deny', reason: 'entitlement expired' };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { decision: 'allow' };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build an UNSIGNED citation credential — the AI-safe citation record. The
|
|
36
|
+
* caller signs it via `verifiable-credentials.signCredential` and emits it to
|
|
37
|
+
* the audit/provenance package (spec 266 §audit; do NOT invent a parallel audit
|
|
38
|
+
* model). Carries the commitment + verification result + agentic provenance,
|
|
39
|
+
* NEVER the rendering text (ADR-0033 R3).
|
|
40
|
+
*/
|
|
41
|
+
export function buildCitationAssertion(input) {
|
|
42
|
+
return {
|
|
43
|
+
'@context': ['https://www.w3.org/ns/credentials/v2'],
|
|
44
|
+
type: ['VerifiableCredential', 'CitationAssertion'],
|
|
45
|
+
issuer: input.issuer,
|
|
46
|
+
validFrom: input.validFrom,
|
|
47
|
+
credentialSubject: {
|
|
48
|
+
id: input.subjectId,
|
|
49
|
+
...(input.agentRunId ? { agentRunId: input.agentRunId } : {}),
|
|
50
|
+
...(input.outputId ? { outputId: input.outputId } : {}),
|
|
51
|
+
citationKind: input.citationKind,
|
|
52
|
+
canonicalId: input.canonicalId,
|
|
53
|
+
descriptorId: input.descriptorId,
|
|
54
|
+
contentType: input.contentType,
|
|
55
|
+
...(input.commitment ? { commitment: input.commitment } : {}),
|
|
56
|
+
commitmentVerified: input.commitmentVerified,
|
|
57
|
+
...(input.quoteSpanHash ? { quoteSpanHash: input.quoteSpanHash } : {}),
|
|
58
|
+
...(input.normalizationSpec ? { normalizationSpec: input.normalizationSpec } : {}),
|
|
59
|
+
issuer: input.contentIssuer,
|
|
60
|
+
...(input.underEntitlement ? { underEntitlement: input.underEntitlement } : {}),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=entitlement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement.js","sourceRoot":"","sources":["../src/entitlement.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** A reference path failed its scheme's normalization. */
|
|
2
|
+
export declare class InvalidReferenceError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/** A rendering's keccak commitment did not match its descriptor. */
|
|
6
|
+
export declare class CommitmentMismatchError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,oEAAoE;AACpE,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** A reference path failed its scheme's normalization. */
|
|
2
|
+
export class InvalidReferenceError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'InvalidReferenceError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** A rendering's keccak commitment did not match its descriptor. */
|
|
9
|
+
export class CommitmentMismatchError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'CommitmentMismatchError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "@agenticprimitives/content-primitives";
|
|
2
|
+
export declare const PACKAGE_STATUS: "phase-1";
|
|
3
|
+
export declare const SPEC_REF = "specs/266-verifiable-content-substrate.md";
|
|
4
|
+
export type { AccessPolicy, ProofPolicy, DescriptorStatus, RightsStatus, TrustProfile, CanonicalLocus, CanonicalLocusEnvelope, CanonicalReference, ContentCommitment, CorpusManifest, WorkMeta, IssuerIdentityRef, ContentDescriptor, BuildDescriptorInput, SignatureVerifier, DelegatingSigner, DelegatedAuthorityVerifier, Entitlement, CitationAssertion, } from './types.js';
|
|
5
|
+
export { computeCanonicalId, canonicalReference, corpusRef, LOCUS_ID_SCHEME } from './reference.js';
|
|
6
|
+
export { jcsCanonicalize, canonicalHash } from '@agenticprimitives/verifiable-credentials';
|
|
7
|
+
export { hashPair, leafHash, buildCorpusTree, merkleRoot, merkleProof, verifyInclusion, type CorpusTree, } from './merkle.js';
|
|
8
|
+
export { NORMALIZATION_V1, canonicalizeRendering, contentCommitment, verifyCommitment, assertCommitment, descriptorHash, buildContentDescriptor, verifyContentDescriptor, type VerifyDescriptorOpts, type VerificationResult, } from './descriptor.js';
|
|
9
|
+
export { resolveCandidates, type ResolutionConstraints, type Candidate, type ResolutionResult, type TrustProfileConfig, } from './resolution.js';
|
|
10
|
+
export { evaluateEntitlement, buildCitationAssertion, type EntitlementDecision, type CitationInput, } from './entitlement.js';
|
|
11
|
+
export { InvalidReferenceError, CommitmentMismatchError } from './errors.js';
|
|
12
|
+
export { buildInclusionZkProof, bindPaymentMandate } from './reserved.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,YAAY,0CAA0C,CAAC;AACpE,eAAO,MAAM,cAAc,EAAG,SAAkB,CAAC;AACjD,eAAO,MAAM,QAAQ,8CAA8C,CAAC;AAEpE,YAAY,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,WAAW,EACX,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGpG,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE3F,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,UAAU,EACV,WAAW,EACX,eAAe,EACf,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG7E,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @agenticprimitives/content-primitives — the Verifiable Content Substrate SDK.
|
|
2
|
+
//
|
|
3
|
+
// Name → resolve → verify → retrieve → cite → audit, for content that lives
|
|
4
|
+
// off-platform and is controlled by third-party rights holders. Content-
|
|
5
|
+
// agnostic (ADR-0033): a verse is content, not an Agent; a canonical locus is a
|
|
6
|
+
// deterministic hash of a scheme-INDEPENDENT structured form (registry-free, no
|
|
7
|
+
// allocator), never a Smart Agent facet. No vertical/faith vocabulary, no
|
|
8
|
+
// rendering text — descriptors carry a retrievalPointer + a SHA-256 commitment.
|
|
9
|
+
//
|
|
10
|
+
// See: ../../specs/266-verifiable-content-substrate.md
|
|
11
|
+
// ../../docs/architecture/decisions/0033-content-agnostic-verifiable-content-firewall.md
|
|
12
|
+
export const PACKAGE_NAME = '@agenticprimitives/content-primitives';
|
|
13
|
+
export const PACKAGE_STATUS = 'phase-1';
|
|
14
|
+
export const SPEC_REF = 'specs/266-verifiable-content-substrate.md';
|
|
15
|
+
export { computeCanonicalId, canonicalReference, corpusRef, LOCUS_ID_SCHEME } from './reference.js';
|
|
16
|
+
// Re-export the ONE canonicalization stack (RFC 8785) — shared with credential
|
|
17
|
+
// hashing so descriptor/locus ids and VC hashes never drift.
|
|
18
|
+
export { jcsCanonicalize, canonicalHash } from '@agenticprimitives/verifiable-credentials';
|
|
19
|
+
export { hashPair, leafHash, buildCorpusTree, merkleRoot, merkleProof, verifyInclusion, } from './merkle.js';
|
|
20
|
+
export { NORMALIZATION_V1, canonicalizeRendering, contentCommitment, verifyCommitment, assertCommitment, descriptorHash, buildContentDescriptor, verifyContentDescriptor, } from './descriptor.js';
|
|
21
|
+
export { resolveCandidates, } from './resolution.js';
|
|
22
|
+
export { evaluateEntitlement, buildCitationAssertion, } from './entitlement.js';
|
|
23
|
+
export { InvalidReferenceError, CommitmentMismatchError } from './errors.js';
|
|
24
|
+
// Reserved for later phases (throw until implemented; spec 266 §6).
|
|
25
|
+
export { buildInclusionZkProof, bindPaymentMandate } from './reserved.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,0EAA0E;AAC1E,gFAAgF;AAChF,EAAE;AACF,uDAAuD;AACvD,8FAA8F;AAE9F,MAAM,CAAC,MAAM,YAAY,GAAG,uCAAuC,CAAC;AACpE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAkB,CAAC;AACjD,MAAM,CAAC,MAAM,QAAQ,GAAG,2CAA2C,CAAC;AAwBpE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACpG,+EAA+E;AAC/E,6DAA6D;AAC7D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE3F,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,UAAU,EACV,WAAW,EACX,eAAe,GAEhB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,uBAAuB,GAGxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,GAKlB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GAGvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE7E,oEAAoE;AACpE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/jcs.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jcs.d.ts","sourceRoot":"","sources":["../src/jcs.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYpD;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD"}
|
package/dist/jcs.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Deterministic, sorted-key canonical JSON (JCS-style). Sufficient for our
|
|
2
|
+
// records (string/hex/number/bool fields); not a full RFC 8785 number
|
|
3
|
+
// normalizer — keep numeric fields integer-valued. Used for both the canonical
|
|
4
|
+
// locus id and the descriptor signing digest so hashing is stable regardless of
|
|
5
|
+
// key insertion order.
|
|
6
|
+
export function canonicalize(value) {
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map(canonicalize);
|
|
9
|
+
if (value && typeof value === 'object') {
|
|
10
|
+
const out = {};
|
|
11
|
+
for (const k of Object.keys(value).sort()) {
|
|
12
|
+
const v = value[k];
|
|
13
|
+
if (v === undefined)
|
|
14
|
+
continue; // omit undefined so optional fields don't shift the hash
|
|
15
|
+
out[k] = canonicalize(v);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
/** Canonical JSON string of `value` (sorted keys, undefined omitted). */
|
|
22
|
+
export function jcsString(value) {
|
|
23
|
+
return JSON.stringify(canonicalize(value));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=jcs.js.map
|
package/dist/jcs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jcs.js","sourceRoot":"","sources":["../src/jcs.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,+EAA+E;AAC/E,gFAAgF;AAChF,uBAAuB;AAEvB,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACrE,MAAM,CAAC,GAAI,KAAiC,CAAC,CAAC,CAAC,CAAC;YAChD,IAAI,CAAC,KAAK,SAAS;gBAAE,SAAS,CAAC,yDAAyD;YACxF,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|