@moonglade/provenance 0.0.0-stage → 2.6.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 +21 -0
- package/NOTICE.md +9 -0
- package/README.md +76 -2
- package/SPEC.md +176 -0
- package/TIMESTAMP.md +21 -0
- package/dist/artifact-cli.d.ts +2 -0
- package/dist/artifact-cli.js +108 -0
- package/dist/artifactEnvelope.d.ts +38 -0
- package/dist/artifactEnvelope.js +61 -0
- package/dist/artifactHook.d.ts +30 -0
- package/dist/artifactHook.js +114 -0
- package/dist/badge.d.ts +83 -0
- package/dist/badge.js +403 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.js +782 -0
- package/dist/mcp.d.ts +87 -0
- package/dist/mcp.js +352 -0
- package/dist/merkle.d.ts +71 -0
- package/dist/merkle.js +176 -0
- package/dist/self-test.d.ts +21 -0
- package/dist/self-test.js +280 -0
- package/dist/timestamp.d.ts +274 -0
- package/dist/timestamp.js +1103 -0
- package/package.json +75 -4
- package/trust/README.md +19 -0
- package/trust/registry.json +12 -0
- package/trust/root-public.asc +10 -0
- package/wasm/mgc_wasm.d.ts +53 -0
- package/wasm/mgc_wasm.js +389 -0
- package/wasm/mgc_wasm_bg.wasm +0 -0
- package/wasm/mgc_wasm_bg.wasm.d.ts +20 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import { InclusionProof, ArtifactMap } from './merkle';
|
|
3
|
+
import { DTAResult, DTAOptions } from './timestamp';
|
|
4
|
+
import { ArtifactEnvelope } from './artifactEnvelope';
|
|
5
|
+
/**
|
|
6
|
+
* Moonglade Provenance Engine (TypeScript Implementation)
|
|
7
|
+
* Provides Ed25519 micro-signatures, RFC 3161 timestamps, and Merkle root macro-provenance.
|
|
8
|
+
*/
|
|
9
|
+
export interface ProvenanceSidecar {
|
|
10
|
+
standard: string;
|
|
11
|
+
artifact: string;
|
|
12
|
+
claims?: Record<string, any>;
|
|
13
|
+
claims_sha256?: string;
|
|
14
|
+
attestations: Array<{
|
|
15
|
+
method: string;
|
|
16
|
+
signer?: string;
|
|
17
|
+
scope?: string;
|
|
18
|
+
sha256?: string;
|
|
19
|
+
target_sha256?: string;
|
|
20
|
+
claims_sha256?: string;
|
|
21
|
+
created?: string;
|
|
22
|
+
git_commit?: string;
|
|
23
|
+
signature?: string;
|
|
24
|
+
tier?: string;
|
|
25
|
+
tsa?: string;
|
|
26
|
+
tsa_time?: string;
|
|
27
|
+
token_b64?: string;
|
|
28
|
+
probes?: any[];
|
|
29
|
+
}>;
|
|
30
|
+
sha256_at_last_write: string;
|
|
31
|
+
updated: string;
|
|
32
|
+
status?: 'provisional' | 'canonical' | 'revoked';
|
|
33
|
+
generator?: {
|
|
34
|
+
agent: string;
|
|
35
|
+
model?: string;
|
|
36
|
+
session?: string;
|
|
37
|
+
};
|
|
38
|
+
parents?: string[];
|
|
39
|
+
envelope?: ArtifactEnvelope;
|
|
40
|
+
}
|
|
41
|
+
export interface VerificationResult {
|
|
42
|
+
verified: boolean;
|
|
43
|
+
status?: 'VERIFIED' | 'INDETERMINATE';
|
|
44
|
+
documentSignatureVerified?: boolean;
|
|
45
|
+
timestampStatus?: 'ABSENT' | 'INVALID' | 'INTEGRITY_ONLY' | 'ANCHORED';
|
|
46
|
+
signer?: string;
|
|
47
|
+
timestamp?: string;
|
|
48
|
+
timestampTier?: string;
|
|
49
|
+
timestampVerified?: boolean;
|
|
50
|
+
timestampSigner?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
/** How many signature attestations were checked. Zero means an empty bundle. */
|
|
53
|
+
attestationsChecked?: number;
|
|
54
|
+
/** Methods present that this version cannot verify. Reported, never ignored. */
|
|
55
|
+
unknownMethods?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* The scope covered by the verified signature. Returned so a caller can compare it
|
|
58
|
+
* with the location or namespace it expected: the signature binds the scope string,
|
|
59
|
+
* but nothing in this library binds that string to where the file actually is.
|
|
60
|
+
* A caller that does not compare it has not enforced scope.
|
|
61
|
+
*/
|
|
62
|
+
scope?: string;
|
|
63
|
+
/** Standard markdown provenance badge representation of this artifact verdict. */
|
|
64
|
+
badge?: string;
|
|
65
|
+
claims?: Record<string, any>;
|
|
66
|
+
claimsSha256?: string;
|
|
67
|
+
}
|
|
68
|
+
export declare const TEXT_EXTENSIONS: Set<string>;
|
|
69
|
+
export declare const BINARY_EXTENSIONS: Set<string>;
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a filename or byte buffer represents text rather than binary data.
|
|
72
|
+
*/
|
|
73
|
+
export declare function isTextContent(filename: string, bytes: Buffer | Uint8Array): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Deterministic JSON Canonicalization Scheme (RFC 8785 subset for generic claims, draft -03 section 3.3).
|
|
76
|
+
* Sorts object keys lexicographically by UTF-16 code units with no whitespace. Numbers must be safe
|
|
77
|
+
* integers; floats, NaN, Infinity and lone surrogates are rejected, exactly as in the Python and Rust engines.
|
|
78
|
+
*/
|
|
79
|
+
export declare function canonicalizeJson(value: any): string;
|
|
80
|
+
export interface VerificationPolicy {
|
|
81
|
+
expectedScope?: string;
|
|
82
|
+
requiredSigners?: string[];
|
|
83
|
+
timestampTrustAnchors?: string[];
|
|
84
|
+
}
|
|
85
|
+
export declare const BUNDLE_V2 = "Moonglade Provenance Attestation Bundle v2";
|
|
86
|
+
export declare const BUNDLE_V3 = "Moonglade Provenance Attestation Bundle v3";
|
|
87
|
+
export declare const MAX_ARTIFACT_BYTES: number;
|
|
88
|
+
export declare const MAX_SIDECAR_BYTES: number;
|
|
89
|
+
/** Reject links and oversized inputs. This local tool assumes no concurrent hostile filesystem writer. */
|
|
90
|
+
export declare function readRegularFile(file: string, limit: number): Promise<Buffer>;
|
|
91
|
+
export declare function writeSidecar(file: string, value: ProvenanceSidecar): Promise<void>;
|
|
92
|
+
export declare function validateSidecar(sidecar: any): asserts sidecar is ProvenanceSidecar;
|
|
93
|
+
export declare class ProvenanceEngine {
|
|
94
|
+
/**
|
|
95
|
+
* Canonicalizes an in-memory buffer according to Moonglade Provenance rules.
|
|
96
|
+
* Text documents: strips UTF-8 BOM, normalizes CRLF to LF, and applies Unicode NFC.
|
|
97
|
+
* Binary documents (PDFs, images, binaries): preserves exact byte fidelity.
|
|
98
|
+
*/
|
|
99
|
+
static canonicalizeBytes(raw: Buffer, filename?: string): Buffer;
|
|
100
|
+
/**
|
|
101
|
+
* Canonicalize a file's bytes deterministically:
|
|
102
|
+
* For text documents, normalize CRLF to LF, strip UTF-8 BOM, and apply Unicode NFC.
|
|
103
|
+
* For binary documents (PDFs, images, binaries), preserve byte-exact fidelity.
|
|
104
|
+
*
|
|
105
|
+
* This exists so a git checkout on Windows does not invalidate text signatures
|
|
106
|
+
* in the repository, while ensuring binary structures (like PDF xref tables)
|
|
107
|
+
* remain completely untouched.
|
|
108
|
+
*/
|
|
109
|
+
static canonicalizeFile(filePath: string): Promise<Buffer>;
|
|
110
|
+
/**
|
|
111
|
+
* Generates an Ed25519 keypair for testing/local identities if none exists.
|
|
112
|
+
*/
|
|
113
|
+
static generateEd25519KeyPair(): crypto.KeyPairSyncResult<string, string>;
|
|
114
|
+
/**
|
|
115
|
+
* The bytes that are actually signed.
|
|
116
|
+
*
|
|
117
|
+
* Until v2 the signature covered the document bytes alone, which meant the
|
|
118
|
+
* scope, the signer identity and the timestamp were unauthenticated JSON
|
|
119
|
+
* sitting beside it. Anyone could rewrite the scope in a sidecar and the
|
|
120
|
+
* signature still verified - so the stated property, that a signature made
|
|
121
|
+
* for one scope will not verify under another, was not true here.
|
|
122
|
+
*
|
|
123
|
+
* v2 signs a structured claim instead. Three properties matter:
|
|
124
|
+
*
|
|
125
|
+
* 1. Domain separation - the version string is inside the payload, so a
|
|
126
|
+
* signature over one payload type cannot be replayed as another.
|
|
127
|
+
* 2. Field binding - scope, artifact hash and signer are all covered.
|
|
128
|
+
* 3. Unambiguous framing - newlines delimit fields, and fields are
|
|
129
|
+
* validated to contain none, so a crafted scope cannot smuggle in a
|
|
130
|
+
* forged signer line.
|
|
131
|
+
*/
|
|
132
|
+
static readonly PAYLOAD_VERSION = "moonglade-provenance/v2";
|
|
133
|
+
static readonly PAYLOAD_VERSION_V3 = "moonglade-provenance/v3";
|
|
134
|
+
static buildSigningPayload(fields: {
|
|
135
|
+
scope: string;
|
|
136
|
+
sha256: string;
|
|
137
|
+
signer: string;
|
|
138
|
+
}): Buffer;
|
|
139
|
+
static buildSigningPayloadV3(fields: {
|
|
140
|
+
scope: string;
|
|
141
|
+
targetSha256: string;
|
|
142
|
+
signer: string;
|
|
143
|
+
claimsSha256: string;
|
|
144
|
+
}): Buffer;
|
|
145
|
+
/**
|
|
146
|
+
* Build the attestation scope for an artifact.
|
|
147
|
+
*
|
|
148
|
+
* Scope isolation is the stated security property: a signature made for one
|
|
149
|
+
* scope must not be presentable for another. Until 2026-08-30 this used
|
|
150
|
+
* `path.basename(filePath)`, which gave every README.md in the tree the same
|
|
151
|
+
* scope - a collision in any repository with repeated filenames, which is all
|
|
152
|
+
* of them.
|
|
153
|
+
*
|
|
154
|
+
* It is now the path relative to `vaultRoot`, matching the Python
|
|
155
|
+
* implementation's `namespace_for()`.
|
|
156
|
+
*
|
|
157
|
+
* Separators are forced to POSIX. `path.relative` yields backslashes on
|
|
158
|
+
* Windows and forward slashes elsewhere, so without this the same file signed
|
|
159
|
+
* on two machines would carry two different scopes. That is the same class of
|
|
160
|
+
* defect canonicalizeFile exists to prevent for line endings, and it would be
|
|
161
|
+
* careless to fix one and not the other.
|
|
162
|
+
*/
|
|
163
|
+
static scopeFor(filePath: string, vaultRoot?: string, explicit?: string): string;
|
|
164
|
+
/**
|
|
165
|
+
* Sign a document and generate the .provenance.json sidecar.
|
|
166
|
+
* Supports both v2 standard bundles and v3 extended bundles with generic claims.
|
|
167
|
+
*/
|
|
168
|
+
static sealDocument(filePath: string, privateKeyPem: string, signerIdentity: string, gitCommit?: string, opts?: {
|
|
169
|
+
vaultRoot?: string;
|
|
170
|
+
scope?: string;
|
|
171
|
+
timestamp?: boolean | DTAOptions;
|
|
172
|
+
claims?: Record<string, any>;
|
|
173
|
+
version?: 'v2' | 'v3';
|
|
174
|
+
}): Promise<ProvenanceSidecar>;
|
|
175
|
+
/**
|
|
176
|
+
* Countersign an existing document and append the attestation to the sidecar.
|
|
177
|
+
*/
|
|
178
|
+
static countersignDocument(filePath: string, privateKeyPem: string, signerIdentity: string, gitCommit?: string, opts?: {
|
|
179
|
+
vaultRoot?: string;
|
|
180
|
+
scope?: string;
|
|
181
|
+
verificationKeys?: string | Record<string, string>;
|
|
182
|
+
policy?: VerificationPolicy;
|
|
183
|
+
}): Promise<ProvenanceSidecar>;
|
|
184
|
+
/**
|
|
185
|
+
* Attest RFC 3161 timestamping for an artifact via Concurrent Multi-Source DTA.
|
|
186
|
+
*/
|
|
187
|
+
static attestTimestamp(filePath: string, opts?: DTAOptions): Promise<DTAResult>;
|
|
188
|
+
/**
|
|
189
|
+
* Verify a document against its sidecar.
|
|
190
|
+
* Supports both v2 and v3 attestation bundles.
|
|
191
|
+
*/
|
|
192
|
+
static verifyDocument(filePath: string, publicKeyPem: string | Record<string, string>, policy?: VerificationPolicy): Promise<VerificationResult>;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Vault Macro-Provenance: Merkle Tree Hash Registry
|
|
196
|
+
*/
|
|
197
|
+
export declare class ProvenanceVault {
|
|
198
|
+
rootPath: string;
|
|
199
|
+
salt: string;
|
|
200
|
+
constructor(rootPath: string, salt?: string);
|
|
201
|
+
private walk;
|
|
202
|
+
/**
|
|
203
|
+
* Hash every file under the root and build a real Merkle tree over them.
|
|
204
|
+
*
|
|
205
|
+
* Until v2 this returned a flat SHA-256 over the concatenated hashes. That
|
|
206
|
+
* detected change and nothing else - it could not produce an inclusion proof,
|
|
207
|
+
* which is the only reason to use a tree. See merkle.ts.
|
|
208
|
+
*
|
|
209
|
+
* Paths are POSIX-normalised before hashing, so a vault scanned on Windows and
|
|
210
|
+
* on Linux yields the same root. Without that the two would silently disagree,
|
|
211
|
+
* which is the same defect canonicalizeFile prevents for line endings.
|
|
212
|
+
*/
|
|
213
|
+
scanAndAudit(): Promise<{
|
|
214
|
+
merkleRoot: string;
|
|
215
|
+
algorithm: string;
|
|
216
|
+
fileCount: number;
|
|
217
|
+
artifacts: ArtifactMap;
|
|
218
|
+
}>;
|
|
219
|
+
/** Audit path for one artifact, by its POSIX path relative to the root. */
|
|
220
|
+
proveInclusion(relPath: string): Promise<InclusionProof | null>;
|
|
221
|
+
}
|
|
222
|
+
export * from './merkle';
|
|
223
|
+
export * from './timestamp';
|
|
224
|
+
export * from './mcp';
|
|
225
|
+
export * from './artifactHook';
|
|
226
|
+
export * from './artifactEnvelope';
|
|
227
|
+
export * from './self-test';
|
|
228
|
+
export * from './badge';
|