From 5e2b9d803521f17640f58328faaa5ac75538475f Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Tue, 27 Jan 2026 22:34:09 +0000 Subject: [PATCH] github pages docs --- docs/api.html | 116 ++++++++++++++++++++++++++++++++++++++ docs/examples.html | 54 ++++++++++++++++++ docs/index.html | 58 +++++++++++++++++++ docs/integrity-model.html | 55 ++++++++++++++++++ docs/seal-and-trail.html | 63 +++++++++++++++++++++ docs/style.css | 106 ++++++++++++++++++++++++++++++++++ 6 files changed, 452 insertions(+) create mode 100644 docs/api.html create mode 100644 docs/examples.html create mode 100644 docs/index.html create mode 100644 docs/integrity-model.html create mode 100644 docs/seal-and-trail.html create mode 100644 docs/style.css diff --git a/docs/api.html b/docs/api.html new file mode 100644 index 0000000..9686f92 --- /dev/null +++ b/docs/api.html @@ -0,0 +1,116 @@ + + + + + + json-trail - API + + + + +
+ +

API Reference

+ +

+ json-trail exposes a minimal API for creating hash-linked blocks and + verifying their integrity. All operations use deterministic SHA-256 hashing and return plain data structures + containing Uint8Array fields. +

+ +

createBlock(payload, prevBlock)

+ +

+ Creates a new hash-linked block. The payload must be a Uint8Array. If a + previous block is provided, its blockHash becomes the + prevHash of the new block. +

+ +

Import

+
import { createBlock } from "json-trail";
+
+ +

Usage

+
const enc = new TextEncoder();
+const payload = enc.encode("hello");
+
+const block = await createBlock(payload);
+
+ +

Returns

+
{
+  index: number,
+  timestamp: number,
+  payload: Uint8Array,
+  payloadHash: Uint8Array,
+  prevHash: Uint8Array | null,
+  blockHash: Uint8Array
+}
+
+ +

+ Note: modifying a block or its payload after creation will invalidate its + hashes. +

+ +
+ +

verifyBlock(block)

+ +

+ Verifies the integrity of a single block. Returns true if + the payloadHash and blockHash match the block + contents. +

+ +

Import

+
import { verifyBlock } from "json-trail";
+
+ +

Usage

+
const ok = await verifyBlock(block);
+
+ +

Returns

+
boolean
+
+ +
+ +

verifyChain(blocks)

+ +

+ Verifies the integrity of a sequence of blocks. Ensures each block is + valid and that each prevHash matches the previous + blockHash. +

+ +

Import

+
import { verifyChain } from "json-trail";
+
+ +

Usage

+
const ok = await verifyChain([block0, block1, block2]);
+
+ +

Returns

+
boolean
+
+ + + + + +
+ + + \ No newline at end of file diff --git a/docs/examples.html b/docs/examples.html new file mode 100644 index 0000000..35f5a1b --- /dev/null +++ b/docs/examples.html @@ -0,0 +1,54 @@ + + + + + + json-trail - Examples + + + + +
+ +

Examples

+ +

Basic usage

+ +
import { createBlock, verifyBlock, verifyChain } from "json-trail";
+
+const enc = new TextEncoder();
+const payload = enc.encode("hello world");
+
+const block0 = await createBlock(payload);
+const block1 = await createBlock(payload, block0);
+
+console.log(await verifyBlock(block0));
+console.log(await verifyBlock(block1));
+console.log(await verifyChain([block0, block1]));
+
+ +

Tampering detection

+ +
block1.payload[0] ^= 1;
+
+console.log(await verifyBlock(block1)); // false
+console.log(await verifyChain([block0, block1])); // false
+
+ + + + + +
+ + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..acc9a57 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,58 @@ + + + + + + json-trail + + + + +
+ +

json-trail

+

+ Tamper-evident, hash-linked blocks for apps. Zero dependencies. +

+ +

+ json-trail is a tiny library for creating hash-linked blocks that detect + any modification to payloads or history. It provides chronological + integrity without keys, signatures, or complex state. +

