{ "//": "Generated by go run ./cmd/changelog from git, the published release notes and Amadan. Do not edit; regenerate.", "repo": "https://amadan.net/carlos/platform", "releases": [ { "tag": "v0.28.7", "date": "2026-10-21", "sha": "771ca01b6be00cec299a8e265c34136daa1fe0d1", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.7", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**`carlos logs` now shows each run.** It shows when each `carlos run` started, who asked for it and how it ended. The command's output is not kept there.", "branches": [ "run-lifecycle-logs-325" ], "docs": [ "/docs/cli#carlos-logs", "/docs/cli#carlos-run" ] }, { "md": "**An app can ask for one uploaded object to be scanned.** Where scanning is enabled for the deployment and an operator has granted the store, the app requests a scan with an app-scoped `service` credential and polls for the result, or receives a signed callback. See [Receiving scan results](https://carlosframework.com/docs/scan-callbacks).", "branches": [ "scan-demand-revival" ] }, { "md": "**The console's account switcher has a search box.** It helps when you belong to many accounts.", "branches": [ "console-switcher-filter" ] } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.** Coming from v0.28.4, read the [v0.28.5 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.5) first. Coming from v0.28.5, upgrade the console with `carlos system claims install-console --upgrade` as the [v0.28.6 notes](https://github.com/carlosframework/releases/releases/tag/v0.28.6) describe.", "docs": [ "/docs/cli#carlos-system-claims" ] }, { "md": "**An offline move can copy most of its data before the instance goes offline.** Run `carlos move offline stage --host --to ` while the instance keeps serving. It copies the instance's replica to the target region, and running it again copies only what is new. `start` then takes the instance offline as before, and the seal copies only what changed since the last stage. Until `start`, `abort` removes the staged copy and the instance is never offline. While a move is staged, the instance refuses `carlos run` and deletion, and no other instance can move offline.", "branches": [ "moves-offline-precopy" ], "docs": [ "/docs/cli#carlos-move", "/docs/cli#carlos-run" ] }, { "md": "**Every box in both regions must run this release before `stage` works.** Each box now publishes `offline-stage-1`, and `stage` refuses while any box in either region lacks it.", "branches": [ "moves-offline-precopy" ] }, { "md": "**Moves and replica copies now run eight object copies at once.** Before, they copied one object at a time, so a large replica took minutes to copy while the instance was offline.", "branches": [ "moves-offline-precopy" ] }, { "md": "**On-demand scanning needs `CARLOS_SCAN_WEBHOOK_KEY` only for callbacks.** Polling works without it. To rotate it, keep the old key in `CARLOS_SCAN_WEBHOOK_KEY_PREVIOUS` and run `carlos system antivirus rotate-webhook-key`. Apps whose delivered secret changes restart.", "branches": [ "scan-demand-revival" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS`, `CARLOS_REQUIRE_POINTER_DIGEST` and `CARLOS_PLAN_CATALOG` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`. Keep `CARLOS_SWAP` as it is: a box armed for zero-downtime deploys stays armed.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "771ca01b6be00cec299a8e265c34136daa1fe0d1", "date": "2026-10-21", "subject": "moves-offline-precopy: carlos move offline stage (pre-copy while serving) and parallel mirror copies", "branch": "moves-offline-precopy", "branch_url": "https://amadan.net/carlos/platform/-/branch/moves-offline-precopy", "commits": 10, "docs": [ "/docs/cli#carlos-move" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "f550afa21b8098460cbcf0bac2cac6748e52b802", "date": "2026-10-21", "subject": "spec: plans, caps and packs for the first paid launch (draft)", "branch": "design-plans-and-caps", "branch_url": "https://amadan.net/carlos/platform/-/branch/design-plans-and-caps", "commits": 15, "discussions": [ { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" } ], "docs": [ "/docs/cli#carlos-accounts-plan", "/docs/plans#turning-plans-on", "/docs/plans#the-catalog", "/docs/plans#assigning-a-plan", "/docs/plans#sponsored-accounts" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "aaa6a9498092d0e24910991127b45a9b21cdf7b4", "date": "2026-10-21", "subject": "root tests: the carlos binary is built once per test run, not once per test", "branch": "test-suite-speed", "branch_url": "https://amadan.net/carlos/platform/-/branch/test-suite-speed", "commits": 3, "discussions": [ { "id": 291, "title": "Test suite speed: where the CI gate's 375 seconds go, and a ranked plan", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "512baa591dc275b5b3a01a353d6993fb19509a0b", "date": "2026-10-21", "subject": "Prompts:", "branch": "test-suite-debt", "branch_url": "https://amadan.net/carlos/platform/-/branch/test-suite-debt", "commits": 4, "discussions": [ { "id": 226, "title": "Root package tests read the operator's real ~/.carlos/config (TestShipVerbAPIMode aborts the package)", "state": "open" }, { "id": 258, "title": "Root CLI tests read the operator's real ~/.carlos/config when TMPDIR is under HOME", "state": "open" }, { "id": 263, "title": "Hibernating instances: keep 7 days of snapshot history like awake ones", "state": "closed" }, { "id": 266, "title": "Flaky: sim/litestreamfacts TestGate_AnchoredLineageThroughStore under load", "state": "open" }, { "id": 274, "title": "Flaky CI test: stopevidence TestFDIdentityFindsAHardLinkHeldByAChild", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "f54aa2fc50b8481b9ba4b6bfe09a21bc7c7172e2", "date": "2026-10-21", "subject": "carlos run: a run's start and end reach carlos logs (#325)", "branch": "run-lifecycle-logs-325", "branch_url": "https://amadan.net/carlos/platform/-/branch/run-lifecycle-logs-325", "commits": 5, "discussions": [ { "id": 325, "title": "Run a one-off command with one instance's delivered config (carlos exec)", "state": "closed" } ], "docs": [ "/docs/cli#carlos-run" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "3d444a1b63c6e853527292cd8845f1347fdbb544", "date": "2026-10-21", "subject": "Merge reviewed on-demand object scanning revival", "branch": "scan-demand-revival", "branch_url": "https://amadan.net/carlos/platform/-/branch/scan-demand-revival", "commits": 1, "docs": [ "/docs/cli#carlos-store-scan", "/docs/cli#deployment-wide-object-scanning", "/docs/scan-callbacks#asking-for-a-scan", "/docs/scan-callbacks#the-states", "/docs/scan-callbacks#callbacks-take-a-path" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "ce8c425ae5930e1c2807018bde1880b93b0947cd", "date": "2026-10-20", "subject": "console: searchable, scrolling account switcher", "branch": "console-switcher-filter", "branch_url": "https://amadan.net/carlos/platform/-/branch/console-switcher-filter", "commits": 2, "discussions": [ { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.6", "date": "2026-10-20", "sha": "e0986eabc5bc2a7ae5e17e58b19c9a6b92beecfe", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.6", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Nothing changes for people deploying apps with the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.28.4, read the [v0.28.5 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.5) first.** They all still apply, including the one update by hand for a box whose guard is armed on v0.28.4." }, { "md": "**A guard-armed box's protected console can now be replaced.** Once the box's router is on this release, run `carlos system claims install-console --upgrade --from ` as root, where the sha256 is the console that is running now. It refuses unless that build is what the box holds, then installs the new console and keeps the old one as `console.prev`. No hand recovery is needed. A box upgraded this way can't be armed again with `carlos system guard-arm` until `install-console` has been run without `--upgrade`, because `guard-arm` expects both copies to be the same build.", "branches": [ "install-console-upgrade" ], "docs": [ "/docs/cli#carlos-system-claims", "/docs/cli#carlos-system" ] }, { "md": "**A nested instance's `config.env` is written as soon as its directory appears.** Before, a move could leave the instance without it for up to an hour.", "branches": [ "moves-offline-fixes" ] }, { "md": "**Each offline move step now sees a current box report.** While a move is running, the source boxes and the sealing box report again every 15 seconds. A move takes under a minute of downtime when its steps run back to back. A step that meets a stale report now says the report is not current and to try again. Before, `flip` could answer that a process was still running.", "branches": [ "moves-offline-fixes" ] }, { "md": "**A source box's scheduler leaves a moved instance alone.** After `open`, it no longer writes a stray token file into the instance it gave up.", "branches": [ "moves-offline-fixes" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`. Keep `CARLOS_SWAP` as it is: a box armed for zero-downtime deploys stays armed.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "e0986eabc5bc2a7ae5e17e58b19c9a6b92beecfe", "date": "2026-10-20", "subject": "moves-offline-fixes: harness Run 9 defects. Config sync writes a nested row's config.env as soon as its instance directory appears, keeping it pending until delivered (6); each offline door step sees a current report: source boxes and the sealer refresh reports every 15 s while a move is active without renewing the tombstone, and a stale report answers MV-O178 (7); a source box's scheduler leaves a held or moved host alone, excluding it before the batch limit (8). Astra reviewed (two rounds); census-ruling-moves-offline-fixes (platform-1c); MV-O178 approved by Paul.", "branch": "moves-offline-fixes", "branch_url": "https://amadan.net/carlos/platform/-/branch/moves-offline-fixes", "commits": 9, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "2185035698a1111c487651bddb1bf2500cd92ccc", "date": "2026-10-20", "subject": "install-console --upgrade --from : replace a guard-armed box's protected console", "branch": "install-console-upgrade", "branch_url": "https://amadan.net/carlos/platform/-/branch/install-console-upgrade", "commits": 4, "docs": [ "/docs/cli#carlos-system-claims" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.5", "date": "2026-10-20", "sha": "54ae7fb93820f37f123bbdcbcb57f4f2efb25dc9", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.5", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Nothing changes for people deploying apps with the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.28.3, read the [v0.28.4 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.4) first.** They all still apply." }, { "md": "**`carlos move offline` moves one instance to another region, with planned downtime.** It is for operators, works only while host claims and Phase 1 moves are off, and takes one step per command: `start`, `seal`, `flip`, `open`, then `finish`. The instance is offline from `start` until `open`. Until `open`, `abort` puts it back where it was with nothing lost.", "branches": [ "moves-offline" ], "docs": [ "/docs/cli#carlos-move" ] }, { "md": "**Roll every box in both regions before you start an offline move.** `seal` waits until every box in the instance's current region has stopped it and said so, and a box on an earlier release never says so.", "branches": [ "moves-offline" ] }, { "md": "**A box whose guard is armed on v0.28.4 needs one update by hand.** Its updater reads a candidate's progress messages as part of the answer, so `carlos system update` refuses every candidate on that box. Stop `carlos-system-update.path`, put the v0.28.5 binary in a root-only file after checking it against `SHA256SUMS.txt`, run it as root as `system apply` with the box's `host.env`, check the box, then start `carlos-system-update.path` again. Do one box at a time. From then on the box updates as usual.", "branches": [ "armed-box-updates" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**The guarded updater reads only what a candidate writes to standard output.** Progress and warnings no longer break its checks, so this is the last update a guard-armed box needs by hand.", "branches": [ "armed-box-updates" ] }, { "md": "**`carlos system guard-arm` makes its directories readable whatever your umask is.** Before, a root shell with a strict umask left them unreadable to the box's updater.", "branches": [ "claim-guard-umask" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Routes with their own unit now restart on boxes whose agent doesn't run as root.** The agent now hands those restarts to root. Before, the restart was refused, the agent read that as a broken build and rolled back.", "branches": [ "adopt-unit-root-pass-315" ] }, { "md": "**`carlos system status` warns when a new box would boot an older build.** It compares `bootstrap/carlos` with stable's build and says which file to copy over it.", "branches": [ "bootstrap-carlos-follows-316" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`. Keep `CARLOS_SWAP` as it is: a box armed for zero-downtime deploys stays armed.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "54ae7fb93820f37f123bbdcbcb57f4f2efb25dc9", "date": "2026-10-20", "subject": "armed-box-updates: guard-armed boxes can take a release by the ruled re-arm. Cherry-picks 8376a468 (rootaws: an inherited disabled sentinel is not an override) and 8a313548 (hostfloor: a guarded run returns stdout only) from claim-empty-cohort-done, Astra-reviewed there; census-ruling-claim-rootaws-sentinel, confirmed for main by platform-1c. For v0.28.5.", "branch": "armed-box-updates", "branch_url": "https://amadan.net/carlos/platform/-/branch/armed-box-updates", "commits": 3, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "7af881553052fb0b1a26308f49e446df5bcf71be", "date": "2026-10-20", "subject": "moves-offline: operator-run offline region move (claims-off, planned downtime): holds and fences every source box, seals the source with a create-only tombstone after a final checkpoint, lands every dependency on the target proven against an immutable seal.json, opens, then retires the source by rename. Reviewed task by task and by Fable and Astra; census-ruling-moves-offline; copy approved by Paul 2026-10-20. Dormant until an operator runs carlos move offline.", "branch": "moves-offline", "branch_url": "https://amadan.net/carlos/platform/-/branch/moves-offline", "commits": 40, "docs": [ "/docs/cli#carlos-move" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "57569bfffa089b9e0ac9c61b9d6f1a03ccf4fc97", "date": "2026-10-20", "subject": "changelog-v0.28.4: release step 10 for v0.28.1 to v0.28.4", "branch": "changelog-v0.28.4", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.28.4", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "cd2dd6e1a3f208b31932d28721bfa0eedb7e7a76", "date": "2026-10-20", "subject": "system status: say when bootstrap/carlos is not stable's build (#316)", "branch": "bootstrap-carlos-follows-316", "branch_url": "https://amadan.net/carlos/platform/-/branch/bootstrap-carlos-follows-316", "commits": 2, "discussions": [ { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" }, { "id": 316, "title": "Keep bootstrap/carlos in step with the fleet: system update mirrors releases but new boxes still boot a stale binary", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "d7a5b8eb640535f54dd7e0380eda59e9bcc6c2c6", "date": "2026-10-20", "subject": "adopt: an agent that is not root hands named-unit routes to root's pass (#315)", "branch": "adopt-unit-root-pass-315", "branch_url": "https://amadan.net/carlos/platform/-/branch/adopt-unit-root-pass-315", "commits": 3, "discussions": [ { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" }, { "id": 315, "title": "Agent's in-process adopt rolls back a console update when it can't restart console.service itself (\"Interactive authentication required\")", "state": "closed" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "96db0efe4a0dc94f26cc8e01d5ed8c5c07e3296a", "date": "2026-10-19", "subject": "claim-guard-umask: guard-arm makes its guard, system and lock directories 0755 whatever the caller's umask (#317)", "branch": "claim-guard-umask", "branch_url": "https://amadan.net/carlos/platform/-/branch/claim-guard-umask", "commits": 2, "discussions": [ { "id": 317, "title": "guard-arm's directories inherit the caller's umask: umask 077 broke the guarded console (exit 203)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.4", "date": "2026-10-18", "sha": "f3bf1214ab92a47ecb626400e7bd1b35cd7a112c", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.4", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Console invites now arrive.** Before this release, an invite to anyone but yourself never reached them. The console now delivers the link to the invitee's Keymail inbox, or by email where console email is on. If neither works, it shows you the link to pass on yourself.", "branches": [ "design-console-invite-delivery" ] }, { "md": "**An account can have 20 invites waiting at most, and send 20 an hour.** An invite emailed to an address that isn't a Keymail address can't be accepted yet.", "branches": [ "design-console-invite-delivery" ] }, { "md": "**Nothing changes in the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.28.2, read the [v0.28.3 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.3) first.** They all still apply." }, { "md": "**Console email can switch itself on.** A console with `CARLOS_MAIL_REGION` set and a sending address (`CARLOS_CONSOLE_MAIL_FROM`, or one made from `CARLOS_MAIL_PLATFORM_SUFFIX`) creates an SES identity and DNS records for that address's domain the first time it starts. It sends nothing until SES has verified the domain.", "branches": [ "design-console-invite-delivery" ] }, { "md": "**Sending also needs a grant that is off by default.** The region-host module's `console_mail_from` and `console_mail_region` add an `ses:SendEmail` grant to the host role. Leave them unset until that grant's security review is done. Without it, invites fall back to Keymail or the link.", "branches": [ "design-console-invite-delivery" ] }, { "md": "**With `CARLOS_HOST_CLAIM` on, the console sends no email.** Invites then go by Keymail, or the owner passes the link on.", "branches": [ "design-console-invite-delivery" ] }, { "md": "**`carlos system install-channel --policy` now also takes `--identity`.** The two go together. The published release identity stands in for the channel's release tag, which the channel only carries once a floor is published.", "branches": [ "claim-untagged-pointer" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "f3bf1214ab92a47ecb626400e7bd1b35cd7a112c", "date": "2026-10-18", "subject": "claim-untagged-pointer: install-channel --policy takes --identity (the published release identity) so an untagged channel pointer works; the activation witness has no hand-made input on the trial's path", "branch": "claim-untagged-pointer", "branch_url": "https://amadan.net/carlos/platform/-/branch/claim-untagged-pointer", "commits": 6, "docs": [ "/docs/cli#boxes-a-region-s-group-launches" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "91a651b3c5a0837445c1a5bc738aafe340928510", "date": "2026-10-18", "subject": "spec: console invite delivery by Keymail federation, then email", "branch": "design-console-invite-delivery", "branch_url": "https://amadan.net/carlos/platform/-/branch/design-console-invite-delivery", "commits": 20, "discussions": [ { "id": 216, "title": "Mail: a member can adopt another party's verified SES domain; sending-domain claims never expire", "state": "open" }, { "id": 250, "title": "Host floor guard: fix these before anyone runs guard-arm (Opus review of Plans 8/8b)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.3", "date": "2026-10-18", "sha": "50f5b26845fb0a23109078654ec5d59b3723d9c4", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.3", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.28.1, read the [v0.28.2 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.2) first.** They all still apply." }, { "md": "**`carlos system install-channel --policy` prepares a box that is already running for host claims.** It puts the box's current release in both its copies, so the guard can be armed before a floor is published. That replaces the box's rollback copy, and the command says so. Nothing runs it on its own, and host claims stay off.", "branches": [ "claim-prev-rehearsal" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "50f5b26845fb0a23109078654ec5d59b3723d9c4", "date": "2026-10-18", "subject": "claim-prev-rehearsal: install-channel --policy arms an updater-installed box; the activation witness starts from a box the platform made and rehearses steps 6 and 7", "branch": "claim-prev-rehearsal", "branch_url": "https://amadan.net/carlos/platform/-/branch/claim-prev-rehearsal", "commits": 7, "docs": [ "/docs/cli#boxes-a-region-s-group-launches" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.2", "date": "2026-10-18", "sha": "7c1beb2da18da1a659e57a4514aee8abe2146d2d", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.2", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.28.0, read the [v0.28.1 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.1) first.** They all still apply." }, { "md": "**Three new `carlos system claims` commands prepare a box for host claims.** `run` gains two phases, `sign-policy` and `publish-identity`, which make the files `carlos system guard-arm` needs, and `install-console` puts a protected copy of the console where the guard expects it. Nothing runs them on its own, and host claims stay off.", "branches": [ "claim-step6-artifacts" ], "docs": [ "/docs/cli#carlos-system-claims", "/docs/cli#carlos-system" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "7c1beb2da18da1a659e57a4514aee8abe2146d2d", "date": "2026-10-18", "subject": "claim-step6-artifacts: claims run sign-policy and publish-identity, --out byte-exact files, claims install-console (dev cohort-1 trial step 6)", "branch": "claim-step6-artifacts", "branch_url": "https://amadan.net/carlos/platform/-/branch/claim-step6-artifacts", "commits": 9, "docs": [ "/docs/cli#carlos-system-claims" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.1", "date": "2026-10-18", "sha": "3b69c2bd60edaecb04771d4cd85792e326010cc0", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.1", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.27.x, read the [v0.28.0 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.28.0) first.** They all still apply." }, { "md": "**A box can publish its identity record by hand.** `systemd-run --wait --pipe -p EnvironmentFile=/etc/carlos/host.env -p User=carlos /opt/carlos/carlos system box publish-record` writes the box's AWS-signed identity record to the deployment bucket once. It never replaces or deletes a record, and it refuses to run as root or on a fleet box. Nothing runs it on its own.", "branches": [ "box-publish-record" ] }, { "md": "**A box that has not reported for a day drops out of status.** `carlos status --live`, the console's box list and the promote list leave out a box whose report has not been written for 24 hours. Before, it stayed in the list as stale. Fleet boxes are listed as before.", "branches": [ "region-ha-2b-build" ], "docs": [ "/docs/cli#carlos-status" ] }, { "md": "**Each box gets an idle join unit.** `carlos-system-join.path` is installed and enabled at the roll. Nothing touches the file it watches until region HA is turned on, so it never starts its service.", "branches": [ "region-ha-2b-build" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, don't put `CARLOS_BOX_REFERENCE=group` in any host.env, and don't run `carlos channels digests on`.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "3b69c2bd60edaecb04771d4cd85792e326010cc0", "date": "2026-10-18", "subject": "box-publish-record", "branch": "box-publish-record", "branch_url": "https://amadan.net/carlos/platform/-/branch/box-publish-record", "commits": 1, "docs": [ "/docs/cli#a-box-s-identity-record" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "b11189254fcf542ba99a428e63742c0aa81f069e", "date": "2026-10-18", "subject": "region HA 2b: the joining and enrolment plan and its copy record", "branch": "region-ha-2b-build", "branch_url": "https://amadan.net/carlos/platform/-/branch/region-ha-2b-build", "commits": 32, "discussions": [ { "id": 275, "title": "carlos binary doubled to 74.5 MB: the EC2 SDK client via internal/awsinv", "state": "closed" } ], "docs": [ "/docs/cli#boxes-a-region-s-group-launches" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "04819f9b42ed6767d816bdfecd53490407ee20bb", "date": "2026-10-17", "subject": "changelog-v0.28.0: release step 10 for v0.28.0, plus the darwin/windows vet fix for claimunit_inputs_test.go", "branch": "changelog-v0.28.0", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.28.0", "commits": 2, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.28.0", "date": "2026-10-17", "sha": "260653783857088d6ba940101191c599974f8caa", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.28.0", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**`carlos run` runs one of your app's commands against one instance.** `carlos run --app shop --instance www.example.com -- ` runs it as that instance, with its environment and database, and prints the output as it arrives. `--file` uploads files with the run, up to 64 MB in total. `--timeout` sets how long it may take, 30 minutes by default and up to 6 hours. `--follow ` reattaches to a run already started. It needs an instance on the nested layout.", "branches": [ "carlos-run" ], "docs": [ "/docs/cli#carlos-run" ] }, { "md": "**New instances can start from a copy of another instance's database.** `carlos instances create --from ` copies the source's replica, so the source keeps serving. `carlos instances import` brings an instance the platform never ran onto it, from a snapshot of its database. Both refuse until your operator turns them on.", "branches": [ "seeded-instances" ], "docs": [ "/docs/cli#carlos-instances" ] }, { "md": "**The download is smaller again.** The linux/arm64 binary is about 46 MB, down from about 78 MB.", "branches": [ "fix-binary-size-275" ] }, { "md": "**Nothing else changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then every console.** A run is refused until the console that records it is on this release.", "branches": [ "carlos-run" ] }, { "md": "**Coming from v0.26.x, read the [v0.27.0](https://github.com/carlosframework/releases/releases/tag/v0.27.0) to [v0.27.3](https://github.com/carlosframework/releases/releases/tag/v0.27.3) operator notes first.** They all still apply." }, { "md": "**Instance control restarts with this update.** `carlos system update` does that on its own. A box updated any other way needs `systemctl restart carlos-instance-control` afterwards.", "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Give the console room for run uploads.** The console holds a run's uploaded files, up to 64 MB per run, in its temporary directory until the box takes them.", "branches": [ "carlos-run" ] }, { "md": "**Seeded instances stay off until you publish the seeds floor.** `carlos instances create --from` and `carlos instances import` refuse until `carlos system seeds-floor publish` names a release every box runs. Publishing it is one way: from then on no box recovers to an older build. Leave it unpublished until you have decided to turn seeding on.", "branches": [ "seeded-instances" ], "docs": [ "/docs/cli#carlos-instances", "/docs/cli#carlos-system" ] }, { "md": "**The host claim is still off.** This release carries the claim's protected host state and the `carlos system claims` activation commands, but nothing uses them while `CARLOS_HOST_CLAIM` is unset. Errors from a route that has no app are now filed so the claim's checks can read the bucket.", "branches": [ "feat/claim-root-trusted-state-p3", "feat/claim-files-backfill" ], "docs": [ "/docs/cli#carlos-system-claims" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "260653783857088d6ba940101191c599974f8caa", "date": "2026-10-17", "subject": "WIP claim Plan 14 foundations (not for merge): descriptor-backed root state, root-owned D3 health history, release lock, floor reconstruction and migration preflight helpers, constructor census, root-read census with the legacy freeze", "branch": "feat/claim-root-trusted-state-p3", "branch_url": "https://amadan.net/carlos/platform/-/branch/feat%2Fclaim-root-trusted-state-p3", "commits": 102, "docs": [ "/docs/cli#carlos-proof-history", "/docs/cli#carlos-system-claims" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude", "codex" ] } ] }, { "sha": "6834659c9f0d5453051902c72ca24539562cd010", "date": "2026-10-16", "subject": "region HA spec 2 (two-box regions), rev 1 draft, and its Astra brief", "branch": "region-ha-2a-build", "branch_url": "https://amadan.net/carlos/platform/-/branch/region-ha-2a-build", "commits": 63, "discussions": [ { "id": 299, "title": "Provider-neutral box identity, so multi-box regions work without AWS", "state": "open" } ], "docs": [ "/docs/cli#box-and-group-roles" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "e8e961dd423f03399ec7922f3cd7100defb0af62", "date": "2026-10-16", "subject": "fix-empty-app-key", "branch": "fix-empty-app-key", "branch_url": "https://amadan.net/carlos/platform/-/branch/fix-empty-app-key", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "e963691d5571fcca926a454d7c485657681c4fcc", "date": "2026-10-16", "subject": "runbook-home2-sha", "branch": "runbook-home2-sha", "branch_url": "https://amadan.net/carlos/platform/-/branch/runbook-home2-sha", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "a89e35caaf969492d10d93c556a2ef303f2bc679", "date": "2026-10-16", "subject": "moves-home-part2-plan", "branch": "moves-home-part2-plan", "branch_url": "https://amadan.net/carlos/platform/-/branch/moves-home-part2-plan", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "ee35dee27b68a2280d69d4954154e67e75f699bd", "date": "2026-10-16", "subject": "Prompts:", "branch": "feat/claim-files-backfill", "branch_url": "https://amadan.net/carlos/platform/-/branch/feat%2Fclaim-files-backfill", "commits": 32, "discussions": [ { "id": 250, "title": "Host floor guard: fix these before anyone runs guard-arm (Opus review of Plans 8/8b)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "70e57449fc8d8106e8e5ef729cc5df85f84ad7be", "date": "2026-10-15", "subject": "moves-overcap-cut", "branch": "moves-overcap-cut", "branch_url": "https://amadan.net/carlos/platform/-/branch/moves-overcap-cut", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "a5003aba7b1786e96073d6d23438a40c042c3c03", "date": "2026-10-15", "subject": "docs-rehearsal-dev-uk-secrets-key", "branch": "docs-rehearsal-dev-uk-secrets-key", "branch_url": "https://amadan.net/carlos/platform/-/branch/docs-rehearsal-dev-uk-secrets-key", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "9f17062aec02421b8c0eb540650754cac778a637", "date": "2026-10-15", "subject": "docs-rehearsal-dev-uk-bootstrap", "branch": "docs-rehearsal-dev-uk-bootstrap", "branch_url": "https://amadan.net/carlos/platform/-/branch/docs-rehearsal-dev-uk-bootstrap", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "5f4ace8b841036f273b10ce0eb26430e85a83db0", "date": "2026-10-15", "subject": "hostfiles: the path cap applies to live entries only (LiveEntriesWithinCap), so a manifest written before the cap that holds a tombstone or skipped entry past it stays readable, and a moves recovery that selects it is not stranded (#236, moves' review of a8cebad)", "branch": "hostfiles-cap-live-entries", "branch_url": "https://amadan.net/carlos/platform/-/branch/hostfiles-cap-live-entries", "commits": 16, "discussions": [ { "id": 236, "title": "Host claim Plan 8: three decisions for Paul (floor release, declare-lost attestation, actor roster)", "state": "open" }, { "id": 250, "title": "Host floor guard: fix these before anyone runs guard-arm (Opus review of Plans 8/8b)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "f97898f1224f08f7da5ee4e12d9f39ce04b083a6", "date": "2026-10-15", "subject": "copy gate: the seeded-instances deck, pinned (Plan 1 Task 1)", "branch": "seeded-instances", "branch_url": "https://amadan.net/carlos/platform/-/branch/seeded-instances", "commits": 96, "discussions": [ { "id": 292, "title": "Edge restart: the stopping agent recycles an instance and strands its lease for 15 minutes", "state": "closed" } ], "docs": [ "/docs/cli#carlos-instances", "/docs/cli#the-seeds-floor", "/docs/cli#legacy-route-assignments" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "37ab10c5fe5937d6b9699cc158669bdb3dc3a1a9", "date": "2026-10-15", "subject": "specs: carlos run (#259) and instances seeded from a database (#260, #261)", "branch": "carlos-run", "branch_url": "https://amadan.net/carlos/platform/-/branch/carlos-run", "commits": 73, "discussions": [ { "id": 259, "title": "carlos run: a member runs an app command against one instance, without root on the box", "state": "open" }, { "id": 260, "title": "carlos instances create --from : a new instance seeded from another's replica copy (same app, same account)", "state": "open" }, { "id": 261, "title": "Schedule the adoption door (instance-moves §9, G9) after the nested-layout rollout: every controld titogo instance converts to platform-run", "state": "open" }, { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" }, { "id": 294, "title": "carlos run: the cmd role is outside #292's start/stop fence in instance control", "state": "open" } ], "docs": [ "/docs/cli#carlos-run", "/docs/cli#what-an-instance-is-started-with", "/docs/cli#carlos-instance-exec", "/docs/cli#carlos-instance-exit" ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "1c5718f695c72e47bd4a3b13b0abfde49ae765f3", "date": "2026-10-15", "subject": "activator: write a cold wake's build-state record at its exec (#272)", "branch": "zdd-owed-items", "branch_url": "https://amadan.net/carlos/platform/-/branch/zdd-owed-items", "commits": 6, "discussions": [ { "id": 250, "title": "Host floor guard: fix these before anyone runs guard-arm (Opus review of Plans 8/8b)", "state": "open" }, { "id": 272, "title": "Zero-downtime deploys: owed before CARLOS_SWAP can go on", "state": "open" }, { "id": 292, "title": "Edge restart: the stopping agent recycles an instance and strands its lease for 15 minutes", "state": "closed" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "132cf996ee438fd9760a5920307608559acb9720", "date": "2026-10-14", "subject": "binary-size-275: store SDK method values, not clients, in interfaces; add a size ceiling", "branch": "fix-binary-size-275", "branch_url": "https://amadan.net/carlos/platform/-/branch/fix-binary-size-275", "commits": 3, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "2683f16acaf7a47124c1140ed53c54fa432e72ea", "date": "2026-10-14", "subject": "Prompts:", "branch": "changelog-v0.27.3", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.27.3", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.27.3", "date": "2026-10-14", "sha": "e15ad6be618aeea85b19b5b8cab42df6d75a8af8", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.27.3", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Restarting the edge no longer leaves an instance unreachable for 15 minutes.** If the edge stopped while an instance was waking, the instance could stay locked until its lease expired, and every request to it was refused for up to 15 minutes. The stopping edge now cancels the wake, makes sure it has stopped, and lets the lease go. If it cannot confirm the stop, it keeps the lease, as before.", "branches": [ "fix-shutdown-lease-292" ] }, { "md": "**New accounts never get a reserved address.** An account's address is part of every app's hostname. Account creation now skips any address that spells a name the platform uses itself, such as dev, edge or www. Existing accounts keep theirs.", "branches": [ "reserved-sqids-285" ] }, { "md": "**Nothing else changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.26.x, read the [v0.27.0](https://github.com/carlosframework/releases/releases/tag/v0.27.0), [v0.27.1](https://github.com/carlosframework/releases/releases/tag/v0.27.1) and [v0.27.2](https://github.com/carlosframework/releases/releases/tag/v0.27.2) operator notes first.** They all still apply." }, { "md": "**Instance control restarts with this update.** This release changes `carlos-instance-control`, so it has to restart on each box. `carlos system update` does that on its own. A box updated any other way needs `systemctl restart carlos-instance-control` afterwards.", "branches": [ "fix-shutdown-lease-292" ], "docs": [ "/docs/cli#carlos-system" ] }, { "md": "**Roll the console as well as the boxes.** Only a console on this release skips reserved account addresses when it creates an account.", "branches": [ "reserved-sqids-285" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`. With `CARLOS_HOST_CLAIM` set, instance control now refuses to start until a later release.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "e15ad6be618aeea85b19b5b8cab42df6d75a8af8", "date": "2026-10-14", "subject": "activator: Shutdown aborts a waking entry and gives its lease back with swaps off (#292)", "branch": "fix-shutdown-lease-292", "branch_url": "https://amadan.net/carlos/platform/-/branch/fix-shutdown-lease-292", "commits": 7, "discussions": [ { "id": 272, "title": "Zero-downtime deploys: owed before CARLOS_SWAP can go on", "state": "open" }, { "id": 292, "title": "Edge restart: the stopping agent recycles an instance and strands its lease for 15 minutes", "state": "closed" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "fc70bc754dec1c2fe2f04d14d77f716b4180a7df", "date": "2026-10-14", "subject": "reserved-sqids-285", "branch": "reserved-sqids-285", "branch_url": "https://amadan.net/carlos/platform/-/branch/reserved-sqids-285", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "8b5c75610c0c169d2213f7bb71a68b01b7dda96a", "date": "2026-10-14", "subject": "changelog-v0.27.x: v0.27.0, v0.27.1 and v0.27.2 (README release step 10)", "branch": "changelog-v0.27", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.27", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "a3bfa662250917557faed570efcc67c8c788254a", "date": "2026-10-14", "subject": "rehearsal-p0-two-box", "branch": "rehearsal-p0-two-box", "branch_url": "https://amadan.net/carlos/platform/-/branch/rehearsal-p0-two-box", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "4e577fdef0c840e7868013d936c7471a1fc42d7b", "date": "2026-10-14", "subject": "readme-roll-order", "branch": "readme-roll-order", "branch_url": "https://amadan.net/carlos/platform/-/branch/readme-roll-order", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.27.2", "date": "2026-10-14", "sha": "ebddcfc6f4c55a6239d07faa6b46d3773186327b", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.27.2", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Two builds at once keeps each build's socket name.** Where a deployment turns on two builds at once, each build's socket now has the same file name it has with the switch off, in a directory of its own. An app that works out its own address from its socket's name now starts the same way either way. Before, such an app saw a different name for the new build and refused to start. Nothing changes where the switch is off, which is every deployment by default.", "branches": [ "zdd-gen-socket-names" ] }, { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.26.x, read the [v0.27.0](https://github.com/carlosframework/releases/releases/tag/v0.27.0) and [v0.27.1](https://github.com/carlosframework/releases/releases/tag/v0.27.1) operator notes first.** They all still apply." }, { "md": "**A long host name keeps stop-then-start updates.** With two builds at once on, a host on a flat box whose name is longer than 59 bytes is updated the old way: stopped, then started on the new build. Its socket path would otherwise be too long.", "branches": [ "zdd-gen-socket-names" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`. With `CARLOS_HOST_CLAIM` set, instance control now refuses to start until a later release.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "ebddcfc6f4c55a6239d07faa6b46d3773186327b", "date": "2026-10-14", "subject": "zdd rev 19 amendment: generation sockets keep the flag-off file name", "branch": "zdd-gen-socket-names", "branch_url": "https://amadan.net/carlos/platform/-/branch/zdd-gen-socket-names", "commits": 5, "discussions": [ { "id": 272, "title": "Zero-downtime deploys: owed before CARLOS_SWAP can go on", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "d2f58fd330726e461b1022edfc9e4c4688696dd1", "date": "2026-10-13", "subject": "startup-largebox-test-281", "branch": "startup-largebox-test-281", "branch_url": "https://amadan.net/carlos/platform/-/branch/startup-largebox-test-281", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "21d91fd6087aa1ef7a21f35fc9188b263e3d0529", "date": "2026-10-13", "subject": "host: rootinventory_test.go is linux-only", "branch": "fix-cross-test-compile", "branch_url": "https://amadan.net/carlos/platform/-/branch/fix-cross-test-compile", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.27.1", "date": "2026-10-13", "sha": "6dfbff103985e1e81899f2e611b1afff0ddb3769", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.27.1", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**A box with many instances no longer fails its update while it starts.** On a box with over a hundred instances, v0.27.0's update could take longer than the 20 seconds it allows, because the edge waited for the agent to check every instance first. The box then rolled back after about 45 seconds of failed requests. The edge now starts serving straight away. Requests for an instance wait up to 30 seconds for that check to finish, then get the usual answer that the instance is waking.", "branches": [ "hotfix-startup-sweep-281" ] }, { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.26.x, read the [v0.27.0 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.27.0) first.** They all still apply." }, { "md": "**After each box updates, check that it finished starting.** The update's 20-second check now passes once the edge serves, before the agent has checked every instance. So also check the box's journal for `agent: hibernation activator armed`, and that one request to a sleeping instance succeeds." }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`. With `CARLOS_HOST_CLAIM` set, instance control now refuses to start until a later release.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "6dfbff103985e1e81899f2e611b1afff0ddb3769", "date": "2026-10-13", "subject": "hotfix #281: the edge no longer waits on the startup sweep and recovery", "branch": "hotfix-startup-sweep-281", "branch_url": "https://amadan.net/carlos/platform/-/branch/hotfix-startup-sweep-281", "commits": 1, "discussions": [ { "id": 281, "title": "Agent startup is not bounded under the 20s update gate: sweep and recovery still run before the edge listens", "state": "open" } ], "credit": [ { "person": "Paul Campbell" } ] } ] }, { "tag": "v0.27.0", "date": "2026-10-13", "sha": "f4cf27dd0c48eac32d7b4beaec77686752ca6cff", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.27.0", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Root keeps its own record of each box's releases.** A box's root process now keeps the builds it has seen run healthy, and the build each service ran before its current one, in directories only root can write. Before, it read them from places the platform's own service could write. Restarts, rollbacks and recovering from a bad build work exactly as they did.", "branches": [ "feat/claim-root-trusted-state" ] }, { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.25.x, read the [v0.26.0 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.26.0) and the [v0.26.1 notes](https://github.com/carlosframework/releases/releases/tag/v0.26.1) first.** They all still apply." }, { "md": "**Before rolling, check two things on each box.** `/`, `/var` and `/var/lib` must be owned by root and not writable by group or others, and `/var/lib/carlos-claims` must not exist. If either check fails, root can't update a service that has its own unit, such as the console, on that box, and logs `repoint failed: permission denied` on every pass." }, { "md": "**Health history starts empty.** Root no longer reads the health history the service kept before this release. Don't add a `require` or `prove` entry for a service to `/etc/carlos/proof-policy.json` until both its current build and the one before it have been seen healthy since the roll. Otherwise rolling back to the earlier build waits for a proof." }, { "md": "**One earlier build per service is kept.** `/opt/carlos/previous/` points at the build each service ran before its current one, and the box keeps that build when it prunes old versions. Disk use can grow by up to one build per service." }, { "md": "**`carlos __cgexec` can appear in `ps`.** Each new instance process now starts through a short-lived helper that places it in its cgroup before it runs. For a few milliseconds `ps` can show `carlos __cgexec` as a child of the agent. Monitoring, or a `pkill` that matches \"carlos\" in a command line, can catch it.", "branches": [ "zdd-flag-readiness-3" ] }, { "md": "**After the roll, check placement on each box.** `journalctl -u carlos-edge --since '' | grep 'placement before exec failed'` should print nothing. A line there means a process was moved into its cgroup after it started instead. That affects resource accounting, not serving.", "branches": [ "zdd-flag-readiness-3" ] }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`. With `CARLOS_HOST_CLAIM` set, instance control now refuses to start until a later release.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "f4cf27dd0c48eac32d7b4beaec77686752ca6cff", "date": "2026-10-13", "subject": "changelog: v0.26.1 (release step 10)", "branch": "changelog-v0.26.1", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.26.1", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "2e00b17e18f49cd391b2c6890f8abd68bbdeba23", "date": "2026-10-13", "subject": "plan: zdd flag readiness round 3 (draft)", "branch": "zdd-flag-readiness-3", "branch_url": "https://amadan.net/carlos/platform/-/branch/zdd-flag-readiness-3", "commits": 23, "discussions": [ { "id": 272, "title": "Zero-downtime deploys: owed before CARLOS_SWAP can go on", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "645a7d0541cc77bbf52523f88a901f720ceaf64a", "date": "2026-10-13", "subject": "claim Plan 14 Part 1 (#280): root-trusted state foundations, families B, C and D", "branch": "feat/claim-root-trusted-state", "branch_url": "https://amadan.net/carlos/platform/-/branch/feat%2Fclaim-root-trusted-state", "commits": 1, "discussions": [ { "id": 280, "title": "Root trusts service-writable hostguard and health records under /data (claim root paths)", "state": "open" } ], "credit": [ { "person": "Paul Campbell", "agents": [ "codex" ] } ] }, { "sha": "34a4328f7d29cfed7b5404cec39cef616e115a26", "date": "2026-10-13", "subject": "hotfix-instance-key-gate", "branch": "hotfix-instance-key-gate", "branch_url": "https://amadan.net/carlos/platform/-/branch/hotfix-instance-key-gate", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "ba52d5efe55a4a8a78acaa0af9d500e803aeb4c0", "date": "2026-10-13", "subject": "plan: zero-downtime flag readiness, round 2 (draft for Astra's review)", "branch": "zdd-flag-readiness-2", "branch_url": "https://amadan.net/carlos/platform/-/branch/zdd-flag-readiness-2", "commits": 48, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "67698c9c016e1f34fbb4a337ebc833380d023937", "date": "2026-10-13", "subject": "changelog-nested-bullets", "branch": "changelog-nested-bullets", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-nested-bullets", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "729e85080ff0891fc8c622367dc52a5d27823c8a", "date": "2026-10-13", "subject": "changelog-v0.26.0", "branch": "changelog-v0.26.0", "branch_url": "https://amadan.net/carlos/platform/-/branch/changelog-v0.26.0", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.26.1", "date": "2026-10-13", "sha": "9aaeace220780db1a844595f2d8f690bf2cba375", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.26.1", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**A box with many instances no longer fails the update.** v0.26.0 gave every instance on a box its key before the edge started, with one bucket read per instance. On a box with over a hundred instances that took longer than the 20 seconds the update allows, so the box rolled back after about half a minute of failed requests. The keys are now written in the background once the edge is serving, and the update no longer waits for them." }, { "md": "**Nothing changes for people using the CLI.**" } ] }, { "title": "For operators", "bullets": [ { "md": "**Roll the boxes first, then the console.**" }, { "md": "**Coming from v0.25.x, read the [v0.26.0 operator notes](https://github.com/carlosframework/releases/releases/tag/v0.26.0) first.** They all still apply, and that includes any box that rolled back from v0.26.0. Restart `carlos-instance-control.service` on each box after it updates, run the instance key checks before any box rolls, and check what root's AWS settings rely on (#267)." }, { "md": "**Wait for the instance key line.** `agent: instance keys:` now comes after `hibernation activator armed`, up to about a minute after the agent starts. Wait for it before you check for `0 failed`. It also counts rows skipped because a move or a layout change held the host. Those get their key at their next start." }, { "md": "**Every dormant switch stays off.** Leave `CARLOS_SWAP`, `CARLOS_MOVES_PHASE1`, `CARLOS_HOST_CLAIM`, `CARLOS_HOST_CLAIM_ORIGINS`, `CARLOS_HOST_CLAIM_RETIRED_LOCATIONS`, `CARLOS_CONSOLE_ID`, `CARLOS_REGION_HA`, `CONSOLE_APP_REGIONS`, `CARLOS_TEMPORARY_APPS` and `CARLOS_REQUIRE_POINTER_DIGEST` unset, and don't run `carlos channels digests on`. The v0.26.0 notes say where each one is set.", "docs": [ "/docs/cli#carlos-channels-digests" ] } ] } ] }, "changes": [ { "sha": "9aaeace220780db1a844595f2d8f690bf2cba375", "date": "2026-10-13", "subject": "console test: the layout guard's at-floor update asks for a version the vendor feed can never publish", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] }, { "sha": "b8ddad180a6a9da7553b8e7210c9bc1e8db5e7df", "date": "2026-10-13", "subject": "agent: run the instance-key backfill after Start, off the edge's path (v0.26.1)", "commits": 1, "credit": [ { "person": "Paul Campbell", "agents": [ "claude" ] } ] } ] }, { "tag": "v0.26.0", "date": "2026-10-13", "sha": "09c5a4a5f30690d99a3d5c6ef4af2d8bea99a741", "published": true, "notes_url": "https://github.com/carlosframework/releases/releases/tag/v0.26.0", "notes": { "form": "bullets", "sections": [ { "title": "", "bullets": [ { "md": "**Two builds at once, off until your deployment turns it on.** Where it is on, a new build starts beside the old one and takes over once it passes its health check, so no request waits. For a few seconds both builds share the instance's database and its scheduled work, and the new build's migrations run while the old one still serves. So add a column, table or index in one release and drop the old one in a later release. Keep the names of scheduled work (`carlos schedule`) the same across the two builds. A release that can't follow these rules ships with `--swap hold` on `carlos deploy` or `carlos ship`, which stops each instance before its new build starts. `carlos apps set --swap hold` makes that the app's rule, and `--swap-concurrency` sets how many instances swap at once on one box. Every deployment ships with this off.", "branches": [ "zdd-1-generations", "zdd-2-containment", "zdd-3-swap", "zdd-4-release-side", "zdd-5-scheduler", "zdd-6-surfaces", "zdd-flag-readiness" ], "docs": [ "/docs/cli#carlos-schedule", "/docs/cli#carlos-deploy", "/docs/cli#carlos-ship", "/docs/cli#carlos-apps" ] }, { "md": "**`carlos deploy` follows the rollout box by box.** Where a box reports its rollout, deploy counts an awake instance once the new build has passed its health check and taken over, and an asleep one once its box has installed the build. If the build fails on a box, that box stops rolling it out, and deploy names the failure and exits with an error. Boxes report only where two builds at once is on. Everywhere else deploy watches the app's URL, as before. If the console can't list the app's instances, deploy keeps asking until its deadline and then says the list could not be read.", "branches": [ "zdd-6-surfaces", "zdd-flag-readiness" ], "docs": [ "/docs/cli#carlos-deploy" ] }, { "md": "**`carlos instances list` shows two builds.** INSTALLED is the build on the instance's box, and SERVING is the one answering its requests. They differ while a build rolls out, while an asleep instance waits for its next request, and after a build has failed on that box. REASON says which. A dash under SERVING means the box doesn't report it, which is every box until two builds at once is on. `carlos instances retry ` clears the failure hold on one instance, so the build that failed there is tried again as a canary.", "branches": [ "zdd-4-release-side", "zdd-6-surfaces" ], "docs": [ "/docs/cli#carlos-instances" ] }, { "md": "**Choose how each instance takes new builds.** `carlos instances set --updates manual` stops an instance taking new builds on its own. `--channel`, `--major` (stable only) and `--pin ` set the rest, and an empty value goes back to the app's default. `carlos instances update ` asks the box to install the newest build it considers ready, even with manual updates. `--fast-track` skips the safety delay, and only account owners can use it. On the app, `carlos apps set --instance-control updates,pin` names the settings an instance may change for itself once instances can act for themselves. `--default-updates`, `--default-major` and `--default-channel` set the app's defaults. The console accepts manual updates, a major hold or a pin only once every box can keep the builds they name, and says how many boxes it is waiting for.", "branches": [ "settings-3-console-settings", "settings-4-root-applies", "settings-5-surfaces" ], "docs": [ "/docs/cli#carlos-instances-set", "/docs/cli#carlos-instances-update", "/docs/cli#carlos-apps" ] }, { "md": "**Read a database as it was.** `carlos db pull --at