@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.
@@ -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';