@mikeargento/bitgraph-verify 1.8.0 → 1.9.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/dist/index.d.ts CHANGED
@@ -19,4 +19,6 @@ export type { FuseMemberCategory, FuseSetEvidence, FuseMemberOptions, FuseMember
19
19
  export { SET2_PLACEMENT_ID, SET_MEMBER_METADATA_KEY, MAX_SET2_MEMBERS, canonicalSetRow, setLeaf, sortSetMembers, buildSetTree, setMemberPath, buildSetRoot, parseSetRoot, buildSetMemberProof, parseSetMemberProof, setRootFromMember } from "./fuse.js";
20
20
  export type { SetRoot, SetMemberProof } from "./fuse.js";
21
21
  export { merkleLeafHash, merkleNodeHash, merkleRoot, merklePath, merkleRootFromPath, MerkleTree } from "./fuse-merkle.js";
22
+ export { verifyRun, readRunAttribution, runAttribution, RUN_PROFILE, RUN_ENCODING_BASE64URL } from "./run.js";
23
+ export type { RunCategory, RunMarker, RunVerifyResult, RunVerifyOptions } from "./run.js";
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClF,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7G,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGvF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvJ,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjH,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACzP,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClF,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7G,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGvF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvJ,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjH,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACzP,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM1H,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAC9G,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -8,4 +8,9 @@ export { verifyFuse, assembledAfterCommit } from "./fuse-verify.js";
8
8
  export { verifyFuseMember } from "./fuse-member.js";
9
9
  export { SET2_PLACEMENT_ID, SET_MEMBER_METADATA_KEY, MAX_SET2_MEMBERS, canonicalSetRow, setLeaf, sortSetMembers, buildSetTree, setMemberPath, buildSetRoot, parseSetRoot, buildSetMemberProof, parseSetMemberProof, setRootFromMember } from "./fuse.js";
10
10
  export { merkleLeafHash, merkleNodeHash, merkleRoot, merklePath, merkleRootFromPath, MerkleTree } from "./fuse-merkle.js";
11
+ // BitGraph Run (profile bitgraph-run/1): an artifact MADE with its slot
12
+ // commitment inside it, rather than wrapped around an original afterwards.
13
+ // Fuse rebuilds from an original; a run has none, so the check is that the
14
+ // commitment the enclave signed is present in the committed bytes.
15
+ export { verifyRun, readRunAttribution, runAttribution, RUN_PROFILE, RUN_ENCODING_BASE64URL } from "./run.js";
11
16
  //# 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,qFAAqF;AAyBrF,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7G,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvF,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEzP,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAyBrF,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7G,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvF,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEzP,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1H,wEAAwE;AACxE,2EAA2E;AAC3E,2EAA2E;AAC3E,mEAAmE;AACnE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC"}
package/dist/run.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { BitGraphProof, VerificationPolicy } from "./types.js";
2
+ import { type FuseSpan } from "./fuse-verify.js";
3
+ /** The signed profile id: attribution.name on a run proof. */
4
+ export declare const RUN_PROFILE = "bitgraph-run/1";
5
+ /**
6
+ * The only encoding this build registers: the commitment as unpadded base64url
7
+ * ASCII, the form a producer pastes into a prompt, a message or a header. It is
8
+ * attribution.title on a run proof.
9
+ */
10
+ export declare const RUN_ENCODING_BASE64URL = "base64url";
11
+ export type RunCategory = "RUN_CONFIRMED" | "COMMITMENT_ABSENT" | "INVALID_SLOT_COMMITMENT" | "INVALID_UNDERLYING_PROOF" | "UNDETERMINED_ENCODING" | "NOT_A_RUN";
12
+ export interface RunMarker {
13
+ /** attribution.title: how the commitment appears in the bytes. */
14
+ encoding: string | null;
15
+ /** attribution.message: the commitment the producer declared, as written. */
16
+ declaredCommitment: string | null;
17
+ }
18
+ export interface RunVerifyResult {
19
+ category: RunCategory;
20
+ /** The underlying bitgraph/1 verification, reported on its own. */
21
+ proof: {
22
+ valid: boolean;
23
+ reason?: string;
24
+ };
25
+ marker: RunMarker | null;
26
+ /** Recomputed from the proof's slot record; null when the proof carries none. */
27
+ slotCommitmentB64u: string | null;
28
+ /** Byte offset of the first occurrence in the artifact, or null when absent. */
29
+ offset: number | null;
30
+ /** How many times it appears. One is the ordinary case; more is not an error. */
31
+ occurrences: number;
32
+ artifactDigestB64: string;
33
+ fileDigestB64: string;
34
+ span: FuseSpan | null;
35
+ /** What this result establishes, bounded. */
36
+ statements: string[];
37
+ /** What it does not establish, stated so nothing has to be inferred. */
38
+ limits: string[];
39
+ reason: string | null;
40
+ }
41
+ export interface RunVerifyOptions {
42
+ proof: BitGraphProof;
43
+ bytes: Uint8Array;
44
+ trustAnchors?: VerificationPolicy;
45
+ }
46
+ /** The marker a run proof carries, or null when it is not a run proof. */
47
+ export declare function readRunAttribution(proof: BitGraphProof): RunMarker | null;
48
+ /** The attribution a producer sends when committing a run. */
49
+ export declare function runAttribution(commitment: Uint8Array | string): {
50
+ name: string;
51
+ title: string;
52
+ message: string;
53
+ };
54
+ /**
55
+ * Verify a bitgraph-run/1 artifact from its own bytes. Nothing here touches the
56
+ * network, and nothing is rebuilt: a run has no original to rebuild from.
57
+ */
58
+ export declare function verifyRun(opts: RunVerifyOptions): Promise<RunVerifyResult>;
59
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzD,8DAA8D;AAC9D,eAAO,MAAM,WAAW,mBAAmB,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,mBAAmB,GACnB,yBAAyB,GACzB,0BAA0B,GAC1B,uBAAuB,GACvB,WAAW,CAAC;AAEhB,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,6EAA6E;IAC7E,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,mEAAmE;IACnE,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gFAAgF;IAChF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,wEAAwE;IACxE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAKD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAOzE;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAMhH;AA0BD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA8DhF"}
package/dist/run.js ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * BitGraph Run (profile bitgraph-run/1): an artifact that was MADE with its
3
+ * slot commitment inside it, rather than wrapped around an original afterwards.
4
+ *
5
+ * Fuse and Run are the two ways an artifact can carry its commitment, and they
6
+ * differ in what a verifier can do about it:
7
+ *
8
+ * fuse the commitment was written into the bytes of an existing file, in a
9
+ * registered placement, so a verifier holding the ORIGINAL can rebuild
10
+ * the committed artifact byte for byte. The placement is the recipe.
11
+ * run there is no original and no recipe. The producer asked for a slot,
12
+ * put the commitment into the thing it was about to make, and committed
13
+ * the digest of what came out. A model prompt, a build id, a commit
14
+ * message, a signed payload: the commitment is an ingredient, not a
15
+ * wrapper, so there is nothing to rebuild from.
16
+ *
17
+ * What this file checks, and only this:
18
+ *
19
+ * 1. the underlying bitgraph/1 proof verifies against these exact bytes
20
+ * 2. the marker is present and names an encoding this build knows
21
+ * 3. the commitment recomputed from the proof's own slot record equals the
22
+ * one the signed marker declares
23
+ * 4. that commitment, in the declared encoding, appears in the artifact's
24
+ * bytes, and where
25
+ *
26
+ * Together those establish that the bytes were assembled after the enclave
27
+ * signed the slot, because they contain a value the enclave generated and
28
+ * nobody could predict. They do NOT establish that the commitment was an INPUT
29
+ * to the production rather than inserted at the end: a producer who appends the
30
+ * string to a finished file passes every check here, and that producer has
31
+ * really just done a hand-rolled fuse. The distinction matters for how it reads
32
+ * to a human (an acrostic on the commitment is evidence of conditioning; a
33
+ * trailing line is not), and this file says so rather than implying otherwise.
34
+ * `statements` carries the bound; `limits` carries what is not shown.
35
+ */
36
+ import { sha256 } from "@noble/hashes/sha256";
37
+ import { verify } from "./verifier.js";
38
+ import { computeSlotCommitment } from "./fuse.js";
39
+ import { spanOf } from "./fuse-verify.js";
40
+ /** The signed profile id: attribution.name on a run proof. */
41
+ export const RUN_PROFILE = "bitgraph-run/1";
42
+ /**
43
+ * The only encoding this build registers: the commitment as unpadded base64url
44
+ * ASCII, the form a producer pastes into a prompt, a message or a header. It is
45
+ * attribution.title on a run proof.
46
+ */
47
+ export const RUN_ENCODING_BASE64URL = "base64url";
48
+ const b64 = (b) => Buffer.from(b).toString("base64");
49
+ const b64u = (b) => b64(b).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
50
+ /** The marker a run proof carries, or null when it is not a run proof. */
51
+ export function readRunAttribution(proof) {
52
+ const a = proof.attribution;
53
+ if (a === undefined || a.name !== RUN_PROFILE)
54
+ return null;
55
+ return {
56
+ encoding: typeof a.title === "string" && a.title.length > 0 ? a.title : null,
57
+ declaredCommitment: typeof a.message === "string" && a.message.length > 0 ? a.message : null,
58
+ };
59
+ }
60
+ /** The attribution a producer sends when committing a run. */
61
+ export function runAttribution(commitment) {
62
+ return {
63
+ name: RUN_PROFILE,
64
+ title: RUN_ENCODING_BASE64URL,
65
+ message: typeof commitment === "string" ? commitment : b64u(commitment),
66
+ };
67
+ }
68
+ /** Every byte offset at which `needle` occurs in `haystack`. */
69
+ function findAll(haystack, needle) {
70
+ const out = [];
71
+ if (needle.length === 0 || needle.length > haystack.length)
72
+ return out;
73
+ outer: for (let i = 0; i + needle.length <= haystack.length; i++) {
74
+ for (let j = 0; j < needle.length; j++)
75
+ if (haystack[i + j] !== needle[j])
76
+ continue outer;
77
+ out.push(i);
78
+ }
79
+ return out;
80
+ }
81
+ function statementsFor(category, span, offset) {
82
+ if (span === null || category !== "RUN_CONFIRMED")
83
+ return [];
84
+ return [
85
+ `The enclave signed the slot at position ${span.slotCounter} before this artifact's digest reached it.`,
86
+ `These exact bytes contain that slot's commitment, first at byte ${offset}, so they were assembled after the slot existed and were committed at position ${span.commitCounter}.`,
87
+ ];
88
+ }
89
+ const LIMITS = [
90
+ "Containing the commitment does not show it was an input to the artifact's production: bytes appended to a finished file pass the same check. What the commitment was to the work is the producer's claim, not this result's.",
91
+ "Nothing here bounds the artifact from above in wall-clock time. The position bound is the commit position; a clock comes only from an anchor.",
92
+ ];
93
+ /**
94
+ * Verify a bitgraph-run/1 artifact from its own bytes. Nothing here touches the
95
+ * network, and nothing is rebuilt: a run has no original to rebuild from.
96
+ */
97
+ export async function verifyRun(opts) {
98
+ const { proof, bytes, trustAnchors } = opts;
99
+ const fileDigestB64 = b64(sha256(bytes));
100
+ const artifactDigestB64 = proof.artifact?.digestB64 ?? "";
101
+ const span = spanOf(proof);
102
+ const marker = readRunAttribution(proof);
103
+ const base = (category, extra, reason) => ({
104
+ category,
105
+ proof: extra.proof ?? { valid: false, reason: "not run" },
106
+ marker,
107
+ slotCommitmentB64u: extra.slotCommitmentB64u ?? null,
108
+ offset: extra.offset ?? null,
109
+ occurrences: extra.occurrences ?? 0,
110
+ artifactDigestB64,
111
+ fileDigestB64,
112
+ span,
113
+ statements: statementsFor(category, span, extra.offset ?? null),
114
+ limits: category === "RUN_CONFIRMED" ? LIMITS : [],
115
+ reason,
116
+ });
117
+ if (marker === null) {
118
+ return base("NOT_A_RUN", {}, `the proof carries no ${RUN_PROFILE} attribution`);
119
+ }
120
+ let pv;
121
+ try {
122
+ pv = await verify(trustAnchors === undefined ? { proof, bytes } : { proof, bytes, trustAnchors });
123
+ }
124
+ catch (e) {
125
+ pv = { valid: false, reason: e.message };
126
+ }
127
+ if (!pv.valid) {
128
+ return base("INVALID_UNDERLYING_PROOF", { proof: pv }, pv.reason ?? "the underlying proof does not verify against these bytes");
129
+ }
130
+ if (marker.encoding !== RUN_ENCODING_BASE64URL) {
131
+ return base("UNDETERMINED_ENCODING", { proof: pv }, `the marker names encoding "${marker.encoding ?? "none"}", which this build does not register`);
132
+ }
133
+ const slot = proof.slotAllocation;
134
+ if (slot === undefined) {
135
+ return base("INVALID_SLOT_COMMITMENT", { proof: pv }, "the proof carries no slot record, so the commitment cannot be recomputed");
136
+ }
137
+ const slotCommitmentB64u = b64u(computeSlotCommitment(slot));
138
+ if (marker.declaredCommitment !== slotCommitmentB64u) {
139
+ return base("INVALID_SLOT_COMMITMENT", { proof: pv, slotCommitmentB64u }, `the signed marker declares commitment ${marker.declaredCommitment ?? "none"}, but this proof's slot record commits to ${slotCommitmentB64u}`);
140
+ }
141
+ const hits = findAll(bytes, new TextEncoder().encode(slotCommitmentB64u));
142
+ if (hits.length === 0) {
143
+ return base("COMMITMENT_ABSENT", { proof: pv, slotCommitmentB64u }, "the artifact's bytes do not contain the commitment in the declared encoding");
144
+ }
145
+ return base("RUN_CONFIRMED", { proof: pv, slotCommitmentB64u, offset: hits[0] ?? null, occurrences: hits.length }, null);
146
+ }
147
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikeargento/bitgraph-verify",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Offline verification of BitGraph proofs. Permissionless by design.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -72,3 +72,10 @@ export type { FuseMemberCategory, FuseSetEvidence, FuseMemberOptions, FuseMember
72
72
  export { SET2_PLACEMENT_ID, SET_MEMBER_METADATA_KEY, MAX_SET2_MEMBERS, canonicalSetRow, setLeaf, sortSetMembers, buildSetTree, setMemberPath, buildSetRoot, parseSetRoot, buildSetMemberProof, parseSetMemberProof, setRootFromMember } from "./fuse.js";
73
73
  export type { SetRoot, SetMemberProof } from "./fuse.js";
74
74
  export { merkleLeafHash, merkleNodeHash, merkleRoot, merklePath, merkleRootFromPath, MerkleTree } from "./fuse-merkle.js";
75
+
76
+ // BitGraph Run (profile bitgraph-run/1): an artifact MADE with its slot
77
+ // commitment inside it, rather than wrapped around an original afterwards.
78
+ // Fuse rebuilds from an original; a run has none, so the check is that the
79
+ // commitment the enclave signed is present in the committed bytes.
80
+ export { verifyRun, readRunAttribution, runAttribution, RUN_PROFILE, RUN_ENCODING_BASE64URL } from "./run.js";
81
+ export type { RunCategory, RunMarker, RunVerifyResult, RunVerifyOptions } from "./run.js";
package/src/run.ts ADDED
@@ -0,0 +1,206 @@
1
+ /**
2
+ * BitGraph Run (profile bitgraph-run/1): an artifact that was MADE with its
3
+ * slot commitment inside it, rather than wrapped around an original afterwards.
4
+ *
5
+ * Fuse and Run are the two ways an artifact can carry its commitment, and they
6
+ * differ in what a verifier can do about it:
7
+ *
8
+ * fuse the commitment was written into the bytes of an existing file, in a
9
+ * registered placement, so a verifier holding the ORIGINAL can rebuild
10
+ * the committed artifact byte for byte. The placement is the recipe.
11
+ * run there is no original and no recipe. The producer asked for a slot,
12
+ * put the commitment into the thing it was about to make, and committed
13
+ * the digest of what came out. A model prompt, a build id, a commit
14
+ * message, a signed payload: the commitment is an ingredient, not a
15
+ * wrapper, so there is nothing to rebuild from.
16
+ *
17
+ * What this file checks, and only this:
18
+ *
19
+ * 1. the underlying bitgraph/1 proof verifies against these exact bytes
20
+ * 2. the marker is present and names an encoding this build knows
21
+ * 3. the commitment recomputed from the proof's own slot record equals the
22
+ * one the signed marker declares
23
+ * 4. that commitment, in the declared encoding, appears in the artifact's
24
+ * bytes, and where
25
+ *
26
+ * Together those establish that the bytes were assembled after the enclave
27
+ * signed the slot, because they contain a value the enclave generated and
28
+ * nobody could predict. They do NOT establish that the commitment was an INPUT
29
+ * to the production rather than inserted at the end: a producer who appends the
30
+ * string to a finished file passes every check here, and that producer has
31
+ * really just done a hand-rolled fuse. The distinction matters for how it reads
32
+ * to a human (an acrostic on the commitment is evidence of conditioning; a
33
+ * trailing line is not), and this file says so rather than implying otherwise.
34
+ * `statements` carries the bound; `limits` carries what is not shown.
35
+ */
36
+ import { sha256 } from "@noble/hashes/sha256";
37
+ import type { BitGraphProof, VerificationPolicy } from "./types.js";
38
+ import { verify } from "./verifier.js";
39
+ import { computeSlotCommitment } from "./fuse.js";
40
+ import { spanOf, type FuseSpan } from "./fuse-verify.js";
41
+
42
+ /** The signed profile id: attribution.name on a run proof. */
43
+ export const RUN_PROFILE = "bitgraph-run/1";
44
+
45
+ /**
46
+ * The only encoding this build registers: the commitment as unpadded base64url
47
+ * ASCII, the form a producer pastes into a prompt, a message or a header. It is
48
+ * attribution.title on a run proof.
49
+ */
50
+ export const RUN_ENCODING_BASE64URL = "base64url";
51
+
52
+ export type RunCategory =
53
+ | "RUN_CONFIRMED"
54
+ | "COMMITMENT_ABSENT"
55
+ | "INVALID_SLOT_COMMITMENT"
56
+ | "INVALID_UNDERLYING_PROOF"
57
+ | "UNDETERMINED_ENCODING"
58
+ | "NOT_A_RUN";
59
+
60
+ export interface RunMarker {
61
+ /** attribution.title: how the commitment appears in the bytes. */
62
+ encoding: string | null;
63
+ /** attribution.message: the commitment the producer declared, as written. */
64
+ declaredCommitment: string | null;
65
+ }
66
+
67
+ export interface RunVerifyResult {
68
+ category: RunCategory;
69
+ /** The underlying bitgraph/1 verification, reported on its own. */
70
+ proof: { valid: boolean; reason?: string };
71
+ marker: RunMarker | null;
72
+ /** Recomputed from the proof's slot record; null when the proof carries none. */
73
+ slotCommitmentB64u: string | null;
74
+ /** Byte offset of the first occurrence in the artifact, or null when absent. */
75
+ offset: number | null;
76
+ /** How many times it appears. One is the ordinary case; more is not an error. */
77
+ occurrences: number;
78
+ artifactDigestB64: string;
79
+ fileDigestB64: string;
80
+ span: FuseSpan | null;
81
+ /** What this result establishes, bounded. */
82
+ statements: string[];
83
+ /** What it does not establish, stated so nothing has to be inferred. */
84
+ limits: string[];
85
+ reason: string | null;
86
+ }
87
+
88
+ export interface RunVerifyOptions {
89
+ proof: BitGraphProof;
90
+ bytes: Uint8Array;
91
+ trustAnchors?: VerificationPolicy;
92
+ }
93
+
94
+ const b64 = (b: Uint8Array): string => Buffer.from(b).toString("base64");
95
+ const b64u = (b: Uint8Array): string => b64(b).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
96
+
97
+ /** The marker a run proof carries, or null when it is not a run proof. */
98
+ export function readRunAttribution(proof: BitGraphProof): RunMarker | null {
99
+ const a = proof.attribution;
100
+ if (a === undefined || a.name !== RUN_PROFILE) return null;
101
+ return {
102
+ encoding: typeof a.title === "string" && a.title.length > 0 ? a.title : null,
103
+ declaredCommitment: typeof a.message === "string" && a.message.length > 0 ? a.message : null,
104
+ };
105
+ }
106
+
107
+ /** The attribution a producer sends when committing a run. */
108
+ export function runAttribution(commitment: Uint8Array | string): { name: string; title: string; message: string } {
109
+ return {
110
+ name: RUN_PROFILE,
111
+ title: RUN_ENCODING_BASE64URL,
112
+ message: typeof commitment === "string" ? commitment : b64u(commitment),
113
+ };
114
+ }
115
+
116
+ /** Every byte offset at which `needle` occurs in `haystack`. */
117
+ function findAll(haystack: Uint8Array, needle: Uint8Array): number[] {
118
+ const out: number[] = [];
119
+ if (needle.length === 0 || needle.length > haystack.length) return out;
120
+ outer: for (let i = 0; i + needle.length <= haystack.length; i++) {
121
+ for (let j = 0; j < needle.length; j++) if (haystack[i + j] !== needle[j]) continue outer;
122
+ out.push(i);
123
+ }
124
+ return out;
125
+ }
126
+
127
+ function statementsFor(category: RunCategory, span: FuseSpan | null, offset: number | null): string[] {
128
+ if (span === null || category !== "RUN_CONFIRMED") return [];
129
+ return [
130
+ `The enclave signed the slot at position ${span.slotCounter} before this artifact's digest reached it.`,
131
+ `These exact bytes contain that slot's commitment, first at byte ${offset}, so they were assembled after the slot existed and were committed at position ${span.commitCounter}.`,
132
+ ];
133
+ }
134
+
135
+ const LIMITS = [
136
+ "Containing the commitment does not show it was an input to the artifact's production: bytes appended to a finished file pass the same check. What the commitment was to the work is the producer's claim, not this result's.",
137
+ "Nothing here bounds the artifact from above in wall-clock time. The position bound is the commit position; a clock comes only from an anchor.",
138
+ ];
139
+
140
+ /**
141
+ * Verify a bitgraph-run/1 artifact from its own bytes. Nothing here touches the
142
+ * network, and nothing is rebuilt: a run has no original to rebuild from.
143
+ */
144
+ export async function verifyRun(opts: RunVerifyOptions): Promise<RunVerifyResult> {
145
+ const { proof, bytes, trustAnchors } = opts;
146
+ const fileDigestB64 = b64(sha256(bytes));
147
+ const artifactDigestB64 = proof.artifact?.digestB64 ?? "";
148
+ const span = spanOf(proof);
149
+ const marker = readRunAttribution(proof);
150
+
151
+ const base = (category: RunCategory, extra: Partial<RunVerifyResult>, reason: string | null): RunVerifyResult => ({
152
+ category,
153
+ proof: extra.proof ?? { valid: false, reason: "not run" },
154
+ marker,
155
+ slotCommitmentB64u: extra.slotCommitmentB64u ?? null,
156
+ offset: extra.offset ?? null,
157
+ occurrences: extra.occurrences ?? 0,
158
+ artifactDigestB64,
159
+ fileDigestB64,
160
+ span,
161
+ statements: statementsFor(category, span, extra.offset ?? null),
162
+ limits: category === "RUN_CONFIRMED" ? LIMITS : [],
163
+ reason,
164
+ });
165
+
166
+ if (marker === null) {
167
+ return base("NOT_A_RUN", {}, `the proof carries no ${RUN_PROFILE} attribution`);
168
+ }
169
+
170
+ let pv: { valid: boolean; reason?: string };
171
+ try {
172
+ pv = await verify(trustAnchors === undefined ? { proof, bytes } : { proof, bytes, trustAnchors });
173
+ } catch (e) {
174
+ pv = { valid: false, reason: (e as Error).message };
175
+ }
176
+ if (!pv.valid) {
177
+ return base("INVALID_UNDERLYING_PROOF", { proof: pv }, pv.reason ?? "the underlying proof does not verify against these bytes");
178
+ }
179
+
180
+ if (marker.encoding !== RUN_ENCODING_BASE64URL) {
181
+ return base("UNDETERMINED_ENCODING", { proof: pv }, `the marker names encoding "${marker.encoding ?? "none"}", which this build does not register`);
182
+ }
183
+
184
+ const slot = proof.slotAllocation;
185
+ if (slot === undefined) {
186
+ return base("INVALID_SLOT_COMMITMENT", { proof: pv }, "the proof carries no slot record, so the commitment cannot be recomputed");
187
+ }
188
+ const slotCommitmentB64u = b64u(computeSlotCommitment(slot));
189
+ if (marker.declaredCommitment !== slotCommitmentB64u) {
190
+ return base(
191
+ "INVALID_SLOT_COMMITMENT",
192
+ { proof: pv, slotCommitmentB64u },
193
+ `the signed marker declares commitment ${marker.declaredCommitment ?? "none"}, but this proof's slot record commits to ${slotCommitmentB64u}`,
194
+ );
195
+ }
196
+
197
+ const hits = findAll(bytes, new TextEncoder().encode(slotCommitmentB64u));
198
+ if (hits.length === 0) {
199
+ return base(
200
+ "COMMITMENT_ABSENT",
201
+ { proof: pv, slotCommitmentB64u },
202
+ "the artifact's bytes do not contain the commitment in the declared encoding",
203
+ );
204
+ }
205
+ return base("RUN_CONFIRMED", { proof: pv, slotCommitmentB64u, offset: hits[0] ?? null, occurrences: hits.length }, null);
206
+ }