|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +cactus is a Go ACME server that issues **Merkle Tree certificates** per |
| 6 | +[draft-ietf-plants-merkle-tree-certs-04], for **testing only** (not a production CA). |
| 7 | +Read [MTC.md](MTC.md) for how MTC works (why certs have no real signature, what the |
| 8 | +inclusion proof proves, how landmarks and mirrors fit) and [README.md](README.md) for |
| 9 | +operating details and the full config reference. |
| 10 | + |
| 11 | +## Toolchain (non-obvious, important) |
| 12 | + |
| 13 | +cactus is **ML-DSA-44 only** and uses Go's built-in `crypto/mldsa`, so it **requires |
| 14 | +Go 1.27+** (`go.mod` declares `go 1.27`). There are no build tags — an older toolchain |
| 15 | +simply won't compile. Until 1.27 ships, build and test with **`gotip`** (a 1.27-devel |
| 16 | +toolchain). The `Makefile` defaults `GO ?= gotip`; override with `make GO=go` once a |
| 17 | +1.27 release is installed. ECDSA support has been removed. |
| 18 | + |
| 19 | +## Commands |
| 20 | + |
| 21 | +```sh |
| 22 | +make build # builds bin/cactus, bin/cactus-cli, bin/cactus-keygen |
| 23 | +make test # gotip test ./... |
| 24 | +make test-race # gotip test -race ./... |
| 25 | +make vet # gotip vet ./... |
| 26 | +make integration # gotip test -race -count=1 -tags=integration ./integration/... |
| 27 | + |
| 28 | +# Single test / package: |
| 29 | +gotip test ./log/... |
| 30 | +gotip test -run TestParallelIssuance -tags=integration ./integration/... |
| 31 | + |
| 32 | +# Fuzz targets: |
| 33 | +gotip test -fuzz=FuzzParseMTCProof -fuzztime=30s ./cert/... |
| 34 | +gotip test -fuzz=FuzzParseSignSubtreeRequest -fuzztime=30s ./mirror/... |
| 35 | +``` |
| 36 | + |
| 37 | +Integration tests live in `./integration/` behind the `integration` build tag; some of |
| 38 | +them compile and run the actual `cactus` binary over HTTP. |
| 39 | + |
| 40 | +## Architecture |
| 41 | + |
| 42 | +One binary (`cmd/cactus`) brings up whichever subsystems the JSON config asks for — there |
| 43 | +is no fixed mode enum. The three composable concerns are **CA** (`acme` + `log` + |
| 44 | +`ca_cosigner`), **CA-side mirror quorum collection** (`ca_cosigner_quorum.mirrors[]`), and |
| 45 | +**mirror operating mode** (`mirror.enabled`). The config validator enforces hygiene rules |
| 46 | +— notably, CA and mirror cosigner keys in the same binary must differ. |
| 47 | + |
| 48 | +Cert issuance data flow (CSR → verifiable bytes on disk): |
| 49 | + |
| 50 | +1. **`acme/`** — RFC 8555 server with the draft §9 cert-download extensions. Validates |
| 51 | + the order, then calls the CA issuer. |
| 52 | +2. **`ca/`** — turns a CSR into a standalone X.509 cert whose `signatureAlgorithm` is |
| 53 | + `id-alg-mtcProof` and whose `signatureValue` is an `MTCProof` blob (inclusion proof + |
| 54 | + cosigner signatures). Builds the log entry and waits on the log. |
| 55 | +3. **`log/`** — the single-writer issuance log. `Log.Append` assigns an index |
| 56 | + immediately; `Log.Wait` blocks until the entry is in a published checkpoint **and** a |
| 57 | + covering signed subtree (§4.5) exists. A sequencer flushes pooled entries every |
| 58 | + `checkpoint_period_ms`, writes tiles, and signs a new checkpoint (c2sp signed-note). |
| 59 | + In CA-quorum mode, each flush fires parallel `sign-subtree` requests to configured |
| 60 | + mirrors and `Wait` blocks for `1 + min_signatures` cosignatures. |
| 61 | +4. **`tile/`** — read-path HTTP server with a tlog-tiles-compatible layout |
| 62 | + (`/<lognum>/checkpoint`, `/<lognum>/tile/…`, `/<lognum>/landmarks`). |
| 63 | + |
| 64 | +Supporting packages: **`cert/`** holds the wire types (`TBSCertificateLogEntry`, |
| 65 | +`MTCProof`, `MTCSubtreeSignatureInput`, `CertificatePropertyList`) and the multi-mirror |
| 66 | +request client. **`tlogx/`** extends `x/mod/sumdb/tlog` with the §4 subtree primitives |
| 67 | +(consistency, inclusion, covering subtrees). **`signer/`** is the ML-DSA cosigner |
| 68 | +abstraction. **`landmark/`** allocates §6.3 landmark sequences and serves `/landmarks`. |
| 69 | +**`mirror/`** is the upstream follower plus the `/sign-subtree` server. **`storage/`** is |
| 70 | +on-disk K/V using atomic-rename writes. |
| 71 | + |
| 72 | +**Single-writer assumption**: the log has no locks or shared-state coordination across |
| 73 | +processes — single-writer is enforced by documentation, not by code. See |
| 74 | +[docs/threat-model.md](docs/threat-model.md), [docs/disk-layout.md](docs/disk-layout.md). |
| 75 | + |
| 76 | +## IDs are derived, not independent |
| 77 | + |
| 78 | +The **CA ID** (`ca_cosigner.id`, e.g. `1.3.6.1.4.1.44363.47.1.99`) is load-bearing: draft |
| 79 | +§5.4 requires the CA cosigner ID to equal the CA ID, so this one value identifies the CA, |
| 80 | +seeds the issuer DN, and roots all derived IDs — the log ID is `<CA-ID>.0.<lognum>` and |
| 81 | +landmark trust-anchor IDs are `<CA-ID>.1.<lognum>.L`. There is no separate `base_id`. |
| 82 | + |
| 83 | +## cactus-cli (debugging / verification) |
| 84 | + |
| 85 | +``` |
| 86 | +cactus-cli tree show|verify <log-url> # checkpoint vs. recomputed root from tiles |
| 87 | +cactus-cli entry <log-url> <index> # decode one §5.2.1 entry |
| 88 | +cactus-cli cert verify <cert.pem> <log-url> # full §7.2 verification, prints OK |
| 89 | +cactus-cli prove <log-url> <index> # JSON inclusion proof |
| 90 | +``` |
| 91 | + |
| 92 | +[draft-ietf-plants-merkle-tree-certs-04]: https://www.ietf.org/archive/id/draft-ietf-plants-merkle-tree-certs-04.txt |
0 commit comments