{"id":51121821,"url":"https://github.com/tegmentum/secure-log","last_synced_at":"2026-06-25T03:30:32.079Z","repository":{"id":364077949,"uuid":"1246097011","full_name":"tegmentum/secure-log","owner":"tegmentum","description":"Tamper-evident audit log for Rust: hash-chained entries, Merkle-sealed segments, externally-signed checkpoints, witness anti-equivocation, and AEAD payload sealing — native crates plus WASI Preview 2 components.","archived":false,"fork":false,"pushed_at":"2026-06-11T13:56:58.000Z","size":172,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T15:27:27.239Z","etag":null,"topics":["audit-log","audit-logging","component-model","cryptography","hash-chain","merkle-tree","rust","security","tamper-evident","tpm","transparency-log","wasi","wasm","webassembly"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tegmentum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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}},"created_at":"2026-05-21T21:46:20.000Z","updated_at":"2026-06-11T13:57:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tegmentum/secure-log","commit_stats":null,"previous_names":["tegmentum/secure-log"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/tegmentum/secure-log","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fsecure-log","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fsecure-log/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fsecure-log/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fsecure-log/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tegmentum","download_url":"https://codeload.github.com/tegmentum/secure-log/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fsecure-log/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34758773,"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-06-25T02:00:05.521Z","response_time":101,"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":["audit-log","audit-logging","component-model","cryptography","hash-chain","merkle-tree","rust","security","tamper-evident","tpm","transparency-log","wasi","wasm","webassembly"],"created_at":"2026-06-25T03:30:31.503Z","updated_at":"2026-06-25T03:30:32.070Z","avatar_url":"https://github.com/tegmentum.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# secure-log\n\nTamper-evident audit log for Rust. Hash-chained entries, Merkle-sealed\nsegments, externally-signed checkpoints, witness anti-equivocation,\nand optional AEAD payload sealing.\n\n## Crates\n\nNative (build with `cargo build` / `cargo test`):\n\n- **`secure-log`**: core types, `SecureLog` trait, `NativeSecureLog`\n  implementation, canonical CBOR encoder, hash chain, Merkle tree,\n  inclusion proofs, witness submission format, payload AEAD, the\n  `SecureLogStore` persistence trait, and the `CheckpointSigner`\n  abstraction.\n- **`secure-log-sqlite`**: `SqliteSecureLogStore` — SQLite-backed\n  storage (rusqlite) that implements `SecureLogStore`. Manages its\n  own migrations.\n- **`secure-log-rpc`**: the JSON-RPC wire contract (row mirror types +\n  method-name constants) shared by the remote store provider and the\n  remote endpoint, so the two ends cannot drift. Pure serde; compiles to\n  both wasm and native.\n\nWASI Preview 2 components (build with\n`cargo component build --target wasm32-wasip2`):\n\n- **`secure-log-component`**: the core, packaged as a component.\n  Imports `secure-log:log/store` + `keys:keystore/signer`, exports\n  `secure-log:log/{encoder,log,checkpoint}`. Checkpoint signing happens\n  in-graph; verification dispatches on the key's algorithm (ed25519 /\n  ecdsa-p256 / rsa-pss-sha256).\n- **`secure-log-store-sqlite`**: a `secure-log:store` provider backed\n  by the [`sqlite:wasm`](../sqlite-wasm) component.\n- **`secure-log-store-file`**: a `secure-log:store` provider backed by\n  an append-only JSON-lines file on the WASI filesystem.\n- **`secure-log-store-remote`**: a `secure-log:store` provider that\n  forwards each operation as JSON-RPC over a pluggable `transport`\n  interface (network-agnostic).\n- **`secure-log-transport-http`**: the default `transport` provider,\n  backed by `wasi:http`. Each `rpc` call becomes an HTTP POST to the URL\n  in `SECURE_LOG_RPC_URL`. Swap it for any other `transport` provider\n  (host function, message queue, ...) without touching the store.\n- **`secure-log-keystore-software`**: a `keys:keystore/signer` provider,\n  pure software (ed25519 / ecdsa-p256 / rsa-pss-sha256, chosen via\n  `SECURE_LOG_KEYSTORE_ALG`). The default in-graph signing backend; the\n  composed softhsm keystore (`keys:keystore` over PKCS#11) is the\n  production alternative.\n- **`secure-log-rpc-server`**: the remote store's endpoint as a\n  `wasi:http` guest — imports `secure-log:log/store`, exports\n  `wasi:http/incoming-handler`. Compose with a store provider and run\n  with `wasmtime serve`. Handlers are request-scoped, so it opens a\n  file-backed store per request (`SECURE_LOG_STORE_CONFIG`).\n\n## Component architecture (pluggable persistence)\n\n```text\n                    secure-log-component (core)\n                    exports secure-log:log/{encoder,log,checkpoint}\n                    imports secure-log:log/store + keys:keystore/signer\n                              │                         │\n        store ┌──────────────┼───────────────┐        │ keys:keystore/signer\n              ▼              ▼                ▼         ▼\n       store-sqlite     store-file      store-remote   ├── keystore-software\n       imports          (wasi:fs)       imports        │   (ed25519/ecdsa/rsa)\n       sqlite:wasm                      transport      └── keystore (softhsm)\n              │                            │               keystore-pkcs11\n              ▼                            ▼               + pkcs11-provider\n       sqlite-wasm                  transport-http         + softhsm2.component\n       (build/sqlite.wasm)          (wasi:http)\n                                         │ POST $SECURE_LOG_RPC_URL\n                                         ▼\n                                  secure-log-rpc-server (wasi:http guest)\n                                  + store-sqlite  —  `wasmtime serve`\n```\n\nBoth persistence and signing are pluggable at two levels: the Rust\ntraits (`SecureLogStore`, `CheckpointSigner`) for native use, and the\n`secure-log:log/store` + `keys:keystore/signer` WIT interfaces for\ncomponents, chosen at composition time via `wac plug`.\n\n### Build \u0026 compose\n\n```bash\n# builds the component crates and composes each stack (storage + keystore)\n./scripts/build-components.sh\n# -\u003e dist/secure-log-sqlite.wasm        core + store-sqlite + sqlite + software keystore\n# -\u003e dist/secure-log-file.wasm          core + store-file + software keystore\n# -\u003e dist/secure-log-remote.wasm        core + store-remote + transport-http + software keystore\n# -\u003e dist/secure-log-sqlite-pkcs11.wasm core + store-sqlite + sqlite + softhsm keystore\n# -\u003e dist/secure-log-rpc-server.wasm    rpc-server + store-sqlite + sqlite (remote endpoint)\n```\n\nEverything is a wasm guest — there is no host-side secure-log. Since the\ncore signs in-graph, every stack bundles a keystore. The\nsqlite/file/remote client stacks bundle the software keystore and import\nonly WASI (remote additionally imports `wasi:http`; point\n`SECURE_LOG_RPC_URL` at a running `secure-log-rpc-server.wasm`). The\npkcs11 stack bundles the composed softhsm keystore (from\n[`softhsm-wasm`](../softhsm-wasm), via `KEYSTORE_SOFTHSM`) and imports\n`pkcs11:util` + needs a SoftHSM config; it is skipped if that artifact is\nabsent.\n\n### Configuration\n\nThe backing store is opened explicitly: call `log.open(config)` (which\nforwards to `store.init(config)`) exactly once before any other\noperation. There is no implicit default — an empty config is an error.\n\n| backend | `config` value |\n| ------- | -------------- |\n| sqlite  | SQLite database path, or `\":memory:\"` (tests only) |\n| file    | path to the append-only JSON-lines log file |\n| remote  | a handshake value (the endpoint URL comes from `SECURE_LOG_RPC_URL`; the server owns its storage via `SECURE_LOG_STORE_CONFIG`) |\n\n\u003e Note: file-backed sqlite requires the `sqlite:wasm` component to\n\u003e select its WASI VFS for file opens (it defaults to an in-memory\n\u003e VFS). This is fixed upstream as of sqlite-wasm commit `19c6ac7`;\n\u003e with that build, a path under a preopened directory produces a real\n\u003e on-disk `SQLite format 3` database that survives reopening. Both the\n\u003e `sqlite` (file) and `file` backends now persist across instances.\n\n### Remote transport protocol\n\nThe remote backend calls `transport.rpc(method, params-json)` once per\nstore operation. The contract (method names + row shapes) is the\n`secure-log-rpc` crate, shared by both ends:\n\n- `method` — the store function name (e.g. `secure-log-insert`).\n- `params-json` — a JSON array of the call's arguments, in order.\n- the returned string — a JSON encoding of the return value, or the\n  call returns `err(message)`.\n\nThe default `transport-http` provider sends each call as an HTTP POST\nto `SECURE_LOG_RPC_URL` with body `{\"method\": ..., \"params\": ...}`; a\n2xx response body is the result, any other status is the error. The\n`transport` interface is itself swappable (host function, message\nqueue, ...) — only this one provider depends on `wasi:http`.\n\n`secure-log-rpc-server.wasm` is the endpoint — itself a `wasi:http`\nguest, run with `wasmtime serve`:\n\n```bash\n# -S cli enables filesystem + environment for the wasm guest\nwasmtime serve -S cli --addr 127.0.0.1:8787 \\\n  --dir ./state::/data --env SECURE_LOG_STORE_CONFIG=/data/secure-log.db \\\n  dist/secure-log-rpc-server.wasm\n```\n\n`wasi:http` handlers are request-scoped, so the server holds no state\nbetween requests: it opens the file-backed store named by\n`SECURE_LOG_STORE_CONFIG` on each request, runs the op, and the\nfilesystem persists it. The client's `init` is a handshake; the server\nowns its storage location.\n\n### End-to-end verification\n\n`verify/` is a standalone wasmtime host harness (excluded from the\ncomponent workspace) that instantiates a composed component and\nexercises append / read / verify-chain / segment / inclusion-proof /\ncheckpoint signing. For the remote stack it launches the\n`secure-log-rpc-server.wasm` endpoint with `wasmtime serve` (a\nsubprocess) and provides `wasi:http`, so no external setup is needed:\n\n```bash\ncd verify\n# args: \u003ccomposed.wasm\u003e [store-config]; the harness also exercises\n# checkpoint sign + verify-checkpoint-chain in-graph.\ncargo run --release -- ../dist/secure-log-sqlite.wasm \":memory:\"\ncargo run --release -- ../dist/secure-log-file.wasm   \"audit.jsonl\"\ncargo run --release -- ../dist/secure-log-remote.wasm \":memory:\"\n# choose the software keystore algorithm:\nSECURE_LOG_KEYSTORE_ALG=ecdsa-p256 cargo run --release -- ../dist/secure-log-sqlite.wasm \":memory:\"\n# pkcs11/softhsm signing entirely in-graph (needs the softhsm config):\ncargo run --release -- ../dist/secure-log-sqlite-pkcs11.wasm \":memory:\"\n```\n\n### Checkpoint signing (`keys:keystore`)\n\nPhase 3 signs each closed segment's checkpoint hash through a keystore\nthat exposes `keys:keystore/signer`. Signing is **in-graph**: the core\ncomponent imports `keys:keystore/signer` and exports a `checkpoint`\ninterface, and a keystore provider is composed into the stack via\n`wac plug`. The signing key never leaves the keystore (for softhsm, it\nnever leaves the wasm sandbox); verification needs only the public key,\nso it dispatches on the key's algorithm — **ed25519**, **ecdsa-p256**,\nor **rsa-pss-sha256**.\n\n```text\ncheckpoint.sign-segment(identity, segment-id)   (secure-log-component)\n  -\u003e keys:keystore/signer\n       ├─ keystore-software (ed25519 / ecdsa-p256 / rsa-pss-sha256), or\n       └─ softhsm: keystore-pkcs11 -\u003e pkcs11:* -\u003e softhsm:pkcs11\n```\n\nThe softhsm keystore (`keystore-softhsm.wasm`) and its SoftHSM config\ncome from the [`softhsm-wasm`](../softhsm-wasm) project; the pkcs11\nstack composes it guest-side. The verify harness above exercises\n`sign-segment` + `verify-checkpoint-chain` for every stack.\n\nNative consumers that embed `NativeSecureLog` as a library can still\nplug their own `CheckpointSigner` (the trait remains the seam) — for a\nbackend that genuinely cannot be a wasm guest, e.g. a hardware HSM via\nnative PKCS#11, a TPM, or a cloud KMS. A wasm keystore like softhsm\nbelongs in the graph instead.\n\n## Architecture\n\n```text\ncanonical event → per-entry hash chain → Merkle-sealed segments →\n  signed checkpoint chain → external witnessing → anti-rollback\n```\n\n- **Phase 1** — entries + per-stream hash chain.\n- **Phase 2** — Merkle-sealed segments + inclusion proofs.\n- **Phase 3** — checkpoint signatures, signed in-graph via a composed\n  `keys:keystore/signer` (software or softhsm/PKCS#11), verified by\n  algorithm (ed25519 / ecdsa-p256 / rsa-pss-sha256).\n- **Phase 4** — anti-rollback head file + witness submission.\n- **Phase 5** — optional payload envelope encryption with\n  per-segment AEAD keys.\n\nThe WIT contract in `wit/log.wit` is the authoritative interface —\nimplementations in other languages must conform to it.\n\n## Quick start\n\nsecure-log runs as a composed wasm component — storage and the signing\nkeystore are plugged in at composition time, and the core logic (the\n`secure-log` crate, compiled into `secure-log-component`) never runs\nhost-side.\n\n```bash\n# 1. Build + compose the stacks (storage + keystore).\n./scripts/build-components.sh\n\n# 2. Run one end-to-end under wasmtime: append, verify the chain, close\n#    a segment, build an inclusion proof, and sign + verify a checkpoint\n#    — all inside the wasm graph.\ncd verify\ncargo run --release -- ../dist/secure-log-sqlite.wasm \":memory:\"\n```\n\nA component implementing the `secure-log:log` world (see `wit/`) is a\ndrop-in for the whole subsystem; embedders host it with any wasi:p2\nruntime (wasmtime, `wasmtime serve`, jco, …).\n\n## License\n\nApache-2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fsecure-log","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftegmentum%2Fsecure-log","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fsecure-log/lists"}