+ +

Why json-trail exists

+ +

+ Local-first apps store data on the device and sync when possible. Without + a server acting as the source of truth, you still need a way to know + whether your local history has been tampered with. json-trail provides a + minimal, deterministic, tamper-evident structure for that purpose. +

+ +

Features

+ + + + + + +
+ + + \ No newline at end of file diff --git a/docs/integrity-model.html b/docs/integrity-model.html new file mode 100644 index 0000000..80713a4 --- /dev/null +++ b/docs/integrity-model.html @@ -0,0 +1,55 @@ + + + + + + json-trail - Integrity Model + + + + +
+ +

Integrity Model

+ +

+ json-trail uses a simple hashing model to detect tampering. +

+ +

Block structure

+ + +

Block validity

+ + +

Chain validity

+ + + + + + +
+ + + \ No newline at end of file diff --git a/docs/seal-and-trail.html b/docs/seal-and-trail.html new file mode 100644 index 0000000..36be3af --- /dev/null +++ b/docs/seal-and-trail.html @@ -0,0 +1,63 @@ + + + + + + json-seal + json-trail + + + + +
+ +

json-seal + json-trail

+ +

+ json-seal provides authenticity. json-trail provides chronological integrity. + Together, they form a signed, tamper-evident, hash-linked log entry. +

+ +

+ npm: json-seal +

+ +

Example

+ +
import { generateKeyPair, signPayload, canonicalize } from "json-seal";
+import { createBlock } from "json-trail";
+
+const { privateKey, publicKey } = await generateKeyPair();
+
+const sealed = await signPayload({ foo: 123 }, privateKey, publicKey);
+
+// canonical, deterministic bytes for the block
+const canonical = canonicalize(sealed);
+const block = await createBlock(new TextEncoder().encode(canonical));
+
+ +

+ Using canonicalize ensures that the bytes passed into + createBlock are deterministic. json-seal produces a stable, + canonical representation of the sealed envelope, so hashing it with + json-trail yields consistent results across platforms and runtimes. +

+ +

Verification flow

+ + + + +
+ + + \ No newline at end of file diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 0000000..d6f43f0 --- /dev/null +++ b/docs/style.css @@ -0,0 +1,106 @@ +body { + margin: 0; + padding: 0; + font-family: system-ui, sans-serif; + background: radial-gradient(circle at 50% 20%, #1a1a1a, #000); + color: #e6e6e6; + display: flex; + justify-content: center; + align-items: flex-start; + min-height: 100vh; + padding-top: 8vh; + overflow-x: hidden; + width: 100%; + box-sizing: border-box; +} + +main { + max-width: 640px; + width: 100%; + padding: 0 24px; + box-sizing: border-box; + text-align: left; +} + +h1 { + font-weight: 500; + letter-spacing: -0.5px; + margin-bottom: 1rem; + text-align: center; +} + +h2 { + margin-top: 2rem; + font-weight: 500; +} + +p, +ul { + line-height: 1.6; + opacity: 0.9; +} + +pre { + background: rgba(255, 255, 255, 0.05); + padding: 16px; + border-radius: 8px; + overflow-x: auto; + margin-top: 1rem; +} + +nav { + margin-top: 2.5rem; + display: flex; + justify-content: center; + gap: 1.5rem; + flex-wrap: wrap; + opacity: 0.8; +} + +nav a { + color: #e6e6e6; + text-decoration: none; + font-size: 0.95rem; +} + +nav a:hover { + opacity: 1; +} + +a { + color: #e6e6e6; + text-decoration: none; +} + +a:hover { + opacity: 1; +} + +.external-links { + text-align: center; + margin-top: 2rem; + opacity: 0.8; + margin-bottom: 3rem; +} + +.subtitle { + text-align: center; +} + +@media (max-width: 480px) { + body { + padding-top: 4vh; + } + + main { + padding: 0 16px; + } + + nav { + gap: 1rem; + } + + nav a { + font-size: 0.9rem; + } +} \ No newline at end of file