Repository navigation
Bring the READMEs up to v0.13 - #409
Merged
Merged
Conversation
Continues #353, and not where I said it would be. `authkestra-ssf` was next on my own list and turns out to need nothing: every rejection in `verify_at` funnels through a `reject()` helper that logs each `SetError` with a stable code and a catch-all arm, and reasons explicitly about not leaking the deployment's issuer, audience or clock configuration into the RFC 8935 failure body. The raw `Err` returns I had counted live in private helpers whose results are wrapped with `.map_err(reject)` at the call site. Nothing to do there. The real remaining gap is `authkestra-resource`, which is where #353 tier 3 put it, and it is the hot path: `validate_jwt_generic` is where `validate_jwt` and `validate_jwt_with_resolver` both converge, so every resource server runs it per request. It had no span and six failure paths that all arrived by `?` and emitted nothing — a rejected token said only that it was rejected. It now carries a span named `validate_jwt` recording `kid` and `alg`, recorded as soon as the header is decoded so a later failure still has them, and one outcome line covering all six. #353 cites #335's `InvalidAlgorithm` — diagnosable only by reading `jsonwebtoken`'s source — as what this absence costs. `validate_jwt_with_resolver` gets its own line for `resolve_cache`: it runs before the span exists, so `UntrustedIssuer` and `MissingIssuer` would otherwise still be invisible, and those are exactly the misconfigurations worth naming. `authkestra-policy` gets two lines. Its decision path was already well covered — loader failures, policy evaluation errors, a refused Allow and the decision itself all say something — and the two `InvalidRequest` returns were the only paths leaving in silence. A typo'd context key is precisely what the schema check exists to turn into a rejection rather than a silently unsatisfied `when` clause, so whoever hit one should be told which key. Four tests, and I checked they fail without the change rather than assuming: two of them do. One asserts the token never reaches the logs, which #353 calls out as the line any new instrumentation must hold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu
Every install snippet pinned `version = "0.7"` while the crates are at 0.12 heading to 0.13 — five minors stale across four files, so anyone copying the quick start got a version from long before half the framework existed. That is the kind of error that costs a first impression rather than a debugging session. The feature list had also not kept up with what shipped. Phase 2's whole passwordless track was missing: magic links, email/SMS one-time codes and recovery codes, plus the re-proof gate that had to land before any of them. Recovery codes are described as what they are — a factor-agnostic look-up secret authenticator, NIST SP 800-63B §5.1.2 — rather than "backup codes", because the distinction is the point: they work for an account that never enrolled TOTP. Two published crates were absent from both crate tables: authkestra-store-sqlx and authkestra-store-testsuite. The second is worth listing precisely because it is the thing a third-party store implementor needs to know exists — it is how they prove their backend honours the traits' atomicity contracts rather than discovering it in production. Checked rather than assumed: authkestra-policy is still only re-exported through the facade and called by nothing, so both READMEs' "proof of concept, nothing calls it yet" wording stands unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merged
marcjazz
added a commit
that referenced
this pull request
Sep 23, 2026
* docs: mark Phase 2 shipped, correct RFC status lines, fix ssf/policy status Mechanical pass ahead of a book-wide sweep for v0.13.0. RFC-010/011/012 all said "proposed, not implemented" despite shipping in v0.13.0 (#400, #402, #404) — the same class of staleness as the README version pins fixed in #409, just in the status line instead of an install snippet. Corrected to "implemented", each naming its feature flag and module. Phase 2's three checkboxes in roadmap.md were unchecked even though the gate plus all three methods are merged; checked them and added a one-line note on the two pieces of shared store infrastructure (AtomicDecrement, the tightened CredentialStore contract) that came out of building them, since a future feature is the likeliest reader of that note. `authkestra-ssf` was listed as "(planned)" in the crate-status section and named again under Phase 4 as future work ("Implement SSF/CAEP for real-time revocation") — but it shipped as #309, before this session's work began. Relabelled "(shipped, receiver only)", and Phase 4 now names precisely what the crate's own module doc says is still missing: revoking or attenuating a live session on an event, transmitting SETs of its own, and CAEP-aware middleware. `authkestra-policy`'s "proof of concept, nothing calls it yet" status was checked against the source and left unchanged — it is still true. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * docs: bring the mdBook up to v0.13.0 Fanned out over the book's 11 chapters (a Workflow run, one agent per chapter plus a cross-chapter verify pass) after scouting which ones plausibly needed anything: five had no v0.13.0-relevant content and are untouched (ch01, ch04, ch05, ch06, ch07, ch10, ch11 — checked, not skipped) or made no change after checking; four did. ch08 (Getting Started Tutorial): the same `version = "0.7"` pin bug already fixed in the top-level READMEs (#409), four occurrences, fixed to 0.13 the same way. ch02 (Core Engine and Identity): its KvStore/CredentialStore persistence list was missing recovery-code credentials and the magic-link/OTP one-time secrets — verified each against its actual store bound in source before adding it. ch03 (Core Traits): the chapter that documents AuthMethod and the KvStore atomic-primitive family in depth, so it had the most real gaps. Added the three new AuthMethod impls with their feature flags and RFC references; added AtomicDecrement alongside AtomicConsume/AtomicInsert; and corrected two "planned traits" bullets that flatly denied authkestra-ssf and authkestra-policy exist, when both are real, published crates (receiver-only and unwired-PoC respectively) — the chapter predates both shipping. ch09 (Security and Threat Model): added re-proof gating as a threat this release closes — a hijacked-but-merely-logged-in session can no longer enrol the attacker's own factor — since this chapter already enumerates shipped security properties and re-proof gating is squarely one. The cross-chapter verify pass then caught what no single-chapter read could: this same chapter's CAEP section still said "no SSF/CAEP receiver... in the workspace", flatly contradicting the ssf correction just made in ch03 and docs/roadmap.md §2. Split that section into a shipped-ingestion bullet and a still-missing-transmitter/revocation-wiring bullet, matching the labelling pattern the chapter already uses elsewhere, and fixed the chapter's own intro sentence, which claimed all of sections 1-3 were pure roadmap targets and was now wrong twice over. One thing I corrected after the workflow returned, having written the methods myself: it described magic link and OTP as leaving has_enrolled "at their defaults". Both actually override it to explicitly return false — the same value, but a stated decision rather than an inherited one, which their own doc comments say in as many words. Reworded so the chapter reflects the deliberateness rather than implying an oversight. Two things surfaced and deliberately left alone, per the same don't-invent-scope instruction the chapter agents were given: - CredentialStore::delete_credential's tightened atomicity contract (recovery codes' single-use guarantee depends on it) has nowhere to attach — neither ch02 nor ch03 shows CredentialStore's actual definition, only mentions it in passing, so adding the contract detail would mean writing a new section rather than fixing a stale one. - ch02 has a pre-existing, unrelated inconsistency (its "Architectural Decisions" section describes DID/GNAP work its own callout two paragraphs above says doesn't exist) that predates this release and is out of scope for a v0.13.0 sweep. Paired with the mechanical pass in the previous commit (RFC status lines, roadmap.md Phase 2 checkboxes and crate-status corrections). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * fix(docs): restore two sentences my status-line edit had truncated Review on #410 caught it: the mechanical status-line rewrite in 5051f10 replaced each RFC's leading blockquote wholesale with new text, and my replacement text for RFC-011 and RFC-012 was itself truncated — each ended mid-sentence ("this document is" / "makes it different from") where the original continued. Restored both continuations verbatim from the pre-edit commits (59f3b35 for RFC-011, ed19e45 for RFC-012) rather than writing new prose, since the point was to correct the status line, not rephrase content that was already right. RFC-010's blockquote was checked too, on the same theory the bug might recur — it does not; that one's original text ended cleanly and my edit preserved it intact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
marcjazz
added a commit
that referenced
this pull request
Sep 26, 2026
* docs: mark Phase 2 shipped, correct RFC status lines, fix ssf/policy status Mechanical pass ahead of a book-wide sweep for v0.13.0. RFC-010/011/012 all said "proposed, not implemented" despite shipping in v0.13.0 (#400, #402, #404) — the same class of staleness as the README version pins fixed in #409, just in the status line instead of an install snippet. Corrected to "implemented", each naming its feature flag and module. Phase 2's three checkboxes in roadmap.md were unchecked even though the gate plus all three methods are merged; checked them and added a one-line note on the two pieces of shared store infrastructure (AtomicDecrement, the tightened CredentialStore contract) that came out of building them, since a future feature is the likeliest reader of that note. `authkestra-ssf` was listed as "(planned)" in the crate-status section and named again under Phase 4 as future work ("Implement SSF/CAEP for real-time revocation") — but it shipped as #309, before this session's work began. Relabelled "(shipped, receiver only)", and Phase 4 now names precisely what the crate's own module doc says is still missing: revoking or attenuating a live session on an event, transmitting SETs of its own, and CAEP-aware middleware. `authkestra-policy`'s "proof of concept, nothing calls it yet" status was checked against the source and left unchanged — it is still true. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * docs: bring the mdBook up to v0.13.0 Fanned out over the book's 11 chapters (a Workflow run, one agent per chapter plus a cross-chapter verify pass) after scouting which ones plausibly needed anything: five had no v0.13.0-relevant content and are untouched (ch01, ch04, ch05, ch06, ch07, ch10, ch11 — checked, not skipped) or made no change after checking; four did. ch08 (Getting Started Tutorial): the same `version = "0.7"` pin bug already fixed in the top-level READMEs (#409), four occurrences, fixed to 0.13 the same way. ch02 (Core Engine and Identity): its KvStore/CredentialStore persistence list was missing recovery-code credentials and the magic-link/OTP one-time secrets — verified each against its actual store bound in source before adding it. ch03 (Core Traits): the chapter that documents AuthMethod and the KvStore atomic-primitive family in depth, so it had the most real gaps. Added the three new AuthMethod impls with their feature flags and RFC references; added AtomicDecrement alongside AtomicConsume/AtomicInsert; and corrected two "planned traits" bullets that flatly denied authkestra-ssf and authkestra-policy exist, when both are real, published crates (receiver-only and unwired-PoC respectively) — the chapter predates both shipping. ch09 (Security and Threat Model): added re-proof gating as a threat this release closes — a hijacked-but-merely-logged-in session can no longer enrol the attacker's own factor — since this chapter already enumerates shipped security properties and re-proof gating is squarely one. The cross-chapter verify pass then caught what no single-chapter read could: this same chapter's CAEP section still said "no SSF/CAEP receiver... in the workspace", flatly contradicting the ssf correction just made in ch03 and docs/roadmap.md §2. Split that section into a shipped-ingestion bullet and a still-missing-transmitter/revocation-wiring bullet, matching the labelling pattern the chapter already uses elsewhere, and fixed the chapter's own intro sentence, which claimed all of sections 1-3 were pure roadmap targets and was now wrong twice over. One thing I corrected after the workflow returned, having written the methods myself: it described magic link and OTP as leaving has_enrolled "at their defaults". Both actually override it to explicitly return false — the same value, but a stated decision rather than an inherited one, which their own doc comments say in as many words. Reworded so the chapter reflects the deliberateness rather than implying an oversight. Two things surfaced and deliberately left alone, per the same don't-invent-scope instruction the chapter agents were given: - CredentialStore::delete_credential's tightened atomicity contract (recovery codes' single-use guarantee depends on it) has nowhere to attach — neither ch02 nor ch03 shows CredentialStore's actual definition, only mentions it in passing, so adding the contract detail would mean writing a new section rather than fixing a stale one. - ch02 has a pre-existing, unrelated inconsistency (its "Architectural Decisions" section describes DID/GNAP work its own callout two paragraphs above says doesn't exist) that predates this release and is out of scope for a v0.13.0 sweep. Paired with the mechanical pass in the previous commit (RFC status lines, roadmap.md Phase 2 checkboxes and crate-status corrections). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * feat(landing): add the three v0.13.0 passwordless methods The "Eight ways to prove who someone is" grid had not moved since it was written — three real AuthMethod implementations shipped in v0.13.0 (magic link, email/SMS one-time codes, recovery codes) and none of them were on the page a visitor actually lands on. Inserted after TOTP, before the OAuth2/OIDC group, matching the order docs.authkestra.com's sidebar now uses (fixed in the paired docs PR). Heading updated to "Eleven ways". Recovery codes' card body says what it is precisely — a look-up secret authenticator, not tied to TOTP — since that distinction is the one thing worth getting right about that method in one sentence. Three new icons needed adding to the hand-rolled Lucide registry (`link`, `message-square-code`, `list-checks`), fetched verbatim from lucide-static 1.47.0 rather than approximated from memory, matching this file's own stated sourcing practice ("extracted verbatim so no runtime icon package ships"). Also fixed: Quickstart.astro's install snippet was still pinned to `version = "0.9"`, the same class of staleness as the top-level READMEs (#409) and the mdBook (#410) — five minors behind. Verified with a real `astro build`: the output HTML has all eleven `<h3>` cards in the intended order, and the heading text updated correctly. A stale, gitignored `dist/` directory from an unrelated prior build briefly looked like it meant something was wrong (it listed routes like /providers/totp/) — it didn't; those come from astro.config.mjs's legacy MOVED_TO_DOCS redirect list for URLs that existed before docs.authkestra.com existed, which correctly needs no entry for pages that never lived on this domain. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * fix(landing): drop the nonexistent "session" feature from Quickstart Review on #411 caught it: the facade's `session` feature doesn't exist — it was folded into `authkestra-engine` unconditionally when sessions became part of the engine (#375), and crates/authkestra/Cargo.toml says so explicitly ("There is deliberately no `session` ... feature here"). Anyone copying this Cargo.toml line got `error: none of the selected packages contains these features: session` before ever reaching the Rust. Verified against source rather than taking the suggestion on faith: the sample uses `MemoryStore` and `Authkestra::builder()`, both of which need the facade's `store` re-export and `core` alias — gated on `memory` (which itself pulls in `engine`, so nothing else needs adding). Traced the exact cfg gates in crates/authkestra/src/lib.rs to confirm `["axum", "memory", "github"]` is both sufficient and minimal for this sample, not just plausible. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu * feat(landing): rebuild the comparison table around real strengths Prompted by "have you updated the feature comparison board? should that table even exist honestly?" The honest answer was no on both counts: I hadn't looked at it since checking one unrelated version pin, and once I actually checked the three names in it, one was a real problem — better-auth.rs is a pre-1.0 alpha (1.0.0-alpha.2), and its own docs say APIs may change without notice between alpha releases. Comparing a stable v0.13.0 release against an undisclosed alpha with five confident "yes" checkmarks was not an honest comparison, independent of anything this release changed. Replaced it with oauth2-passkey: real, shipped, not alpha-labelled, and honestly narrower in scope (OAuth2 relying-party + WebAuthn only, confirmed via its own README — no OP role, no TOTP, no resource server). A fair comparison against a real project beats a favourable one against an unstable strawman. Rauthy and Kanidm both re-verified against their own docs rather than carried over from whenever this was last written: Kanidm's native OAuth2/OIDC provider and WebAuthn support confirmed on kanidm.github.io, Rauthy's passkey-first design and brute-force/IP rate-limiting confirmed on its GitHub README. Both check out. Rewrote rows to show what actually differs, using only what v0.13.0 and existing crates can back up: - OAuth2/OIDC role: Authkestra plays all three (provider, client, resource server) from one crate. Neither alternative does — this is the genuine structural difference, not a marketing framing. - Resource server: named DPoP (RFC 9449) and mTLS (RFC 8705) binding specifically, both real (crates/authkestra-resource), rather than a bare "yes". - Passwordless methods: names the actual count (5) instead of a bare "Multi-factor auth: yes", which is what the whole session's work had made most stale. - New row, "Fresh proof to enrol a factor": RFC-009's re-proof gate is a genuine, checkable differentiator I could find no equivalent documented primitive for in either alternative. Marked "Not documented" for both rather than a confident "No" — the honest claim is an absence of evidence, not evidence of absence. - Bot protection: named Authkestra's actual three providers (Turnstile/hCaptcha/reCAPTCHA, crates/authkestra-engine/src/captcha.rs) against what Rauthy's docs actually describe (rate-limiting and IP-blocking — a different mechanism, not "not offered" and not a false equivalence to CAPTCHA either). One thing caught in my own second pass, after finishing the rewrite: the oauth2-passkey cell for "Configuration safety" first read "Standard builder", carried over from the old better-auth.rs row without verifying oauth2-passkey uses a builder at all. Fixed to "Not documented" — the same discipline the rest of the rewrite was supposed to hold everywhere, caught here on a re-read rather than in review. Renamed the internal `betterAuth` variable to `embeddedAlt` throughout so the code doesn't keep naming a competitor that's no longer in it. Verified with a real `astro build`: all ten row labels render, the table caption and both `.detail` write-ups describe the new middle column, and no reference to "better-auth" remains anywhere in the built output. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Docs only. Pairs with the v0.13.0 release in #399.
Every install snippet was five minors stale
Eight occurrences across four files (
README.md, actix, axum, macros). Anyone copying the quick start got a version from long before half the framework existed — the kind of error that costs a first impression rather than a debugging session.The feature list had not kept up with what shipped
Phase 2's whole passwordless track was missing. Added:
Recovery codes are described as a factor-agnostic look-up secret authenticator (NIST SP 800-63B §5.1.2) rather than "backup codes", because the distinction is the whole point: they work for an account that never enrolled TOTP.
Two published crates were missing from both tables
authkestra-store-sqlxandauthkestra-store-testsuite, absent from the root README anddocs/README.mdalike. The second is worth listing precisely because it's what a third-party store implementor needs to know exists — it's how they prove their backend honours the traits' atomicity contracts rather than discovering it in production.Checked, not assumed
authkestra-policyis still only re-exported through the facade and called by nothing, so both READMEs' "proof of concept — nothing calls it yet" wording stands unchanged. I verified that rather than refreshing it along with everything else.Ordering note
This advertises
0.13before #399 merges. If the release slips, the READMEs name a version that isn't on crates.io yet — so this wants to land with or just after #399, not well before it.Also done, outside this PR
The GitHub About section and topics, which can't live in a commit:
Framework-agnostic authentication orchestrator for Rust — passkeys, magic links, one-time codes, OAuth2/OIDC, and a built-in OpenID Providerrust,passwordless,passkeys,webauthn,mfa,totp,magic-link,openid-connect,jwt,dpop,identity,securityalongside the seven that were already there.🤖 Generated with Claude Code
https://claude.ai/code/session_01XaoYHiT9Vb85XhBdVCg9Xu