shadow-attest-core 2.0.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/store-file.js ADDED
@@ -0,0 +1,65 @@
1
+ // packages/attest-core/store-file.js
2
+ // ─────────────────────────────────────────────────────────────────
3
+ // Default file-based append store for evidence-bundle sessions.
4
+ //
5
+ // Layout: one JSONL file per session at `${dir}/${sessionId}.jsonl`.
6
+ // Every session write (header, event, seal) is one line. On process
7
+ // crash, the file contains whatever events made it to fsync — recovery
8
+ // picks up from there.
9
+ //
10
+ // This is the reference implementation. Custom stores backed by
11
+ // DynamoDB / Kafka / append-only S3 / a signer daemon UNIX socket
12
+ // implement the same {appendLine, readLines} shape.
13
+
14
+ import { existsSync, mkdirSync, readFileSync, readdirSync, appendFileSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+
17
+ /**
18
+ * Create a file-backed evidence store. Writes are synchronous appendFileSync
19
+ * calls so a process crash after a successful `appendEvent` return leaves
20
+ * the event on disk. The trade-off is throughput; for very high-fanout
21
+ * sessions the caller should batch on top or use a specialized adapter.
22
+ *
23
+ * @param {object} params
24
+ * @param {string} params.path — file path for this session's JSONL log
25
+ * @returns {{appendLine(text: string): void, readLines(): string[]}}
26
+ */
27
+ export function createFileStore(params) {
28
+ const { path } = params ?? {};
29
+ if (!path || typeof path !== "string") {
30
+ throw new Error("createFileStore: path required");
31
+ }
32
+
33
+ const dir = dirname(path);
34
+ if (!existsSync(dir)) {
35
+ mkdirSync(dir, { recursive: true });
36
+ }
37
+
38
+ return {
39
+ appendLine(text) {
40
+ if (typeof text !== "string") throw new Error("appendLine: text must be string");
41
+ // The trailing newline is intentional; JSONL readers expect it.
42
+ appendFileSync(path, text + "\n", "utf8");
43
+ },
44
+ readLines() {
45
+ if (!existsSync(path)) return [];
46
+ const raw = readFileSync(path, "utf8");
47
+ // Trim trailing newline before split so we don't emit a spurious "".
48
+ return raw.replace(/\n$/, "").split("\n");
49
+ },
50
+ // Introspection helper — the path is public so recovery tooling can
51
+ // point directly at the file if it already knows the session id.
52
+ _path: path,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * List all session IDs present in a session directory. Recovery tooling
58
+ * uses this to find orphan bundles after a process restart.
59
+ */
60
+ export function listSessionFiles(dir) {
61
+ if (!existsSync(dir)) return [];
62
+ return readdirSync(dir)
63
+ .filter(f => f.endsWith(".jsonl"))
64
+ .map(f => ({ path: join(dir, f), sessionId: f.replace(/\.jsonl$/, "") }));
65
+ }
@@ -0,0 +1,5 @@
1
+ // shadow-attest-core/verify-chain — chain walker (re-exports lib/hash-chain-verifier.js if present)
2
+ // hash-chain-verifier is currently a bin/verify-chain.mjs CLI; the reusable
3
+ // library form will move here in v2.0.0. Stub for now so consumers can
4
+ // resolve the export path today.
5
+ export {};