docs: changelog backfill + TLS/ACME guide (#1860) + a11y/skills freshness#1967
Conversation
…s freshness - CHANGELOG.md: backfill 21 bullets under the existing [Unreleased] section for work merged since docs batch 3 (#1877) — autumn deploy CLI (#1607), #[lifecycle] state machines (#1675/#1911), SQLite foundation (#1614), a11y primitives + scaffold track (#1706), content negotiation (#1881), nested has_many forms (#1915), security manifest v2 (#1879), owner-scoped codegen (#1882), and assorted CSRF/tenant/multipart/blob/schema fixes. No version bump; no new dated section. - docs/guide/tls.md (NEW): TLS & HTTPS guide — direct in-process TLS, automatic ACME HTTP-01, reverse-proxy termination, certbot + mkcert walkthroughs. Closes #1860. - docs/guide/deployment.md: replace the #1860 placeholder with a live cross-link to the new TLS guide (cross-ref only). - skills/autumn-web/references/api-reference.md: merge two duplicate a11y sections into one canonical section and add a transition_controls entry (#1917). - skills/*: freshness edits for lifecycle, autumn deploy, a11y primitives, SQLite backend, and nested forms.
There was a problem hiding this comment.
Code Review
This pull request updates the changelog, documentation, and skill guides to reflect several new features, including the autumn deploy SSH deployment flow, #[lifecycle] typed state machines, SQLite backend support, and new typed accessible form primitives. It also adds a comprehensive TLS & HTTPS guide. The review feedback suggests minor improvements to the documentation, specifically replacing an HTML-escaped ampersand with a literal one in a markdown link and rephrasing a grammatically awkward sentence regarding the ACME renewal loop.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| > **HTTPS/TLS.** kamal-proxy fronts your app on the public port and terminates | ||
| > TLS, so the app itself serves plain HTTP behind the proxy. TLS termination and | ||
| > ACME certificate issuance — including serving HTTPS directly from the app | ||
| > without a proxy — are covered in the [TLS & HTTPS guide](./tls.md). |
There was a problem hiding this comment.
In markdown, ampersands (&) do not need to be HTML-escaped as & within link text. Using the literal & is standard, more readable in raw form, and avoids potential rendering issues in some markdown parsers.
| > without a proxy — are covered in the [TLS & HTTPS guide](./tls.md). | |
| without a proxy — are covered in the [TLS & HTTPS guide](./tls.md). |
| - **Provisioning** is over HTTP-01: the `:80` listener serves the challenge | ||
| token, the CA validates it, and the issued certificate is cached under | ||
| `cache_dir` (default `config/acme`) and swapped into the live resolver. | ||
| - **Renewal** runs on a coordinator-**leader-elected** loop that wakes hourly and |
There was a problem hiding this comment.
The phrase 'coordinator-leader-elected loop' is grammatically awkward. Rephrasing it to 'leader-elected coordinator loop' is much more natural and improves readability.
| - **Renewal** runs on a coordinator-**leader-elected** loop that wakes hourly and | |
| - **Renewal** runs on a leader-elected coordinator loop that wakes hourly and |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca3294a81b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| path already works this way: **kamal-proxy fronts your app on the public port and | ||
| terminates TLS**, while the app binds a private loopback port and serves HTTP. |
There was a problem hiding this comment.
Don't document deploy as terminating TLS
For the managed autumn deploy path, this tells operators they will get HTTPS without enabling Autumn TLS/ACME, but the implementation I checked in autumn-cli/src/deploy/proxy.rs only renders kamal-proxy run --http-port ... and kamal-proxy deploy ... --target ... --health-check-path ..., with no --host or --tls. Upstream kamal-proxy docs say Automatic TLS requires adding --tls and specifying hosts, so following this guide currently exposes only plain HTTP on the public port while users believe the proxy owns a certificate.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in c282021. Verified against autumn-cli/src/deploy/proxy.rs: the deploy path renders kamal-proxy run --http-port … and kamal-proxy deploy … --target … --health-check-path … with no --tls/--host, so the public port is plain HTTP. Corrected both docs/guide/tls.md and docs/guide/deployment.md to state the autumn deploy path serves HTTP and that HTTPS requires either an external TLS-terminating proxy/LB or in-process [server.tls]/ACME. This also surfaces a genuine product gap (deploy provisions no TLS of its own) — flagged for a separate follow-up issue; no product code changed in this docs PR.
Generated by Claude Code
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c282021c0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - **generate:** `autumn generate scaffold --searchable title,body` makes the named text fields full-text searchable — it adds `#[searchable]` attributes to the model, `searchable` to the `#[repository]`, a migration adding a `search_vector tsvector GENERATED ALWAYS AS (…) STORED` column plus a GIN index, and a search box on the index wired to `GET /<plural>/search` (an empty `?q` falls back to the plain paginated list). Omitting `--searchable` leaves scaffold output byte-identical; the `--live`/`--live-validation` index variants gate the search box off; naming a non-text/unknown field, a uuid-PK model, or an owner-scoped model fails generation before any files are written (issue #1319). | ||
| - **generate:** `autumn generate scaffold post avatar:Attachment` now produces working no-JS file uploads end-to-end — the create/update handlers take an `autumn_web::extract::Multipart` extractor, stream each uploaded file to the `BlobStore` via `field.save_to_blob_store(&*store, key)`, and bind the returned `Blob` from a plain `multipart/form-data` submit. `autumn destroy` also no longer strips the `multipart`/`storage` features when a hand-written route still uses them (part of #1236). | ||
| - **cli:** `autumn new <name> --api` scaffolds a JSON-first project — handlers return `Json<…>`, `autumn-web` is pinned `default-features = false` to a lean API feature set (`db`, `cache-moka`, `http-client`, `reporting`, `flash`) that drops the maud/htmx/tailwind view stack, and no `static/` tree, `input.css`, `tailwind.config.js`, vendored assets, or Tailwind CI/README notes are generated (the first `cargo run` serves JSON). `--api` cannot be combined with `--daemon` or `--bundled-pg` but composes with `--with-i18n` and `--with-seed`. | ||
| - **cli:** `autumn deploy` — first-class push-button deployment of an Autumn app to your own Linux host over SSH (issue #1607). `autumn deploy check` reads a `[deploy]` config block, runs a connectivity/secrets/Docker preflight, and prints a dry-run plan without touching the server (#1884); `autumn deploy up` executes a real first deploy over an injectable SSH executor — building/pushing the image, uploading the env, and starting the app (#1912); subsequent deploys perform a **zero-downtime cutover** via kamal-proxy, running pending migrations against the live database *before* flipping the proxy to the new container so a request is never served by an un-migrated build (#1928); and `autumn deploy rollback` — plus automatic rollback when a release fails to come up healthy — restores the previous container and secrets in one step (#1937). Deploy runs in the **production** profile by default, overridable through a `[deploy]` profile knob (#1956), and a push-button walkthrough is documented in the deploy guide (#1950). |
There was a problem hiding this comment.
Correct deploy release notes to the binary workflow
In the deploy implementation I checked (run_up/first_deploy_ops), the preflight checks SSH/signing/database/migrations and then uploads target/release/<app> into a systemd slot; there is no Docker check, image build/push, or container cutover. For users skimming the release notes, this points them at the wrong failure mode and setup path (registry/image/new container) instead of autumn build --embed plus systemd/kamal-proxy.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in c10fca8. Verified against autumn-cli/src/deploy/ (run_up/first_deploy_ops): the deploy uploads target/release/ into a systemd slot with kamal-proxy cutover — no Docker/image/registry step. Reworded the changelog bullet to the actual binary + systemd + kamal-proxy workflow.
Generated by Claude Code
| - **Renewal** runs on a **leader-elected** coordinator loop that wakes hourly and | ||
| renews any certificate within `renew_before_days` of expiry — so in a | ||
| clustered deployment exactly one instance renews, and the refreshed certificate | ||
| hot-swaps in with no restart. |
There was a problem hiding this comment.
Avoid promising ACME renewal works in clusters
In the autumn/src/app.rs ACME startup path I checked, the HTTP-01 token map and certificate cache are local to the process/disk and the code warns that load-balanced validation can hit a replica without the token; leader election only limits who orders, it does not make a multi-replica deployment work. In a cluster behind a shared entry point, this sentence tells operators renewal is safe even though issuance can 404 or non-leaders won't adopt the cert.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in c10fca8. Verified against autumn/src/app.rs: the HTTP-01 token map and cert cache are process/disk-local and the code warns load-balanced validation can miss the token; leader election only serializes ordering. Added a caveat that in-process ACME is single-process/single-host and that multi-replica deployments should terminate TLS at a shared proxy/LB instead.
Generated by Claude Code
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c10fca830d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| [features] | ||
| tls = ["autumn-web/tls"] | ||
| ``` |
There was a problem hiding this comment.
Document how to activate the TLS feature
For users following this guide with a generated app, this snippet only declares a forwarding package feature; Cargo will not enable it for a normal cargo run or autumn build --embed unless the command passes --features tls or the feature is added to defaults/dependency features. With [server.tls] configured, the runtime guard in AppBuilder::run exits when the binary was built without tls, so the guide as written leads to a boot failure; the ACME snippet later has the same issue.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d370458. Added source-verified activation steps after the [server.tls] and ACME snippets showing how to build/run with the tls/acme feature, and noted the AppBuilder::run fail-fast guard when the feature isn't compiled in.
Generated by Claude Code
| To get HTTPS, either front the public port with a **TLS-terminating load | ||
| balancer or reverse proxy** (including kamal-proxy with TLS configured yourself), | ||
| or serve TLS **in-process** from the app via | ||
| [`[server.tls]`](#direct-in-process-tls-servertls) or | ||
| [ACME](#automatic-acme-certificates-servertlsacme). When TLS is terminated in |
There was a problem hiding this comment.
Don't offer in-process TLS as an autumn deploy fix
For the autumn deploy path described here, serving TLS inside the app cannot be the HTTPS fix: the slot unit forces the app onto a private loopback port, the deploy readiness gate probes http://127.0.0.1:{port}/ready, and KamalProxyController routes kamal-proxy to a plain 127.0.0.1:{port} target. If users enable [server.tls] or ACME on that deployed app, deploy/proxy health checks speak HTTP to a TLS listener and fail; this guidance should stay limited to external/proxy TLS unless deploy gains HTTPS upstream support.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d370458. Verified in deploy.rs/proxy.rs: the slot binds the app to a loopback HTTP port, the readiness gate probes http://127.0.0.1:{port}/ready, and kamal-proxy targets plain 127.0.0.1:{port}. Removed in-process [server.tls]/ACME as a deploy-path HTTPS option in both tls.md and deployment.md — deploy-path HTTPS is now scoped to external/proxy termination; in-process TLS stays documented for self-run apps.
Generated by Claude Code
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d370458878
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| |---|---|---| | ||
| | `domains` | required | One or more non-wildcard domains to issue for. | | ||
| | `contact_email` | required | Contact address registered with the ACME account. | | ||
| | `directory` | Let's Encrypt **staging** | ACME directory: `production`, or a custom directory URL. | |
There was a problem hiding this comment.
Document the actual custom ACME directory syntax
For users pointing ACME at a private CA or Pebble, this wording implies directory = "https://…/dir" is accepted, but AcmeDirectory is an externally tagged serde enum: only the unit variants parse as strings, while the custom case is tested as directory = { custom = { url = "https://pebble.test/dir" } }. A bare URL here makes AutumnConfig fail to load before the app can start, so the guide should show or name the inline-table syntax for custom directories.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in dce18e5. Verified against the externally-tagged AcmeDirectory enum: the built-in directories parse as bare strings (staging default / production) but a custom CA must use the inline table directory = { custom = { url = "…" } } — a bare URL fails config load. Documented both forms in the ACME section.
Generated by Claude Code
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dce18e5f1c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| app — a **TLS-terminating load balancer or reverse proxy**, or kamal-proxy | ||
| configured with TLS by you. Do **not** enable in-process `[server.tls]`/ACME on |
There was a problem hiding this comment.
Don't offer managed kamal-proxy TLS here
Fresh evidence is this new kamal-proxy configured with TLS by you option in the autumn deploy guidance, but the managed deploy path still cannot preserve or create that configuration: I checked autumn-cli/src/deploy/proxy.rs, where the first deploy rewrites the proxy unit as kamal-proxy run --http-port ... and every route/flip command is kamal-proxy deploy ... --target ... --health-check-path ... with no --host/--tls, while the kamal-proxy README documents TLS as requiring deploy ... --host ... --tls. Users following this sentence for the deploy-managed proxy will still end up with an HTTP-only route, so keep this limited to an external TLS terminator unless deploy grows host/TLS flags.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 5a34152. Re-verified in autumn-cli/src/deploy/proxy.rs: the managed path renders kamal-proxy run --http-port and kamal-proxy deploy --target --health-check-path with no --host/--tls, so it can't create or preserve TLS on that proxy. Dropped the 'kamal-proxy TLS by you' option — deploy-path HTTPS is now scoped to an external TLS terminator placed in front of the (HTTP-only) managed kamal-proxy.
Generated by Claude Code
…(managed kamal-proxy is HTTP-only)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a34152d3d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - **generate:** `autumn generate scaffold --searchable title,body` makes the named text fields full-text searchable — it adds `#[searchable]` attributes to the model, `searchable` to the `#[repository]`, a migration adding a `search_vector tsvector GENERATED ALWAYS AS (…) STORED` column plus a GIN index, and a search box on the index wired to `GET /<plural>/search` (an empty `?q` falls back to the plain paginated list). Omitting `--searchable` leaves scaffold output byte-identical; the `--live`/`--live-validation` index variants gate the search box off; naming a non-text/unknown field, a uuid-PK model, or an owner-scoped model fails generation before any files are written (issue #1319). | ||
| - **generate:** `autumn generate scaffold post avatar:Attachment` now produces working no-JS file uploads end-to-end — the create/update handlers take an `autumn_web::extract::Multipart` extractor, stream each uploaded file to the `BlobStore` via `field.save_to_blob_store(&*store, key)`, and bind the returned `Blob` from a plain `multipart/form-data` submit. `autumn destroy` also no longer strips the `multipart`/`storage` features when a hand-written route still uses them (part of #1236). | ||
| - **cli:** `autumn new <name> --api` scaffolds a JSON-first project — handlers return `Json<…>`, `autumn-web` is pinned `default-features = false` to a lean API feature set (`db`, `cache-moka`, `http-client`, `reporting`, `flash`) that drops the maud/htmx/tailwind view stack, and no `static/` tree, `input.css`, `tailwind.config.js`, vendored assets, or Tailwind CI/README notes are generated (the first `cargo run` serves JSON). `--api` cannot be combined with `--daemon` or `--bundled-pg` but composes with `--with-i18n` and `--with-seed`. | ||
| - **cli:** `autumn deploy` — first-class push-button deployment of an Autumn app to your own Linux host over SSH (issue #1607). `autumn deploy check` reads a `[deploy]` config block, runs a preflight (SSH reachability, signing secret, database URL, and pending migrations), and prints a dry-run plan without touching the server (#1884); `autumn deploy up` builds a single embedded release binary locally (`autumn build --embed` → `target/release/<app>`) and, over an injectable SSH executor, uploads it into a versioned systemd slot on the host, writes the env file, and starts the app (#1912); subsequent deploys perform a **zero-downtime cutover** via kamal-proxy, running pending migrations against the live database *before* flipping the proxy to the new release slot so a request is never served by an un-migrated build (#1928); and `autumn deploy rollback` — plus automatic rollback when a release fails to come up healthy — restores the previous release slot and secrets in one step (#1937). Deploy runs in the **production** profile by default, overridable through a `[deploy]` profile knob (#1956), and a push-button walkthrough is documented in the deploy guide (#1950). |
There was a problem hiding this comment.
Keep deploy subcommand responsibilities accurate
Fresh evidence is the remaining wording on this line: DeployAction::Check only runs collect_preflight/report_preflight and never calls print_plan, while run_up calls resolve_release_binary and fails if target/release/<app> is absent; the build is a separate autumn build --embed step. As written, users skimming the release notes may run deploy check expecting the rollout plan, or run deploy up without a prebuilt binary and hit an avoidable release binary not found failure.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in d10ca8f. Verified in autumn-cli/src/deploy.rs: check runs preflight only (not print_plan — that's plan), and run_up/resolve_release_binary require a prebuilt autumn build --embed binary (fails if target/release/<app> is absent). Reworded the changelog bullet to split check/plan/up/rollback and note the binary is built by a separate autumn build --embed step before up.
Generated by Claude Code
…cedes up; check ≠ plan)
What changed
Documentation-only batch (batch 4). Four scope items:
CHANGELOG.md — backfill the existing
## [Unreleased]section (21 bullets)Backfilled 21 bullets under the existing
## [Unreleased]section for work merged since docs batch 3 (#1877). No version bump; no new dated/numbered section. Coverage:autumn deployCLI (Add autumn deploy for push-button zero-downtime VPS deploys #1607:check/upfoundation, zero-downtimecutover,rollback, and the profile knob)#[lifecycle]typed state machines +autumn lifecycle check|diagram(Prove business workflows sound at build time as typed state machines #1675/Field-level #[state_machine] can reference a #[lifecycle] enum as its transitions source of truth #1911), including thetransition_controlsview helper (Add transition_controls view helper (Part of #1326) #1917)has_manyforms (Nested has_many form binding for atomic master-detail saves #1915)docs/guide/tls.md (NEW) — Closes #1860
New TLS & HTTPS guide covering:
[server.tls])[server.tls.acme]) — staging-default and renewal-quota rationaledeployment.mddocs/guide/deployment.md — cross-ref edit
Before: a placeholder pointing at the not-yet-written #1860 TLS content.
After: a live cross-link to the new TLS guide. Append/cross-ref only — the #1950 deploy-guide rewrite is otherwise untouched.
skills/autumn-web/references/api-reference.md — a11y dedupe + widget entry
Before: two duplicate a11y sections (one from docs batch 3 #1877, one from #1886).
After: one canonical a11y section covering the full source-verified primitive set (
TextField/TextArea/Select+SelectOption/Checkbox/FileField+.hx()), plus a newtransition_controlswidget entry (#1917).skills/* — freshness edits
Minimal freshness edits to
skills/autumn-web/SKILL.md,skills/autumn-patterns/SKILL.md,skills/doctor/SKILL.md, andskills/generate/SKILL.mdfor lifecycle,autumn deploy, a11y primitives, SQLite backend, and nested forms. All command/attribute names source-verified.Verification
git diff --statconfirms no Rust source files were touched (8 files: CHANGELOG.md, 2 docs, 5 skills).docs/guide/tls.md→./deployment.mdanddocs/guide/deployment.md→./tls.md; both files exist indocs/guide/.scripts/check-plugin-freshness.sh): exit 0 — static checks pass (plugin.json parses, alldocs/guide/*.mdreferences from skills/agents exist) and the drift gate reports "changelog entries added and plugin files touched" (this PR updates skills/ alongside the changelog bullets).scripts/check-docs.sh(rustdoc intra-doc-link gate over the Rust crates): this diff is markdown-only and touches no Rust doc-comments, so it is independent of this change. Result appended to the PR thread once the local build completes.Notes
## [Unreleased]section is extended in place.Generated by Claude Code