@sanning/proof 0.17.0 → 0.19.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/README.md +2 -2
- package/dist/case-file.js +2 -1
- package/dist/case.js +11 -4
- package/dist/chain.d.ts +28 -0
- package/dist/chain.js +61 -4
- package/dist/cli.js +226 -33
- package/dist/crypto.d.ts +17 -0
- package/dist/crypto.js +116 -16
- package/dist/declaration.d.ts +43 -4
- package/dist/declaration.js +198 -14
- package/dist/environment.d.ts +42 -0
- package/dist/environment.js +136 -0
- package/dist/evidence.d.ts +6 -0
- package/dist/evidence.js +32 -1
- package/dist/exit-codes.d.ts +18 -0
- package/dist/exit-codes.js +25 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +21 -5
- package/dist/run-adapters.d.ts +10 -0
- package/dist/run-adapters.js +35 -0
- package/dist/run.d.ts +209 -0
- package/dist/run.js +1405 -0
- package/dist/verifier.d.ts +43 -0
- package/dist/verifier.js +113 -6
- package/dist/witness-lookup.d.ts +13 -1
- package/dist/witness-lookup.js +12 -2
- package/package.json +2 -2
- package/src/case-file.ts +2 -1
- package/src/case.ts +11 -4
- package/src/chain.ts +81 -4
- package/src/cli.ts +259 -28
- package/src/crypto.ts +125 -16
- package/src/declaration.ts +242 -12
- package/src/environment.ts +145 -0
- package/src/evidence.ts +29 -1
- package/src/exit-codes.ts +28 -0
- package/src/index.ts +31 -1
- package/src/run-adapters.ts +44 -0
- package/src/run.ts +1598 -0
- package/src/verifier.ts +102 -5
- package/src/witness-lookup.ts +18 -1
- package/vectors/README.md +37 -0
- package/vectors/sandbox-evidence.json +3161 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// The environment marker: is this sandbox evidence?
|
|
2
|
+
//
|
|
3
|
+
// `environment` is an OPTIONAL skeleton field inside every envelope's signed
|
|
4
|
+
// scope, `"dev"` or `"production"` (envelope-spec §2, A1 and A9). The spec
|
|
5
|
+
// says a verifier MAY surface the value as `environment_marker` in its result
|
|
6
|
+
// and a profile MAY refuse to count dev-marked envelopes toward production
|
|
7
|
+
// evidence. Both commands now do the first, and print a pack or case that
|
|
8
|
+
// holds a `dev` envelope as sandbox evidence rather than as `VERIFIED`.
|
|
9
|
+
//
|
|
10
|
+
// The marker is read from the signed envelopes the pack holds, never from a
|
|
11
|
+
// wrapper field, a pack name or anything else the holder could write without a
|
|
12
|
+
// key. It never moves the verdict word: a sandbox pack that verifies is still
|
|
13
|
+
// `verified`, and the commands map that to the headline `SANDBOX` and exit 5
|
|
14
|
+
// (`sandboxExit` in `./exit-codes.ts`). The Python twin is
|
|
15
|
+
// `src/sanning_proof/environment.py`.
|
|
16
|
+
|
|
17
|
+
import { isAgentProofSpec } from "./agent-proof.js";
|
|
18
|
+
import {
|
|
19
|
+
ANCHOR_TRACE_BODY_TYPE,
|
|
20
|
+
EXPORT_BODY_TYPE,
|
|
21
|
+
type EvidenceBundleResult,
|
|
22
|
+
SIMULATED_WITNESS_KIND,
|
|
23
|
+
STAMP_EVIDENCE_BODY_TYPE,
|
|
24
|
+
} from "./evidence.js";
|
|
25
|
+
|
|
26
|
+
/** `"dev"` when every envelope is signed `dev`, `"mixed"` when only some are. */
|
|
27
|
+
export type EnvironmentMarker = "dev" | "mixed";
|
|
28
|
+
|
|
29
|
+
/** The headline `verify` prints for sandbox evidence in place of `VERIFIED`. */
|
|
30
|
+
export const SANDBOX_HEADLINE = "SANDBOX";
|
|
31
|
+
|
|
32
|
+
/** The title `case` prints for sandbox evidence in place of `Verified` (case titles are title case). */
|
|
33
|
+
export const SANDBOX_TITLE = "Sandbox";
|
|
34
|
+
|
|
35
|
+
/** The one line printed with it, and with any other headline, wherever the marker is present. */
|
|
36
|
+
export const SANDBOX_LINE = "Sandbox evidence (environment dev): not production evidence";
|
|
37
|
+
|
|
38
|
+
// The same bound `resolveTraceEvents` puts on unwrapping export wrappers.
|
|
39
|
+
const MAX_EXPORT_UNWRAP = 4;
|
|
40
|
+
|
|
41
|
+
function isObject(v: unknown): v is Record<string, unknown> {
|
|
42
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function own(o: Record<string, unknown>, key: string): unknown {
|
|
46
|
+
return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function entryEnvelopes(list: unknown, out: Record<string, unknown>[]): void {
|
|
50
|
+
if (!Array.isArray(list)) return;
|
|
51
|
+
for (const entry of list) {
|
|
52
|
+
if (!isObject(entry)) continue;
|
|
53
|
+
const env = own(entry, "envelope");
|
|
54
|
+
if (isObject(env)) out.push(env);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The signed envelopes a pack holds: every checkpoint and event envelope of an
|
|
60
|
+
* anchor-trace body, every event envelope of a stamp-evidence body, the same
|
|
61
|
+
* inside an export's inline source bundle, and an agent proof's checkpoint
|
|
62
|
+
* envelope. A stamp-trace body holds none. A value that is not an object is
|
|
63
|
+
* not an envelope and is not counted.
|
|
64
|
+
*/
|
|
65
|
+
export function packEnvelopes(bundle: unknown): Record<string, unknown>[] {
|
|
66
|
+
const out: Record<string, unknown>[] = [];
|
|
67
|
+
if (isObject(bundle) && isAgentProofSpec(own(bundle, "spec_version"))) {
|
|
68
|
+
const env = own(bundle, "checkpoint_envelope");
|
|
69
|
+
if (isObject(env)) out.push(env);
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
let cur: unknown = bundle;
|
|
73
|
+
for (let depth = 0; depth < MAX_EXPORT_UNWRAP; depth++) {
|
|
74
|
+
if (!isObject(cur)) return out;
|
|
75
|
+
const bodyType = own(cur, "body_type");
|
|
76
|
+
const body = own(cur, "body");
|
|
77
|
+
if (!isObject(body)) return out;
|
|
78
|
+
if (bodyType === ANCHOR_TRACE_BODY_TYPE) {
|
|
79
|
+
entryEnvelopes(own(body, "checkpoints"), out);
|
|
80
|
+
entryEnvelopes(own(body, "events"), out);
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
if (bodyType === STAMP_EVIDENCE_BODY_TYPE) {
|
|
84
|
+
entryEnvelopes(own(body, "events"), out);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
if (bodyType !== EXPORT_BODY_TYPE) return out;
|
|
88
|
+
cur = own(body, "source_bundle");
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The environment marker over one or more packs: `"dev"` where every envelope
|
|
95
|
+
* they hold is signed `environment: "dev"`, `"mixed"` where some are and some
|
|
96
|
+
* are not (`production`, or no `environment` at all), and `null` where none
|
|
97
|
+
* is, which is every production pack and every pack that holds no envelope.
|
|
98
|
+
*/
|
|
99
|
+
export function environmentMarker(bundles: readonly unknown[]): EnvironmentMarker | null {
|
|
100
|
+
let total = 0;
|
|
101
|
+
let dev = 0;
|
|
102
|
+
for (const bundle of bundles) {
|
|
103
|
+
for (const env of packEnvelopes(bundle)) {
|
|
104
|
+
total++;
|
|
105
|
+
if (own(env, "environment") === "dev") dev++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (dev === 0) return null;
|
|
109
|
+
return dev === total ? "dev" : "mixed";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether a body's only unsettled witnesses are simulated: the body has events
|
|
114
|
+
* and witnesses (checkpoints, stamps or both), every witness is named, at
|
|
115
|
+
* least one is simulated, and every other one a gateway confirmed. Then the
|
|
116
|
+
* one thing that keeps the bundle from `verified` is a simulated witness
|
|
117
|
+
* nobody can look up: no mismatch, no unreachable gateway, no witness of
|
|
118
|
+
* another kind, no checkpoint or stamp witnessed by nobody.
|
|
119
|
+
*/
|
|
120
|
+
export function onlySimulatedUnsettled(result: EvidenceBundleResult): boolean {
|
|
121
|
+
const witnesses = [...result.checkpoints, ...(result.stamps ?? [])];
|
|
122
|
+
if (witnesses.length === 0 || result.events.length === 0) return false;
|
|
123
|
+
let simulated = 0;
|
|
124
|
+
for (const w of witnesses) {
|
|
125
|
+
if (w.anchorRef === null) return false;
|
|
126
|
+
if (w.anchorRef.kind === SIMULATED_WITNESS_KIND) simulated++;
|
|
127
|
+
else if (w.onChain?.rollup !== "confirm") return false;
|
|
128
|
+
}
|
|
129
|
+
return simulated > 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Whether a run with gateways counted only simulated witnesses, so nothing
|
|
134
|
+
* was looked up on a chain: gateways were passed, the run is not counted as
|
|
135
|
+
* checked on a chain, and it holds a simulated witness. A run that stopped
|
|
136
|
+
* before its witnesses were read (a bad wrapper signature or `body_hash`, an
|
|
137
|
+
* unknown `body_type`, an export whose source is not inline) holds none, so it
|
|
138
|
+
* is not one, and prints what it printed before simulated witnesses existed.
|
|
139
|
+
*/
|
|
140
|
+
export function simulatedOnlyRun(result: EvidenceBundleResult, gateways: readonly string[]): boolean {
|
|
141
|
+
if (gateways.length === 0 || result.onChainChecked) return false;
|
|
142
|
+
return [...result.checkpoints, ...(result.stamps ?? [])].some(
|
|
143
|
+
(w) => w.anchorRef?.kind === SIMULATED_WITNESS_KIND,
|
|
144
|
+
);
|
|
145
|
+
}
|
package/src/evidence.ts
CHANGED
|
@@ -1662,6 +1662,10 @@ async function verifyAnchorTrace(
|
|
|
1662
1662
|
let gatewayUnavailable = false;
|
|
1663
1663
|
let uncheckedKind = false;
|
|
1664
1664
|
let uncheckedId = false;
|
|
1665
|
+
// Simulated witnesses against witnesses of any other kind, counted over the
|
|
1666
|
+
// checkpoints that name one, as `refetchStamps` counts stamps.
|
|
1667
|
+
let simulated = 0;
|
|
1668
|
+
let onChainKinds = 0;
|
|
1665
1669
|
if (onChainChecked) {
|
|
1666
1670
|
if (typeof fetchImpl !== "function") {
|
|
1667
1671
|
gatewayUnavailable = true;
|
|
@@ -1701,6 +1705,8 @@ async function verifyAnchorTrace(
|
|
|
1701
1705
|
cpr.errors.push("checkpoint has no anchor_ref -- not yet witnessed, nothing to re-fetch");
|
|
1702
1706
|
continue;
|
|
1703
1707
|
}
|
|
1708
|
+
if (cpr.anchorRef.kind === SIMULATED_WITNESS_KIND) simulated++;
|
|
1709
|
+
else onChainKinds++;
|
|
1704
1710
|
const outcome = await refetchCheckpoint(cp, cpr.anchorRef, gateways, fetchImpl);
|
|
1705
1711
|
cpr.onChain = outcome.onChain;
|
|
1706
1712
|
cpr.onChainOk = outcome.onChain?.onChainOk ?? null;
|
|
@@ -1764,7 +1770,10 @@ async function verifyAnchorTrace(
|
|
|
1764
1770
|
bodyHashOk: true,
|
|
1765
1771
|
signatureOk: true,
|
|
1766
1772
|
bodyType,
|
|
1767
|
-
|
|
1773
|
+
// A run whose only witnessed checkpoints are simulated looked nothing up
|
|
1774
|
+
// on a chain, so it is not counted as checked on one (the stamp rule in
|
|
1775
|
+
// `refetchStamps`). The checkpoints stay not checked and the run partial.
|
|
1776
|
+
onChainChecked: onChainChecked && !(simulated > 0 && onChainKinds === 0),
|
|
1768
1777
|
checkpoints: checkpointResults,
|
|
1769
1778
|
events: eventResults,
|
|
1770
1779
|
assertedStatus,
|
|
@@ -2679,12 +2688,16 @@ async function refetchStamps(
|
|
|
2679
2688
|
let gatewayUnavailable = false;
|
|
2680
2689
|
let uncheckedKind = false;
|
|
2681
2690
|
let uncheckedId = false;
|
|
2691
|
+
let simulated = 0;
|
|
2692
|
+
let onChainKinds = 0;
|
|
2682
2693
|
for (let i = 0; i < stamps.length; i++) {
|
|
2683
2694
|
const st = stamps[i]!;
|
|
2684
2695
|
const witnessedHex = witnessed[i];
|
|
2685
2696
|
// No witness named, or bytes we could not read: nothing to re-fetch, and
|
|
2686
2697
|
// nothing a gateway could confirm or deny. Left as `onChain: null`.
|
|
2687
2698
|
if (st.anchorRef === null || witnessedHex === undefined) continue;
|
|
2699
|
+
if (st.anchorRef.kind === SIMULATED_WITNESS_KIND) simulated++;
|
|
2700
|
+
else onChainKinds++;
|
|
2688
2701
|
|
|
2689
2702
|
const outcome = await refetchStamp(witnessedHex, st.anchorRef, gateways, fetchImpl);
|
|
2690
2703
|
st.onChain = outcome.onChain;
|
|
@@ -2704,9 +2717,24 @@ async function refetchStamps(
|
|
|
2704
2717
|
// unreachable note would fail a stamp for a gateway being down.
|
|
2705
2718
|
st.ok = st.ok && st.onChainOk !== false;
|
|
2706
2719
|
}
|
|
2720
|
+
// A simulated witness is a sandbox plane's stand-in for a chain: there is no
|
|
2721
|
+
// chain to look it up on, so it is never counted as checked on one.
|
|
2722
|
+
// It stays not checked like any kind this verifier has no adapter for, and
|
|
2723
|
+
// the run stays undetermined; what changes is that a run whose only
|
|
2724
|
+
// witnesses are simulated no longer says it checked anything on a chain.
|
|
2725
|
+
if (simulated > 0 && onChainKinds === 0) {
|
|
2726
|
+
return { onChainChecked: false, gatewayUnavailable, uncheckedKind, uncheckedId };
|
|
2727
|
+
}
|
|
2707
2728
|
return { onChainChecked, gatewayUnavailable, uncheckedKind, uncheckedId };
|
|
2708
2729
|
}
|
|
2709
2730
|
|
|
2731
|
+
/**
|
|
2732
|
+
* The `anchor_ref.kind` a sandbox plane writes for a witness it simulated
|
|
2733
|
+
* (chain id `sanning-sandbox`). Never looked up and never counted as checked
|
|
2734
|
+
* on a chain.
|
|
2735
|
+
*/
|
|
2736
|
+
export const SIMULATED_WITNESS_KIND = "simulated";
|
|
2737
|
+
|
|
2710
2738
|
interface StampRefetchSummary {
|
|
2711
2739
|
onChainChecked: boolean;
|
|
2712
2740
|
gatewayUnavailable: boolean;
|
package/src/exit-codes.ts
CHANGED
|
@@ -16,6 +16,14 @@ export const EXIT_FAILED = 1;
|
|
|
16
16
|
export const EXIT_MALFORMED = 2;
|
|
17
17
|
export const EXIT_UNDETERMINED = 3;
|
|
18
18
|
export const EXIT_MIXED = 4;
|
|
19
|
+
/**
|
|
20
|
+
* Sandbox evidence (envelope-spec §2): a run that would exit 0 over a pack or
|
|
21
|
+
* case holding a record signed `environment: "dev"`, or a run over records
|
|
22
|
+
* that are ALL signed `dev` whose only unsettled witnesses are simulated.
|
|
23
|
+
* Not a verdict word: the verdict stays what the checks established
|
|
24
|
+
* (`sandboxExit`). No flag turns 5 into 0.
|
|
25
|
+
*/
|
|
26
|
+
export const EXIT_SANDBOX = 5;
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
* The exit code of each of the eight verdict words (`evidence-bundle.md` §3).
|
|
@@ -40,6 +48,7 @@ export const EXIT_CODES_HELP = [
|
|
|
40
48
|
" 2 usage or malformed",
|
|
41
49
|
" 3 undetermined: partial, pending, incomplete or not_found",
|
|
42
50
|
" 4 mixed: more than one outcome, none failed; read the exceptions",
|
|
51
|
+
" 5 sandbox: a record is signed environment dev; would be 0, or only a simulated witness is unchecked",
|
|
43
52
|
].join("\n");
|
|
44
53
|
|
|
45
54
|
/**
|
|
@@ -52,3 +61,22 @@ export function exitCodeFor(verdict: unknown): number {
|
|
|
52
61
|
}
|
|
53
62
|
return EXIT_MALFORMED;
|
|
54
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The code a verb exits with once the environment marker is read. 5 where the
|
|
67
|
+
* run would otherwise exit 0 and the pack or case holds a record signed `dev`;
|
|
68
|
+
* 5 where the run is undetermined only because a simulated witness could not
|
|
69
|
+
* be looked up (`onlySimulatedUnsettled`, which the caller works out) and
|
|
70
|
+
* EVERY record is signed `dev`, so a production or mixed pack carrying a
|
|
71
|
+
* simulated witness keeps 3; the code unchanged everywhere else. A failure
|
|
72
|
+
* stays 1, malformed 2 and mixed 4.
|
|
73
|
+
*/
|
|
74
|
+
export function sandboxExit(
|
|
75
|
+
code: number,
|
|
76
|
+
environmentMarker: string | null,
|
|
77
|
+
onlySimulatedUnsettled = false,
|
|
78
|
+
): number {
|
|
79
|
+
if (code === EXIT_VERIFIED && environmentMarker !== null) return EXIT_SANDBOX;
|
|
80
|
+
if (code === EXIT_UNDETERMINED && environmentMarker === "dev" && onlySimulatedUnsettled) return EXIT_SANDBOX;
|
|
81
|
+
return code;
|
|
82
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -25,9 +25,19 @@ export {
|
|
|
25
25
|
specVersionSupported,
|
|
26
26
|
verifyEnvelope,
|
|
27
27
|
} from "./verifier.js";
|
|
28
|
+
// The strict reader of bytes that claim to be JCS, the twin of Python's public
|
|
29
|
+
// `canonicalize.jcs_loads`. The enclave attestation adapters read a token's
|
|
30
|
+
// JSON through it, so they refuse what the kernel refuses. Additive.
|
|
31
|
+
export { jcsParse } from "./verifier.js";
|
|
32
|
+
// The same reader within the enclave readers' limits (enclave-attestation
|
|
33
|
+
// section 2.1): depth checked on the bytes first, every number finite. The
|
|
34
|
+
// adapters read a token's header and claims and the manifest through it.
|
|
35
|
+
// Additive.
|
|
36
|
+
export { exceedsTextDepth, hasNonFiniteNumber, jcsParseWithinLimits, MAX_READ_DEPTH } from "./verifier.js";
|
|
28
37
|
export {
|
|
29
38
|
bytesToHex,
|
|
30
39
|
deriveOperatorAddress,
|
|
40
|
+
ed25519Refusal,
|
|
31
41
|
ed25519Verify,
|
|
32
42
|
hexToBytes,
|
|
33
43
|
sha256Bytes,
|
|
@@ -174,6 +184,7 @@ export {
|
|
|
174
184
|
DECLARATION_VERDICTS,
|
|
175
185
|
DECLARATION_VERSION,
|
|
176
186
|
DECLARATION_VERSION_V2,
|
|
187
|
+
DECLARATION_VERSION_V3,
|
|
177
188
|
DECLARATION_VERSIONS,
|
|
178
189
|
checkDeclaration,
|
|
179
190
|
declarationClaims,
|
|
@@ -352,7 +363,10 @@ export type {
|
|
|
352
363
|
// `vectors/chain-reading.json` beside the pack-grading table for the same
|
|
353
364
|
// reason: a rule consumers must agree with needs a statement they can fetch.
|
|
354
365
|
//
|
|
355
|
-
// It returns FINDINGS, never a verdict, and it feeds no status
|
|
366
|
+
// It returns FINDINGS, never a verdict, and it feeds no status on its own: a
|
|
367
|
+
// pack's signed v3 declaration can claim a chain complete and in order, and
|
|
368
|
+
// `checkDeclaration` holds that claim to the links `readChainLinks` reads
|
|
369
|
+
// (evidence-bundle.md 5.4.2 rule 11). Chain links are
|
|
356
370
|
// deletion/reorder-evidence for the events present and NOT a completeness proof
|
|
357
371
|
// (`anchor/docs/profile-sanning.events-v2.md` §5), drop the last N events of a
|
|
358
372
|
// chain and nothing dangles. `CHAIN_READING_SCOPE` is that sentence, exported so
|
|
@@ -361,12 +375,14 @@ export type {
|
|
|
361
375
|
export {
|
|
362
376
|
ADAPTER_CHAIN_NAMESPACES,
|
|
363
377
|
CHAIN_READING_SCOPE,
|
|
378
|
+
readChainLinks,
|
|
364
379
|
readChains,
|
|
365
380
|
renderChainFinding,
|
|
366
381
|
} from "./chain.js";
|
|
367
382
|
export type {
|
|
368
383
|
ChainFinding,
|
|
369
384
|
ChainFindingKind,
|
|
385
|
+
ChainLink,
|
|
370
386
|
ChainReading,
|
|
371
387
|
ChainSource,
|
|
372
388
|
ChainSummary,
|
|
@@ -384,10 +400,24 @@ export {
|
|
|
384
400
|
EXIT_FAILED,
|
|
385
401
|
EXIT_MALFORMED,
|
|
386
402
|
EXIT_MIXED,
|
|
403
|
+
EXIT_SANDBOX,
|
|
387
404
|
EXIT_UNDETERMINED,
|
|
388
405
|
EXIT_VERIFIED,
|
|
389
406
|
exitCodeFor,
|
|
407
|
+
sandboxExit,
|
|
390
408
|
} from "./exit-codes.js";
|
|
409
|
+
// Sandbox evidence: the environment marker over the envelopes a pack or case
|
|
410
|
+
// holds (envelope-spec §2, the `environment_marker` MAY), and the headline and
|
|
411
|
+
// line both commands print for it. Additive.
|
|
412
|
+
export {
|
|
413
|
+
SANDBOX_HEADLINE,
|
|
414
|
+
SANDBOX_LINE,
|
|
415
|
+
SANDBOX_TITLE,
|
|
416
|
+
environmentMarker,
|
|
417
|
+
onlySimulatedUnsettled,
|
|
418
|
+
packEnvelopes,
|
|
419
|
+
} from "./environment.js";
|
|
420
|
+
export type { EnvironmentMarker } from "./environment.js";
|
|
391
421
|
export { attestationSignatureToHex, composeExport } from "./compose.js";
|
|
392
422
|
export type { ComposeExportOptions, ComposeExportResult, ExporterKey } from "./compose.js";
|
|
393
423
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Loading the enclave attestation adapters a reader has installed. Kept out of
|
|
2
|
+
// `index.ts` on purpose: it imports packages by name, which a browser bundle
|
|
3
|
+
// cannot resolve, and only the command line needs it. `run.ts` imports nothing.
|
|
4
|
+
//
|
|
5
|
+
// The packages are named by `ADAPTER_MODULES` and by nothing else: a provider
|
|
6
|
+
// id read from a pack is never turned into a module name.
|
|
7
|
+
|
|
8
|
+
import { ADAPTER_MODULES, declaredKinds, type EnclaveAdapter } from "./run.js";
|
|
9
|
+
|
|
10
|
+
/** Import a module by name. A parameter so a test can watch what is asked for. */
|
|
11
|
+
export type Importer = (name: string) => Promise<unknown>;
|
|
12
|
+
|
|
13
|
+
const dynamicImport: Importer = (name) => import(/* @vite-ignore */ name);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The adapters this environment holds, loaded from `ADAPTER_MODULES` and
|
|
17
|
+
* nothing else. A module counts only when it exports the provider it was
|
|
18
|
+
* loaded for as `provider`, a `check` function, and the failure kinds it
|
|
19
|
+
* declares as `kinds` (`declaredKinds`).
|
|
20
|
+
*/
|
|
21
|
+
export async function loadInstalledAdapters(
|
|
22
|
+
importer: Importer = dynamicImport,
|
|
23
|
+
): Promise<Map<string, EnclaveAdapter>> {
|
|
24
|
+
const found = new Map<string, EnclaveAdapter>();
|
|
25
|
+
for (const [provider, name] of Object.entries(ADAPTER_MODULES)) {
|
|
26
|
+
let module: unknown;
|
|
27
|
+
try {
|
|
28
|
+
module = await importer(name);
|
|
29
|
+
} catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const m = module as Partial<EnclaveAdapter> | null;
|
|
33
|
+
if (
|
|
34
|
+
m !== null &&
|
|
35
|
+
typeof m === "object" &&
|
|
36
|
+
m.provider === provider &&
|
|
37
|
+
typeof m.check === "function" &&
|
|
38
|
+
declaredKinds(m) !== null
|
|
39
|
+
) {
|
|
40
|
+
found.set(provider, m as EnclaveAdapter);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return found;
|
|
44
|
+
}
|