Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions spec/korg-ledger-v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ the key), not merely tamper-evident.
|---|---|
| [`SPEC.md`](./SPEC.md) | the normative specification (canonicalization, preimage, chaining, HMAC, verify + DAG algorithms) |
| [`vectors/`](./vectors/) + [`conformance.json`](./conformance.json) | the golden conformance vectors with **frozen tip hashes** — the cross-language oracle |
| [`conformance.py`](./conformance.py) | a dependency-free reference verifier (the executable oracle) |
| [`conformance.py`](./conformance.py) | a dependency-free Python reference verifier (the executable oracle) |
| [`js/`](./js/) | a dependency-free JavaScript verifier (`verify.mjs`) + its conformance harness, for Node and the browser |

## Conformance

Expand All @@ -31,22 +32,35 @@ An implementation in any language is **conformant** iff, given the vectors in
2. flags each tampered vector at the named `seq`;
3. fails an HMAC vector verified without the key.

Run the reference: `python3 conformance.py` (exit 0 = conformant).
Run a reference — each is one command, exit 0 = conformant:

## Conformant implementations

Four independent implementations reproduce the frozen tips — the spec is real
and multi-language, not one app's detail:
```sh
python3 conformance.py # Python
node js/conformance.mjs # JavaScript
cargo test -p korg-verify # Rust (from the korg workspace root)
```

| Implementation | Language | Where |
|---|---|---|
| **korg-registry** | Rust | `korg/crates/korg-registry/src/ledger_chain.rs` — chains every `CapabilityJournal` event on append |
| **korgex** | Python | `korgex/src/ledger_spec.py` — `korgex verify` over agent journals |
| **thumper** | Rust | `thumper/src/ledger/chain.rs` — chains every self-heal recovery session |
| **Ledger Explorer** | JavaScript | the launch site — recomputes the chain in-browser; tamper a journal and watch it break |
## Conformant implementations

The PyO3 `korg-bridge` writes through the chained `korg-registry` journal, so
any Python caller (korgex, KorgChat) inherits a conformant journal for free.
**Three genuinely independent implementations** — three languages, three separate
codepaths written from this spec — reproduce the frozen tips. That is what makes
the spec real and multi-language rather than one app's internal detail:

| Implementation | Language | Where | Conformance |
|---|---|---|---|
| **Python reference** | Python | [`conformance.py`](./conformance.py) — dependency-free, stdlib only | `python3 conformance.py` |
| **JavaScript** | JavaScript | [`js/verify.mjs`](./js/verify.mjs) — dependency-free, Web Crypto, Node + browser | `node js/conformance.mjs` |
| **Rust** | Rust | [`korg-verify`](../../crates/korg-verify) (`cargo install korg-verify`), built on the publishable [`korg-ledger`](../../crates/korg-ledger) crate | `cargo test -p korg-verify` |

Each reproduces every intact vector's frozen `tip_entry_hash` and flags every
tampered vector — so a green check on one is corroborated by two independent
others. The same Ed25519-signed receipt verifies under all three: Python mints
the signature, Rust and JavaScript re-verify it.

The repo's writers conform by construction: the Rust core
(`crates/korg-registry`, `crates/korg-ledger`) chains every `CapabilityJournal`
event on append, and the PyO3 `korg-bridge` writes through it, so any Python
caller (korgex, KorgChat) inherits a conformant journal for free.

## Implementing it (5 steps)

Expand Down
6 changes: 4 additions & 2 deletions spec/korg-ledger-v1/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ truncation sound (cutting at seq N never orphans a survivor).
- the HMAC vector uses key `"korg-conformance-key"`; verifying it with no key
MUST fail.

Run the reference harness: `python3 spec/korg-ledger-v1/conformance.py`
(exit 0 = conformant). Regenerate vectors: `python3 spec/korg-ledger-v1/_generate_vectors.py`.
Run a conformance harness (exit 0 = conformant): `python3 spec/korg-ledger-v1/conformance.py`
(Python), `node spec/korg-ledger-v1/js/conformance.mjs` (JavaScript), or
`cargo test -p korg-verify` (Rust). Regenerate vectors:
`python3 spec/korg-ledger-v1/_generate_vectors.py`.

## 7. v1 scope / non-goals

Expand Down
63 changes: 63 additions & 0 deletions spec/korg-ledger-v1/js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# @korgg/ledger-verify

The **JavaScript** implementation of **korg-ledger@v1** — one of three independent
verifiers (alongside the Python reference and the Rust `korg-verify` crate), each
written from [the spec](../SPEC.md) and checked against the same frozen
[conformance vectors](../vectors/). Tamper one byte and all three reject it.

