@absolutejs/vulnerabilities-witness 0.1.0 → 0.2.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 +27 -2
- package/dist/index.js +66 -3
- package/dist/server.js +113 -7
- package/dist/service.d.ts +23 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -48,8 +48,9 @@ Bun.serve({ fetch, port: 3000 });
|
|
|
48
48
|
The signing identity contains a private key and belongs in a durable secret
|
|
49
49
|
broker, not PostgreSQL or source control. `service.rotate()` stores the new
|
|
50
50
|
secret state before activating it and publishes the cross-signed transition in
|
|
51
|
-
the registry.
|
|
52
|
-
|
|
51
|
+
the registry. `service.maintain()` performs the same rotation only after the
|
|
52
|
+
configured maximum key age. In a multi-replica service, the supplied
|
|
53
|
+
secret-store integration must serialize rotations.
|
|
53
54
|
|
|
54
55
|
## Standalone service
|
|
55
56
|
|
|
@@ -61,9 +62,33 @@ observations and accepts these deployment secrets:
|
|
|
61
62
|
- `EVIDENCE_WITNESS_SIGNING_STATE_JSON`
|
|
62
63
|
- `EVIDENCE_WITNESS_TOKENS_JSON`, an object mapping stable subjects to bearer
|
|
63
64
|
tokens
|
|
65
|
+
- `EVIDENCE_WITNESS_SECRETS_PATH`, the durable encrypted secret file
|
|
66
|
+
- `EVIDENCE_WITNESS_SECRETS_PASSPHRASE`, the master passphrase kept outside the
|
|
67
|
+
file
|
|
68
|
+
- `EVIDENCE_WITNESS_KEY_MAX_AGE_MS`, defaulting to 90 days
|
|
69
|
+
- `EVIDENCE_WITNESS_MAINTENANCE_INTERVAL_MS`, defaulting to one hour
|
|
70
|
+
|
|
71
|
+
The authenticated `GET /v1/status` endpoint reports the verified key registry
|
|
72
|
+
and optional backup restoration evidence. Store
|
|
73
|
+
`EVIDENCE_WITNESS_BACKUP_VERIFICATION_JSON` in the encrypted secret adapter
|
|
74
|
+
after a drill has restored both the PostgreSQL artifact and encrypted signing
|
|
75
|
+
state. The record must use the
|
|
76
|
+
`absolutejs.vulnerability-evidence-witness-backup-verification/v1` contract,
|
|
77
|
+
contain SHA-256 digests for both artifacts, and record both restoration times.
|
|
78
|
+
|
|
64
79
|
- `PORT`, defaulting to `3000`
|
|
65
80
|
|
|
81
|
+
The two JSON values bootstrap the encrypted file only when their entries do not
|
|
82
|
+
already exist. Later key rotations are written atomically to the encrypted file
|
|
83
|
+
before the new identity becomes active.
|
|
84
|
+
|
|
66
85
|
Run independent instances under different administrative and infrastructure
|
|
67
86
|
boundaries. Clients should pin each genesis witness key through an independent
|
|
68
87
|
channel and require a quorum with
|
|
69
88
|
`verifyEvidenceWitnessQuorum` from `@absolutejs/vulnerabilities`.
|
|
89
|
+
|
|
90
|
+
The repository publishes a deployable container to
|
|
91
|
+
`ghcr.io/absolutejs/vulnerabilities-witness`. See
|
|
92
|
+
[`deploy/README.md`](deploy/README.md) for the independence requirements,
|
|
93
|
+
bootstrap sequence, PAAS quorum configuration, and repeatable Admin drill
|
|
94
|
+
workflow.
|
package/dist/index.js
CHANGED
|
@@ -114,6 +114,33 @@ var verifyEvidenceWitnessKeyRegistry = (input) => {
|
|
|
114
114
|
|
|
115
115
|
// src/service.ts
|
|
116
116
|
var EVIDENCE_WITNESS_REQUEST_CONTRACT = "absolutejs.vulnerability-evidence-witness-request/v1";
|
|
117
|
+
var EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT = "absolutejs.vulnerability-evidence-witness-backup-verification/v1";
|
|
118
|
+
var EVIDENCE_WITNESS_STATUS_CONTRACT = "absolutejs.vulnerability-evidence-witness-status/v1";
|
|
119
|
+
var timestamp = (value, label) => {
|
|
120
|
+
if (!Number.isFinite(Date.parse(value)))
|
|
121
|
+
throw new Error(`${label} is invalid`);
|
|
122
|
+
return value;
|
|
123
|
+
};
|
|
124
|
+
var digest = (value, label) => {
|
|
125
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(value))
|
|
126
|
+
throw new Error(`${label} is invalid`);
|
|
127
|
+
return value;
|
|
128
|
+
};
|
|
129
|
+
var parseEvidenceWitnessBackupVerification = (value) => {
|
|
130
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
131
|
+
throw new Error("Evidence witness backup verification is invalid");
|
|
132
|
+
const input = value;
|
|
133
|
+
if (input.contract !== EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT)
|
|
134
|
+
throw new Error("Evidence witness backup verification is unsupported");
|
|
135
|
+
return {
|
|
136
|
+
contract: input.contract,
|
|
137
|
+
databaseArtifactDigest: digest(input.databaseArtifactDigest ?? "", "Evidence witness database backup digest"),
|
|
138
|
+
databaseRestoredAt: timestamp(input.databaseRestoredAt ?? "", "Evidence witness database restore time"),
|
|
139
|
+
signingStateArtifactDigest: digest(input.signingStateArtifactDigest ?? "", "Evidence witness signing-state backup digest"),
|
|
140
|
+
signingStateRestoredAt: timestamp(input.signingStateRestoredAt ?? "", "Evidence witness signing-state restore time"),
|
|
141
|
+
verifiedAt: timestamp(input.verifiedAt ?? "", "Evidence witness backup verification time")
|
|
142
|
+
};
|
|
143
|
+
};
|
|
117
144
|
var createEvidenceWitnessService = (options) => {
|
|
118
145
|
const origin = new URL(options.origin);
|
|
119
146
|
if (origin.protocol !== "https:")
|
|
@@ -128,6 +155,12 @@ var createEvidenceWitnessService = (options) => {
|
|
|
128
155
|
await refresh();
|
|
129
156
|
return evidenceWitnessKeyRegistryFrom(state);
|
|
130
157
|
};
|
|
158
|
+
const status = async () => ({
|
|
159
|
+
backup: options.loadBackupVerification ? await options.loadBackupVerification().then((value) => value === null ? null : parseEvidenceWitnessBackupVerification(value)) : null,
|
|
160
|
+
checkedAt: new Date().toISOString(),
|
|
161
|
+
contract: EVIDENCE_WITNESS_STATUS_CONTRACT,
|
|
162
|
+
registry: await registry()
|
|
163
|
+
});
|
|
131
164
|
const checkpoint = async (subject, request) => {
|
|
132
165
|
if (request.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
|
|
133
166
|
throw new Error("Evidence witness request contract is unsupported");
|
|
@@ -152,7 +185,7 @@ var createEvidenceWitnessService = (options) => {
|
|
|
152
185
|
});
|
|
153
186
|
const observed = await options.store.observe({
|
|
154
187
|
checkpoint: signed,
|
|
155
|
-
history: request.log.entries.map(({ digest }) =>
|
|
188
|
+
history: request.log.entries.map(({ digest: digest2 }) => digest2),
|
|
156
189
|
subject: normalizedSubject
|
|
157
190
|
});
|
|
158
191
|
return {
|
|
@@ -175,7 +208,28 @@ var createEvidenceWitnessService = (options) => {
|
|
|
175
208
|
});
|
|
176
209
|
return queued;
|
|
177
210
|
};
|
|
178
|
-
|
|
211
|
+
const maintain = (input) => {
|
|
212
|
+
if (!Number.isSafeInteger(input.maxKeyAgeMs) || input.maxKeyAgeMs < 1)
|
|
213
|
+
throw new Error("Evidence witness maximum key age is invalid");
|
|
214
|
+
const now = input.now ?? new Date;
|
|
215
|
+
const queued = mutationQueue.then(async () => {
|
|
216
|
+
await refresh();
|
|
217
|
+
const ageMs = now.getTime() - Date.parse(state.identity.createdAt);
|
|
218
|
+
if (ageMs < input.maxKeyAgeMs)
|
|
219
|
+
return evidenceWitnessKeyRegistryFrom(state);
|
|
220
|
+
const next = rotateEvidenceWitnessSigningState(state, now.toISOString());
|
|
221
|
+
await options.storeSigningState(encodeEvidenceWitnessSigningState(next));
|
|
222
|
+
state = next;
|
|
223
|
+
return evidenceWitnessKeyRegistryFrom(state);
|
|
224
|
+
});
|
|
225
|
+
mutationQueue = queued.then(() => {
|
|
226
|
+
return;
|
|
227
|
+
}, () => {
|
|
228
|
+
return;
|
|
229
|
+
});
|
|
230
|
+
return queued;
|
|
231
|
+
};
|
|
232
|
+
return { checkpoint, maintain, registry, rotate, status };
|
|
179
233
|
};
|
|
180
234
|
|
|
181
235
|
// src/store.ts
|
|
@@ -248,6 +302,12 @@ var createEvidenceWitnessHttpHandler = (options) => async (request) => {
|
|
|
248
302
|
return json({ status: "ok" });
|
|
249
303
|
if (request.method === "GET" && url.pathname === "/v1/keys")
|
|
250
304
|
return json(await options.service.registry());
|
|
305
|
+
if (request.method === "GET" && url.pathname === "/v1/status") {
|
|
306
|
+
const subject2 = await options.authenticate(bearer(request));
|
|
307
|
+
if (!subject2)
|
|
308
|
+
return json({ error: "unauthorized" }, 401);
|
|
309
|
+
return json(await options.service.status());
|
|
310
|
+
}
|
|
251
311
|
if (request.method !== "POST" || url.pathname !== "/v1/checkpoints")
|
|
252
312
|
return json({ error: "not_found" }, 404);
|
|
253
313
|
const subject = await options.authenticate(bearer(request));
|
|
@@ -273,6 +333,7 @@ export {
|
|
|
273
333
|
verifyEvidenceWitnessKeyRegistry,
|
|
274
334
|
rotateEvidenceWitnessSigningState,
|
|
275
335
|
parseEvidenceWitnessSigningState,
|
|
336
|
+
parseEvidenceWitnessBackupVerification,
|
|
276
337
|
evidenceWitnessKeyRegistryFrom,
|
|
277
338
|
encodeEvidenceWitnessSigningState,
|
|
278
339
|
createMemoryEvidenceWitnessStore,
|
|
@@ -281,5 +342,7 @@ export {
|
|
|
281
342
|
createEvidenceWitnessHttpHandler,
|
|
282
343
|
EvidenceWitnessRollbackError,
|
|
283
344
|
EvidenceWitnessEquivocationError,
|
|
284
|
-
|
|
345
|
+
EVIDENCE_WITNESS_STATUS_CONTRACT,
|
|
346
|
+
EVIDENCE_WITNESS_REQUEST_CONTRACT,
|
|
347
|
+
EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT
|
|
285
348
|
};
|
package/dist/server.js
CHANGED
|
@@ -116,6 +116,33 @@ var verifyEvidenceWitnessKeyRegistry = (input) => {
|
|
|
116
116
|
|
|
117
117
|
// src/service.ts
|
|
118
118
|
var EVIDENCE_WITNESS_REQUEST_CONTRACT = "absolutejs.vulnerability-evidence-witness-request/v1";
|
|
119
|
+
var EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT = "absolutejs.vulnerability-evidence-witness-backup-verification/v1";
|
|
120
|
+
var EVIDENCE_WITNESS_STATUS_CONTRACT = "absolutejs.vulnerability-evidence-witness-status/v1";
|
|
121
|
+
var timestamp = (value, label) => {
|
|
122
|
+
if (!Number.isFinite(Date.parse(value)))
|
|
123
|
+
throw new Error(`${label} is invalid`);
|
|
124
|
+
return value;
|
|
125
|
+
};
|
|
126
|
+
var digest = (value, label) => {
|
|
127
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(value))
|
|
128
|
+
throw new Error(`${label} is invalid`);
|
|
129
|
+
return value;
|
|
130
|
+
};
|
|
131
|
+
var parseEvidenceWitnessBackupVerification = (value) => {
|
|
132
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
133
|
+
throw new Error("Evidence witness backup verification is invalid");
|
|
134
|
+
const input = value;
|
|
135
|
+
if (input.contract !== EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT)
|
|
136
|
+
throw new Error("Evidence witness backup verification is unsupported");
|
|
137
|
+
return {
|
|
138
|
+
contract: input.contract,
|
|
139
|
+
databaseArtifactDigest: digest(input.databaseArtifactDigest ?? "", "Evidence witness database backup digest"),
|
|
140
|
+
databaseRestoredAt: timestamp(input.databaseRestoredAt ?? "", "Evidence witness database restore time"),
|
|
141
|
+
signingStateArtifactDigest: digest(input.signingStateArtifactDigest ?? "", "Evidence witness signing-state backup digest"),
|
|
142
|
+
signingStateRestoredAt: timestamp(input.signingStateRestoredAt ?? "", "Evidence witness signing-state restore time"),
|
|
143
|
+
verifiedAt: timestamp(input.verifiedAt ?? "", "Evidence witness backup verification time")
|
|
144
|
+
};
|
|
145
|
+
};
|
|
119
146
|
var createEvidenceWitnessService = (options) => {
|
|
120
147
|
const origin = new URL(options.origin);
|
|
121
148
|
if (origin.protocol !== "https:")
|
|
@@ -130,6 +157,12 @@ var createEvidenceWitnessService = (options) => {
|
|
|
130
157
|
await refresh();
|
|
131
158
|
return evidenceWitnessKeyRegistryFrom(state);
|
|
132
159
|
};
|
|
160
|
+
const status = async () => ({
|
|
161
|
+
backup: options.loadBackupVerification ? await options.loadBackupVerification().then((value) => value === null ? null : parseEvidenceWitnessBackupVerification(value)) : null,
|
|
162
|
+
checkedAt: new Date().toISOString(),
|
|
163
|
+
contract: EVIDENCE_WITNESS_STATUS_CONTRACT,
|
|
164
|
+
registry: await registry()
|
|
165
|
+
});
|
|
133
166
|
const checkpoint = async (subject, request) => {
|
|
134
167
|
if (request.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
|
|
135
168
|
throw new Error("Evidence witness request contract is unsupported");
|
|
@@ -154,7 +187,7 @@ var createEvidenceWitnessService = (options) => {
|
|
|
154
187
|
});
|
|
155
188
|
const observed = await options.store.observe({
|
|
156
189
|
checkpoint: signed,
|
|
157
|
-
history: request.log.entries.map(({ digest }) =>
|
|
190
|
+
history: request.log.entries.map(({ digest: digest2 }) => digest2),
|
|
158
191
|
subject: normalizedSubject
|
|
159
192
|
});
|
|
160
193
|
return {
|
|
@@ -177,7 +210,28 @@ var createEvidenceWitnessService = (options) => {
|
|
|
177
210
|
});
|
|
178
211
|
return queued;
|
|
179
212
|
};
|
|
180
|
-
|
|
213
|
+
const maintain = (input) => {
|
|
214
|
+
if (!Number.isSafeInteger(input.maxKeyAgeMs) || input.maxKeyAgeMs < 1)
|
|
215
|
+
throw new Error("Evidence witness maximum key age is invalid");
|
|
216
|
+
const now = input.now ?? new Date;
|
|
217
|
+
const queued = mutationQueue.then(async () => {
|
|
218
|
+
await refresh();
|
|
219
|
+
const ageMs = now.getTime() - Date.parse(state.identity.createdAt);
|
|
220
|
+
if (ageMs < input.maxKeyAgeMs)
|
|
221
|
+
return evidenceWitnessKeyRegistryFrom(state);
|
|
222
|
+
const next = rotateEvidenceWitnessSigningState(state, now.toISOString());
|
|
223
|
+
await options.storeSigningState(encodeEvidenceWitnessSigningState(next));
|
|
224
|
+
state = next;
|
|
225
|
+
return evidenceWitnessKeyRegistryFrom(state);
|
|
226
|
+
});
|
|
227
|
+
mutationQueue = queued.then(() => {
|
|
228
|
+
return;
|
|
229
|
+
}, () => {
|
|
230
|
+
return;
|
|
231
|
+
});
|
|
232
|
+
return queued;
|
|
233
|
+
};
|
|
234
|
+
return { checkpoint, maintain, registry, rotate, status };
|
|
181
235
|
};
|
|
182
236
|
|
|
183
237
|
// src/store.ts
|
|
@@ -250,6 +304,12 @@ var createEvidenceWitnessHttpHandler = (options) => async (request) => {
|
|
|
250
304
|
return json({ status: "ok" });
|
|
251
305
|
if (request.method === "GET" && url.pathname === "/v1/keys")
|
|
252
306
|
return json(await options.service.registry());
|
|
307
|
+
if (request.method === "GET" && url.pathname === "/v1/status") {
|
|
308
|
+
const subject2 = await options.authenticate(bearer(request));
|
|
309
|
+
if (!subject2)
|
|
310
|
+
return json({ error: "unauthorized" }, 401);
|
|
311
|
+
return json(await options.service.status());
|
|
312
|
+
}
|
|
253
313
|
if (request.method !== "POST" || url.pathname !== "/v1/checkpoints")
|
|
254
314
|
return json({ error: "not_found" }, 404);
|
|
255
315
|
const subject = await options.authenticate(bearer(request));
|
|
@@ -345,25 +405,64 @@ var createPostgresEvidenceWitnessStore = (sql) => {
|
|
|
345
405
|
|
|
346
406
|
// src/server.ts
|
|
347
407
|
var {SQL } = globalThis.Bun;
|
|
408
|
+
import { encryptedFileAdapter } from "@absolutejs/secrets/broker";
|
|
348
409
|
var required = (name) => {
|
|
349
410
|
const value = process.env[name]?.trim();
|
|
350
411
|
if (!value)
|
|
351
412
|
throw new Error(`Missing required environment: ${name}`);
|
|
352
413
|
return value;
|
|
353
414
|
};
|
|
415
|
+
var positiveInteger = (name, fallback) => {
|
|
416
|
+
const encoded = process.env[name]?.trim();
|
|
417
|
+
if (!encoded)
|
|
418
|
+
return fallback;
|
|
419
|
+
const value = Number(encoded);
|
|
420
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
421
|
+
throw new Error(`${name} must be a positive integer`);
|
|
422
|
+
return value;
|
|
423
|
+
};
|
|
424
|
+
var signingStateName = "EVIDENCE_WITNESS_SIGNING_STATE_JSON";
|
|
425
|
+
var tokensName = "EVIDENCE_WITNESS_TOKENS_JSON";
|
|
426
|
+
var backupVerificationName = "EVIDENCE_WITNESS_BACKUP_VERIFICATION_JSON";
|
|
427
|
+
var masterPassphrase = required("EVIDENCE_WITNESS_SECRETS_PASSPHRASE");
|
|
428
|
+
var secrets = encryptedFileAdapter({
|
|
429
|
+
key: { passphrase: masterPassphrase, type: "passphrase" },
|
|
430
|
+
path: required("EVIDENCE_WITNESS_SECRETS_PATH")
|
|
431
|
+
});
|
|
432
|
+
var loadOrBootstrap = async (name) => {
|
|
433
|
+
const stored = await secrets.fetch(name);
|
|
434
|
+
if (stored)
|
|
435
|
+
return stored;
|
|
436
|
+
const bootstrap = required(name);
|
|
437
|
+
if (!secrets.put)
|
|
438
|
+
throw new Error("Encrypted secret adapter is read-only");
|
|
439
|
+
await secrets.put(name, bootstrap);
|
|
440
|
+
return bootstrap;
|
|
441
|
+
};
|
|
354
442
|
var database = new SQL(required("DATABASE_URL"));
|
|
355
443
|
await ensurePostgresEvidenceWitnessSchema(database);
|
|
356
|
-
var encodedSigningState =
|
|
444
|
+
var encodedSigningState = await loadOrBootstrap(signingStateName);
|
|
357
445
|
var signingState = parseEvidenceWitnessSigningState(JSON.parse(encodedSigningState));
|
|
358
|
-
var tokens = JSON.parse(
|
|
446
|
+
var tokens = JSON.parse(await loadOrBootstrap(tokensName));
|
|
359
447
|
var subjectsByToken = new Map(Object.entries(tokens).map(([subject, token]) => [token, subject]));
|
|
360
448
|
var service2 = createEvidenceWitnessService({
|
|
361
|
-
|
|
449
|
+
loadBackupVerification: async () => {
|
|
450
|
+
const encoded = await secrets.fetch(backupVerificationName);
|
|
451
|
+
return encoded ? JSON.parse(encoded) : null;
|
|
452
|
+
},
|
|
453
|
+
loadSigningState: async () => {
|
|
454
|
+
const encoded = await secrets.fetch(signingStateName);
|
|
455
|
+
if (!encoded)
|
|
456
|
+
throw new Error("Evidence witness signing state is missing");
|
|
457
|
+
return encoded;
|
|
458
|
+
},
|
|
362
459
|
origin: required("EVIDENCE_WITNESS_ORIGIN"),
|
|
363
460
|
signingState,
|
|
364
461
|
store: createPostgresEvidenceWitnessStore(database),
|
|
365
|
-
storeSigningState: async () => {
|
|
366
|
-
|
|
462
|
+
storeSigningState: async (encoded) => {
|
|
463
|
+
if (!secrets.put)
|
|
464
|
+
throw new Error("Encrypted secret adapter is read-only");
|
|
465
|
+
await secrets.put(signingStateName, encoded);
|
|
367
466
|
}
|
|
368
467
|
});
|
|
369
468
|
var handler = createEvidenceWitnessHttpHandler({
|
|
@@ -374,3 +473,10 @@ var port = Number(process.env.PORT ?? "3000");
|
|
|
374
473
|
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
375
474
|
throw new Error("PORT must be a valid TCP port");
|
|
376
475
|
Bun.serve({ fetch: handler, port });
|
|
476
|
+
var dayMs = 86400000;
|
|
477
|
+
var maxKeyAgeMs = positiveInteger("EVIDENCE_WITNESS_KEY_MAX_AGE_MS", 90 * dayMs);
|
|
478
|
+
var maintenanceIntervalMs = positiveInteger("EVIDENCE_WITNESS_MAINTENANCE_INTERVAL_MS", 3600000);
|
|
479
|
+
var maintain = () => service2.maintain({ maxKeyAgeMs }).catch((error) => console.error("Evidence witness key maintenance failed", error));
|
|
480
|
+
var maintenanceTimer = setInterval(() => void maintain(), maintenanceIntervalMs);
|
|
481
|
+
maintenanceTimer.unref();
|
|
482
|
+
maintain();
|
package/dist/service.d.ts
CHANGED
|
@@ -10,7 +10,25 @@ export type EvidenceWitnessResponse = {
|
|
|
10
10
|
checkpoint: Awaited<ReturnType<EvidenceWitnessStore["observe"]>>["checkpoint"];
|
|
11
11
|
registry: EvidenceWitnessKeyRegistry;
|
|
12
12
|
};
|
|
13
|
+
export declare const EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT: "absolutejs.vulnerability-evidence-witness-backup-verification/v1";
|
|
14
|
+
export declare const EVIDENCE_WITNESS_STATUS_CONTRACT: "absolutejs.vulnerability-evidence-witness-status/v1";
|
|
15
|
+
export type EvidenceWitnessBackupVerification = {
|
|
16
|
+
contract: typeof EVIDENCE_WITNESS_BACKUP_VERIFICATION_CONTRACT;
|
|
17
|
+
databaseArtifactDigest: `sha256:${string}`;
|
|
18
|
+
databaseRestoredAt: string;
|
|
19
|
+
signingStateArtifactDigest: `sha256:${string}`;
|
|
20
|
+
signingStateRestoredAt: string;
|
|
21
|
+
verifiedAt: string;
|
|
22
|
+
};
|
|
23
|
+
export type EvidenceWitnessStatus = {
|
|
24
|
+
backup: EvidenceWitnessBackupVerification | null;
|
|
25
|
+
checkedAt: string;
|
|
26
|
+
contract: typeof EVIDENCE_WITNESS_STATUS_CONTRACT;
|
|
27
|
+
registry: EvidenceWitnessKeyRegistry;
|
|
28
|
+
};
|
|
29
|
+
export declare const parseEvidenceWitnessBackupVerification: (value: unknown) => EvidenceWitnessBackupVerification;
|
|
13
30
|
export declare const createEvidenceWitnessService: (options: {
|
|
31
|
+
loadBackupVerification?: () => Promise<unknown | null>;
|
|
14
32
|
loadSigningState: () => Promise<string>;
|
|
15
33
|
origin: string;
|
|
16
34
|
signingState: EvidenceWitnessSigningState;
|
|
@@ -18,6 +36,11 @@ export declare const createEvidenceWitnessService: (options: {
|
|
|
18
36
|
storeSigningState: (encoded: string) => Promise<void>;
|
|
19
37
|
}) => {
|
|
20
38
|
checkpoint: (subject: string, request: EvidenceWitnessRequest) => Promise<EvidenceWitnessResponse>;
|
|
39
|
+
maintain: (input: {
|
|
40
|
+
maxKeyAgeMs: number;
|
|
41
|
+
now?: Date;
|
|
42
|
+
}) => Promise<EvidenceWitnessKeyRegistry>;
|
|
21
43
|
registry: () => Promise<EvidenceWitnessKeyRegistry>;
|
|
22
44
|
rotate: (rotatedAt?: string) => Promise<EvidenceWitnessKeyRegistry>;
|
|
45
|
+
status: () => Promise<EvidenceWitnessStatus>;
|
|
23
46
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/vulnerabilities-witness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Independent durable transparency witness with rollback, equivocation, key-rotation, and HTTP service primitives for AbsoluteJS vulnerability evidence.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSL-1.1",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
49
49
|
"typecheck": "tsc --noEmit",
|
|
50
50
|
"test": "bun test",
|
|
51
|
-
"build": "rm -rf dist && bun build --root src src/index.ts src/postgres.ts src/server.ts src/manifest.ts --outdir dist --target=bun --external @absolutejs/manifest --external @absolutejs/vulnerabilities --external @sinclair/typebox && tsc -p tsconfig.build.json && absolute-manifest emit",
|
|
51
|
+
"build": "rm -rf dist && bun build --root src src/index.ts src/postgres.ts src/server.ts src/manifest.ts --outdir dist --target=bun --external @absolutejs/manifest --external @absolutejs/secrets --external @absolutejs/vulnerabilities --external @sinclair/typebox && tsc -p tsconfig.build.json && absolute-manifest emit",
|
|
52
52
|
"check:package": "bun run format && bun run typecheck && bun run test && bun run build"
|
|
53
53
|
},
|
|
54
54
|
"keywords": [
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
],
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@absolutejs/manifest": "0.3.0",
|
|
64
|
-
"@absolutejs/
|
|
64
|
+
"@absolutejs/secrets": "0.7.2",
|
|
65
|
+
"@absolutejs/vulnerabilities": "0.14.0",
|
|
65
66
|
"@sinclair/typebox": "0.34.52"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|