{"id":51169898,"url":"https://github.com/moss-piglet/metamorphic-log","last_synced_at":"2026-07-19T03:01:44.001Z","repository":{"id":367576478,"uuid":"1281356620","full_name":"moss-piglet/metamorphic-log","owner":"moss-piglet","description":"Tamper-evident, append-only transparency log + verification SDK (RFC 6962/9162, C2SP tlog-tiles, witnessed checkpoints, hybrid PQ signing, CONIKS index privacy). Primitives from metamorphic-crypto.","archived":false,"fork":false,"pushed_at":"2026-07-16T23:01:04.000Z","size":78526,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-16T23:49:24.828Z","etag":null,"topics":["coniks","merkle","post-quantum","rfc6962","rust","transparency-log","wasm"],"latest_commit_sha":null,"homepage":"https://mosspiglet.dev","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moss-piglet.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["moss-piglet"]}},"created_at":"2026-06-26T13:26:00.000Z","updated_at":"2026-07-16T23:00:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/moss-piglet/metamorphic-log","commit_stats":null,"previous_names":["moss-piglet/metamorphic-log"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/moss-piglet/metamorphic-log","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moss-piglet%2Fmetamorphic-log","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moss-piglet%2Fmetamorphic-log/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moss-piglet%2Fmetamorphic-log/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moss-piglet%2Fmetamorphic-log/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moss-piglet","download_url":"https://codeload.github.com/moss-piglet/metamorphic-log/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moss-piglet%2Fmetamorphic-log/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35638917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-19T02:00:06.923Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coniks","merkle","post-quantum","rfc6962","rust","transparency-log","wasm"],"created_at":"2026-06-27T00:00:18.294Z","updated_at":"2026-07-19T03:01:43.989Z","avatar_url":"https://github.com/moss-piglet.png","language":"Rust","funding_links":["https://github.com/sponsors/moss-piglet"],"categories":[],"sub_categories":[],"readme":"# metamorphic-log\n\nA tamper-evident, append-only **transparency log** engine and verification SDK.\nBuilt by [Moss Piglet Corporation](https://mosspiglet.dev) to support privacy-\nfirst software, including [Metamorphic](https://metamorphic.app) and [Mosslet](https://mosslet.com).\n\nIt implements the cryptographic _verification core_ over an append-only Merkle\nlog (RFC 6962 / RFC 9162), wraps the [C2SP `tlog-tiles`][tlog-tiles] substrate\nfor storage and serving, supports externally **witnessed** checkpoints for\nanti-equivocation, layers in **hybrid post-quantum** checkpoint signatures, and\nadds CONIKS-style index privacy via a swappable VRF.\n\n\u003e **Status:** v0.1, building slice-by-slice. Implemented: the **conformance\n\u003e core** (canonical Layer-0 leaf encoding, RFC 6962 Merkle hashing, RFC 6962 /\n\u003e RFC 9162 inclusion + consistency verification), the **C2SP `tlog-tiles`**\n\u003e substrate with `checkpoint` / `signed-note` parsing and classical Ed25519\n\u003e witness verification, **additive hybrid post-quantum** checkpoint signing,\n\u003e **CONIKS** index privacy (swappable VRF + SHA3-512 commitments + lookup/absence\n\u003e proofs), the signed per-namespace **policy** layer with declared == observed\n\u003e enforcement, and the **browser verification + monitor SDK** (WASM). The leaf\n\u003e layer is application-agnostic — any app defines its own opaque record type\n\u003e under a versioned `\u003cnamespace\u003e/\u003crecord-type\u003e/v\u003cN\u003e` context label; the bundled\n\u003e `key_history_v1` conformance instance (the format used by\n\u003e [Mosslet](https://mosslet.com), the first consumer) is reproduced\n\u003e byte-for-byte by the native crate **and** the WASM SDK. Ingestion/scale\n\u003e primitives land in a later slice.\n\n## Browser verification + monitor SDK (WASM)\n\nThe crate ships a `wasm-bindgen` personality (the `wasm` module, `wasm32`-only)\nso a browser can **monitor the log itself** instead of trusting a server. It is\na thin shell over the rlib core — no parallel logic — published to npm as\n[`@f0rest8/metamorphic-log`](npm-README.md). It exposes the full verification\nsurface: `verifyInclusion` / `verifyConsistency`, checkpoint/signed-note\nverification (Ed25519 + hybrid PQ), CONIKS `coniksVerifyLookup` /\n`coniksVerifyAbsence`, and `signedPolicyVerify` + the declared == observed\n`policyEnforce*` checks. A **cross-language byte-parity KAT**\n(`tests/cross_language.rs`, run under `wasm-pack test --node`) proves the WASM\nexports reproduce the native KAT vectors byte-for-byte. The Elixir NIF\n(`metamorphic_log`, Rustler + dirty schedulers) ships in its own sibling Hex\npackage, mirroring the `metamorphic_crypto` precedent of a thin NIF over the\npublished crate.\n\n## Verifying proofs\n\n```rust\nuse metamorphic_log::{verify_inclusion, verify_consistency};\n\n// Prove a leaf is committed at `index` in a tree of `size` whose head is `root`.\nverify_inclusion(index, size, leaf_hash, \u0026audit_path, root)?;\n\n// Prove the tree of `size2`/`root2` is an append-only extension of `size1`/`root1`.\nverify_consistency(size1, size2, \u0026proof, root1, root2)?;\n```\n\n## Defining a leaf (any application)\n\nA log leaf is **opaque, app-defined bytes** — the Merkle layer never inspects\nthem, so your canonical record drops in with zero reformatting. You choose a\nversioned context label (`\"acme/user-keys/v1\"`, `\"example-app/audit-event/v2\"`,\n…) as the domain separator for the per-record content hash. The bundled\n`metamorphic_log::leaf::key_history_v1` module is a worked example of such a\nrecord type (and the byte-locked conformance fixture); model your own on it.\n\nFor the common **key-history** shape specifically, you do not need to hand-roll\nthe hash: build a `key_history_v1::Entry` and call\n`entry.entry_hash_with_context(\u0026label)` (or the free\n`key_history_entry_hash_with_context(\u0026label, \u0026entry)`) with your own\n`\"\u003cnamespace\u003e/key-history/v1\"` label. This is the **recommended** way to produce\na branded key-history leaf — the label binds the intra-chain domain separator to\nyour namespace, so auditors can tell whose key history a chain belongs to. The\ncanonical bytes and the RFC 6962 leaf hash are brand-independent; only the\ncontinuity `entry_hash` varies by label. The frozen `entry_hash()` (the\n`mosslet/key-history/v1` conformance value) is retained and simply delegates to\n`entry_hash_with_context` with that fixed label. In the browser SDK the matching\nexport is `keyHistoryEntryHashWithContext(context, …)`.\n\n## Index privacy (CONIKS)\n\nA `coniks::ConiksDirectory` maps identities to committed values at\nVRF-derived, privacy-preserving tree positions, and answers lookups with\n**presence** or **absence** proofs that a relying party verifies independently —\nfrom only the namespace, the VRF public key, the directory root, and the proof.\n\n```rust\nuse metamorphic_log::coniks::{ConiksDirectory, LookupResult, Namespace, verify_lookup};\nuse metamorphic_log::vrf::Ecvrf;\n\nlet mut dir = ConiksDirectory::new(Namespace::parse(\"acme\")?, Box::new(Ecvrf));\ndir.insert(b\"alice@example.com\", b\"key-history-head\")?;\nlet root = dir.root();\n\nlet LookupResult::Present(proof) = dir.lookup(b\"alice@example.com\")? else {\n    unreachable!()\n};\n// Independent verification — no access to the directory needed.\nlet value = verify_lookup(\n    \u0026Ecvrf, dir.namespace(), dir.vrf_public_key(), \u0026root, b\"alice@example.com\", \u0026proof,\n)?;\nassert_eq!(value, b\"key-history-head\");\n```\n\nThe VRF is swappable behind the `vrf::Vrf` trait (classical ECVRF today; a\nhybrid PQ construction slots in later with no format change). Commitments\n(`commitment`) are SHA3-512 — post-quantum and binding regardless of the VRF.\n\n## Single source of truth for primitives\n\nThis crate contains **no cryptographic primitives of its own**. Every hash\n(SHA-256 / SHA3-512), signature (composite hybrid PQ), KEM (ML-KEM), and KDF\ncomes from [`metamorphic-crypto`][crypto], the audited, RustCrypto-only core.\n**There is no parallel crypto stack here.**\n\n## What a transparency log does — and does not — provide\n\nA transparency log gives you, **after** you have a key to anchor on:\n\n- **Continuity** — the history you observe is append-only and self-consistent\n  over time (consistency proofs).\n- **Anti-equivocation** — the operator cannot show different histories to\n  different observers without detection, because independent **witnesses**\n  co-sign checkpoints.\n- **Tamper-evidence** — any retroactive edit to the log breaks an inclusion or\n  consistency proof.\n\nIt does **not** solve:\n\n- **First-contact / bootstrap trust.** A transparency log cannot tell you whether\n  the _first_ key you ever saw for a peer is the genuine one. That is a\n  Trust-On-First-Use (TOFU) problem your application must handle separately from\n  this library (for example, with out-of-band fingerprint or safety-number\n  verification). The log makes a key _accountable over time_; it does not vouch\n  for its origin.\n\n## Cryptographic posture\n\n- **PQ from day one** for integrity, authentication, confidentiality, and\n  commitments. Checkpoints are designed for hybrid post-quantum signing via\n  `metamorphic-crypto`'s composite (ML-DSA + classical) signatures.\n- **Index-privacy** (the CONIKS VRF) defaults to a classical\n  ECVRF-edwards25519-SHA512-TAI construction (RFC 9381 ciphersuite `0x03`),\n  behind a swappable trait. The constant-time `ELL2` (`0x04`) suite and a\n  hybrid (PQ + classical) output path are designed in but not built — the\n  former lands with a curve backend that exposes a conformant hash-to-curve, the\n  latter when an audited lattice VRF exists. This is the _only_ layer with a\n  classical default in v0.1.\n- Primitives are hybrid post-quantum, pure-Rust, and NCC-audited (via\n  `metamorphic-crypto`). They are **not** FIPS-validated, and this project does\n  not claim FIPS validation.\n\n## Standards spine\n\n- **RFC 6962** / **RFC 9162** — Merkle log; inclusion + consistency proofs\n- **C2SP** [`tlog-tiles`][tlog-tiles], `tlog-witness`, `checkpoint` /\n  `signed-note` — interoperable substrate enabling reciprocal witnessing\n- **RFC 9381** — ECVRF-edwards25519 (`0x03`) and ECVRF-P256 (`0x01`) —\n  index-privacy VRFs\n- **`draft-ietf-keytrans-protocol`** — experimental IETF KEYTRANS combined-tree\n  directory (`keytrans`, `KEYTRANS_EXP_04` — movable, not byte-frozen)\n- **FIPS 203 / 204** + **CNSA 2.0** — post-quantum primitives (via\n  [`metamorphic-crypto`][crypto])\n- **NIST SP 800-56C / 800-108** — KDF roles\n\n## Module layout\n\n| Module       | Layer | Responsibility                                              |\n| ------------ | ----- | ----------------------------------------------------------- |\n| `leaf`       | 0     | Canonical, length-prefixed leaf encoding                    |\n| `merkle`     | 1     | RFC 6962 SHA-256 tree-node hashing (fixed, witness-audited) |\n| `proof`      | 1     | Inclusion + consistency proof verification                  |\n| `checkpoint` | 2     | Signed-note / witnessed checkpoints; hybrid PQ signing      |\n| `vrf`        | 3     | Swappable VRF trait; ECVRF-Ed25519 (default) + ECVRF-P256   |\n| `commitment` | 3     | SHA3-512 hiding/binding index→value commitments             |\n| `coniks`     | 3     | Per-namespace directory; presence + absence (index privacy) |\n| `keytrans`   | 3     | Experimental IETF KEYTRANS combined-tree directory (movable)|\n| `anchor`     | —     | Backend-agnostic checkpoint anchoring/attestation records   |\n| `policy`     | 0     | Signed, versioned namespace policy; declared == observed    |\n| `note`       | 2     | C2SP `signed-note` parse/verify (Ed25519 + hybrid PQ lines) |\n| `tile`       | 2     | C2SP `tlog-tiles` coordinates / serving geometry            |\n| `wasm`       | —     | Browser verification + monitor SDK (`wasm32`-only)          |\n| `error`      | —     | Crate-wide error type                                       |\n\n## Safety \u0026 supply chain\n\n- `#![forbid(unsafe_code)]` at this layer\n- RustCrypto-only dependencies; primitives delegated to `metamorphic-crypto`\n- Edition 2024, MSRV 1.85, dual-licensed `MIT OR Apache-2.0`\n- CI runs `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, tests, a `wasm32-unknown-unknown`\n  check, a `wasm-pack` SDK build, the cross-language byte-parity KAT\n  (`wasm-pack test --node`), `cargo audit`, and an MSRV-floor build; all action\n  refs are SHA-pinned. The tagged release pipeline adds CycloneDX SBOM, cosign\n  keyless signing, build-provenance attestation, and OIDC trusted publishing to\n  crates.io + npm\n- See [`SECURITY.md`](SECURITY.md) for the disclosure process\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))\n- MIT license ([LICENSE-MIT](LICENSE-MIT))\n\nat your option.\n\n[crypto]: https://github.com/moss-piglet/metamorphic-crypto\n[tlog-tiles]: https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoss-piglet%2Fmetamorphic-log","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoss-piglet%2Fmetamorphic-log","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoss-piglet%2Fmetamorphic-log/lists"}