metamorphic-log
A tamper-evident, append-only transparency log engine and verification SDK. Built by Moss Piglet Corporation to support privacy- first software, including Metamorphic and Mosslet.
It implements the cryptographic verification core over an append-only Merkle
log (RFC 6962 / RFC 9162), wraps the C2SP tlog-tiles substrate
for storage and serving, supports externally witnessed checkpoints for
anti-equivocation, layers in hybrid post-quantum checkpoint signatures, and
adds CONIKS-style index privacy via a swappable VRF.
Status: v0.1, building slice-by-slice. Implemented: the conformance core (canonical Layer-0 leaf encoding, RFC 6962 Merkle hashing, RFC 6962 / RFC 9162 inclusion + consistency verification), the C2SP
tlog-tilessubstrate withcheckpoint/signed-noteparsing and classical Ed25519 witness verification, additive hybrid post-quantum checkpoint signing, CONIKS index privacy (swappable VRF + SHA3-512 commitments + lookup/absence proofs), the signed per-namespace policy layer with declared == observed enforcement, and the browser verification + monitor SDK (WASM). The leaf layer is application-agnostic — any app defines its own opaque record type under a versioned<namespace>/<record-type>/v<N>context label; the bundledkey_history_v1conformance instance (the format used by Mosslet, the first consumer) is reproduced byte-for-byte by the native crate and the WASM SDK. Ingestion/scale primitives land in a later slice.
Browser verification + monitor SDK (WASM)
The crate ships a wasm-bindgen personality (the wasm module, wasm32-only)
so a browser can monitor the log itself instead of trusting a server. It is
a thin shell over the rlib core — no parallel logic — published to npm as
@f0rest8/metamorphic-log. It exposes the full verification
surface: verifyInclusion / verifyConsistency, checkpoint/signed-note
verification (Ed25519 + hybrid PQ), CONIKS coniksVerifyLookup /
coniksVerifyAbsence, and signedPolicyVerify + the declared == observed
policyEnforce* checks. A cross-language byte-parity KAT
(tests/cross_language.rs, run under wasm-pack test --node) proves the WASM
exports reproduce the native KAT vectors byte-for-byte. The Elixir NIF
(metamorphic_log, Rustler + dirty schedulers) is a deferred follow-up in its
own sibling Hex package, mirroring the metamorphic_crypto precedent of a thin
NIF over the published crate.
Verifying proofs
use ;
// Prove a leaf is committed at `index` in a tree of `size` whose head is `root`.
verify_inclusion?;
// Prove the tree of `size2`/`root2` is an append-only extension of `size1`/`root1`.
verify_consistency?;
Defining a leaf (any application)
A log leaf is opaque, app-defined bytes — the Merkle layer never inspects
them, so your canonical record drops in with zero reformatting. You choose a
versioned context label ("acme/user-keys/v1", "example-app/audit-event/v2",
…) as the domain separator for the per-record content hash. The bundled
metamorphic_log::leaf::key_history_v1 module is a worked example of such a
record type (and the byte-locked conformance fixture); model your own on it.
Index privacy (CONIKS)
A coniks::ConiksDirectory maps identities to committed values at
VRF-derived, privacy-preserving tree positions, and answers lookups with
presence or absence proofs that a relying party verifies independently —
from only the namespace, the VRF public key, the directory root, and the proof.
use ;
use Ecvrf;
let mut dir = new;
dir.insert?;
let root = dir.root;
let Present = dir.lookup? else ;
// Independent verification — no access to the directory needed.
let value = verify_lookup?;
assert_eq!;
The VRF is swappable behind the vrf::Vrf trait (classical ECVRF today; a
hybrid PQ construction slots in later with no format change). Commitments
(commitment) are SHA3-512 — post-quantum and binding regardless of the VRF.
Single source of truth for primitives
This crate contains no cryptographic primitives of its own. Every hash
(SHA-256 / SHA3-512), signature (composite hybrid PQ), KEM (ML-KEM), and KDF
comes from metamorphic-crypto, the audited, RustCrypto-only core.
There is no parallel crypto stack here.
What a transparency log does — and does not — provide
A transparency log gives you, after you have a key to anchor on:
- Continuity — the history you observe is append-only and self-consistent over time (consistency proofs).
- Anti-equivocation — the operator cannot show different histories to different observers without detection, because independent witnesses co-sign checkpoints.
- Tamper-evidence — any retroactive edit to the log breaks an inclusion or consistency proof.
It does not solve:
- First-contact / bootstrap trust. A transparency log cannot tell you whether the first key you ever saw for a peer is the genuine one. That is a Trust-On-First-Use (TOFU) problem your application must handle separately from this library (for example, with out-of-band fingerprint or safety-number verification). The log makes a key accountable over time; it does not vouch for its origin.
Cryptographic posture
- PQ from day one for integrity, authentication, confidentiality, and
commitments. Checkpoints are designed for hybrid post-quantum signing via
metamorphic-crypto's composite (ML-DSA + classical) signatures. - Index-privacy (the CONIKS VRF) defaults to a classical
ECVRF-edwards25519-SHA512-TAI construction (RFC 9381 ciphersuite
0x03), behind a swappable trait. The constant-timeELL2(0x04) suite and a hybrid (PQ + classical) output path are designed in but not built — the former lands with a curve backend that exposes a conformant hash-to-curve, the latter when an audited lattice VRF exists. This is the only layer with a classical default in v0.1. - Primitives are hybrid post-quantum, pure-Rust, and NCC-audited (via
metamorphic-crypto). They are not FIPS-validated, and this project does not claim FIPS validation.
Standards spine
- RFC 6962 / RFC 9162 — Merkle log; inclusion + consistency proofs
- C2SP
tlog-tiles,tlog-witness,checkpoint/signed-note— interoperable substrate enabling reciprocal witnessing - RFC 9381 — ECVRF-edwards25519 (CONIKS index privacy)
- FIPS 203 / 204 + CNSA 2.0 — post-quantum primitives (via
metamorphic-crypto) - NIST SP 800-56C / 800-108 — KDF roles
Module layout
| Module | Layer | Responsibility |
|---|---|---|
leaf |
0 | Canonical, length-prefixed leaf encoding |
merkle |
1 | RFC 6962 SHA-256 tree-node hashing (fixed, witness-audited) |
proof |
1 | Inclusion + consistency proof verification |
checkpoint |
2 | Signed-note / witnessed checkpoints; hybrid PQ signing |
vrf |
3 | Swappable VRF trait; classical ECVRF (RFC 9381 TAI) default |
commitment |
3 | SHA3-512 hiding/binding index→value commitments |
coniks |
3 | Per-namespace directory; presence + absence (index privacy) |
policy |
0 | Signed, versioned namespace policy; declared == observed |
note |
2 | C2SP signed-note parse/verify (Ed25519 + hybrid PQ lines) |
tile |
2 | C2SP tlog-tiles coordinates / serving geometry |
wasm |
— | Browser verification + monitor SDK (wasm32-only) |
error |
— | Crate-wide error type |
Safety & supply chain
#![forbid(unsafe_code)]at this layer- RustCrypto-only dependencies; primitives delegated to
metamorphic-crypto - Edition 2024, MSRV 1.85, dual-licensed
MIT OR Apache-2.0 - CI runs
fmt --check,clippy -D warnings, tests, awasm32-unknown-unknowncheck, awasm-packSDK build, the cross-language byte-parity KAT (wasm-pack test --node),cargo audit, and an MSRV-floor build; all action refs are SHA-pinned. The tagged release pipeline adds CycloneDX SBOM, cosign keyless signing, build-provenance attestation, and OIDC trusted publishing to crates.io + npm - See
SECURITY.mdfor the disclosure process
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.