diff --git a/DESIGN.md b/DESIGN.md index 7bed094..040948d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -152,23 +152,49 @@ it needs a human identity model of its own, which is a separate piece of work. ## Audit trail -Append-only, and the raw material for signed work attestations: every +Hash-chained, and the raw material for signed work attestations: every enrolment, challenge, verification, code issuance, redemption, refusal and -revocation, with actor, subject and key fingerprint. Code hashes are stored, -never codes; enrolment secrets are hashed and deleted on use, so a database -leak yields neither. +revocation, with actor, subject and key fingerprint. + +Each row folds in the previous row's digest, so editing or deleting an event +breaks every digest after it, and `verify_chain` reports the first bad id. That +makes tampering *detectable*. It does not make it *impossible*: an attacker with +write access to the database can recompute the chain, because the digests are +unkeyed. To commit to a log you need the head digest anchored outside the +database's reach, or signed. Worth doing when the attestations start mattering, +not before. + +Secrets are not in the log and not in the database in the clear. Codes and +session tokens are stored as digests, enrolment secrets are hashed and spent +once. So a stolen database yields no live credential — which is only true +because the review looked for it. ## Status -Working reference implementation with 54 passing tests. **Not production ready, -and not reviewed.** Specifically missing: independent cryptographic review, -encryption at rest, rate limiting on challenge issuance, agent revocation -expiry, and transport assumptions. The tests show the logic is correct; they do -not make it safe. +80 passing tests, and a hardening pass that closed real defects. The worst were +concurrency: one-time codes were redeemable repeatedly (753 sessions from 200 +codes under load), and a bootstrap secret could enrol more than one key. Those +are now atomic compare-and-set operations, each covered by a test that races +twelve threads. Session tokens are stored hashed, keys can be encrypted, +challenge issuance is rate limited, rekey revokes old sessions, and the log is +hash-chained. + +**Still not production ready**, and the reason is not a to-do list: + +- **No independent review.** Everything above is self-review. Self-review finds + the bugs you already suspected. This needs someone else. +- **The chain is unkeyed** — detectable, not tamper-proof (see *Audit trail*). +- **The service is unwritten.** TLS, CSRF, transport, secret storage, routes. +- **Revocation has no expiry semantics**, and there is no key recovery. +- **Humans remain unattributable** to themselves, which is accepted and is a + cost, not an oversight. + +The tests show the logic is correct. They do not make it safe. ## Next -1. Independent security review before this guards anything real. +1. Independent security review before this guards anything real. The hardening + pass made it reviewable; it did not substitute for one. 2. Secret broker on top — scoped, short-lived leases bound to a key fingerprint. Its issuance and redemption logs *are* the work-attestation substrate, so it is the natural second piece.