@ghostlogic/ghostseal
v1.0.0-alpha.0
Published
Ghostseal v1: canonical hashing, HMAC seal, Ed25519 sign, and chain link primitives for GhostLogic.
Downloads
27
Readme
@ghostlogic/ghostseal
Node TypeScript parity package for the GhostLogic Ghostseal v1 spec.
Cross-impl byte-equal with ghostseal-py
against the shared conformance corpus (120 vectors, tests/corpus/v1/vectors.jsonl).
Requirements
- Node.js >= 20 (LTS through 2026-04). Node 18 hit EOL 2025-04 and is not supported.
- TypeScript 5.4+ if you consume the source. Compiled
dist/ships ES2022 + ESM only.
Install
Phase 2 is not yet published to npm. To consume locally:
git clone https://github.com/ghostlogic/ghostseal-node
cd ghostseal-node
npm install
npm run buildThen link from your project, or depend on the local path.
Public surface (mirrors ghostseal-py)
import {
// §4 canonical serialization
canonicalBytes,
NotCanonicalizable,
// §3.2 / §5.1 content hash
contentSha256,
// §3.3 batch identifier
computeBatchId,
// §5.2 HMAC seal
hmacSeal,
hmacVerify,
// §3.5 / §5.3 Ed25519
ed25519Generate,
ed25519Sign,
ed25519Verify,
// §6 chain linkage
CHAIN_ALGORITHM_ID,
ChainLink,
chainLink,
chainVerify,
// §9 + §4.4 key validity
KeyValidityWindow,
isValidAt,
validateCanonicalTimestamp,
} from '@ghostlogic/ghostseal';Names are camelCase mirrors of the Python snake_case equivalents
(canonicalBytes ↔ canonical_bytes, etc.).
Usage
Canonical encoding and content hash
import { canonicalBytes, contentSha256 } from '@ghostlogic/ghostseal';
const bytes = canonicalBytes({ event: 'login', user: 'adam' });
// → Uint8Array of UTF-8 JSON, deterministic per spec §4.
const hash = contentSha256({ event: 'login', user: 'adam' });
// → lowercase 64-char hex sha256 of the canonical bytes.HMAC seal
import { hmacSeal, hmacVerify } from '@ghostlogic/ghostseal';
import { randomBytes } from 'node:crypto';
const key = randomBytes(32);
const payload = canonicalBytes({ id: 'evt-123', n: 42 });
const sig = hmacSeal(payload, key); // 64-char hex
const ok = hmacVerify(payload, sig, key); // trueEd25519 signature
import { ed25519Generate, ed25519Sign, ed25519Verify } from '@ghostlogic/ghostseal';
const { privateKey, publicKey } = ed25519Generate(); // 32 + 32 raw bytes
const sig = ed25519Sign(canonicalBytes({ a: 1 }), privateKey);
const ok = ed25519Verify(canonicalBytes({ a: 1 }), sig, publicKey);Chain linkage
import { chainLink, chainVerify } from '@ghostlogic/ghostseal';
const a = chainLink(null, canonicalBytes({ event: 1 })); // genesis
const b = chainLink(a.currentSealId, canonicalBytes({ event: 2 }));
const c = chainLink(b.currentSealId, canonicalBytes({ event: 3 }));
chainVerify([a, b, c]); // trueNode-specific tightening relative to ghostseal-py
The two impls produce byte-equal output for the same logical input (proven across all 120 corpus vectors). Node tightens the input contract in a handful of places where JS coercion or under-spec would silently corrupt the canonical form:
| Surface | Python accepts | Node rejects |
|------------------|----------------|------------------------------------------------------------------------------|
| canonicalBytes | floats already rejected | also undefined, sparse arrays, Map/Set/WeakMap/WeakSet, Date, ArrayBuffer, foreign typed arrays, functions, symbols, class instances, strings with unpaired UTF-16 surrogates |
| Integers | Python int (arbitrary precision) | Number only when Number.isSafeInteger(n); use bigint for \|n\| > 2^53-1 |
| computeBatchId | any string | rejects "" and any control char in U+0000–U+001F (see §4.6.1 spec delta below) |
Errors (Error and any subclass, including NotCanonicalizable itself)
are never canonical inputs — canonicalBytes(new Error()) throws
NotCanonicalizable with typeName="Error", enforced before any other
type dispatch so errors cannot leak into the signing domain.
Conformance
Run the v1 corpus against the local impl:
npm testAll 120 vectors produce byte-equal canonical_bytes_sha256, content_sha256,
and batch_id outputs with ghostseal-py.
Downstream consumers
Phase 2 unblocks node-agent-watchdog (planned per
node-agent.md)
— the Node parity of the GhostLogic Agent Watchdog forensic forwarder. All
canonical-serialization, hashing, sealing, and signing calls in
node-agent-watchdog will route through this package, with byte-equality
with logicd's emission as the load-bearing guarantee (item 106).
Authority
In order, when impls disagree:
- Spec §4 intent —
ghostlogic-spec/ghostseal-v1-spec.md(human contract). - Conformance corpus —
tests/corpus/v1/vectors.jsonl(ground truth). - Python reference —
ghostseal-py(implementation guide, not truth). - Node implementation — this package.
See PROGRESS.md for Phase 2 build history, observed spec
deltas, and spec follow-ups.
Authors
GhostLogic / Adam Thomas ([email protected]).
License
TBD.
