@absolutejs/vulnerabilities-witness 0.1.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 ADDED
@@ -0,0 +1,80 @@
1
+ # Business Source License 1.1
2
+
3
+ **Licensor:** Alex Kahn
4
+
5
+ **Licensed Work:** @absolutejs/vulnerabilities-witness (https://github.com/absolutejs/vulnerabilities-witness)
6
+
7
+ **Change Date:** July 19, 2030
8
+
9
+ **Change License:** Apache License, Version 2.0
10
+
11
+ ---
12
+
13
+ ## Terms
14
+
15
+ The Licensor hereby grants you the right to copy, modify, create derivative
16
+ works, redistribute, and make non-production use of the Licensed Work. The
17
+ Licensor may make an Additional Use Grant, permitting limited production use.
18
+
19
+ ### Additional Use Grant
20
+
21
+ You may use the Licensed Work in production, provided your use does not include
22
+ any of the following:
23
+
24
+ 1. **Offering a Competing Service.** You may not offer the Licensed Work, or
25
+ any derivative or substantial portion of it, to third parties as a hosted or
26
+ managed vulnerability-management, exposure-management, container-security,
27
+ cloud-security-posture-management, transparency-witnessing, or
28
+ attack-surface-management service. This includes any product whose primary
29
+ value to its users is the functionality the Licensed Work provides.
30
+
31
+ 2. **Resale or Redistribution as a Standalone Product.** You may not sell,
32
+ license, or distribute the Licensed Work, or any derivative or fork of it,
33
+ as a standalone commercial product.
34
+
35
+ 3. **Removal of Attribution.** Any derivative work, fork, or redistribution of
36
+ the Licensed Work must prominently credit AbsoluteJS and include a link to
37
+ the original project repository
38
+ (https://github.com/absolutejs/vulnerabilities-witness).
39
+
40
+ For clarity, the following uses are expressly permitted:
41
+
42
+ - Using the Licensed Work to witness, verify, audit, or report on vulnerability
43
+ evidence for your own applications, websites, internal tools, or SaaS
44
+ products, so long as vulnerability management or transparency witnessing is
45
+ not the primary product you are selling.
46
+ - Using the Licensed Work as a dependency in commercial software you build and
47
+ sell, as long as that software is not a competing hosted security service.
48
+ - Providing consulting, development, or professional services to clients using
49
+ the Licensed Work.
50
+ - Forking and modifying the Licensed Work for your own internal use, provided
51
+ attribution is maintained.
52
+
53
+ ### Change Date and Change License
54
+
55
+ On the Change Date specified above, the Licensed Work will be made available
56
+ under the Change License (Apache License, Version 2.0).
57
+
58
+ ### Trademark
59
+
60
+ This license does not grant rights to use the AbsoluteJS name or trademarks in
61
+ a manner that suggests endorsement of an unaffiliated fork.
62
+
63
+ ### Notices
64
+
65
+ You must not remove or obscure any licensing, copyright, or other notices
66
+ included in the Licensed Work.
67
+
68
+ ### No Warranty
69
+
70
+ THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73
+ LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM, OUT
74
+ OF, OR IN CONNECTION WITH THE LICENSED WORK OR ITS USE.
75
+
76
+ ## Contact
77
+
78
+ - **Alex Kahn**
79
+ - alexkahndev@gmail.com
80
+ - alexkahndev.github.io
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @absolutejs/vulnerabilities-witness
2
+
3
+ Independent transparency witnessing for AbsoluteJS vulnerability evidence.
4
+ The package verifies the complete signed evidence-key transparency log before
5
+ issuing an Ed25519 checkpoint receipt. Durable stores reject a lower log size
6
+ as rollback and reject two different heads observed for the same authenticated
7
+ subject and log size as equivocation.
8
+
9
+ The HTTP service has three routes:
10
+
11
+ - `POST /v1/checkpoints` authenticates a bearer token, verifies the submitted
12
+ transparency log, and returns `{ checkpoint, registry }`.
13
+ - `GET /v1/keys` publishes the witness key registry and cross-signed rotation
14
+ chain.
15
+ - `GET /health` returns service liveness.
16
+
17
+ ```ts
18
+ import {
19
+ EVIDENCE_WITNESS_REQUEST_CONTRACT,
20
+ createEvidenceWitnessHttpHandler,
21
+ createEvidenceWitnessService,
22
+ createEvidenceWitnessSigningState,
23
+ } from "@absolutejs/vulnerabilities-witness";
24
+ import {
25
+ createPostgresEvidenceWitnessStore,
26
+ ensurePostgresEvidenceWitnessSchema,
27
+ } from "@absolutejs/vulnerabilities-witness/postgres";
28
+
29
+ const signingState = createEvidenceWitnessSigningState();
30
+ await ensurePostgresEvidenceWitnessSchema(sql);
31
+
32
+ const service = createEvidenceWitnessService({
33
+ loadSigningState,
34
+ origin: "https://witness.example",
35
+ signingState,
36
+ store: createPostgresEvidenceWitnessStore(sql),
37
+ storeSigningState,
38
+ });
39
+
40
+ const fetch = createEvidenceWitnessHttpHandler({
41
+ authenticate: async (token) => subjectsByToken.get(token) ?? null,
42
+ service,
43
+ });
44
+
45
+ Bun.serve({ fetch, port: 3000 });
46
+ ```
47
+
48
+ The signing identity contains a private key and belongs in a durable secret
49
+ broker, not PostgreSQL or source control. `service.rotate()` stores the new
50
+ secret state before activating it and publishes the cross-signed transition in
51
+ the registry. In a multi-replica service, the supplied secret-store integration
52
+ must serialize rotations.
53
+
54
+ ## Standalone service
55
+
56
+ The included `absolute-vulnerability-witness` executable uses PostgreSQL for
57
+ observations and accepts these deployment secrets:
58
+
59
+ - `DATABASE_URL`
60
+ - `EVIDENCE_WITNESS_ORIGIN`
61
+ - `EVIDENCE_WITNESS_SIGNING_STATE_JSON`
62
+ - `EVIDENCE_WITNESS_TOKENS_JSON`, an object mapping stable subjects to bearer
63
+ tokens
64
+ - `PORT`, defaulting to `3000`
65
+
66
+ Run independent instances under different administrative and infrastructure
67
+ boundaries. Clients should pin each genesis witness key through an independent
68
+ channel and require a quorum with
69
+ `verifyEvidenceWitnessQuorum` from `@absolutejs/vulnerabilities`.
package/dist/http.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type createEvidenceWitnessService } from "./service";
2
+ export declare const createEvidenceWitnessHttpHandler: (options: {
3
+ authenticate: (token: string) => Promise<string | null>;
4
+ service: ReturnType<typeof createEvidenceWitnessService>;
5
+ }) => (request: Request) => Promise<Response>;
@@ -0,0 +1,4 @@
1
+ export * from "./http";
2
+ export * from "./service";
3
+ export * from "./signing";
4
+ export * from "./store";
package/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ // @bun
2
+ // src/service.ts
3
+ import {
4
+ verifyEvidenceKeyTransparencyLog
5
+ } from "@absolutejs/vulnerabilities/evidence-transparency";
6
+ import { createEvidenceWitnessCheckpointForHead } from "@absolutejs/vulnerabilities/evidence-witness";
7
+
8
+ // src/signing.ts
9
+ import {
10
+ createEvidenceSigningIdentity,
11
+ createEvidenceSigningKeyTransition,
12
+ evidenceVerificationKeyFrom,
13
+ parseEvidenceSigningIdentity,
14
+ parseEvidenceVerificationKey,
15
+ verifyEvidenceSigningKeyTransition
16
+ } from "@absolutejs/vulnerabilities/evidence-bundle";
17
+ var SIGNING_STATE_VERSION = 1;
18
+ var keysMatch = (left, right) => left.keyId === right.keyId && left.fingerprint === right.fingerprint && left.publicKey === right.publicKey;
19
+ var registryKeys = (state) => {
20
+ const keys = new Map;
21
+ const add = (key) => keys.set(key.keyId, key);
22
+ add(evidenceVerificationKeyFrom(state.identity));
23
+ for (const transition of state.transitions) {
24
+ add(transition.previousKey);
25
+ add(transition.nextKey);
26
+ }
27
+ return [...keys.values()].sort((left, right) => left.createdAt.localeCompare(right.createdAt));
28
+ };
29
+ var createEvidenceWitnessSigningState = (createdAt = new Date().toISOString()) => ({
30
+ identity: createEvidenceSigningIdentity({ createdAt }),
31
+ transitions: [],
32
+ version: SIGNING_STATE_VERSION
33
+ });
34
+ var parseEvidenceWitnessSigningState = (value) => {
35
+ if (!value || typeof value !== "object" || Array.isArray(value))
36
+ throw new Error("Evidence witness signing state is invalid");
37
+ const input = value;
38
+ if (input.version !== SIGNING_STATE_VERSION || !input.identity || !Array.isArray(input.transitions))
39
+ throw new Error("Evidence witness signing state is invalid");
40
+ const identity = parseEvidenceSigningIdentity(input.identity);
41
+ const transitions = input.transitions.map((transition) => {
42
+ if (!verifyEvidenceSigningKeyTransition(transition))
43
+ throw new Error("Evidence witness key transition is invalid");
44
+ return structuredClone(transition);
45
+ });
46
+ const registry = evidenceWitnessKeyRegistryFrom({
47
+ identity,
48
+ transitions,
49
+ version: SIGNING_STATE_VERSION
50
+ });
51
+ if (verifyEvidenceWitnessKeyRegistry({
52
+ registry,
53
+ trustedKeys: [registry.keys[0]]
54
+ }).trust !== "trusted")
55
+ throw new Error("Evidence witness key chain is disconnected");
56
+ return { identity, transitions, version: SIGNING_STATE_VERSION };
57
+ };
58
+ var encodeEvidenceWitnessSigningState = (state) => JSON.stringify(parseEvidenceWitnessSigningState(state));
59
+ var rotateEvidenceWitnessSigningState = (state, rotatedAt = new Date().toISOString()) => {
60
+ const current = parseEvidenceWitnessSigningState(state);
61
+ const identity = createEvidenceSigningIdentity({ createdAt: rotatedAt });
62
+ const transition = createEvidenceSigningKeyTransition({
63
+ nextKey: evidenceVerificationKeyFrom(identity),
64
+ previousIdentity: current.identity,
65
+ rotatedAt
66
+ });
67
+ return {
68
+ identity,
69
+ transitions: [...current.transitions, transition],
70
+ version: SIGNING_STATE_VERSION
71
+ };
72
+ };
73
+ var evidenceWitnessKeyRegistryFrom = (state) => ({
74
+ activeKeyId: state.identity.keyId,
75
+ keys: registryKeys(state),
76
+ transitions: structuredClone(state.transitions),
77
+ version: SIGNING_STATE_VERSION
78
+ });
79
+ var verifyEvidenceWitnessKeyRegistry = (input) => {
80
+ try {
81
+ if (input.registry.version !== SIGNING_STATE_VERSION)
82
+ throw new Error("Evidence witness registry version is unsupported");
83
+ const keys = input.registry.keys.map(parseEvidenceVerificationKey);
84
+ const transitions = input.registry.transitions.map((transition) => {
85
+ if (!verifyEvidenceSigningKeyTransition(transition))
86
+ throw new Error("Evidence witness key transition is invalid");
87
+ return transition;
88
+ });
89
+ let cursor = keys.find(({ keyId }) => keyId === input.registry.activeKeyId);
90
+ if (!cursor)
91
+ throw new Error("Evidence witness active key is missing");
92
+ const visited = new Set;
93
+ let traversed = 0;
94
+ while (!input.trustedKeys.some((trusted) => keysMatch(trusted, cursor))) {
95
+ if (visited.has(cursor.keyId))
96
+ throw new Error("Evidence witness key chain contains a cycle");
97
+ visited.add(cursor.keyId);
98
+ const transition = transitions.find((entry) => keysMatch(entry.nextKey, cursor));
99
+ if (!transition)
100
+ return { activeKey: cursor, trust: "untrusted" };
101
+ cursor = transition.previousKey;
102
+ traversed += 1;
103
+ }
104
+ if (traversed !== transitions.length)
105
+ throw new Error("Evidence witness key registry contains disconnected history");
106
+ return {
107
+ activeKey: keys.find(({ keyId }) => keyId === input.registry.activeKeyId),
108
+ trust: "trusted"
109
+ };
110
+ } catch {
111
+ return { activeKey: null, trust: "untrusted" };
112
+ }
113
+ };
114
+
115
+ // src/service.ts
116
+ var EVIDENCE_WITNESS_REQUEST_CONTRACT = "absolutejs.vulnerability-evidence-witness-request/v1";
117
+ var createEvidenceWitnessService = (options) => {
118
+ const origin = new URL(options.origin);
119
+ if (origin.protocol !== "https:")
120
+ throw new Error("Evidence witness origin must use HTTPS");
121
+ let state = parseEvidenceWitnessSigningState(options.signingState);
122
+ let mutationQueue = Promise.resolve();
123
+ const refresh = async () => {
124
+ state = parseEvidenceWitnessSigningState(JSON.parse(await options.loadSigningState()));
125
+ return state;
126
+ };
127
+ const registry = async () => {
128
+ await refresh();
129
+ return evidenceWitnessKeyRegistryFrom(state);
130
+ };
131
+ const checkpoint = async (subject, request) => {
132
+ if (request.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
133
+ throw new Error("Evidence witness request contract is unsupported");
134
+ const [genesis] = request.log.entries;
135
+ if (!genesis || genesis.event.kind !== "key_created")
136
+ throw new Error("Evidence transparency genesis is missing");
137
+ const verification = verifyEvidenceKeyTransparencyLog({
138
+ log: request.log,
139
+ trustedKeys: [genesis.event.key]
140
+ });
141
+ if (verification.trust !== "trusted")
142
+ throw new Error("Evidence transparency log is invalid");
143
+ const normalizedSubject = subject.trim();
144
+ if (!normalizedSubject)
145
+ throw new Error("Evidence witness subject is required");
146
+ await refresh();
147
+ const signed = createEvidenceWitnessCheckpointForHead({
148
+ identity: state.identity,
149
+ logHead: request.log.head,
150
+ logSize: request.log.entries.length,
151
+ origin: origin.href
152
+ });
153
+ const observed = await options.store.observe({
154
+ checkpoint: signed,
155
+ history: request.log.entries.map(({ digest }) => digest),
156
+ subject: normalizedSubject
157
+ });
158
+ return {
159
+ checkpoint: observed.checkpoint,
160
+ registry: evidenceWitnessKeyRegistryFrom(state)
161
+ };
162
+ };
163
+ const rotate = (rotatedAt = new Date().toISOString()) => {
164
+ const queued = mutationQueue.then(async () => {
165
+ await refresh();
166
+ const next = rotateEvidenceWitnessSigningState(state, rotatedAt);
167
+ await options.storeSigningState(encodeEvidenceWitnessSigningState(next));
168
+ state = next;
169
+ return evidenceWitnessKeyRegistryFrom(state);
170
+ });
171
+ mutationQueue = queued.then(() => {
172
+ return;
173
+ }, () => {
174
+ return;
175
+ });
176
+ return queued;
177
+ };
178
+ return { checkpoint, registry, rotate };
179
+ };
180
+
181
+ // src/store.ts
182
+ class EvidenceWitnessEquivocationError extends Error {
183
+ }
184
+
185
+ class EvidenceWitnessRollbackError extends Error {
186
+ }
187
+ var normalizedSubject = (subject) => {
188
+ const value = subject.trim();
189
+ if (!value)
190
+ throw new Error("Evidence witness subject is required");
191
+ return value;
192
+ };
193
+ var createMemoryEvidenceWitnessStore = () => {
194
+ const subjects = new Map;
195
+ const latest = async (subject) => {
196
+ const values = subjects.get(normalizedSubject(subject));
197
+ if (!values || values.size === 0)
198
+ return null;
199
+ const size = Math.max(...values.keys());
200
+ return structuredClone(values.get(size));
201
+ };
202
+ const observe = async (input) => {
203
+ const subject = normalizedSubject(input.subject);
204
+ let values = subjects.get(subject);
205
+ if (!values) {
206
+ values = new Map;
207
+ subjects.set(subject, values);
208
+ }
209
+ const existing = values.get(input.checkpoint.logSize);
210
+ if (existing) {
211
+ if (existing.checkpoint.logHead !== input.checkpoint.logHead)
212
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log equivocated at an observed size");
213
+ return {
214
+ checkpoint: structuredClone(existing.checkpoint),
215
+ status: "replay"
216
+ };
217
+ }
218
+ const current = await latest(subject);
219
+ if (current && current.checkpoint.logSize > input.checkpoint.logSize)
220
+ throw new EvidenceWitnessRollbackError("Evidence transparency log attempted to roll back");
221
+ if (current && input.history[current.checkpoint.logSize - 1] !== current.checkpoint.logHead)
222
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log forked from observed history");
223
+ const observation = structuredClone({
224
+ checkpoint: input.checkpoint,
225
+ subject
226
+ });
227
+ values.set(input.checkpoint.logSize, observation);
228
+ return {
229
+ checkpoint: structuredClone(input.checkpoint),
230
+ status: "accepted"
231
+ };
232
+ };
233
+ return { latest, observe };
234
+ };
235
+
236
+ // src/http.ts
237
+ var json = (value, status = 200) => Response.json(value, {
238
+ headers: { "cache-control": "no-store" },
239
+ status
240
+ });
241
+ var bearer = (request) => {
242
+ const authorization = request.headers.get("authorization") ?? "";
243
+ return authorization.startsWith("Bearer ") ? authorization.slice("Bearer ".length).trim() : "";
244
+ };
245
+ var createEvidenceWitnessHttpHandler = (options) => async (request) => {
246
+ const url = new URL(request.url);
247
+ if (request.method === "GET" && url.pathname === "/health")
248
+ return json({ status: "ok" });
249
+ if (request.method === "GET" && url.pathname === "/v1/keys")
250
+ return json(await options.service.registry());
251
+ if (request.method !== "POST" || url.pathname !== "/v1/checkpoints")
252
+ return json({ error: "not_found" }, 404);
253
+ const subject = await options.authenticate(bearer(request));
254
+ if (!subject)
255
+ return json({ error: "unauthorized" }, 401);
256
+ try {
257
+ const input = await request.json();
258
+ if (input.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
259
+ return json({ error: "unsupported_contract" }, 400);
260
+ return json(await options.service.checkpoint(subject, input));
261
+ } catch (error) {
262
+ if (error instanceof EvidenceWitnessEquivocationError)
263
+ return json({ error: "equivocation", message: error.message }, 409);
264
+ if (error instanceof EvidenceWitnessRollbackError)
265
+ return json({ error: "rollback", message: error.message }, 409);
266
+ return json({
267
+ error: "invalid_request",
268
+ message: error instanceof Error ? error.message : "Invalid request"
269
+ }, 400);
270
+ }
271
+ };
272
+ export {
273
+ verifyEvidenceWitnessKeyRegistry,
274
+ rotateEvidenceWitnessSigningState,
275
+ parseEvidenceWitnessSigningState,
276
+ evidenceWitnessKeyRegistryFrom,
277
+ encodeEvidenceWitnessSigningState,
278
+ createMemoryEvidenceWitnessStore,
279
+ createEvidenceWitnessSigningState,
280
+ createEvidenceWitnessService,
281
+ createEvidenceWitnessHttpHandler,
282
+ EvidenceWitnessRollbackError,
283
+ EvidenceWitnessEquivocationError,
284
+ EVIDENCE_WITNESS_REQUEST_CONTRACT
285
+ };
@@ -0,0 +1 @@
1
+ export declare const manifest: import("@absolutejs/manifest").PackageManifest<Record<string, never>, never>;
@@ -0,0 +1,38 @@
1
+ // @bun
2
+ // src/manifest.ts
3
+ import { defineManifest } from "@absolutejs/manifest";
4
+ import { Type } from "@sinclair/typebox";
5
+ var manifest = defineManifest()({
6
+ contract: 2,
7
+ discovery: {
8
+ audiences: ["platform-operators", "security-teams", "auditors"],
9
+ intents: [
10
+ "independently witness vulnerability evidence transparency",
11
+ "detect transparency rollback and equivocation",
12
+ "rotate witness signing keys",
13
+ "serve durable signed witness receipts"
14
+ ],
15
+ keywords: [
16
+ "vulnerabilities",
17
+ "witness",
18
+ "key-transparency",
19
+ "equivocation",
20
+ "quorum",
21
+ "PostgreSQL"
22
+ ],
23
+ protocols: ["HTTPS", "Ed25519", "PostgreSQL"]
24
+ },
25
+ identity: {
26
+ accent: "#dc2626",
27
+ category: "operations",
28
+ description: "Independent durable witness service for signed vulnerability-evidence transparency logs.",
29
+ docsUrl: "https://github.com/absolutejs/vulnerabilities-witness",
30
+ name: "@absolutejs/vulnerabilities-witness",
31
+ tagline: "Make evidence-log rollback and equivocation externally visible."
32
+ },
33
+ settings: Type.Object({}),
34
+ wiring: []
35
+ });
36
+ export {
37
+ manifest
38
+ };
@@ -0,0 +1,42 @@
1
+ {
2
+ "contract": 2,
3
+ "discovery": {
4
+ "audiences": [
5
+ "platform-operators",
6
+ "security-teams",
7
+ "auditors"
8
+ ],
9
+ "intents": [
10
+ "independently witness vulnerability evidence transparency",
11
+ "detect transparency rollback and equivocation",
12
+ "rotate witness signing keys",
13
+ "serve durable signed witness receipts"
14
+ ],
15
+ "keywords": [
16
+ "vulnerabilities",
17
+ "witness",
18
+ "key-transparency",
19
+ "equivocation",
20
+ "quorum",
21
+ "PostgreSQL"
22
+ ],
23
+ "protocols": [
24
+ "HTTPS",
25
+ "Ed25519",
26
+ "PostgreSQL"
27
+ ]
28
+ },
29
+ "identity": {
30
+ "accent": "#dc2626",
31
+ "category": "operations",
32
+ "description": "Independent durable witness service for signed vulnerability-evidence transparency logs.",
33
+ "docsUrl": "https://github.com/absolutejs/vulnerabilities-witness",
34
+ "name": "@absolutejs/vulnerabilities-witness",
35
+ "tagline": "Make evidence-log rollback and equivocation externally visible."
36
+ },
37
+ "settings": {
38
+ "type": "object",
39
+ "properties": {}
40
+ },
41
+ "wiring": []
42
+ }
@@ -0,0 +1,4 @@
1
+ import type { SQL } from "bun";
2
+ import { type EvidenceWitnessStore } from "./store";
3
+ export declare const ensurePostgresEvidenceWitnessSchema: (sql: SQL) => Promise<void>;
4
+ export declare const createPostgresEvidenceWitnessStore: (sql: SQL) => EvidenceWitnessStore;
@@ -0,0 +1,131 @@
1
+ // @bun
2
+ // src/store.ts
3
+ class EvidenceWitnessEquivocationError extends Error {
4
+ }
5
+
6
+ class EvidenceWitnessRollbackError extends Error {
7
+ }
8
+ var normalizedSubject = (subject) => {
9
+ const value = subject.trim();
10
+ if (!value)
11
+ throw new Error("Evidence witness subject is required");
12
+ return value;
13
+ };
14
+ var createMemoryEvidenceWitnessStore = () => {
15
+ const subjects = new Map;
16
+ const latest = async (subject) => {
17
+ const values = subjects.get(normalizedSubject(subject));
18
+ if (!values || values.size === 0)
19
+ return null;
20
+ const size = Math.max(...values.keys());
21
+ return structuredClone(values.get(size));
22
+ };
23
+ const observe = async (input) => {
24
+ const subject = normalizedSubject(input.subject);
25
+ let values = subjects.get(subject);
26
+ if (!values) {
27
+ values = new Map;
28
+ subjects.set(subject, values);
29
+ }
30
+ const existing = values.get(input.checkpoint.logSize);
31
+ if (existing) {
32
+ if (existing.checkpoint.logHead !== input.checkpoint.logHead)
33
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log equivocated at an observed size");
34
+ return {
35
+ checkpoint: structuredClone(existing.checkpoint),
36
+ status: "replay"
37
+ };
38
+ }
39
+ const current = await latest(subject);
40
+ if (current && current.checkpoint.logSize > input.checkpoint.logSize)
41
+ throw new EvidenceWitnessRollbackError("Evidence transparency log attempted to roll back");
42
+ if (current && input.history[current.checkpoint.logSize - 1] !== current.checkpoint.logHead)
43
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log forked from observed history");
44
+ const observation = structuredClone({
45
+ checkpoint: input.checkpoint,
46
+ subject
47
+ });
48
+ values.set(input.checkpoint.logSize, observation);
49
+ return {
50
+ checkpoint: structuredClone(input.checkpoint),
51
+ status: "accepted"
52
+ };
53
+ };
54
+ return { latest, observe };
55
+ };
56
+
57
+ // src/postgres.ts
58
+ var LOCK_NAME = "absolutejs:evidence-witness-observation";
59
+ var json = (value) => typeof value === "string" ? JSON.parse(value) : value;
60
+ var ensurePostgresEvidenceWitnessSchema = async (sql) => {
61
+ await sql.unsafe(`
62
+ CREATE TABLE IF NOT EXISTS absolute_vulnerability_evidence_witness_observations (
63
+ subject TEXT NOT NULL,
64
+ log_size INTEGER NOT NULL CHECK (log_size > 0),
65
+ log_head VARCHAR(71) NOT NULL,
66
+ observed_at TIMESTAMPTZ NOT NULL,
67
+ receipt JSONB NOT NULL,
68
+ witness_key_id TEXT NOT NULL,
69
+ PRIMARY KEY (subject, log_size)
70
+ )
71
+ `);
72
+ };
73
+ var createPostgresEvidenceWitnessStore = (sql) => {
74
+ const latest = async (subject) => {
75
+ const rows = await sql`
76
+ SELECT receipt
77
+ FROM absolute_vulnerability_evidence_witness_observations
78
+ WHERE subject = ${subject}
79
+ ORDER BY log_size DESC
80
+ LIMIT 1
81
+ `;
82
+ const row = Array.from(rows)[0];
83
+ return row ? { checkpoint: json(row.receipt), subject } : null;
84
+ };
85
+ const observe = (input) => sql.begin(async (transaction) => {
86
+ await transaction`
87
+ SELECT pg_advisory_xact_lock(
88
+ hashtext(${LOCK_NAME}), hashtext(${input.subject})
89
+ )
90
+ `;
91
+ const rows = await transaction`
92
+ SELECT log_head, log_size, receipt
93
+ FROM absolute_vulnerability_evidence_witness_observations
94
+ WHERE subject = ${input.subject}
95
+ ORDER BY log_size DESC
96
+ LIMIT 1
97
+ `;
98
+ const current = Array.from(rows)[0];
99
+ if (current?.log_size === input.checkpoint.logSize) {
100
+ if (current.log_head !== input.checkpoint.logHead)
101
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log equivocated at an observed size");
102
+ return {
103
+ checkpoint: json(current.receipt),
104
+ status: "replay"
105
+ };
106
+ }
107
+ if (current && current.log_size > input.checkpoint.logSize)
108
+ throw new EvidenceWitnessRollbackError("Evidence transparency log attempted to roll back");
109
+ if (current && input.history[current.log_size - 1] !== current.log_head)
110
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log forked from observed history");
111
+ await transaction`
112
+ INSERT INTO absolute_vulnerability_evidence_witness_observations (
113
+ subject, log_size, log_head, observed_at, receipt, witness_key_id
114
+ ) VALUES (
115
+ ${input.subject}, ${input.checkpoint.logSize},
116
+ ${input.checkpoint.logHead}, ${input.checkpoint.observedAt},
117
+ ${JSON.stringify(input.checkpoint)}::jsonb,
118
+ ${input.checkpoint.witness.keyId}
119
+ )
120
+ `;
121
+ return {
122
+ checkpoint: structuredClone(input.checkpoint),
123
+ status: "accepted"
124
+ };
125
+ });
126
+ return { latest, observe };
127
+ };
128
+ export {
129
+ ensurePostgresEvidenceWitnessSchema,
130
+ createPostgresEvidenceWitnessStore
131
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,376 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/service.ts
5
+ import {
6
+ verifyEvidenceKeyTransparencyLog
7
+ } from "@absolutejs/vulnerabilities/evidence-transparency";
8
+ import { createEvidenceWitnessCheckpointForHead } from "@absolutejs/vulnerabilities/evidence-witness";
9
+
10
+ // src/signing.ts
11
+ import {
12
+ createEvidenceSigningIdentity,
13
+ createEvidenceSigningKeyTransition,
14
+ evidenceVerificationKeyFrom,
15
+ parseEvidenceSigningIdentity,
16
+ parseEvidenceVerificationKey,
17
+ verifyEvidenceSigningKeyTransition
18
+ } from "@absolutejs/vulnerabilities/evidence-bundle";
19
+ var SIGNING_STATE_VERSION = 1;
20
+ var keysMatch = (left, right) => left.keyId === right.keyId && left.fingerprint === right.fingerprint && left.publicKey === right.publicKey;
21
+ var registryKeys = (state) => {
22
+ const keys = new Map;
23
+ const add = (key) => keys.set(key.keyId, key);
24
+ add(evidenceVerificationKeyFrom(state.identity));
25
+ for (const transition of state.transitions) {
26
+ add(transition.previousKey);
27
+ add(transition.nextKey);
28
+ }
29
+ return [...keys.values()].sort((left, right) => left.createdAt.localeCompare(right.createdAt));
30
+ };
31
+ var createEvidenceWitnessSigningState = (createdAt = new Date().toISOString()) => ({
32
+ identity: createEvidenceSigningIdentity({ createdAt }),
33
+ transitions: [],
34
+ version: SIGNING_STATE_VERSION
35
+ });
36
+ var parseEvidenceWitnessSigningState = (value) => {
37
+ if (!value || typeof value !== "object" || Array.isArray(value))
38
+ throw new Error("Evidence witness signing state is invalid");
39
+ const input = value;
40
+ if (input.version !== SIGNING_STATE_VERSION || !input.identity || !Array.isArray(input.transitions))
41
+ throw new Error("Evidence witness signing state is invalid");
42
+ const identity = parseEvidenceSigningIdentity(input.identity);
43
+ const transitions = input.transitions.map((transition) => {
44
+ if (!verifyEvidenceSigningKeyTransition(transition))
45
+ throw new Error("Evidence witness key transition is invalid");
46
+ return structuredClone(transition);
47
+ });
48
+ const registry = evidenceWitnessKeyRegistryFrom({
49
+ identity,
50
+ transitions,
51
+ version: SIGNING_STATE_VERSION
52
+ });
53
+ if (verifyEvidenceWitnessKeyRegistry({
54
+ registry,
55
+ trustedKeys: [registry.keys[0]]
56
+ }).trust !== "trusted")
57
+ throw new Error("Evidence witness key chain is disconnected");
58
+ return { identity, transitions, version: SIGNING_STATE_VERSION };
59
+ };
60
+ var encodeEvidenceWitnessSigningState = (state) => JSON.stringify(parseEvidenceWitnessSigningState(state));
61
+ var rotateEvidenceWitnessSigningState = (state, rotatedAt = new Date().toISOString()) => {
62
+ const current = parseEvidenceWitnessSigningState(state);
63
+ const identity = createEvidenceSigningIdentity({ createdAt: rotatedAt });
64
+ const transition = createEvidenceSigningKeyTransition({
65
+ nextKey: evidenceVerificationKeyFrom(identity),
66
+ previousIdentity: current.identity,
67
+ rotatedAt
68
+ });
69
+ return {
70
+ identity,
71
+ transitions: [...current.transitions, transition],
72
+ version: SIGNING_STATE_VERSION
73
+ };
74
+ };
75
+ var evidenceWitnessKeyRegistryFrom = (state) => ({
76
+ activeKeyId: state.identity.keyId,
77
+ keys: registryKeys(state),
78
+ transitions: structuredClone(state.transitions),
79
+ version: SIGNING_STATE_VERSION
80
+ });
81
+ var verifyEvidenceWitnessKeyRegistry = (input) => {
82
+ try {
83
+ if (input.registry.version !== SIGNING_STATE_VERSION)
84
+ throw new Error("Evidence witness registry version is unsupported");
85
+ const keys = input.registry.keys.map(parseEvidenceVerificationKey);
86
+ const transitions = input.registry.transitions.map((transition) => {
87
+ if (!verifyEvidenceSigningKeyTransition(transition))
88
+ throw new Error("Evidence witness key transition is invalid");
89
+ return transition;
90
+ });
91
+ let cursor = keys.find(({ keyId }) => keyId === input.registry.activeKeyId);
92
+ if (!cursor)
93
+ throw new Error("Evidence witness active key is missing");
94
+ const visited = new Set;
95
+ let traversed = 0;
96
+ while (!input.trustedKeys.some((trusted) => keysMatch(trusted, cursor))) {
97
+ if (visited.has(cursor.keyId))
98
+ throw new Error("Evidence witness key chain contains a cycle");
99
+ visited.add(cursor.keyId);
100
+ const transition = transitions.find((entry) => keysMatch(entry.nextKey, cursor));
101
+ if (!transition)
102
+ return { activeKey: cursor, trust: "untrusted" };
103
+ cursor = transition.previousKey;
104
+ traversed += 1;
105
+ }
106
+ if (traversed !== transitions.length)
107
+ throw new Error("Evidence witness key registry contains disconnected history");
108
+ return {
109
+ activeKey: keys.find(({ keyId }) => keyId === input.registry.activeKeyId),
110
+ trust: "trusted"
111
+ };
112
+ } catch {
113
+ return { activeKey: null, trust: "untrusted" };
114
+ }
115
+ };
116
+
117
+ // src/service.ts
118
+ var EVIDENCE_WITNESS_REQUEST_CONTRACT = "absolutejs.vulnerability-evidence-witness-request/v1";
119
+ var createEvidenceWitnessService = (options) => {
120
+ const origin = new URL(options.origin);
121
+ if (origin.protocol !== "https:")
122
+ throw new Error("Evidence witness origin must use HTTPS");
123
+ let state = parseEvidenceWitnessSigningState(options.signingState);
124
+ let mutationQueue = Promise.resolve();
125
+ const refresh = async () => {
126
+ state = parseEvidenceWitnessSigningState(JSON.parse(await options.loadSigningState()));
127
+ return state;
128
+ };
129
+ const registry = async () => {
130
+ await refresh();
131
+ return evidenceWitnessKeyRegistryFrom(state);
132
+ };
133
+ const checkpoint = async (subject, request) => {
134
+ if (request.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
135
+ throw new Error("Evidence witness request contract is unsupported");
136
+ const [genesis] = request.log.entries;
137
+ if (!genesis || genesis.event.kind !== "key_created")
138
+ throw new Error("Evidence transparency genesis is missing");
139
+ const verification = verifyEvidenceKeyTransparencyLog({
140
+ log: request.log,
141
+ trustedKeys: [genesis.event.key]
142
+ });
143
+ if (verification.trust !== "trusted")
144
+ throw new Error("Evidence transparency log is invalid");
145
+ const normalizedSubject = subject.trim();
146
+ if (!normalizedSubject)
147
+ throw new Error("Evidence witness subject is required");
148
+ await refresh();
149
+ const signed = createEvidenceWitnessCheckpointForHead({
150
+ identity: state.identity,
151
+ logHead: request.log.head,
152
+ logSize: request.log.entries.length,
153
+ origin: origin.href
154
+ });
155
+ const observed = await options.store.observe({
156
+ checkpoint: signed,
157
+ history: request.log.entries.map(({ digest }) => digest),
158
+ subject: normalizedSubject
159
+ });
160
+ return {
161
+ checkpoint: observed.checkpoint,
162
+ registry: evidenceWitnessKeyRegistryFrom(state)
163
+ };
164
+ };
165
+ const rotate = (rotatedAt = new Date().toISOString()) => {
166
+ const queued = mutationQueue.then(async () => {
167
+ await refresh();
168
+ const next = rotateEvidenceWitnessSigningState(state, rotatedAt);
169
+ await options.storeSigningState(encodeEvidenceWitnessSigningState(next));
170
+ state = next;
171
+ return evidenceWitnessKeyRegistryFrom(state);
172
+ });
173
+ mutationQueue = queued.then(() => {
174
+ return;
175
+ }, () => {
176
+ return;
177
+ });
178
+ return queued;
179
+ };
180
+ return { checkpoint, registry, rotate };
181
+ };
182
+
183
+ // src/store.ts
184
+ class EvidenceWitnessEquivocationError extends Error {
185
+ }
186
+
187
+ class EvidenceWitnessRollbackError extends Error {
188
+ }
189
+ var normalizedSubject = (subject) => {
190
+ const value = subject.trim();
191
+ if (!value)
192
+ throw new Error("Evidence witness subject is required");
193
+ return value;
194
+ };
195
+ var createMemoryEvidenceWitnessStore = () => {
196
+ const subjects = new Map;
197
+ const latest = async (subject) => {
198
+ const values = subjects.get(normalizedSubject(subject));
199
+ if (!values || values.size === 0)
200
+ return null;
201
+ const size = Math.max(...values.keys());
202
+ return structuredClone(values.get(size));
203
+ };
204
+ const observe = async (input) => {
205
+ const subject = normalizedSubject(input.subject);
206
+ let values = subjects.get(subject);
207
+ if (!values) {
208
+ values = new Map;
209
+ subjects.set(subject, values);
210
+ }
211
+ const existing = values.get(input.checkpoint.logSize);
212
+ if (existing) {
213
+ if (existing.checkpoint.logHead !== input.checkpoint.logHead)
214
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log equivocated at an observed size");
215
+ return {
216
+ checkpoint: structuredClone(existing.checkpoint),
217
+ status: "replay"
218
+ };
219
+ }
220
+ const current = await latest(subject);
221
+ if (current && current.checkpoint.logSize > input.checkpoint.logSize)
222
+ throw new EvidenceWitnessRollbackError("Evidence transparency log attempted to roll back");
223
+ if (current && input.history[current.checkpoint.logSize - 1] !== current.checkpoint.logHead)
224
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log forked from observed history");
225
+ const observation = structuredClone({
226
+ checkpoint: input.checkpoint,
227
+ subject
228
+ });
229
+ values.set(input.checkpoint.logSize, observation);
230
+ return {
231
+ checkpoint: structuredClone(input.checkpoint),
232
+ status: "accepted"
233
+ };
234
+ };
235
+ return { latest, observe };
236
+ };
237
+
238
+ // src/http.ts
239
+ var json = (value, status = 200) => Response.json(value, {
240
+ headers: { "cache-control": "no-store" },
241
+ status
242
+ });
243
+ var bearer = (request) => {
244
+ const authorization = request.headers.get("authorization") ?? "";
245
+ return authorization.startsWith("Bearer ") ? authorization.slice("Bearer ".length).trim() : "";
246
+ };
247
+ var createEvidenceWitnessHttpHandler = (options) => async (request) => {
248
+ const url = new URL(request.url);
249
+ if (request.method === "GET" && url.pathname === "/health")
250
+ return json({ status: "ok" });
251
+ if (request.method === "GET" && url.pathname === "/v1/keys")
252
+ return json(await options.service.registry());
253
+ if (request.method !== "POST" || url.pathname !== "/v1/checkpoints")
254
+ return json({ error: "not_found" }, 404);
255
+ const subject = await options.authenticate(bearer(request));
256
+ if (!subject)
257
+ return json({ error: "unauthorized" }, 401);
258
+ try {
259
+ const input = await request.json();
260
+ if (input.contract !== EVIDENCE_WITNESS_REQUEST_CONTRACT)
261
+ return json({ error: "unsupported_contract" }, 400);
262
+ return json(await options.service.checkpoint(subject, input));
263
+ } catch (error) {
264
+ if (error instanceof EvidenceWitnessEquivocationError)
265
+ return json({ error: "equivocation", message: error.message }, 409);
266
+ if (error instanceof EvidenceWitnessRollbackError)
267
+ return json({ error: "rollback", message: error.message }, 409);
268
+ return json({
269
+ error: "invalid_request",
270
+ message: error instanceof Error ? error.message : "Invalid request"
271
+ }, 400);
272
+ }
273
+ };
274
+ // src/postgres.ts
275
+ var LOCK_NAME = "absolutejs:evidence-witness-observation";
276
+ var json2 = (value) => typeof value === "string" ? JSON.parse(value) : value;
277
+ var ensurePostgresEvidenceWitnessSchema = async (sql) => {
278
+ await sql.unsafe(`
279
+ CREATE TABLE IF NOT EXISTS absolute_vulnerability_evidence_witness_observations (
280
+ subject TEXT NOT NULL,
281
+ log_size INTEGER NOT NULL CHECK (log_size > 0),
282
+ log_head VARCHAR(71) NOT NULL,
283
+ observed_at TIMESTAMPTZ NOT NULL,
284
+ receipt JSONB NOT NULL,
285
+ witness_key_id TEXT NOT NULL,
286
+ PRIMARY KEY (subject, log_size)
287
+ )
288
+ `);
289
+ };
290
+ var createPostgresEvidenceWitnessStore = (sql) => {
291
+ const latest = async (subject) => {
292
+ const rows = await sql`
293
+ SELECT receipt
294
+ FROM absolute_vulnerability_evidence_witness_observations
295
+ WHERE subject = ${subject}
296
+ ORDER BY log_size DESC
297
+ LIMIT 1
298
+ `;
299
+ const row = Array.from(rows)[0];
300
+ return row ? { checkpoint: json2(row.receipt), subject } : null;
301
+ };
302
+ const observe = (input) => sql.begin(async (transaction) => {
303
+ await transaction`
304
+ SELECT pg_advisory_xact_lock(
305
+ hashtext(${LOCK_NAME}), hashtext(${input.subject})
306
+ )
307
+ `;
308
+ const rows = await transaction`
309
+ SELECT log_head, log_size, receipt
310
+ FROM absolute_vulnerability_evidence_witness_observations
311
+ WHERE subject = ${input.subject}
312
+ ORDER BY log_size DESC
313
+ LIMIT 1
314
+ `;
315
+ const current = Array.from(rows)[0];
316
+ if (current?.log_size === input.checkpoint.logSize) {
317
+ if (current.log_head !== input.checkpoint.logHead)
318
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log equivocated at an observed size");
319
+ return {
320
+ checkpoint: json2(current.receipt),
321
+ status: "replay"
322
+ };
323
+ }
324
+ if (current && current.log_size > input.checkpoint.logSize)
325
+ throw new EvidenceWitnessRollbackError("Evidence transparency log attempted to roll back");
326
+ if (current && input.history[current.log_size - 1] !== current.log_head)
327
+ throw new EvidenceWitnessEquivocationError("Evidence transparency log forked from observed history");
328
+ await transaction`
329
+ INSERT INTO absolute_vulnerability_evidence_witness_observations (
330
+ subject, log_size, log_head, observed_at, receipt, witness_key_id
331
+ ) VALUES (
332
+ ${input.subject}, ${input.checkpoint.logSize},
333
+ ${input.checkpoint.logHead}, ${input.checkpoint.observedAt},
334
+ ${JSON.stringify(input.checkpoint)}::jsonb,
335
+ ${input.checkpoint.witness.keyId}
336
+ )
337
+ `;
338
+ return {
339
+ checkpoint: structuredClone(input.checkpoint),
340
+ status: "accepted"
341
+ };
342
+ });
343
+ return { latest, observe };
344
+ };
345
+
346
+ // src/server.ts
347
+ var {SQL } = globalThis.Bun;
348
+ var required = (name) => {
349
+ const value = process.env[name]?.trim();
350
+ if (!value)
351
+ throw new Error(`Missing required environment: ${name}`);
352
+ return value;
353
+ };
354
+ var database = new SQL(required("DATABASE_URL"));
355
+ await ensurePostgresEvidenceWitnessSchema(database);
356
+ var encodedSigningState = required("EVIDENCE_WITNESS_SIGNING_STATE_JSON");
357
+ var signingState = parseEvidenceWitnessSigningState(JSON.parse(encodedSigningState));
358
+ var tokens = JSON.parse(required("EVIDENCE_WITNESS_TOKENS_JSON"));
359
+ var subjectsByToken = new Map(Object.entries(tokens).map(([subject, token]) => [token, subject]));
360
+ var service2 = createEvidenceWitnessService({
361
+ loadSigningState: async () => encodedSigningState,
362
+ origin: required("EVIDENCE_WITNESS_ORIGIN"),
363
+ signingState,
364
+ store: createPostgresEvidenceWitnessStore(database),
365
+ storeSigningState: async () => {
366
+ throw new Error("Server-managed key rotation requires a durable secret-store integration");
367
+ }
368
+ });
369
+ var handler = createEvidenceWitnessHttpHandler({
370
+ authenticate: async (token) => subjectsByToken.get(token) ?? null,
371
+ service: service2
372
+ });
373
+ var port = Number(process.env.PORT ?? "3000");
374
+ if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
375
+ throw new Error("PORT must be a valid TCP port");
376
+ Bun.serve({ fetch: handler, port });
@@ -0,0 +1,23 @@
1
+ import { type EvidenceKeyTransparencyLog } from "@absolutejs/vulnerabilities/evidence-transparency";
2
+ import { type EvidenceWitnessKeyRegistry, type EvidenceWitnessSigningState } from "./signing";
3
+ import type { EvidenceWitnessStore } from "./store";
4
+ export declare const EVIDENCE_WITNESS_REQUEST_CONTRACT: "absolutejs.vulnerability-evidence-witness-request/v1";
5
+ export type EvidenceWitnessRequest = {
6
+ contract: typeof EVIDENCE_WITNESS_REQUEST_CONTRACT;
7
+ log: EvidenceKeyTransparencyLog;
8
+ };
9
+ export type EvidenceWitnessResponse = {
10
+ checkpoint: Awaited<ReturnType<EvidenceWitnessStore["observe"]>>["checkpoint"];
11
+ registry: EvidenceWitnessKeyRegistry;
12
+ };
13
+ export declare const createEvidenceWitnessService: (options: {
14
+ loadSigningState: () => Promise<string>;
15
+ origin: string;
16
+ signingState: EvidenceWitnessSigningState;
17
+ store: EvidenceWitnessStore;
18
+ storeSigningState: (encoded: string) => Promise<void>;
19
+ }) => {
20
+ checkpoint: (subject: string, request: EvidenceWitnessRequest) => Promise<EvidenceWitnessResponse>;
21
+ registry: () => Promise<EvidenceWitnessKeyRegistry>;
22
+ rotate: (rotatedAt?: string) => Promise<EvidenceWitnessKeyRegistry>;
23
+ };
@@ -0,0 +1,32 @@
1
+ import { type EvidenceSigningIdentity, type EvidenceSigningKeyTransition, type EvidenceVerificationKey } from "@absolutejs/vulnerabilities/evidence-bundle";
2
+ declare const SIGNING_STATE_VERSION = 1;
3
+ export type EvidenceWitnessSigningState = {
4
+ identity: EvidenceSigningIdentity;
5
+ transitions: EvidenceSigningKeyTransition[];
6
+ version: typeof SIGNING_STATE_VERSION;
7
+ };
8
+ export type EvidenceWitnessKeyRegistry = {
9
+ activeKeyId: string;
10
+ keys: EvidenceVerificationKey[];
11
+ transitions: EvidenceSigningKeyTransition[];
12
+ version: typeof SIGNING_STATE_VERSION;
13
+ };
14
+ export declare const createEvidenceWitnessSigningState: (createdAt?: string) => EvidenceWitnessSigningState;
15
+ export declare const parseEvidenceWitnessSigningState: (value: unknown) => EvidenceWitnessSigningState;
16
+ export declare const encodeEvidenceWitnessSigningState: (state: EvidenceWitnessSigningState) => string;
17
+ export declare const rotateEvidenceWitnessSigningState: (state: EvidenceWitnessSigningState, rotatedAt?: string) => EvidenceWitnessSigningState;
18
+ export declare const evidenceWitnessKeyRegistryFrom: (state: EvidenceWitnessSigningState) => EvidenceWitnessKeyRegistry;
19
+ export declare const verifyEvidenceWitnessKeyRegistry: (input: {
20
+ registry: EvidenceWitnessKeyRegistry;
21
+ trustedKeys: readonly EvidenceVerificationKey[];
22
+ }) => {
23
+ activeKey: EvidenceVerificationKey;
24
+ trust: "untrusted";
25
+ } | {
26
+ activeKey: EvidenceVerificationKey;
27
+ trust: "trusted";
28
+ } | {
29
+ activeKey: null;
30
+ trust: "untrusted";
31
+ };
32
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { SignedEvidenceWitnessCheckpoint } from "@absolutejs/vulnerabilities/evidence-witness";
2
+ export type EvidenceWitnessObservation = {
3
+ checkpoint: SignedEvidenceWitnessCheckpoint;
4
+ subject: string;
5
+ };
6
+ export type EvidenceWitnessObservationInput = EvidenceWitnessObservation & {
7
+ history: readonly `sha256:${string}`[];
8
+ };
9
+ export type EvidenceWitnessObservationResult = {
10
+ checkpoint: SignedEvidenceWitnessCheckpoint;
11
+ status: "accepted" | "replay";
12
+ };
13
+ export interface EvidenceWitnessStore {
14
+ latest(subject: string): Promise<EvidenceWitnessObservation | null>;
15
+ observe(observation: EvidenceWitnessObservationInput): Promise<EvidenceWitnessObservationResult>;
16
+ }
17
+ export declare class EvidenceWitnessEquivocationError extends Error {
18
+ }
19
+ export declare class EvidenceWitnessRollbackError extends Error {
20
+ }
21
+ export declare const createMemoryEvidenceWitnessStore: () => EvidenceWitnessStore;
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@absolutejs/vulnerabilities-witness",
3
+ "version": "0.1.0",
4
+ "description": "Independent durable transparency witness with rollback, equivocation, key-rotation, and HTTP service primitives for AbsoluteJS vulnerability evidence.",
5
+ "type": "module",
6
+ "license": "BSL-1.1",
7
+ "author": "Alex Kahn",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/absolutejs/vulnerabilities-witness.git"
11
+ },
12
+ "homepage": "https://github.com/absolutejs/vulnerabilities-witness",
13
+ "bugs": {
14
+ "url": "https://github.com/absolutejs/vulnerabilities-witness/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "bin": {
20
+ "absolute-vulnerability-witness": "./dist/server.js"
21
+ },
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "absolutejs": {
25
+ "manifestContract": 2
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ },
32
+ "./postgres": {
33
+ "types": "./dist/postgres.d.ts",
34
+ "import": "./dist/postgres.js"
35
+ },
36
+ "./manifest": {
37
+ "types": "./dist/manifest.d.ts",
38
+ "import": "./dist/manifest.js"
39
+ },
40
+ "./manifest.json": "./dist/manifest.json"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "scripts": {
48
+ "format": "prettier --write \"./**/*.{ts,json,md}\"",
49
+ "typecheck": "tsc --noEmit",
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",
52
+ "check:package": "bun run format && bun run typecheck && bun run test && bun run build"
53
+ },
54
+ "keywords": [
55
+ "absolutejs",
56
+ "equivocation",
57
+ "key-transparency",
58
+ "security",
59
+ "vulnerability-management",
60
+ "witness"
61
+ ],
62
+ "dependencies": {
63
+ "@absolutejs/manifest": "0.3.0",
64
+ "@absolutejs/vulnerabilities": "0.13.0",
65
+ "@sinclair/typebox": "0.34.52"
66
+ },
67
+ "devDependencies": {
68
+ "@types/bun": "1.3.14",
69
+ "prettier": "3.8.1",
70
+ "typescript": "5.9.3"
71
+ }
72
+ }