@agenticprimitives/types 1.0.0-alpha.20 → 1.0.0-alpha.22

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/dist/index.d.ts CHANGED
@@ -129,4 +129,51 @@ export interface SignatureProof {
129
129
  */
130
130
  export declare function canonicalizeJson(v: unknown): string;
131
131
  export type { SubjectRef, ConfirmationKey, ActorContextV1, SubjectBindingRelationship, BindingAssurance, SubjectBindingV1, } from './actor-context.js';
132
+ export { UNAUTHENTICATED_PEER, isAgentAuthenticatedPeer, isChannelBoundPeer, meetsPeerStrength, validatePeerContext } from './verified-peer-context.js';
133
+ export type { ChannelBinding, PeerAuthMethod, PeerAuthStrength, PeerContextViolation, PeerKeyBinding, VerifiedPeerContextV1, } from './verified-peer-context.js';
134
+ /**
135
+ * ERC-1271 `isValidSignature` success value — `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
136
+ *
137
+ * A PROTOCOL constant, not an implementation detail, which is why it lives in the one package
138
+ * everything may import. It was previously declared in `agent-account`, `connect-auth`, `delegation`,
139
+ * `payments` and five apps — and `connect-auth` had no choice, because `agent-account` imports IT, so
140
+ * reaching the other way would be a cycle. A shared constant in `types` is the only home that does
141
+ * not force somebody to keep a copy.
142
+ */
143
+ export declare const ERC1271_MAGIC_VALUE: "0x1626ba7e";
144
+ export declare class JcsError extends Error {
145
+ readonly path: string;
146
+ constructor(message: string, path: string);
147
+ }
148
+ /**
149
+ * Canonicalize a JSON-compatible value per RFC 8785.
150
+ * Returns the canonical string (UTF-8 text; callers wrap with `utf8ToBytes` to hash).
151
+ */
152
+ export declare function jcsCanonicalize(value: unknown, path?: string): string;
153
+ /** CAIP-10 `namespace:reference:address`. Shape only — says nothing about which namespaces are allowed. */
154
+ export declare const CAIP10_GRAMMAR: RegExp;
155
+ export interface Caip10PartsShape {
156
+ namespace: string;
157
+ reference: string;
158
+ address: string;
159
+ }
160
+ /**
161
+ * Parse a CAIP-10 identifier by GRAMMAR. Returns `null` — never throws, never guesses.
162
+ *
163
+ * NOT a substitute for `agent-profile.parseCaip10`, which additionally enforces the namespace
164
+ * allowlist. Use this where you only need to know "is this shaped like a CAIP-10 id, and what are its
165
+ * parts"; use that one where the answer decides authority.
166
+ */
167
+ export declare function parseCaip10Parts(value: string): Caip10PartsShape | null;
168
+ /** Is this an EVM CAIP-10 id — `eip155:<chainId>:<20-byte address>`? Strict on the address length. */
169
+ export declare function isEvmCaip10(value: string): boolean;
170
+ /**
171
+ * Build an EVM CAIP-10 id.
172
+ *
173
+ * DOES NOT NORMALIZE CASE, deliberately. Callers differ — some emit checksummed addresses and their
174
+ * output is asserted byte-for-byte — and a shared helper that quietly lowercased would change what
175
+ * every one of them produces. A dedup that changes bytes is not a dedup. Lowercase at the call site
176
+ * if that is what you mean.
177
+ */
178
+ export declare function evmCaip10(chainId: number | string, address: string): string;
132
179
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,GAAG,KAAK,MAAM,EAAE,CAAC;AACpC,MAAM,MAAM,GAAG,GAAG,KAAK,MAAM,EAAE,CAAC;AAChC,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AACrE,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAQ7C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAE7C,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AASD,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAEzF,8DAA8D;AAI9D,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9F,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,eAAe,CAAC;AAE7D,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,8EAA8E;IAC9E,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,SAAS,CAAC;IACrB,mFAAmF;IACnF,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,GAAG,EAAE,gBAAgB,CAAC;IACtB,sDAAsD;IACtD,SAAS,EAAE,mBAAmB,CAAC;IAC/B,oEAAoE;IACpE,SAAS,EAAE,SAAS,CAAC;IACrB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAYD;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sGAAsG;IACtG,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAOD,qGAAqG;AACrG,MAAM,MAAM,KAAK,GAAG,KAAK,MAAM,EAAE,CAAC;AAElC;yDACyD;AACzD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;IAC5C,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CASnD;AAID,YAAY,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,0BAA0B,EAC1B,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
- // @agenticprimitives/types — cross-cutting branded primitives. Types-only.
1
+ // @agenticprimitives/types — cross-cutting branded primitives.
2
+ //
3
+ // Near-types-only: the package ships a small number of PURE helpers (`canonicalizeJson`, the
4
+ // peer-context predicates) that are deterministic functions over shapes defined here. No side
5
+ // effects, no I/O, no clocks, no crypto, no state, no imports. See CLAUDE.md for the exact rule.
2
6
  /**
3
7
  * Deterministic JSON canonicalization: sorted keys, `undefined` members dropped. The shared digest
4
8
  * convention used across `messaging` / `verification-receipts` / `key-authorization` so cross-package tooling
@@ -16,4 +20,182 @@ export function canonicalizeJson(v) {
16
20
  .map((k) => `${JSON.stringify(k)}:${canonicalizeJson(o[k])}`)
17
21
  .join(',')}}`;
18
22
  }
23
+ // Verified peer context — transport-layer peer identity (dntls-comparison §17 P1; spec 338 W5).
24
+ // Placed here for the same reason as `ActorContextV1`: >=2 consumers (`a2a`, `mcp-runtime`, `audit`,
25
+ // edge adapters) must share ONE definition without runtime-coupling to each other.
26
+ //
27
+ // IDENTITY, NEVER AUTHORITY. Knowing who connected never implies what they may do.
28
+ export { UNAUTHENTICATED_PEER, isAgentAuthenticatedPeer, isChannelBoundPeer, meetsPeerStrength, validatePeerContext } from './verified-peer-context.js';
29
+ /**
30
+ * ERC-1271 `isValidSignature` success value — `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
31
+ *
32
+ * A PROTOCOL constant, not an implementation detail, which is why it lives in the one package
33
+ * everything may import. It was previously declared in `agent-account`, `connect-auth`, `delegation`,
34
+ * `payments` and five apps — and `connect-auth` had no choice, because `agent-account` imports IT, so
35
+ * reaching the other way would be a cycle. A shared constant in `types` is the only home that does
36
+ * not force somebody to keep a copy.
37
+ */
38
+ export const ERC1271_MAGIC_VALUE = '0x1626ba7e';
39
+ // ─── RFC 8785 JSON Canonicalization Scheme ──────────────────────────────────
40
+ //
41
+ // WHY THIS LIVES IN `types` AND NOT IN `verifiable-credentials`, WHERE IT WAS WRITTEN.
42
+ // `situations` carried a BYTE-IDENTICAL copy — ~100 lines including the number-formatting rules,
43
+ // duplicated. The obvious fix (situations re-exports from verifiable-credentials) is impossible:
44
+ // `verifiable-credentials` IMPORTS `situations`, so that edge is a cycle. Like `ERC1271_MAGIC_VALUE`,
45
+ // the only home that does not force somebody to keep a copy is the one both can reach.
46
+ //
47
+ // It is PURE — no hashing, no dependency — so `types` stays dependency-free. The keccak wrapper
48
+ // (`canonicalHash`) stays in each consumer, where the hash function it needs already lives.
49
+ //
50
+ // NOT interchangeable with `canonicalizeJson` above. That one is sorted-key JSON.stringify; THIS one
51
+ // implements RFC 8785's number formatting, escaping and code-unit ordering. Swapping one for the
52
+ // other changes bytes, which changes digests, which breaks signatures. Pick deliberately.
53
+ /** UTF-16 code unit comparison — exactly what RFC 8785 §3.2.3 requires. */
54
+ function sortByCodeUnit(a, b) {
55
+ const len = Math.min(a.length, b.length);
56
+ for (let i = 0; i < len; i++) {
57
+ const av = a.charCodeAt(i);
58
+ const bv = b.charCodeAt(i);
59
+ if (av !== bv)
60
+ return av - bv;
61
+ }
62
+ return a.length - b.length;
63
+ }
64
+ /** RFC 8259 string serialisation with JCS-compatible escaping (RFC 8785 §3.2.2.2). */
65
+ function serializeString(s) {
66
+ let out = '"';
67
+ for (let i = 0; i < s.length; i++) {
68
+ const code = s.charCodeAt(i);
69
+ if (code === 0x22) {
70
+ out += '\\"';
71
+ }
72
+ else if (code === 0x5c) {
73
+ out += '\\\\';
74
+ }
75
+ else if (code < 0x20) {
76
+ // Control characters
77
+ switch (code) {
78
+ case 0x08:
79
+ out += '\\b';
80
+ break;
81
+ case 0x09:
82
+ out += '\\t';
83
+ break;
84
+ case 0x0a:
85
+ out += '\\n';
86
+ break;
87
+ case 0x0c:
88
+ out += '\\f';
89
+ break;
90
+ case 0x0d:
91
+ out += '\\r';
92
+ break;
93
+ default:
94
+ out += `\\u${code.toString(16).padStart(4, '0')}`;
95
+ }
96
+ }
97
+ else {
98
+ out += s[i];
99
+ }
100
+ }
101
+ out += '"';
102
+ return out;
103
+ }
104
+ /** RFC 8785 §3.2.2.3 — shortest round-tripping IEEE 754 representation. */
105
+ function serializeNumber(n, path) {
106
+ if (!Number.isFinite(n)) {
107
+ throw new JcsError(`non-finite number (${n})`, path);
108
+ }
109
+ if (Object.is(n, -0))
110
+ return '0';
111
+ if (n === 0)
112
+ return '0';
113
+ if (Number.isInteger(n) && Math.abs(n) < 1e21) {
114
+ return n.toString();
115
+ }
116
+ // Browsers + Node give us the shortest round-trip via `toString()`
117
+ // (per ECMA-262 §6.1.6.1.13). RFC 8785 expects the ES round-trip form.
118
+ return n.toString();
119
+ }
120
+ export class JcsError extends Error {
121
+ path;
122
+ constructor(message, path) {
123
+ super(`[JCS] ${message} at ${path}`);
124
+ this.path = path;
125
+ }
126
+ }
127
+ /**
128
+ * Canonicalize a JSON-compatible value per RFC 8785.
129
+ * Returns the canonical string (UTF-8 text; callers wrap with `utf8ToBytes` to hash).
130
+ */
131
+ export function jcsCanonicalize(value, path = '$') {
132
+ if (value === null)
133
+ return 'null';
134
+ if (value === undefined) {
135
+ throw new JcsError('undefined is not JSON', path);
136
+ }
137
+ switch (typeof value) {
138
+ case 'boolean':
139
+ return value ? 'true' : 'false';
140
+ case 'string':
141
+ return serializeString(value);
142
+ case 'number':
143
+ return serializeNumber(value, path);
144
+ case 'object': {
145
+ if (Array.isArray(value)) {
146
+ const items = value.map((v, i) => jcsCanonicalize(v, `${path}[${i}]`));
147
+ return `[${items.join(',')}]`;
148
+ }
149
+ // Plain object
150
+ const entries = Object.entries(value)
151
+ .filter(([, v]) => v !== undefined)
152
+ .sort(([a], [b]) => sortByCodeUnit(a, b));
153
+ const items = entries.map(([k, v]) => `${serializeString(k)}:${jcsCanonicalize(v, `${path}.${k}`)}`);
154
+ return `{${items.join(',')}}`;
155
+ }
156
+ default:
157
+ throw new JcsError(`unsupported JSON type: ${typeof value}`, path);
158
+ }
159
+ }
160
+ // ─── CAIP-10 grammar (ADR-0008, promoted per its own §"ADR-0016 promotes…") ──
161
+ //
162
+ // WHY HERE AND NOT IN `agent-profile`, WHERE THE BUILDER LIVES. Five packages —
163
+ // `edge-runtime`, `geo-features`, `capability-claims`, `rate-control`, `verifiable-credentials` —
164
+ // each re-derived this, and NONE of them may import `agent-profile`. Every copy was forced by the
165
+ // dependency graph, not written out of laziness, and the copies did NOT agree: some accepted any
166
+ // `eip155:<digits>:0x…`, one accepted a SHORT address (`0x[0-9a-fA-F]+`, no length bound), and one
167
+ // was a bare `split(':')`.
168
+ //
169
+ // The split that matters: GRAMMAR is a protocol fact and lives here; the NAMESPACE ALLOWLIST is
170
+ // policy and stays in `agent-profile`, so nothing can bypass it by reaching for the loose parser.
171
+ // `parseCaip10Parts` therefore validates SHAPE only and says so.
172
+ /** CAIP-10 `namespace:reference:address`. Shape only — says nothing about which namespaces are allowed. */
173
+ export const CAIP10_GRAMMAR = /^([-a-z0-9]{3,8}):([-_a-zA-Z0-9]{1,32}):([-.%a-zA-Z0-9]{1,128})$/;
174
+ /**
175
+ * Parse a CAIP-10 identifier by GRAMMAR. Returns `null` — never throws, never guesses.
176
+ *
177
+ * NOT a substitute for `agent-profile.parseCaip10`, which additionally enforces the namespace
178
+ * allowlist. Use this where you only need to know "is this shaped like a CAIP-10 id, and what are its
179
+ * parts"; use that one where the answer decides authority.
180
+ */
181
+ export function parseCaip10Parts(value) {
182
+ const m = CAIP10_GRAMMAR.exec(value);
183
+ return m ? { namespace: m[1], reference: m[2], address: m[3] } : null;
184
+ }
185
+ /** Is this an EVM CAIP-10 id — `eip155:<chainId>:<20-byte address>`? Strict on the address length. */
186
+ export function isEvmCaip10(value) {
187
+ const p = parseCaip10Parts(value);
188
+ return !!p && p.namespace === 'eip155' && /^\d+$/.test(p.reference) && /^0x[0-9a-fA-F]{40}$/.test(p.address);
189
+ }
190
+ /**
191
+ * Build an EVM CAIP-10 id.
192
+ *
193
+ * DOES NOT NORMALIZE CASE, deliberately. Callers differ — some emit checksummed addresses and their
194
+ * output is asserted byte-for-byte — and a shared helper that quietly lowercased would change what
195
+ * every one of them produces. A dedup that changes bytes is not a dedup. Lowercase at the call site
196
+ * if that is what you mean.
197
+ */
198
+ export function evmCaip10(chainId, address) {
199
+ return `eip155:${chainId}:${address}`;
200
+ }
19
201
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAqK3E;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAU;IACzC,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAC5E,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACtE,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;SACtB,IAAI,EAAE;SACN,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import type { CanonicalAgentId, Hex32 } from './index.js';
2
+ import type { SubjectRef } from './actor-context.js';
3
+ /** How the peer's identity was established. Open-ended: a new transport is an adapter, not a core change. */
4
+ export type PeerAuthMethod =
5
+ /** Web PKI server certificate. Authenticates a HOSTNAME, not an agent. */
6
+ 'web-pki'
7
+ /** Mutual TLS with a client certificate. */
8
+ | 'mtls'
9
+ /** SPIFFE/SPIRE workload identity. */
10
+ | 'spiffe'
11
+ /** An OAuth/OIDC bearer presented at the transport edge. An envelope, never authority (ADR-0041). */
12
+ | 'oauth'
13
+ /** A shared MAC between internal services. Authenticates a DEPLOYMENT, not a principal. */
14
+ | 'service-mac'
15
+ /** DIDComm peer authentication. */
16
+ | 'didcomm'
17
+ /** DNTLS transport peer authentication (not implemented; reserved so adapters can report it). */
18
+ | 'dntls'
19
+ /** An AP challenge signed by the peer's Smart Agent — the only method that authenticates an SA. */
20
+ | 'ap-signed-challenge' | (string & {});
21
+ /**
22
+ * How strongly the peer is authenticated — deliberately NOT a single boolean.
23
+ *
24
+ * The distinction that matters: `host` means "a hostname was verified" (Web PKI), which is what most
25
+ * deployments actually have and is a much weaker claim than "this Smart Agent is on the other end".
26
+ * Collapsing those into `verified: true` is how a TLS certificate ends up being treated as agent
27
+ * identity.
28
+ */
29
+ export type PeerAuthStrength =
30
+ /** Nothing was verified. The default, and what every unconfigured path must report. */
31
+ 'none'
32
+ /** A hostname/deployment was verified. Says nothing about WHICH agent is behind it. */
33
+ | 'host'
34
+ /** A workload/instance identity was verified (SPIFFE, mTLS client cert). */
35
+ | 'workload'
36
+ /** The peer proved control of its canonical Smart Agent. */
37
+ | 'agent';
38
+ /**
39
+ * Binds an application-layer proof to the channel it arrived on.
40
+ *
41
+ * Without this, a signature captured from one connection can be replayed on another — which is the
42
+ * substantive half of the DNTLS thesis. Presence of a binding is what lets a verifier say "this proof
43
+ * was made FOR this channel", not merely "this proof is valid somewhere".
44
+ */
45
+ export interface ChannelBinding {
46
+ /** e.g. `tls-exporter` (RFC 9266), `tls-server-end-point`, `dntls-session`. */
47
+ method: 'tls-exporter' | 'tls-server-end-point' | 'dntls-session' | (string & {});
48
+ /** The binding value the application proof must commit to. */
49
+ value: Hex32 | string;
50
+ }
51
+ /** A key the peer proved control of on this channel. */
52
+ export interface PeerKeyBinding {
53
+ keyId?: string;
54
+ algorithm?: string;
55
+ /** Thumbprint (e.g. JWK `jkt`, cert `x5t#S256`). */
56
+ thumbprint: string;
57
+ }
58
+ /**
59
+ * What a transport adapter verified about the peer.
60
+ *
61
+ * Produced at Ring 1 (a Worker, a gateway, a mesh sidecar); consumed by `a2a` / `mcp-runtime` /
62
+ * `audit` without knowing the producer.
63
+ */
64
+ export interface VerifiedPeerContextV1 {
65
+ specVersion: 'ap.verified-peer-context/1';
66
+ method: PeerAuthMethod;
67
+ strength: PeerAuthStrength;
68
+ /**
69
+ * The peer as identified by the transport. `caip10` ONLY when `strength: 'agent'` — a hostname or
70
+ * workload id must not be reported as a Smart Agent, which is precisely the conflation this type
71
+ * exists to prevent.
72
+ */
73
+ peer?: SubjectRef;
74
+ /** Present only when the transport authenticated the canonical Smart Agent itself. */
75
+ canonicalAgentId?: CanonicalAgentId;
76
+ keyBinding?: PeerKeyBinding;
77
+ channelBinding?: ChannelBinding;
78
+ /** Where the underlying evidence lives (cert chain, SVID, resolution receipt). Never inlined. */
79
+ evidenceRefs?: readonly string[];
80
+ observedAt: string;
81
+ /** When this context stops being usable. A long-lived connection MUST be re-evaluated. */
82
+ expiresAt?: string;
83
+ }
84
+ /** The context an unconfigured or unauthenticated path MUST report. Never omit the field instead. */
85
+ export declare const UNAUTHENTICATED_PEER: Omit<VerifiedPeerContextV1, 'observedAt'>;
86
+ /** True when the peer context meets a minimum strength. Use this rather than comparing strings. */
87
+ export declare function meetsPeerStrength(actual: PeerAuthStrength, minimum: PeerAuthStrength): boolean;
88
+ /**
89
+ * True only when the transport authenticated the CANONICAL SMART AGENT — the one condition under
90
+ * which peer context may be treated as agent identity at all.
91
+ *
92
+ * Even then it is identity, not authority: an authenticated peer must still be separately authorized
93
+ * to act, by the authority chain re-run at the origin.
94
+ */
95
+ export declare function isAgentAuthenticatedPeer(ctx: VerifiedPeerContextV1): ctx is VerifiedPeerContextV1 & {
96
+ canonicalAgentId: CanonicalAgentId;
97
+ };
98
+ /**
99
+ * True when the peer proof is bound to THIS channel — i.e. it cannot be replayed on another
100
+ * connection by a relay that holds a valid certificate for the host.
101
+ *
102
+ * SEPARATE FROM `isAgentAuthenticatedPeer` ON PURPOSE. Those answer different questions:
103
+ * - `isAgentAuthenticatedPeer` → WHO is at the other end.
104
+ * - `isChannelBoundPeer` → is that answer tied to THIS connection.
105
+ *
106
+ * A nonce-signed challenge proves control of a Smart Agent, so it earns `strength: 'agent'`. It does
107
+ * NOT, on its own, stop an active relay: a party that terminates TLS for the host can forward the
108
+ * challenge and the signature. Only a channel binding closes that, which is the substantive half of
109
+ * the DNTLS thesis (dntls-comparison.md §0.3 item 1).
110
+ *
111
+ * A consumer that cares about relay resistance MUST check this, not just strength.
112
+ */
113
+ export declare function isChannelBoundPeer(ctx: VerifiedPeerContextV1): boolean;
114
+ /** Structural coherence problems — combinations that would misreport what was verified. */
115
+ export interface PeerContextViolation {
116
+ field: string;
117
+ message: string;
118
+ }
119
+ /**
120
+ * Validate a peer context.
121
+ *
122
+ * Every rule here guards the same failure: a transport reporting a stronger claim than it actually
123
+ * verified, which downstream code would then trust as agent identity.
124
+ */
125
+ export declare function validatePeerContext(ctx: VerifiedPeerContextV1): PeerContextViolation[];
126
+ //# sourceMappingURL=verified-peer-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-peer-context.d.ts","sourceRoot":"","sources":["../src/verified-peer-context.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,6GAA6G;AAC7G,MAAM,MAAM,cAAc;AACxB,0EAA0E;AACxE,SAAS;AACX,4CAA4C;GAC1C,MAAM;AACR,sCAAsC;GACpC,QAAQ;AACV,qGAAqG;GACnG,OAAO;AACT,2FAA2F;GACzF,aAAa;AACf,mCAAmC;GACjC,SAAS;AACX,iGAAiG;GAC/F,OAAO;AACT,mGAAmG;GACjG,qBAAqB,GACrB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB;AAC1B,uFAAuF;AACrF,MAAM;AACR,uFAAuF;GACrF,MAAM;AACR,4EAA4E;GAC1E,UAAU;AACZ,4DAA4D;GAC1D,OAAO,CAAC;AAEZ;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,MAAM,EAAE,cAAc,GAAG,sBAAsB,GAAG,eAAe,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClF,8DAA8D;IAC9D,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,4BAA4B,CAAC;IAE1C,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,gBAAgB,CAAC;IAE3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,iGAAiG;IACjG,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEjC,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qGAAqG;AACrG,eAAO,MAAM,oBAAoB,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAI1E,CAAC;AAIF,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE9F;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,qBAAqB,GACzB,GAAG,IAAI,qBAAqB,GAAG;IAAE,gBAAgB,EAAE,gBAAgB,CAAA;CAAE,CAOvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAEtE;AAED,2FAA2F;AAC3F,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,qBAAqB,GAAG,oBAAoB,EAAE,CAkCtF"}
@@ -0,0 +1,105 @@
1
+ // Verified peer context — the transport-layer "who is at the other end of this connection?"
2
+ // (dntls-comparison.md §17 P1; spec 338 W5).
3
+ //
4
+ // THE GAP THIS NAMES: before A2A/MCP parse a byte, peer trust in this substrate has usually collapsed
5
+ // to Web PKI, a hosting provider, an OAuth bearer, or a service MAC (dntls-comparison.md §0.3 item 1).
6
+ // App-layer signatures are NOT channel binding — they prove a message was authored, not that the
7
+ // party you are speaking to is the party that authored it.
8
+ //
9
+ // This type does not close that gap. It gives the gap a SHAPE, so a transport adapter can report what
10
+ // it actually verified, and so `a2a`/`mcp-runtime` can consume peer identity WITHOUT knowing which
11
+ // mechanism produced it (Web PKI today, SPIFFE/mTLS in a mesh, DNTLS if it ever ships).
12
+ //
13
+ // ─── THE RULE THAT MAKES THIS SAFE ──────────────────────────────────────────────────────────────
14
+ // Verified peer context is IDENTITY, never AUTHORITY.
15
+ //
16
+ // Knowing who connected never implies what they may do. It does not substitute for ANY link in the
17
+ // authority chain, and it is never an input that *widens* one (ADR-0041, ADR-0043, ADR-0056). Its
18
+ // legitimate uses are: binding an app-layer proof to the channel it arrived on, admission decisions
19
+ // at the edge, audit evidence, and policy PREDICATES an authority decision may CONSULT — never
20
+ // replace.
21
+ //
22
+ // (The authority-chain packages are named in their own docs; this file deliberately does not use
23
+ // their vocabulary — `types` is a shared leaf and must not borrow a neighbour's terms.)
24
+ /** The context an unconfigured or unauthenticated path MUST report. Never omit the field instead. */
25
+ export const UNAUTHENTICATED_PEER = {
26
+ specVersion: 'ap.verified-peer-context/1',
27
+ method: 'web-pki',
28
+ strength: 'none',
29
+ };
30
+ const STRENGTH_RANK = { none: 0, host: 1, workload: 2, agent: 3 };
31
+ /** True when the peer context meets a minimum strength. Use this rather than comparing strings. */
32
+ export function meetsPeerStrength(actual, minimum) {
33
+ return STRENGTH_RANK[actual] >= STRENGTH_RANK[minimum];
34
+ }
35
+ /**
36
+ * True only when the transport authenticated the CANONICAL SMART AGENT — the one condition under
37
+ * which peer context may be treated as agent identity at all.
38
+ *
39
+ * Even then it is identity, not authority: an authenticated peer must still be separately authorized
40
+ * to act, by the authority chain re-run at the origin.
41
+ */
42
+ export function isAgentAuthenticatedPeer(ctx) {
43
+ return (ctx.strength === 'agent' &&
44
+ typeof ctx.canonicalAgentId === 'string' &&
45
+ ctx.peer?.scheme === 'caip10' &&
46
+ ctx.peer.id === ctx.canonicalAgentId);
47
+ }
48
+ /**
49
+ * True when the peer proof is bound to THIS channel — i.e. it cannot be replayed on another
50
+ * connection by a relay that holds a valid certificate for the host.
51
+ *
52
+ * SEPARATE FROM `isAgentAuthenticatedPeer` ON PURPOSE. Those answer different questions:
53
+ * - `isAgentAuthenticatedPeer` → WHO is at the other end.
54
+ * - `isChannelBoundPeer` → is that answer tied to THIS connection.
55
+ *
56
+ * A nonce-signed challenge proves control of a Smart Agent, so it earns `strength: 'agent'`. It does
57
+ * NOT, on its own, stop an active relay: a party that terminates TLS for the host can forward the
58
+ * challenge and the signature. Only a channel binding closes that, which is the substantive half of
59
+ * the DNTLS thesis (dntls-comparison.md §0.3 item 1).
60
+ *
61
+ * A consumer that cares about relay resistance MUST check this, not just strength.
62
+ */
63
+ export function isChannelBoundPeer(ctx) {
64
+ return isAgentAuthenticatedPeer(ctx) && ctx.channelBinding !== undefined;
65
+ }
66
+ /**
67
+ * Validate a peer context.
68
+ *
69
+ * Every rule here guards the same failure: a transport reporting a stronger claim than it actually
70
+ * verified, which downstream code would then trust as agent identity.
71
+ */
72
+ export function validatePeerContext(ctx) {
73
+ const out = [];
74
+ if (ctx.strength === 'agent') {
75
+ if (!ctx.canonicalAgentId) {
76
+ out.push({ field: 'canonicalAgentId', message: 'strength "agent" requires the canonical Smart Agent it authenticated' });
77
+ }
78
+ if (ctx.peer && ctx.peer.scheme !== 'caip10') {
79
+ out.push({ field: 'peer', message: 'strength "agent" requires a caip10 peer — a hostname or workload id is not a Smart Agent' });
80
+ }
81
+ if (ctx.peer?.scheme === 'caip10' && ctx.canonicalAgentId && ctx.peer.id !== ctx.canonicalAgentId) {
82
+ out.push({ field: 'peer', message: 'peer id and canonicalAgentId disagree' });
83
+ }
84
+ }
85
+ else if (ctx.canonicalAgentId) {
86
+ // The core conflation: reporting an SA that the transport did not actually authenticate.
87
+ out.push({
88
+ field: 'canonicalAgentId',
89
+ message: `strength "${ctx.strength}" must not carry a canonicalAgentId — only "agent" authenticates a Smart Agent`,
90
+ });
91
+ }
92
+ if (ctx.strength === 'none' && (ctx.keyBinding || ctx.channelBinding)) {
93
+ out.push({ field: 'strength', message: 'strength "none" must not report key or channel bindings' });
94
+ }
95
+ if (!Number.isNaN(Date.parse(ctx.observedAt)) && ctx.expiresAt) {
96
+ if (Date.parse(ctx.expiresAt) <= Date.parse(ctx.observedAt)) {
97
+ out.push({ field: 'expiresAt', message: 'expiresAt must be after observedAt' });
98
+ }
99
+ }
100
+ else if (Number.isNaN(Date.parse(ctx.observedAt))) {
101
+ out.push({ field: 'observedAt', message: 'observedAt must be a parseable ISO-8601 timestamp' });
102
+ }
103
+ return out;
104
+ }
105
+ //# sourceMappingURL=verified-peer-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-peer-context.js","sourceRoot":"","sources":["../src/verified-peer-context.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticprimitives/types",
3
- "version": "1.0.0-alpha.20",
3
+ "version": "1.0.0-alpha.22",
4
4
  "description": "Cross-cutting branded types and chain primitives shared across @agenticprimitives/* packages.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,10 +34,14 @@
34
34
  "types",
35
35
  "agentic"
36
36
  ],
37
+ "devDependencies": {
38
+ "vitest": "^4.1.8"
39
+ },
37
40
  "scripts": {
38
41
  "build": "tsc -p tsconfig.build.json",
39
42
  "typecheck": "tsc -p tsconfig.json --noEmit",
40
- "test": "echo 'no tests yet' && exit 0",
41
- "clean": "rm -rf dist"
43
+ "test": "vitest run",
44
+ "clean": "rm -rf dist",
45
+ "test:unit": "vitest run test/unit --passWithNoTests"
42
46
  }
43
47
  }