@atrib/sdk 0.1.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/LICENSE +216 -0
- package/README.md +525 -0
- package/dist/attest.d.ts +106 -0
- package/dist/attest.d.ts.map +1 -0
- package/dist/attest.js +64 -0
- package/dist/attest.js.map +1 -0
- package/dist/attribution.d.ts +81 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/attribution.js +133 -0
- package/dist/attribution.js.map +1 -0
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +244 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +113 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +91 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.d.ts +32 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +175 -0
- package/dist/daemon.js.map +1 -0
- package/dist/evidence-envelope.d.ts +98 -0
- package/dist/evidence-envelope.d.ts.map +1 -0
- package/dist/evidence-envelope.js +108 -0
- package/dist/evidence-envelope.js.map +1 -0
- package/dist/evidence.d.ts +76 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +20 -0
- package/dist/evidence.js.map +1 -0
- package/dist/hashes.d.ts +26 -0
- package/dist/hashes.d.ts.map +1 -0
- package/dist/hashes.js +33 -0
- package/dist/hashes.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/recall.d.ts +122 -0
- package/dist/recall.d.ts.map +1 -0
- package/dist/recall.js +29 -0
- package/dist/recall.js.map +1 -0
- package/package.json +72 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client configuration for the consolidated atrib SDK.
|
|
3
|
+
*
|
|
4
|
+
* Topology per the SDK session brief: the local primitives runtime (the
|
|
5
|
+
* daemon) is the default peer; in-process signing via `@atrib/emit` is the
|
|
6
|
+
* fallback. Anchors are the D138 anchor-plurality surface from `@atrib/mcp`
|
|
7
|
+
* (spec §2.11.7-§2.11.13): the config takes an anchor SET which is
|
|
8
|
+
* normalized into an `AnchorSetConfig` and fanned out through
|
|
9
|
+
* `createAnchorFanout` on the in-process attest path. When no anchors are
|
|
10
|
+
* configured, the `BUILT_IN_DEFAULT_ANCHOR_SET` (two independent anchors)
|
|
11
|
+
* applies.
|
|
12
|
+
*/
|
|
13
|
+
import { type AnchorDescriptor, type AnchorSetConfig } from '@atrib/mcp';
|
|
14
|
+
import type { ResolvedKey } from '@atrib/emit';
|
|
15
|
+
export type DaemonMode = 'prefer' | 'require' | 'off';
|
|
16
|
+
export interface DaemonConfig {
|
|
17
|
+
/**
|
|
18
|
+
* MCP Streamable HTTP endpoint of the local primitives runtime.
|
|
19
|
+
* Default: $ATRIB_PRIMITIVES_HTTP_ENDPOINT, then http://127.0.0.1:8796/mcp.
|
|
20
|
+
*/
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
/**
|
|
23
|
+
* 'prefer' (default): try the daemon, fall back in-process.
|
|
24
|
+
* 'require': daemon only; operational failure degrades to a warning
|
|
25
|
+
* result (never a throw) per §5.8.
|
|
26
|
+
* 'off': in-process only.
|
|
27
|
+
*/
|
|
28
|
+
mode?: DaemonMode;
|
|
29
|
+
/** Connect/call timeout in ms. Default 1500 (connect) / 10000 (call). */
|
|
30
|
+
connectTimeoutMs?: number;
|
|
31
|
+
callTimeoutMs?: number;
|
|
32
|
+
/** Cooldown before re-probing an unreachable daemon. Default 30000. */
|
|
33
|
+
retryCooldownMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One anchor in the anchor set (D138, spec §2.11.12). A bare string is an
|
|
37
|
+
* atrib-log §2.6.1 endpoint (normalized to `{ url }`). The object form is
|
|
38
|
+
* `AnchorDescriptor` from `@atrib/mcp`: `anchor_type` absent means
|
|
39
|
+
* `'atrib-log'`; `url` wins over `endpoint` when both are set; the
|
|
40
|
+
* registered types are `ANCHOR_TYPES` (§2.11.8 v1 registry).
|
|
41
|
+
*/
|
|
42
|
+
export type AnchorSpec = string | AnchorDescriptor;
|
|
43
|
+
export interface AtribClientConfig {
|
|
44
|
+
daemon?: DaemonConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Anchor set (D138, §2.11.12). When omitted, the two-anchor
|
|
47
|
+
* `BUILT_IN_DEFAULT_ANCHOR_SET` from `@atrib/mcp` applies and the emit
|
|
48
|
+
* pipeline keeps its own env/default atrib-log endpoint. Hostile or
|
|
49
|
+
* malformed entries warn-and-skip, never error (§5.8).
|
|
50
|
+
*/
|
|
51
|
+
anchors?: AnchorSpec[];
|
|
52
|
+
/**
|
|
53
|
+
* Opt-in acknowledgment that a sub-plurality (< 2) anchor set is
|
|
54
|
+
* deliberate (§2.11.12 rule 3) — the anchor analog of
|
|
55
|
+
* `allow_unresolved_informed_by` (D113). Maps to
|
|
56
|
+
* `AnchorSetConfig.allow_single_anchor`.
|
|
57
|
+
*/
|
|
58
|
+
allowSingleAnchor?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Opt-in parsing of `dev.atrib/attribution` attestation receipts from
|
|
61
|
+
* daemon tool results' `_meta` (D141). Default false. Receipts are
|
|
62
|
+
* advisory; trust still derives from verifying signed records. Parsed
|
|
63
|
+
* blocks are additionally run through `verifyAttributionReceipt`.
|
|
64
|
+
*/
|
|
65
|
+
attributionReceipts?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Pre-resolved signing key for the in-process path. Default: the
|
|
68
|
+
* `@atrib/emit` `resolveKey()` ladder (ATRIB_PRIVATE_KEY env, key file,
|
|
69
|
+
* Keychain, 1Password). `null` disables in-process signing (pass-through
|
|
70
|
+
* per §5.8 rule 5).
|
|
71
|
+
*/
|
|
72
|
+
key?: ResolvedKey | null;
|
|
73
|
+
/**
|
|
74
|
+
* Explicit context_id (32 lowercase hex). Stateless-MCP-native posture:
|
|
75
|
+
* context identity is an explicit per-request value; this is only the
|
|
76
|
+
* per-client default. Default: resolveEnvContextId() at call time.
|
|
77
|
+
*/
|
|
78
|
+
contextId?: string;
|
|
79
|
+
/** `_local.producer` mirror sidecar label. Default 'atrib-sdk'. */
|
|
80
|
+
producer?: string;
|
|
81
|
+
}
|
|
82
|
+
export declare const DEFAULT_DAEMON_ENDPOINT = "http://127.0.0.1:8796/mcp";
|
|
83
|
+
export declare const DEFAULT_CONNECT_TIMEOUT_MS = 1500;
|
|
84
|
+
export declare const DEFAULT_CALL_TIMEOUT_MS = 10000;
|
|
85
|
+
export declare const DEFAULT_RETRY_COOLDOWN_MS = 30000;
|
|
86
|
+
export declare const DEFAULT_PRODUCER = "atrib-sdk";
|
|
87
|
+
export declare function resolveDaemonEndpoint(config?: DaemonConfig): string;
|
|
88
|
+
export interface ResolvedAnchorSet {
|
|
89
|
+
/**
|
|
90
|
+
* Canonical §2.11.12 anchor-set config for `createAnchorFanout`. `{}`
|
|
91
|
+
* (no `anchors` key) when the caller configured nothing, so the
|
|
92
|
+
* built-in default set applies downstream.
|
|
93
|
+
*/
|
|
94
|
+
config: AnchorSetConfig;
|
|
95
|
+
/**
|
|
96
|
+
* First effective `'atrib-log'` descriptor's `url ?? endpoint`, passed
|
|
97
|
+
* to the emit pipeline as its §2.6.1 log endpoint. Undefined when the
|
|
98
|
+
* config is empty (the built-in default set applies and emitInProcess
|
|
99
|
+
* keeps its own env/default endpoint).
|
|
100
|
+
*/
|
|
101
|
+
primaryLogEndpoint: string | undefined;
|
|
102
|
+
warnings: string[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Normalize the caller's anchor set into a D138 `AnchorSetConfig`
|
|
106
|
+
* (§2.11.12). Hostile/malformed entries — null, non-object/non-string,
|
|
107
|
+
* missing string `url`/`endpoint`, unparsable URL, unregistered
|
|
108
|
+
* `anchor_type` — warn-and-skip, never throw (§5.8). Plurality posture
|
|
109
|
+
* (warn on < 2 anchors without `allow_single_anchor`) is resolved by the
|
|
110
|
+
* fan-out via `resolveAnchorPosture`, not here.
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveAnchorSet(anchors: AnchorSpec[] | undefined, allowSingleAnchor?: boolean): ResolvedAnchorSet;
|
|
113
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAgB,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAA;AACvG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAA;AAErD,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,gBAAgB,CAAA;AAElD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,uBAAuB,8BAA8B,CAAA;AAClE,eAAO,MAAM,0BAA0B,OAAO,CAAA;AAC9C,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAC7C,eAAO,MAAM,yBAAyB,QAAS,CAAA;AAC/C,eAAO,MAAM,gBAAgB,cAAc,CAAA;AAI3C,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAMnE;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,MAAM,EAAE,eAAe,CAAA;IACvB;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EAAE,GAAG,SAAS,EACjC,iBAAiB,CAAC,EAAE,OAAO,GAC1B,iBAAiB,CAuDnB"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Client configuration for the consolidated atrib SDK.
|
|
4
|
+
*
|
|
5
|
+
* Topology per the SDK session brief: the local primitives runtime (the
|
|
6
|
+
* daemon) is the default peer; in-process signing via `@atrib/emit` is the
|
|
7
|
+
* fallback. Anchors are the D138 anchor-plurality surface from `@atrib/mcp`
|
|
8
|
+
* (spec §2.11.7-§2.11.13): the config takes an anchor SET which is
|
|
9
|
+
* normalized into an `AnchorSetConfig` and fanned out through
|
|
10
|
+
* `createAnchorFanout` on the in-process attest path. When no anchors are
|
|
11
|
+
* configured, the `BUILT_IN_DEFAULT_ANCHOR_SET` (two independent anchors)
|
|
12
|
+
* applies.
|
|
13
|
+
*/
|
|
14
|
+
import { ANCHOR_TYPES } from '@atrib/mcp';
|
|
15
|
+
export const DEFAULT_DAEMON_ENDPOINT = 'http://127.0.0.1:8796/mcp';
|
|
16
|
+
export const DEFAULT_CONNECT_TIMEOUT_MS = 1500;
|
|
17
|
+
export const DEFAULT_CALL_TIMEOUT_MS = 10_000;
|
|
18
|
+
export const DEFAULT_RETRY_COOLDOWN_MS = 30_000;
|
|
19
|
+
export const DEFAULT_PRODUCER = 'atrib-sdk';
|
|
20
|
+
const ANCHOR_TYPE_SET = new Set(ANCHOR_TYPES);
|
|
21
|
+
export function resolveDaemonEndpoint(config) {
|
|
22
|
+
return (config?.endpoint ??
|
|
23
|
+
process.env['ATRIB_PRIMITIVES_HTTP_ENDPOINT'] ??
|
|
24
|
+
DEFAULT_DAEMON_ENDPOINT);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Normalize the caller's anchor set into a D138 `AnchorSetConfig`
|
|
28
|
+
* (§2.11.12). Hostile/malformed entries — null, non-object/non-string,
|
|
29
|
+
* missing string `url`/`endpoint`, unparsable URL, unregistered
|
|
30
|
+
* `anchor_type` — warn-and-skip, never throw (§5.8). Plurality posture
|
|
31
|
+
* (warn on < 2 anchors without `allow_single_anchor`) is resolved by the
|
|
32
|
+
* fan-out via `resolveAnchorPosture`, not here.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveAnchorSet(anchors, allowSingleAnchor) {
|
|
35
|
+
const warnings = [];
|
|
36
|
+
if (anchors === undefined) {
|
|
37
|
+
// No anchor config at all: the BUILT_IN_DEFAULT_ANCHOR_SET applies
|
|
38
|
+
// (§2.11.12 rule 1) and the emit pipeline keeps its own env/default
|
|
39
|
+
// atrib-log endpoint.
|
|
40
|
+
return { config: {}, primaryLogEndpoint: undefined, warnings };
|
|
41
|
+
}
|
|
42
|
+
const descriptors = [];
|
|
43
|
+
let primaryLogEndpoint;
|
|
44
|
+
for (const spec of anchors) {
|
|
45
|
+
// Hostile/malformed entries warn-and-skip, never throw (§5.8).
|
|
46
|
+
let descriptor;
|
|
47
|
+
if (typeof spec === 'string') {
|
|
48
|
+
descriptor = { url: spec };
|
|
49
|
+
}
|
|
50
|
+
else if (typeof spec === 'object' && spec !== null && !Array.isArray(spec)) {
|
|
51
|
+
descriptor = spec;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
warnings.push(`atrib: anchor entry ${String(spec)} is not a string or object; skipping`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const anchorType = descriptor.anchor_type;
|
|
58
|
+
if (anchorType !== undefined && (typeof anchorType !== 'string' || !ANCHOR_TYPE_SET.has(anchorType))) {
|
|
59
|
+
const named = (descriptor.url ?? descriptor.endpoint);
|
|
60
|
+
warnings.push(`atrib: anchor_type '${String(anchorType)}'${typeof named === 'string' ? ` (${named})` : ''} is not in the §2.11.8 registry (${ANCHOR_TYPES.join(', ')}); skipping this anchor`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const effectiveType = anchorType ?? 'atrib-log';
|
|
64
|
+
// `url` wins over `endpoint` when both are set (the §2.11.12 sample
|
|
65
|
+
// config spells the field `url`; both spellings are accepted).
|
|
66
|
+
const endpoint = descriptor.url ?? descriptor.endpoint;
|
|
67
|
+
if (effectiveType === 'atrib-log' || endpoint !== undefined) {
|
|
68
|
+
if (typeof endpoint !== 'string') {
|
|
69
|
+
warnings.push('atrib: anchor entry without a string url/endpoint; skipping');
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
new URL(endpoint);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
warnings.push(`atrib: anchor endpoint '${endpoint}' is not a valid URL; skipping`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (effectiveType === 'atrib-log' && primaryLogEndpoint === undefined) {
|
|
80
|
+
primaryLogEndpoint = endpoint;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
descriptors.push(descriptor);
|
|
84
|
+
}
|
|
85
|
+
const config = {
|
|
86
|
+
anchors: descriptors,
|
|
87
|
+
...(allowSingleAnchor === true ? { allow_single_anchor: true } : {}),
|
|
88
|
+
};
|
|
89
|
+
return { config, primaryLogEndpoint, warnings };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type VerifiedAttributionReceipt } from './attribution.js';
|
|
2
|
+
import { type DaemonConfig } from './config.js';
|
|
3
|
+
export type DaemonCallOutcome = {
|
|
4
|
+
ok: true;
|
|
5
|
+
value: unknown;
|
|
6
|
+
attribution?: VerifiedAttributionReceipt;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class DaemonClient {
|
|
12
|
+
private readonly endpoint;
|
|
13
|
+
private readonly connectTimeoutMs;
|
|
14
|
+
private readonly callTimeoutMs;
|
|
15
|
+
private readonly retryCooldownMs;
|
|
16
|
+
private readonly parseReceipts;
|
|
17
|
+
private client;
|
|
18
|
+
private connecting;
|
|
19
|
+
private lastFailureAt;
|
|
20
|
+
constructor(config?: DaemonConfig, options?: {
|
|
21
|
+
attributionReceipts?: boolean;
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Call one MCP tool on the daemon. Tool results carrying a single JSON
|
|
25
|
+
* text block (the atrib primitive convention) are parsed; other shapes
|
|
26
|
+
* are returned raw.
|
|
27
|
+
*/
|
|
28
|
+
callTool(name: string, args: Record<string, unknown>): Promise<DaemonCallOutcome>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
private ensureClient;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAqBA,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,0BAA0B,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAyCjC,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,aAAa,CAAI;gBAEb,MAAM,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAAE;IAQ9E;;;;OAIG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA0CjF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAYd,YAAY;CAuC3B"}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Daemon transport: MCP Streamable HTTP client for the local primitives
|
|
4
|
+
* runtime.
|
|
5
|
+
*
|
|
6
|
+
* Semantically stateless per the SDK brief: nothing session-scoped carries
|
|
7
|
+
* meaning — context_id and chain tokens travel as explicit tool arguments
|
|
8
|
+
* on every call. The MCP protocol session (initialize handshake +
|
|
9
|
+
* Mcp-Session-Id) is a transport detail of the CURRENT runtime that the
|
|
10
|
+
* official client manages; when the post-2026-07-28 stateless transport
|
|
11
|
+
* ships, this class swaps transports without changing the SDK surface.
|
|
12
|
+
*
|
|
13
|
+
* Degradation (§5.8): every operational failure is caught, logged with the
|
|
14
|
+
* `atrib:` prefix, and reported as an unavailable outcome — never thrown.
|
|
15
|
+
*/
|
|
16
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
17
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
18
|
+
import { verifyAttributionReceipt } from '@atrib/mcp';
|
|
19
|
+
import { ATTRIBUTION_EXTENSION_KEY, parseAttributionReceiptBlock, } from './attribution.js';
|
|
20
|
+
import { DEFAULT_CALL_TIMEOUT_MS, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_RETRY_COOLDOWN_MS, resolveDaemonEndpoint, } from './config.js';
|
|
21
|
+
/**
|
|
22
|
+
* Parse + verify the `dev.atrib/attribution` block on a tool result's
|
|
23
|
+
* `_meta`. The lenient parser extracts the block; `verifyAttributionReceipt`
|
|
24
|
+
* runs over the RAW block (extension spec §6.2). §5.8-safe: any exception
|
|
25
|
+
* degrades to a `malformed` verification, never a throw.
|
|
26
|
+
*/
|
|
27
|
+
function extractAttribution(meta) {
|
|
28
|
+
const block = parseAttributionReceiptBlock(meta);
|
|
29
|
+
if (block === null)
|
|
30
|
+
return null;
|
|
31
|
+
let verification;
|
|
32
|
+
try {
|
|
33
|
+
const raw = typeof meta === 'object' && meta !== null
|
|
34
|
+
? meta[ATTRIBUTION_EXTENSION_KEY]
|
|
35
|
+
: undefined;
|
|
36
|
+
verification = verifyAttributionReceipt(raw);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.warn(`atrib: attribution receipt verification failed: ${String(error)}`);
|
|
40
|
+
verification = { valid: false, mismatched: ['malformed'] };
|
|
41
|
+
}
|
|
42
|
+
return { block, verification };
|
|
43
|
+
}
|
|
44
|
+
const SDK_CLIENT_INFO = { name: 'atrib-sdk', version: '0.1.0' };
|
|
45
|
+
async function withTimeout(promise, ms, label) {
|
|
46
|
+
let timer;
|
|
47
|
+
try {
|
|
48
|
+
return await Promise.race([
|
|
49
|
+
promise,
|
|
50
|
+
new Promise((_, reject) => {
|
|
51
|
+
timer = setTimeout(() => reject(new Error(`atrib: ${label} timed out after ${ms}ms`)), ms);
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
if (timer !== undefined)
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class DaemonClient {
|
|
61
|
+
endpoint;
|
|
62
|
+
connectTimeoutMs;
|
|
63
|
+
callTimeoutMs;
|
|
64
|
+
retryCooldownMs;
|
|
65
|
+
parseReceipts;
|
|
66
|
+
client = null;
|
|
67
|
+
connecting = null;
|
|
68
|
+
lastFailureAt = 0;
|
|
69
|
+
constructor(config, options) {
|
|
70
|
+
this.endpoint = resolveDaemonEndpoint(config);
|
|
71
|
+
this.connectTimeoutMs = config?.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
72
|
+
this.callTimeoutMs = config?.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
|
|
73
|
+
this.retryCooldownMs = config?.retryCooldownMs ?? DEFAULT_RETRY_COOLDOWN_MS;
|
|
74
|
+
this.parseReceipts = options?.attributionReceipts === true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Call one MCP tool on the daemon. Tool results carrying a single JSON
|
|
78
|
+
* text block (the atrib primitive convention) are parsed; other shapes
|
|
79
|
+
* are returned raw.
|
|
80
|
+
*/
|
|
81
|
+
async callTool(name, args) {
|
|
82
|
+
const client = await this.ensureClient();
|
|
83
|
+
if (!client) {
|
|
84
|
+
return { ok: false, reason: `daemon unreachable at ${this.endpoint}` };
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await withTimeout(client.callTool({ name, arguments: args }), this.callTimeoutMs, `tools/call ${name}`);
|
|
88
|
+
const content = result.content;
|
|
89
|
+
const isError = result.isError === true;
|
|
90
|
+
const text = Array.isArray(content) && content[0]?.type === 'text' ? content[0].text : undefined;
|
|
91
|
+
if (isError) {
|
|
92
|
+
return { ok: false, reason: `daemon tool ${name} errored: ${text ?? 'unknown error'}` };
|
|
93
|
+
}
|
|
94
|
+
const attribution = this.parseReceipts
|
|
95
|
+
? extractAttribution(result._meta)
|
|
96
|
+
: null;
|
|
97
|
+
const withAttribution = (value) => attribution !== null ? { ok: true, value, attribution } : { ok: true, value };
|
|
98
|
+
if (text === undefined) {
|
|
99
|
+
return withAttribution(result);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
return withAttribution(JSON.parse(text));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return withAttribution(text);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// A failed call may mean the transport session died; drop the client
|
|
110
|
+
// so the next call reconnects (after cooldown).
|
|
111
|
+
await this.close();
|
|
112
|
+
this.lastFailureAt = Date.now();
|
|
113
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
114
|
+
console.warn(`atrib: daemon call ${name} failed: ${reason}`);
|
|
115
|
+
return { ok: false, reason };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async close() {
|
|
119
|
+
const client = this.client;
|
|
120
|
+
this.client = null;
|
|
121
|
+
if (client) {
|
|
122
|
+
try {
|
|
123
|
+
await client.close();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Best-effort close per §5.8.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async ensureClient() {
|
|
131
|
+
if (this.client)
|
|
132
|
+
return this.client;
|
|
133
|
+
if (this.connecting)
|
|
134
|
+
return this.connecting;
|
|
135
|
+
if (this.lastFailureAt > 0 && Date.now() - this.lastFailureAt < this.retryCooldownMs) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
this.connecting = (async () => {
|
|
139
|
+
let client = null;
|
|
140
|
+
try {
|
|
141
|
+
// URL parsing stays INSIDE the try: a garbage endpoint is an
|
|
142
|
+
// operational failure that must degrade (§5.8), never throw.
|
|
143
|
+
const url = new URL(this.endpoint);
|
|
144
|
+
// The SDK's concrete transport declares `sessionId: string | undefined`
|
|
145
|
+
// while the Transport interface under exactOptionalPropertyTypes wants
|
|
146
|
+
// an optional property; the runtime shapes are identical.
|
|
147
|
+
const transport = new StreamableHTTPClientTransport(url);
|
|
148
|
+
client = new Client(SDK_CLIENT_INFO);
|
|
149
|
+
await withTimeout(client.connect(transport), this.connectTimeoutMs, 'daemon connect');
|
|
150
|
+
this.client = client;
|
|
151
|
+
this.lastFailureAt = 0;
|
|
152
|
+
return client;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
this.lastFailureAt = Date.now();
|
|
156
|
+
if (client) {
|
|
157
|
+
try {
|
|
158
|
+
await client.close();
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Ignore close failures on a connection that never established.
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
165
|
+
console.warn(`atrib: daemon connect failed (${this.endpoint}): ${reason}`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
this.connecting = null;
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
return this.connecting;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence envelope production and validation (D137, spec §5.5.7).
|
|
3
|
+
*
|
|
4
|
+
* The real envelope engine lives in `@atrib/verify`, which is an OPTIONAL
|
|
5
|
+
* peer dependency of the SDK (same pattern as `@atrib/recall` /
|
|
6
|
+
* `@atrib/verify-mcp` in client.ts): the peer module is loaded lazily and
|
|
7
|
+
* its absence degrades to a `null` envelope with a warning naming the
|
|
8
|
+
* package — never a throw (§5.8). The structural envelope TYPES stay in
|
|
9
|
+
* ./evidence.ts so the emitted .d.ts never references the optional peer.
|
|
10
|
+
*
|
|
11
|
+
* Throw-vs-degrade split (the SDK-wide contract): contradictory INPUT
|
|
12
|
+
* (both `hash` and `material`; `hash_rule` without `material`; neither
|
|
13
|
+
* commitment source) throws TypeError — programmer error, thrown before
|
|
14
|
+
* the peer is consulted. Everything operational (peer missing, envelope
|
|
15
|
+
* failing §5.5.7 validation) degrades into the returned `warnings`.
|
|
16
|
+
*/
|
|
17
|
+
import type { EvidenceConstraint, EvidenceEnvelope, EvidencePayloadRef, EvidenceTier } from './evidence.js';
|
|
18
|
+
interface VerifyEnvelopeModule {
|
|
19
|
+
validateEnvelope: (envelope: unknown) => {
|
|
20
|
+
valid: boolean;
|
|
21
|
+
reasons: string[];
|
|
22
|
+
};
|
|
23
|
+
jcsSha256: (value: unknown) => string;
|
|
24
|
+
rawSha256: (text: string) => string;
|
|
25
|
+
}
|
|
26
|
+
/** Test seam: swap the peer loader. Pass undefined to restore the default. */
|
|
27
|
+
export declare function __setVerifyEnvelopeLoaderForTests(loader: (() => Promise<VerifyEnvelopeModule | null>) | undefined): void;
|
|
28
|
+
/** Payload input for {@link buildEvidenceEnvelope}. */
|
|
29
|
+
export interface BuildEvidenceEnvelopePayloadInput {
|
|
30
|
+
media_type?: string;
|
|
31
|
+
/** Retrievability ref. Default: `{ kind: 'inline' }` when `inline` is set, else `{ kind: 'withheld' }`. */
|
|
32
|
+
ref?: EvidencePayloadRef;
|
|
33
|
+
/** Raw payload carried inline (local-only, never public). */
|
|
34
|
+
inline?: unknown;
|
|
35
|
+
/** Pre-computed `sha256:<64-hex>` commitment. Mutually exclusive with `material`. */
|
|
36
|
+
hash?: string;
|
|
37
|
+
/** Evidence material to hash with `hash_rule`. Mutually exclusive with `hash`. */
|
|
38
|
+
material?: unknown;
|
|
39
|
+
/** §5.5.7 hash rule: 'jcs' (JSON media types, default) or 'raw' (UTF-8 text). */
|
|
40
|
+
hash_rule?: 'jcs' | 'raw';
|
|
41
|
+
}
|
|
42
|
+
export interface BuildEvidenceEnvelopeInput {
|
|
43
|
+
/** Absolute HTTPS profile type URI. */
|
|
44
|
+
profile: string;
|
|
45
|
+
/** Semver of the profile document. */
|
|
46
|
+
profile_version: string;
|
|
47
|
+
tier: EvidenceTier;
|
|
48
|
+
payload: BuildEvidenceEnvelopePayloadInput;
|
|
49
|
+
facts?: Record<string, unknown>;
|
|
50
|
+
result?: {
|
|
51
|
+
valid?: boolean;
|
|
52
|
+
constraints?: EvidenceConstraint[];
|
|
53
|
+
errors?: string[];
|
|
54
|
+
warnings?: string[];
|
|
55
|
+
};
|
|
56
|
+
verifier?: {
|
|
57
|
+
name?: string;
|
|
58
|
+
version?: string;
|
|
59
|
+
checked_at_ms?: number;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export interface BuildEvidenceEnvelopeResult {
|
|
63
|
+
/** The validated envelope, or null when degraded (see warnings). */
|
|
64
|
+
envelope: EvidenceEnvelope | null;
|
|
65
|
+
/** The peer's `validateEnvelope` outcome, or null when the peer is missing. */
|
|
66
|
+
validation: unknown | null;
|
|
67
|
+
warnings: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a §5.5.7 evidence envelope and validate it through the optional
|
|
71
|
+
* `@atrib/verify` peer.
|
|
72
|
+
*
|
|
73
|
+
* - `payload.hash` absent: the commitment is computed from
|
|
74
|
+
* `payload.material` via the stated `hash_rule` ('jcs' default; 'raw'
|
|
75
|
+
* requires string material) using the peer's `jcsSha256`/`rawSha256` —
|
|
76
|
+
* the same functions the conformance corpus pins.
|
|
77
|
+
* - Contradictory input throws TypeError before the peer loads: both
|
|
78
|
+
* `hash` and `material`, `hash_rule` without `material`, or neither
|
|
79
|
+
* `hash` nor `material` (no commitment source).
|
|
80
|
+
* - A structurally invalid RESULT (per the peer's `validateEnvelope`)
|
|
81
|
+
* yields `envelope: null` with the reject reasons in `warnings`.
|
|
82
|
+
* - Peer missing yields `envelope: null` plus a warning naming
|
|
83
|
+
* '@atrib/verify' (§5.8 degrade, never a throw).
|
|
84
|
+
*/
|
|
85
|
+
export declare function buildEvidenceEnvelope(input: BuildEvidenceEnvelopeInput): Promise<BuildEvidenceEnvelopeResult>;
|
|
86
|
+
export interface ValidateEvidenceEnvelopeResult {
|
|
87
|
+
/** The peer's `validateEnvelope` outcome, or null when the peer is missing. */
|
|
88
|
+
validation: unknown | null;
|
|
89
|
+
warnings: string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validate an envelope against the normative §5.5.7 shape rules via the
|
|
93
|
+
* optional `@atrib/verify` peer. Peer missing degrades to
|
|
94
|
+
* `validation: null` with a warning (§5.8); never throws.
|
|
95
|
+
*/
|
|
96
|
+
export declare function validateEvidenceEnvelope(envelope: unknown): Promise<ValidateEvidenceEnvelopeResult>;
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=evidence-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-envelope.d.ts","sourceRoot":"","sources":["../src/evidence-envelope.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACb,MAAM,eAAe,CAAA;AAItB,UAAU,oBAAoB;IAC5B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAC9E,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAA;IACrC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CACpC;AAWD,8EAA8E;AAC9E,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,GAC/D,IAAI,CAEN;AAKD,uDAAuD;AACvD,MAAM,WAAW,iCAAiC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2GAA2G;IAC3G,GAAG,CAAC,EAAE,kBAAkB,CAAA;IACxB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iFAAiF;IACjF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAA;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,EAAE,iCAAiC,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAA;QAClC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KACpB,CAAA;IACD,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;CACF;AAED,MAAM,WAAW,2BAA2B;IAC1C,oEAAoE;IACpE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACjC,+EAA+E;IAC/E,UAAU,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC,CAwEtC;AAED,MAAM,WAAW,8BAA8B;IAC7C,+EAA+E;IAC/E,UAAU,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,8BAA8B,CAAC,CAazC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
let verifyEnvelopeModulePromise = null;
|
|
3
|
+
function loadVerifyEnvelopeModule() {
|
|
4
|
+
verifyEnvelopeModulePromise ??= import('@atrib/verify').then((mod) => mod, () => null);
|
|
5
|
+
return verifyEnvelopeModulePromise;
|
|
6
|
+
}
|
|
7
|
+
/** Test seam: swap the peer loader. Pass undefined to restore the default. */
|
|
8
|
+
export function __setVerifyEnvelopeLoaderForTests(loader) {
|
|
9
|
+
verifyEnvelopeModulePromise = loader ? loader() : null;
|
|
10
|
+
}
|
|
11
|
+
const PEER_MISSING_WARNING = "atrib: evidence-envelope support unavailable — install the optional peer '@atrib/verify'";
|
|
12
|
+
/**
|
|
13
|
+
* Build a §5.5.7 evidence envelope and validate it through the optional
|
|
14
|
+
* `@atrib/verify` peer.
|
|
15
|
+
*
|
|
16
|
+
* - `payload.hash` absent: the commitment is computed from
|
|
17
|
+
* `payload.material` via the stated `hash_rule` ('jcs' default; 'raw'
|
|
18
|
+
* requires string material) using the peer's `jcsSha256`/`rawSha256` —
|
|
19
|
+
* the same functions the conformance corpus pins.
|
|
20
|
+
* - Contradictory input throws TypeError before the peer loads: both
|
|
21
|
+
* `hash` and `material`, `hash_rule` without `material`, or neither
|
|
22
|
+
* `hash` nor `material` (no commitment source).
|
|
23
|
+
* - A structurally invalid RESULT (per the peer's `validateEnvelope`)
|
|
24
|
+
* yields `envelope: null` with the reject reasons in `warnings`.
|
|
25
|
+
* - Peer missing yields `envelope: null` plus a warning naming
|
|
26
|
+
* '@atrib/verify' (§5.8 degrade, never a throw).
|
|
27
|
+
*/
|
|
28
|
+
export async function buildEvidenceEnvelope(input) {
|
|
29
|
+
const payload = input.payload;
|
|
30
|
+
if (payload.hash !== undefined && payload.material !== undefined) {
|
|
31
|
+
throw new TypeError('atrib: buildEvidenceEnvelope payload carries both hash and material; provide exactly one commitment source');
|
|
32
|
+
}
|
|
33
|
+
if (payload.hash_rule !== undefined && payload.material === undefined) {
|
|
34
|
+
throw new TypeError('atrib: buildEvidenceEnvelope payload has hash_rule without material; hash_rule only applies to material');
|
|
35
|
+
}
|
|
36
|
+
if (payload.hash === undefined && payload.material === undefined) {
|
|
37
|
+
throw new TypeError('atrib: buildEvidenceEnvelope payload needs a hash or material to commit to');
|
|
38
|
+
}
|
|
39
|
+
const hashRule = payload.hash_rule ?? 'jcs';
|
|
40
|
+
if (hashRule === 'raw' && payload.material !== undefined && typeof payload.material !== 'string') {
|
|
41
|
+
throw new TypeError("atrib: buildEvidenceEnvelope hash_rule 'raw' requires string material (UTF-8 text)");
|
|
42
|
+
}
|
|
43
|
+
const warnings = [];
|
|
44
|
+
const mod = await loadVerifyEnvelopeModule();
|
|
45
|
+
if (mod === null) {
|
|
46
|
+
return { envelope: null, validation: null, warnings: [PEER_MISSING_WARNING] };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const hash = payload.hash ??
|
|
50
|
+
(hashRule === 'raw'
|
|
51
|
+
? mod.rawSha256(payload.material)
|
|
52
|
+
: mod.jcsSha256(payload.material));
|
|
53
|
+
const ref = payload.ref ?? (payload.inline !== undefined ? { kind: 'inline' } : { kind: 'withheld' });
|
|
54
|
+
const candidate = {
|
|
55
|
+
envelope: 1,
|
|
56
|
+
profile: input.profile,
|
|
57
|
+
profile_version: input.profile_version,
|
|
58
|
+
tier: input.tier,
|
|
59
|
+
payload: {
|
|
60
|
+
hash,
|
|
61
|
+
...(payload.media_type !== undefined ? { media_type: payload.media_type } : {}),
|
|
62
|
+
ref,
|
|
63
|
+
...(payload.inline !== undefined ? { inline: payload.inline } : {}),
|
|
64
|
+
},
|
|
65
|
+
...(input.facts !== undefined ? { facts: input.facts } : {}),
|
|
66
|
+
result: {
|
|
67
|
+
valid: input.result?.valid ?? true,
|
|
68
|
+
constraints: input.result?.constraints ?? [],
|
|
69
|
+
errors: input.result?.errors ?? [],
|
|
70
|
+
warnings: input.result?.warnings ?? [],
|
|
71
|
+
},
|
|
72
|
+
...(input.verifier !== undefined ? { verifier: input.verifier } : {}),
|
|
73
|
+
};
|
|
74
|
+
const validation = mod.validateEnvelope(candidate);
|
|
75
|
+
if (!validation.valid) {
|
|
76
|
+
warnings.push(`atrib: built envelope failed §5.5.7 validation (${validation.reasons.join(', ')}); dropping the envelope`);
|
|
77
|
+
return { envelope: null, validation, warnings };
|
|
78
|
+
}
|
|
79
|
+
return { envelope: candidate, validation, warnings };
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
// Operational failure inside the peer (e.g. canonicalization of an
|
|
83
|
+
// uncanonicalizable material value): degrade per §5.8, never throw.
|
|
84
|
+
warnings.push(`atrib: evidence envelope construction failed: ${String(error)}`);
|
|
85
|
+
return { envelope: null, validation: null, warnings };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate an envelope against the normative §5.5.7 shape rules via the
|
|
90
|
+
* optional `@atrib/verify` peer. Peer missing degrades to
|
|
91
|
+
* `validation: null` with a warning (§5.8); never throws.
|
|
92
|
+
*/
|
|
93
|
+
export async function validateEvidenceEnvelope(envelope) {
|
|
94
|
+
const mod = await loadVerifyEnvelopeModule();
|
|
95
|
+
if (mod === null) {
|
|
96
|
+
return { validation: null, warnings: [PEER_MISSING_WARNING] };
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
return { validation: mod.validateEnvelope(envelope), warnings: [] };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
validation: null,
|
|
104
|
+
warnings: [`atrib: evidence envelope validation failed: ${String(error)}`],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=evidence-envelope.js.map
|