- **Zero dependencies.** Uses only the Web Crypto standard (`crypto.subtle`).
- **Isomorphic.** The same `verify.mjs` runs in Node (≥18) and the browser.
- **No network, no trust in the producing tool.** A receipt verifies (or doesn't)
from its bytes alone.

## CLI

```sh
npx @korgg/ledger-verify <receipt.json | journal.jsonl> [--key <str>] [--pubkey <hex>] [--json]
# or, from a checkout:
node verify.mjs deliverable.korgreceipt.json
```

Exit code: `0` valid · `1` invalid/tampered · `2` usage/parse error.

```
✓ receipt VALID — 6 events, hash-chain + DAG intact · signed by b251a84c2d23d318…
```

`--pubkey <hex>` *pins* the expected signer and rejects any other key, so a green
check proves authorship against a key you already trust — not merely against the
one the receipt happens to carry.

## Library

```js
import { verifyText, verifyChain, canonicalize } from "@korgg/ledger-verify";

const verdict = await verifyText(receiptText, { pinPubkey: "b251a84c…" });
verdict.valid; // boolean
```

In the browser, import the same module and pass the receipt text — Web Crypto does
the SHA-256 / HMAC / Ed25519. (Ed25519 in `crypto.subtle` requires a recent
runtime: Node ≥18.4 and current Chrome/Safari/Firefox; the chain + DAG checks work
everywhere.)

## What a green verdict proves

The recorded events hash-chain intact and link in a well-formed causal DAG
(tamper-evident), the receipt's tip matches the chain head, and — if signed — the
holder of the named key attests to that exact tip. It does **not** prove *when* it
happened (needs an external time anchor) or that the key maps to a real-world
identity (the relying party pins that with `--pubkey`).

## Conformance

```sh
npm test # node conformance.mjs — reproduces the frozen tip hashes; exit 0 = conformant
```

This is the executable oracle: an intact vector must reproduce its frozen
`tip_entry_hash`, a tampered vector must be flagged at the named `seq`. The same
manifest ([`../conformance.json`](../conformance.json)) drives the Python
([`../conformance.py`](../conformance.py)) and Rust
([`../../../crates/korg-verify`](../../../crates/korg-verify)) implementations.
5 changes: 5 additions & 0 deletions spec/korg-ledger-v1/js/bin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
// Thin CLI entrypoint for `npx @korgg/ledger-verify`. Kept separate from
// verify.mjs so that module stays shebang-free and browser-importable.
import { cli } from "./verify.mjs";
process.exit(await cli(process.argv.slice(2)));
76 changes: 76 additions & 0 deletions spec/korg-ledger-v1/js/conformance.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// korg-ledger@v1 — JS conformance harness (the executable oracle for verify.mjs).
//
// Mirrors ../conformance.py: reads the same ../conformance.json manifest and the
// same frozen ../vectors/, and is conformant iff it reproduces every intact
// vector's tip_entry_hash and flags every tampered vector. Plus a handful of
// canonicalization edge-case assertions (the place JS and Python most easily
// diverge: non-ASCII and astral-plane escaping).
//
// node conformance.mjs # exit 0 = this JS impl reproduces the vectors

import { readFileSync } from "node:fs";
import { canonicalize, chainHash, verifyChain } from "./verify.mjs";

const enc = new TextEncoder();
const dec = new TextDecoder();
const here = (p) => new URL(p, import.meta.url);

function read(name) {
const text = readFileSync(here(`../vectors/${name}`), "utf8");
return text
.split("\n")
.filter((l) => l.trim())
.map((l) => JSON.parse(l));
}

function assertCanon(value, expected, label) {
const got = dec.decode(canonicalize(value));
const ok = got === expected;
console.log(` [${ok ? "PASS" : "FAIL"}] canon ${label.padEnd(20)} ${ok ? "" : `got ${got} want ${expected}`}`);
return ok ? 0 : 1;
}

async function run() {
let failures = 0;

// §2 canonicalization edge cases (match Python json.dumps(ensure_ascii=True) / Rust).
failures += assertCanon({ z: [3, 2], a: { y: 1, x: 2 } }, '{"a":{"x":2,"y":1},"z":[3,2]}', "sorted+compact");
failures += assertCanon({ a: "é" }, '{"a":"\\u00e9"}', "non-ascii");
failures += assertCanon({ a: "𝄞" }, '{"a":"\\ud834\\udd1e"}', "astral surrogate-pair");

// The frozen vectors — the cross-impl oracle.
const manifest = JSON.parse(readFileSync(here("../conformance.json"), "utf8"));
if (manifest.spec_version !== "korg-ledger@v1") throw new Error("unexpected spec_version");

for (const v of manifest.vectors) {
const events = read(v.file);
const key = v.key ? enc.encode(v.key) : null;
const errors = await verifyChain(events, key);
let ok = true;
let detail = "";
if (v.verify === "intact") {
if (errors.length) {
ok = false;
detail = `expected intact, got ${JSON.stringify(errors)}`;
} else if ((await chainHash(events[events.length - 1], key)) !== v.tip_entry_hash) {
ok = false;
detail = "tip_entry_hash not reproduced";
}
} else {
if (!errors.length) {
ok = false;
detail = "expected tampered, verified clean";
} else if (!errors.some((e) => e.includes(v.error_contains))) {
ok = false;
detail = `errors ${JSON.stringify(errors)} missing ${JSON.stringify(v.error_contains)}`;
}
}
console.log(` [${ok ? "PASS" : "FAIL"}] ${v.file.padEnd(26)} ${v.verify.padEnd(8)} ${detail}`);
if (!ok) failures++;
}

console.log(`\nkorg-ledger@v1 conformance (js): ${failures ? `${failures} FAILURE(S)` : "PASS"}`);
return failures ? 1 : 0;
}

process.exit(await run());
41 changes: 41 additions & 0 deletions spec/korg-ledger-v1/js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@korgg/ledger-verify",
"version": "0.1.0",
"description": "Independent, dependency-free verifier for korg-ledger@v1 receipts and journals (hash chain + causal DAG + Ed25519 tip signature). Runs in Node and the browser via Web Crypto.",
"type": "module",
"main": "verify.mjs",
"exports": {
".": "./verify.mjs"
},
"bin": {
"korg-verify-js": "bin.mjs"
},
"files": [
"verify.mjs",
"bin.mjs",
"README.md"
],
"scripts": {
"test": "node conformance.mjs"
},
"engines": {
"node": ">=18"
},
"keywords": [
"korg",
"korg-ledger",
"tamper-evident",
"hash-chain",
"verification",
"ed25519",
"audit",
"provenance"
],
"license": "MIT OR Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/New1Direction/korg",
"directory": "spec/korg-ledger-v1/js"
},
"homepage": "https://github.com/New1Direction/korg/tree/main/spec/korg-ledger-v1"
}
Loading
Loading