| index 6abfde9ad..91976b35a 100644 |
| --- a/docs/site/changelog.json |
| +++ b/docs/site/changelog.json |
| @@ -16,16 +16,25 @@ |
| "bullets": [ |
| { |
| "md": "**CI can deploy through the console.** `carlos services create --role deploy --app <a> --rungs canary,edge <name>` mints a credential that can ship, and promote or roll back only the rungs you name: `canary`, `edge` and `beta`, never `stable`. In CI, set `CARLOS_SERVICE_TOKEN` and `CARLOS_CONSOLE` and run `carlos deploy` as usual. The first deploy of a new app still needs a person, because a deploy credential cannot create the app or set up its first host. It cannot hotfix, delete the app, or change retention or domains. `carlos services list` gains a RUNGS column. The CLI refuses to run with both `CARLOS_SERVICE_TOKEN` and `CARLOS_DEPLOYMENT_BUCKET` or `CARLOS_DEPLOYMENT_DIR` set.", |
| + "branches": [ |
| + "design-deploy-credential" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-services", |
| "/docs/cli#carlos-deploy" |
| ] |
| }, |
| { |
| - "md": "**A failed rollback says so.** When the console cannot sign a rollback, it now answers \"Nothing was rolled back.\" Before, it said \"Nothing was promoted.\"" |
| + "md": "**A failed rollback says so.** When the console cannot sign a rollback, it now answers \"Nothing was rolled back.\" Before, it said \"Nothing was promoted.\"", |
| + "branches": [ |
| + "fix-deploy-rollback-copy" |
| + ] |
| }, |
| { |
| "md": "**`carlos db` no longer panics.** If `carlos db pull` or `query` could not create its temporary file, the restore crashed. It now reports the error.", |
| + "branches": [ |
| + "feat/hostclaim-door" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-db" |
| ] |
| @@ -36,31 +45,57 @@ |
| "title": "For operators", |
| "bullets": [ |
| { |
| - "md": "**The boxes come back onto the release train.** The flagship boxes have been running a hotfix build of v0.23.0 (f3f6cf73). It carried four fixes. Three are in v0.24.0, and their operator notes are there: a box runs its adopt work on its own loop, lists the bucket by app, and reads in parallel. The fourth is new in this release. When a box cannot stage a release itself, it asks a root pass to do it. That request used to be sent at most once a minute, and one made inside the minute was dropped, so a second release soon after the first waited for the next change to be noticed. On the flagship that was 98 extra seconds. The request is now limited to once every 15 seconds, and one made inside that window is sent at its end instead of dropped. Rolling this release moves those boxes off the hotfix with nothing lost." |
| + "md": "**The boxes come back onto the release train.** The flagship boxes have been running a hotfix build of v0.23.0 (f3f6cf73). It carried four fixes. Three are in v0.24.0, and their operator notes are there: a box runs its adopt work on its own loop, lists the bucket by app, and reads in parallel. The fourth is new in this release. When a box cannot stage a release itself, it asks a root pass to do it. That request used to be sent at most once a minute, and one made inside the minute was dropped, so a second release soon after the first waited for the next change to be noticed. On the flagship that was 98 extra seconds. The request is now limited to once every 15 seconds, and one made inside that window is sent at its end instead of dropped. Rolling this release moves those boxes off the hotfix with nothing lost.", |
| + "branches": [ |
| + "fix-stage-request-deferred" |
| + ] |
| }, |
| { |
| "md": "**If your boxes are coming from v0.23.0 or a hotfix of it, read v0.24.0's operator notes first.** Its changes now apply to the boxes: the keyless config guard, the box with no deployment store, and alerts to SNS. The check that matters most is this one. Every box that runs placed instances must hold the right `CARLOS_SECRETS_KEY` before you roll. A box without it, or with a key that does not open an instance's sealed config, will not wake that instance." |
| }, |
| { |
| "md": "**Leave `CARLOS_HOST_CLAIM` unset on the console.** Any value that is not blank turns it on, including `0` and `false`. The console reads it once, when it starts. Nothing writes host claims yet, so with it on, `carlos db pull` and `carlos db query` fail for every host.", |
| + "branches": [ |
| + "feat/hostclaim-door" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-db" |
| ] |
| }, |
| { |
| - "md": "**Do not set `CARLOS_REGION_HA` on a box yet.** It is the switch for multi-box regions, which this release does not finish. Left unset, a box publishes no `region-ha-1` capability and no headroom, and nothing changes." |
| + "md": "**Do not set `CARLOS_REGION_HA` on a box yet.** It is the switch for multi-box regions, which this release does not finish. Left unset, a box publishes no `region-ha-1` capability and no headroom, and nothing changes.", |
| + "branches": [ |
| + "design-region-ha" |
| + ] |
| }, |
| { |
| - "md": "**Deploy credentials need the console's signing key.** The console signs each deploy promote with a grant limited to the credential's rungs, so the console must hold its release key, as it already does for promotes. It adds a `rungs` column to its `cli_tokens` table when it starts. The change is additive." |
| + "md": "**Deploy credentials need the console's signing key.** The console signs each deploy promote with a grant limited to the credential's rungs, so the console must hold its release key, as it already does for promotes. It adds a `rungs` column to its `cli_tokens` table when it starts. The change is additive.", |
| + "branches": [ |
| + "design-deploy-credential" |
| + ] |
| }, |
| { |
| - "md": "**Region HA foundations ship dormant.** Every box's edges file gains `box_id`, which is the box's label. Box bootstrap also creates `/run/carlos/hostguard` and `/data/hostguard`, mode 2770, owned by `carlos:carlos`, when tmpfiles next runs. Nothing uses them yet. The host claim, generation selection and park libraries have no callers, so the boxes behave as before." |
| + "md": "**Region HA foundations ship dormant.** Every box's edges file gains `box_id`, which is the box's label. Box bootstrap also creates `/run/carlos/hostguard` and `/data/hostguard`, mode 2770, owned by `carlos:carlos`, when tmpfiles next runs. Nothing uses them yet. The host claim, generation selection and park libraries have no callers, so the boxes behave as before.", |
| + "branches": [ |
| + "design-region-ha", |
| + "feat/hostclaim-library", |
| + "feat/hostclaim-genselect", |
| + "feat/hostclaim-boxguard", |
| + "feat/hostclaim-park" |
| + ] |
| }, |
| { |
| - "md": "**Read the plan before applying the infrastructure modules.** `region-host` gains a termination queue, an EventBridge rule and a box-role policy to read the queue, and its root volume is now deleted when the instance terminates. `deployment-buckets` gains a bucket policy that makes `control/boxes/*` create-only and undeletable. A bucket holds one policy, so check whether yours already has one before you apply. Nothing in this release needs them, so applying can wait. The own-bucket S3 version grants are split into separately named statements, with the same permissions." |
| + "md": "**Read the plan before applying the infrastructure modules.** `region-host` gains a termination queue, an EventBridge rule and a box-role policy to read the queue, and its root volume is now deleted when the instance terminates. `deployment-buckets` gains a bucket policy that makes `control/boxes/*` create-only and undeletable. A bucket holds one policy, so check whether yours already has one before you apply. Nothing in this release needs them, so applying can wait. The own-bucket S3 version grants are split into separately named statements, with the same permissions.", |
| + "branches": [ |
| + "design-region-ha", |
| + "feat/hostclaim-door" |
| + ] |
| }, |
| { |
| - "md": "**The scanner host fix is in this release too.** It is the freshclam change from v0.24.1." |
| + "md": "**The scanner host fix is in this release too.** It is the freshclam change from v0.24.1.", |
| + "branches": [ |
| + "fix/scanner-freshclam-apparmor" |
| + ] |
| }, |
| { |
| "md": "**Either roll order works.** Nothing in this release needs the console and the boxes to roll in a particular order." |
| @@ -326,18 +361,32 @@ |
| "bullets": [ |
| { |
| "md": "**Canaries have a one-label address, and can be created again.** A canary's host is now `<name>-<app>.<your account>.<apps domain>`, so canary `new-queue` of app `jam` is `new-queue-jam.bab.oncarlos.com`. The old address put a dot between the name and the app. The console refused that, so `carlos canary` could not create a canary for an app that runs a process. The name and the app together can be at most 63 characters. You need this release's CLI.", |
| + "branches": [ |
| + "fix/canary-one-label" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-canary" |
| ] |
| }, |
| { |
| - "md": "**Deploys reach running instances sooner on busy boxes.** A box notices a promote sooner, and it restarts an awake instance onto the new release on its own two-second check instead of waiting for the rest of its work. On the flagship, noticing a promote had taken 16 to 43 seconds, and the restart then waited about 48 seconds more." |
| + "md": "**Deploys reach running instances sooner on busy boxes.** A box notices a promote sooner, and it restarts an awake instance onto the new release on its own two-second check instead of waiting for the rest of its work. On the flagship, noticing a promote had taken 16 to 43 seconds, and the restart then waited about 48 seconds more.", |
| + "branches": [ |
| + "fix-converge-own-loop", |
| + "fix-reconcile-listing", |
| + "fix-reconcile-parallel-reads" |
| + ] |
| }, |
| { |
| - "md": "**An instance never starts without its secrets.** A box that cannot open an instance's sealed secrets no longer wakes it or takes it on. A box that can takes it instead. If none can, it does not start. Before, it could start with none of its config or secrets." |
| + "md": "**An instance never starts without its secrets.** A box that cannot open an instance's sealed secrets no longer wakes it or takes it on. A box that can takes it instead. If none can, it does not start. Before, it could start with none of its config or secrets.", |
| + "branches": [ |
| + "fix/keyless-box-refuses" |
| + ] |
| }, |
| { |
| "md": "**One address, one app.** `carlos instances create` and `carlos canary` are refused when another app in your account already holds the host, including an app in the Trash, or when the host is a domain attached to another app. Before, both creates succeeded and one of them served nothing.", |
| + "branches": [ |
| + "fix/canary-one-label" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-instances", |
| "/docs/cli#carlos-canary" |
| @@ -352,31 +401,53 @@ |
| "title": "For operators", |
| "bullets": [ |
| { |
| - "md": "**Check every box's secrets key before rolling the boxes.** A box now takes an exec instance only if it can open that instance's sealed config. Without `CARLOS_SECRETS_KEY`, or with a key that does not open the config, it does not wake the instance or mint its record. It logs `agent: config guard: refusing <host>` and lists the host under `metrics.config_refusals` in its private snapshot, `status/boxes/<box>.json`. A box that cannot read the deployment store to check refuses too, and asks again after 30 seconds. Instances with no sealed config are not affected." |
| + "md": "**Check every box's secrets key before rolling the boxes.** A box now takes an exec instance only if it can open that instance's sealed config. Without `CARLOS_SECRETS_KEY`, or with a key that does not open the config, it does not wake the instance or mint its record. It logs `agent: config guard: refusing <host>` and lists the host under `metrics.config_refusals` in its private snapshot, `status/boxes/<box>.json`. A box that cannot read the deployment store to check refuses too, and asks again after 30 seconds. Instances with no sealed config are not affected.", |
| + "branches": [ |
| + "fix/keyless-box-refuses" |
| + ] |
| }, |
| { |
| - "md": "**A box with no deployment store refuses exec instances.** With `CARLOS_HIBERNATE_BUCKET` set and neither `CARLOS_DEPLOYMENT_BUCKET` nor `CARLOS_DEPLOYMENT_DIR`, a box cannot tell whether an instance's config is sealed, so it refuses. On a deployment with no sealed config at all, set `CARLOS_NO_SEALED_CONFIG=1`. Never set it on a box that serves tenants. The box then logs that the check is bypassed and says so in its snapshot. With a deployment store configured, the setting is ignored." |
| + "md": "**A box with no deployment store refuses exec instances.** With `CARLOS_HIBERNATE_BUCKET` set and neither `CARLOS_DEPLOYMENT_BUCKET` nor `CARLOS_DEPLOYMENT_DIR`, a box cannot tell whether an instance's config is sealed, so it refuses. On a deployment with no sealed config at all, set `CARLOS_NO_SEALED_CONFIG=1`. Never set it on a box that serves tenants. The box then logs that the check is bypassed and says so in its snapshot. With a deployment store configured, the setting is ignored.", |
| + "branches": [ |
| + "fix/keyless-box-refuses" |
| + ] |
| }, |
| { |
| - "md": "**Roll the console to this release before pointing it at a scanner host.** An older console records an archive that clamd could only partly scan as infected. This release records it as not scanned, and a scan time-out as failed, so the next sweep tries it again. A real signature match is still infected." |
| + "md": "**Roll the console to this release before pointing it at a scanner host.** An older console records an archive that clamd could only partly scan as infected. This release records it as not scanned, and a scan time-out as failed, so the next sweep tries it again. A real signature match is still infected.", |
| + "branches": [ |
| + "worktree-scanner-host" |
| + ] |
| }, |
| { |
| "md": "**Object-storage scanning has its own box.** The new `infra/modules/scanner-host` module runs clamd on one box per deployment, reachable only from the console's security group. Set its outputs on the console: `clamd_addr` as `CARLOS_CLAMD_ADDR` and `stream_max_bytes` as `CARLOS_CLAMD_MAX_BYTES`. The console also needs `CARLOS_SCAN_TAG_KEY`, and refuses to boot with an address and no key. Set `store_scanning = true` on the console box's `region-host` so its role can read and tag store objects. It is off by default, and a deployment that leaves it off plans no change. Wiring the engine does not switch scanning on. `carlos system antivirus enable` does. A replaced scanner box gets a new private IP, so update `CARLOS_CLAMD_ADDR` after any `-replace`. Pin modules to v0.24.0: v0.23.0 does not contain `scanner-host`.", |
| + "branches": [ |
| + "worktree-scanner-host" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-system" |
| ] |
| }, |
| { |
| - "md": "**The app regions rollback floor is v0.23.0.** v0.23.0 is the first release that understands app regions. Once any app holds a region other than the home, do not roll the console below v0.23.0." |
| + "md": "**The app regions rollback floor is v0.23.0.** v0.23.0 is the first release that understands app regions. Once any app holds a region other than the home, do not roll the console below v0.23.0.", |
| + "branches": [ |
| + "docs-regions-floor-v0.23.0" |
| + ] |
| }, |
| { |
| "md": "**Boxes can send alerts to SNS.** Set `CARLOS_ALERT_SNS_TOPIC_ARN` to the box's own region's alerts topic, and anyone subscribed gets each fire and clear by email. The box's instance role needs `sns:Publish` on that topic. The modules in this repo do not grant it. A box with neither this nor `CARLOS_ALERT_WEBHOOK` logs `NO SINK CONFIGURED` when it starts. `carlos status --live` gains a last column: `alerts=sns`, `alerts=webhook`, `alerts=webhook+sns`, `alerts=not-delivered`, or `alerts=sns-unavailable` when a topic is set but the box could not build its SNS client. A dash means the box runs an older release.", |
| + "branches": [ |
| + "feat/alerts-sns" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-status" |
| ] |
| }, |
| { |
| - "md": "**Boxes list the bucket by app.** To find instance records, a box now lists each account's apps and each app's `instances/` prefix, eight requests at a time, instead of every object under `apps/`. If one app's listing fails, the box logs a `provision: listing` line, converges everything it did see, and removes no route on that pass. The agent also logs an `agent: fast converge:` line when a step of its fast loop takes longer than five seconds." |
| + "md": "**Boxes list the bucket by app.** To find instance records, a box now lists each account's apps and each app's `instances/` prefix, eight requests at a time, instead of every object under `apps/`. If one app's listing fails, the box logs a `provision: listing` line, converges everything it did see, and removes no route on that pass. The agent also logs an `agent: fast converge:` line when a step of its fast loop takes longer than five seconds.", |
| + "branches": [ |
| + "fix-reconcile-listing", |
| + "fix-reconcile-parallel-reads" |
| + ] |
| }, |
| { |
| "md": "**Either roll order works.** Nothing in this release needs the console and the boxes to roll in a particular order." |
| @@ -587,21 +658,33 @@ |
| "bullets": [ |
| { |
| "md": "**A deploy reaches a running instance in seconds.** When a box adopts a new release, an instance that is awake restarts onto it within a few seconds. Before, it kept serving the old build for about two minutes, longer than `carlos deploy` waits, so a deploy could report a timeout and a restart seemed to be needed. Instances on one box restart one at a time, two seconds apart.", |
| + "branches": [ |
| + "fix-adopted-exec-restart" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-deploy" |
| ] |
| }, |
| { |
| - "md": "**Static sites show a deploy on the next page load.** Browsers now check with the edge before reusing a page or file from a static site, and get a quick \"not modified\" answer while nothing has changed. Before, a browser could keep showing the old page for hours or days after a deploy." |
| + "md": "**Static sites show a deploy on the next page load.** Browsers now check with the edge before reusing a page or file from a static site, and get a quick \"not modified\" answer while nothing has changed. Before, a browser could keep showing the old page for hours or days after a deploy.", |
| + "branches": [ |
| + "static-cache-control" |
| + ] |
| }, |
| { |
| "md": "**Read a copy of an instance's database.** `carlos db pull --app <a> --out <file>` saves a copy of a sleeping instance's database on your machine, and `carlos db query --app <a> --sql \"…\"` runs one query against a fresh copy. Both say how recent the copy is. The copy is read-only and never touches the live database. Name an instance with `--instance` when the app has more than one.", |
| + "branches": [ |
| + "feat/carlos-db-read" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-db" |
| ] |
| }, |
| { |
| "md": "**Region flags.** `carlos apps create`, `carlos deploy` and `carlos canary` take `--region`. A console that is not assigning app regions yet says so and starts the app in its home region. `carlos deploy --region` on an existing app stops before shipping if the region differs from the app's own. The line `carlos instances create` prints now names the place the instance runs.", |
| + "branches": [ |
| + "design-nearest-region" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-apps", |
| "/docs/cli#carlos-deploy", |
| @@ -610,19 +693,31 @@ |
| ] |
| }, |
| { |
| - "md": "**Invite links ask before you join.** Opening an invite link shows who invited you and a Join button. Nothing happens until you press it." |
| + "md": "**Invite links ask before you join.** Opening an invite link shows who invited you and a Join button. Nothing happens until you press it.", |
| + "branches": [ |
| + "tenant-iso-leftovers" |
| + ] |
| }, |
| { |
| "md": "**Sending domains you did not set up.** If a domain already has a sending identity that your account did not create, `carlos email domains add` asks you to prove you control it: publish a TXT record at `_carlos-mail.<domain>` with the value `carlos-mail-account=<account>`, then add the domain again. A domain another account holds now reads \"sending domain unavailable\". A claim that is not verified within 7 days can be taken by another account.", |
| + "branches": [ |
| + "tenant-iso-mail" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-email" |
| ] |
| }, |
| { |
| - "md": "**Stores no longer share a bucket by accident.** App and environment names are joined with \"-\", so app `shop` with environment `eu` and app `shop-eu` used to map to the same bucket. The second one is now refused with a 409, and nothing is created." |
| + "md": "**Stores no longer share a bucket by accident.** App and environment names are joined with \"-\", so app `shop` with environment `eu` and app `shop-eu` used to map to the same bucket. The second one is now refused with a 409, and nothing is created.", |
| + "branches": [ |
| + "tenant-iso-objstore" |
| + ] |
| }, |
| { |
| "md": "**Platform domains.** An operator can attach one of the deployment's own domains to an app with `carlos domains attach --platform`. Members cannot attach, change or detach those names.", |
| + "branches": [ |
| + "platform-domain-claims-209" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-domains" |
| ] |
| @@ -636,28 +731,50 @@ |
| "md": "**Roll the boxes, then the console.** With the new switches left off, either order is safe. The two switches below need every box on v0.23.0 first." |
| }, |
| { |
| - "md": "**Leave `CONSOLE_SECRETS_ENVELOPE_V2` off until every box runs v0.23.0.** When it is on, the console seals new secrets so they open only for their own account and app. A box on an older release cannot open them, and the app's whole config stops updating on that box. Rolling a box back below v0.23.0 after turning it on does the same. A value that is not true or false stops the console from starting." |
| + "md": "**Leave `CONSOLE_SECRETS_ENVELOPE_V2` off until every box runs v0.23.0.** When it is on, the console seals new secrets so they open only for their own account and app. A box on an older release cannot open them, and the app's whole config stops updating on that box. Rolling a box back below v0.23.0 after turning it on does the same. A value that is not true or false stops the console from starting.", |
| + "branches": [ |
| + "tenant-iso-binding" |
| + ] |
| }, |
| { |
| - "md": "**v0.23.0 is the first release that understands app regions.** That makes it the rollback floor the README describes: once any app holds a region other than the home, do not roll the console below v0.23.0. `CONSOLE_APP_REGIONS` is off by default. Turn it on only by the README's steps. Boxes now publish `serves_shared_pool`, `accepts_inbound` and, where they hold the secrets key, `secrets_key_fp` in their edges file." |
| + "md": "**v0.23.0 is the first release that understands app regions.** That makes it the rollback floor the README describes: once any app holds a region other than the home, do not roll the console below v0.23.0. `CONSOLE_APP_REGIONS` is off by default. Turn it on only by the README's steps. Boxes now publish `serves_shared_pool`, `accepts_inbound` and, where they hold the secrets key, `secrets_key_fp` in their edges file.", |
| + "branches": [ |
| + "design-nearest-region" |
| + ] |
| }, |
| { |
| - "md": "**Boxes restart awake instances when they adopt a release.** The agent checks every two seconds and restarts at most one instance per check. Workers still converge on the old 90-second timer. A restart drains the old process as before: new requests wait for the new one." |
| + "md": "**Boxes restart awake instances when they adopt a release.** The agent checks every two seconds and restarts at most one instance per check. Workers still converge on the old 90-second timer. A restart drains the old process as before: new requests wait for the new one.", |
| + "branches": [ |
| + "fix-adopted-exec-restart" |
| + ] |
| }, |
| { |
| - "md": "**Static responses change their headers.** They now carry `Cache-Control: no-cache` and a weak `ETag` worked out from each file's bytes and type, and no `Last-Modified`. The first request for a file after a deploy reads it once to work out the tag. The edge's own cache still does not store static responses." |
| + "md": "**Static responses change their headers.** They now carry `Cache-Control: no-cache` and a weak `ETag` worked out from each file's bytes and type, and no `Last-Modified`. The first request for a file after a deploy reads it once to work out the tag. The edge's own cache still does not store static responses.", |
| + "branches": [ |
| + "static-cache-control" |
| + ] |
| }, |
| { |
| - "md": "**Pin infrastructure modules to v0.23.0.** The box role gains permission to list, read and delete object versions in its own data bucket. Nothing uses it yet, so applying it can wait. Check also that nothing in your policies denies `s3:DeleteObject` on the deployment bucket: purges, store teardown and mail claim release delete objects, and fail without it." |
| + "md": "**Pin infrastructure modules to v0.23.0.** The box role gains permission to list, read and delete object versions in its own data bucket. Nothing uses it yet, so applying it can wait. Check also that nothing in your policies denies `s3:DeleteObject` on the deployment bucket: purges, store teardown and mail claim release delete objects, and fail without it.", |
| + "branches": [ |
| + "feat/hostclaim-storage" |
| + ] |
| }, |
| { |
| "md": "**Run `carlos accounts migrate` with a v0.23.0 CLI.** Pointers are now signed for their account, and migrating re-signs them, which needs the root `CARLOS_RELEASE_KEY`. An older CLI copies them unchanged, and the moved app stops adopting releases.", |
| + "branches": [ |
| + "tenant-iso-binding" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-accounts" |
| ] |
| }, |
| { |
| - "md": "**Existing data is left in place.** Purging an app now also deletes its tokens, door keys and ledgers, but apps purged before this release keep theirs. Removing a sending domain that was added before this release leaves its SES identity in SES. Moving an app revokes its door keys, so moved apps need new ones." |
| + "md": "**Existing data is left in place.** Purging an app now also deletes its tokens, door keys and ledgers, but apps purged before this release keep theirs. Removing a sending domain that was added before this release leaves its SES identity in SES. Moving an app revokes its door keys, so moved apps need new ones.", |
| + "branches": [ |
| + "tenant-iso-leftovers", |
| + "tenant-iso-mail" |
| + ] |
| } |
| ] |
| } |
| @@ -1025,10 +1142,16 @@ |
| "title": "", |
| "bullets": [ |
| { |
| - "md": "**Database size on the app Overview.** A new tile shows how much disk your app's databases use, including copies that are asleep." |
| + "md": "**Database size on the app Overview.** A new tile shows how much disk your app's databases use, including copies that are asleep.", |
| + "branches": [ |
| + "feature/instance-db-stats-card" |
| + ] |
| }, |
| { |
| - "md": "**Busy and large instances can finish a move.** An instance that is awake and writing, or that keeps large database files, can now finish moving to another region. Before, such a move could wait in `preparing` and never become ready. The cut still copies everything that is left, exactly, before the instance serves from its new region." |
| + "md": "**Busy and large instances can finish a move.** An instance that is awake and writing, or that keeps large database files, can now finish moving to another region. Before, such a move could wait in `preparing` and never become ready. The cut still copies everything that is left, exactly, before the instance serves from its new region.", |
| + "branches": [ |
| + "move-ready-delta-threshold" |
| + ] |
| }, |
| { |
| "md": "**Nothing new to type.** The CLI has no new commands or flags in this release." |
| @@ -1042,16 +1165,28 @@ |
| "md": "**v0.22.1 was tagged but never published.** v0.22.2 supersedes it, so roll v0.22.2 and pin modules to v0.22.2." |
| }, |
| { |
| - "md": "**A move becomes ready when little is left to copy.** By default, a preparing round now promotes when it copied 16 MiB or less. To change that bar, set `CARLOS_MOVE_READY_DELTA_BYTES` in `/etc/carlos/host.env`; the agent reads it when it starts. `0` restores the old rule that a round must copy nothing. A value that is not a whole number of bytes is ignored with a log line, and the default stays. The ready log line now shows the bytes copied, the number of changes and the bar." |
| + "md": "**A move becomes ready when little is left to copy.** By default, a preparing round now promotes when it copied 16 MiB or less. To change that bar, set `CARLOS_MOVE_READY_DELTA_BYTES` in `/etc/carlos/host.env`; the agent reads it when it starts. `0` restores the old rule that a round must copy nothing. A value that is not a whole number of bytes is ignored with a log line, and the default stays. The ready log line now shows the bytes copied, the number of changes and the bar.", |
| + "branches": [ |
| + "move-ready-delta-threshold" |
| + ] |
| }, |
| { |
| - "md": "**Copy receipts in the source box's data bucket.** A box moving an instance away keeps receipts at `move-receipts/<host>.json` in its own data bucket, so large objects that have not changed are not copied again. The cut and an abort delete them. A deployment whose data-bucket policy denies DeleteObject keeps stale receipts, which are harmless. The box policy in this repo already allows the prefix." |
| + "md": "**Copy receipts in the source box's data bucket.** A box moving an instance away keeps receipts at `move-receipts/<host>.json` in its own data bucket, so large objects that have not changed are not copied again. The cut and an abort delete them. A deployment whose data-bucket policy denies DeleteObject keeps stale receipts, which are harmless. The box policy in this repo already allows the prefix.", |
| + "branches": [ |
| + "move-ready-delta-threshold" |
| + ] |
| }, |
| { |
| - "md": "**The cut is still exact.** It copies everything that is left before it flips the record. If an object changes during that final copy, the cut waits for the next pass instead of landing an incomplete copy." |
| + "md": "**The cut is still exact.** It copies everything that is left before it flips the record. If an object changes during that final copy, the cut waits for the next pass instead of landing an incomplete copy.", |
| + "branches": [ |
| + "move-ready-delta-threshold" |
| + ] |
| }, |
| { |
| - "md": "**The Database tile is console only.** It reads the database sizes boxes have reported since v0.21.0, so it needs no box roll and no env or IAM change. A slow read shows a dash and never holds up the page." |
| + "md": "**The Database tile is console only.** It reads the database sizes boxes have reported since v0.21.0, so it needs no box roll and no env or IAM change. A slow read shows a dash and never holds up the page.", |
| + "branches": [ |
| + "feature/instance-db-stats-card" |
| + ] |
| } |
| ] |
| } |
| @@ -1164,36 +1299,54 @@ |
| "bullets": [ |
| { |
| "md": "**Availability, apart from errors.** `carlos availability` lists the requests an app's instances could not answer, every 502, 503 and 504 the edge saw, grouped by cause instead of by page. One outage is one row, however many pages it hit. `--fp <id>` expands a cause to each request and the path it asked for. The same view is the new Availability tab beside Errors.", |
| + "branches": [ |
| + "availability-tab" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-availability" |
| ] |
| }, |
| { |
| "md": "**Errors is shorter.** `carlos errors` and the Errors tab keep the app's own 500s and the error lines in its logs. Edge 502s, 503s and 504s, failed wakes and exits have moved to Availability.", |
| + "branches": [ |
| + "availability-tab" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-errors" |
| ] |
| }, |
| { |
| "md": "**Replica instances.** `carlos instances create --role replica --region <r>` makes a second instance of an app, holding a one-way copy of the live instance's data, in a region near its buyers. The live instance names the regions it allows replicas in with `--replicas ie,uk` when it is created. Until a batch of orders drains home, a replica holds order data in its own region, so the regions are always a decision on the live instance, never a default.", |
| + "branches": [ |
| + "edge-replica-placements" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-instances" |
| ] |
| }, |
| { |
| "md": "**Door keys.** `carlos instances door-key create`, `list` and `revoke` manage keys for the edge-local door. That is one socket on a box that a process running beside an app's instances can dial to reach any of them, whether it is asleep, moved to another box or awake. A key is shown once, when it is created. The door is off until the box's operator turns it on.", |
| + "branches": [ |
| + "instance-moves-p7" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-instances" |
| ] |
| }, |
| { |
| "md": "**Faster log reads.** Log chunks are now fetched 16 at a time instead of one by one, which speeds up `carlos logs`, Errors and Availability.", |
| + "branches": [ |
| + "parallel-chunk-read" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-logs" |
| ] |
| }, |
| { |
| - "md": "**A restored app keeps its pipeline.** When the console registers an app whose files are already in the bucket, it keeps that app's release pipeline instead of resetting it to the default." |
| + "md": "**A restored app keeps its pipeline.** When the console registers an app whose files are already in the bucket, it keeps that app's release pipeline instead of resetting it to the default.", |
| + "branches": [ |
| + "fix/preserve-adopted-app-pipeline" |
| + ] |
| } |
| ] |
| }, |
| @@ -1201,35 +1354,60 @@ |
| "title": "For operators", |
| "bullets": [ |
| { |
| - "md": "**Roll the console first, then the boxes, close together.** The boxes record why the edge had no answer and the console shows it. A console on its own groups stored 502s under \"cause not recorded\", and new ones too until the boxes roll. Boxes on their own, under an old console, list each cause as its own row on Errors. Rolling the console back is safe: nothing in the bucket is rewritten." |
| + "md": "**Roll the console first, then the boxes, close together.** The boxes record why the edge had no answer and the console shows it. A console on its own groups stored 502s under \"cause not recorded\", and new ones too until the boxes roll. Boxes on their own, under an old console, list each cause as its own row on Errors. Rolling the console back is safe: nothing in the bucket is rewritten.", |
| + "branches": [ |
| + "availability-tab" |
| + ] |
| }, |
| { |
| "md": "**A new CLI against an old console.** `carlos availability` fails with `not found: check --account and --app, and that your token's address is a member`. Until the console rolls, that message means the console predates the command, not that the app or your membership is missing. An old console silently ignores `carlos instances create --role replica` and creates an ordinary instance in that region instead.", |
| + "branches": [ |
| + "availability-tab", |
| + "edge-replica-placements" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-availability", |
| "/docs/cli#carlos-instances" |
| ] |
| }, |
| { |
| - "md": "**No replicas until every box has rolled.** Only this release keeps replicas out of DNS latency routing. Each box adds two columns to its registry when it starts (`routes.replicas` and `routes.replica`). There is nothing to migrate, and a box rolled back ignores them." |
| + "md": "**No replicas until every box has rolled.** Only this release keeps replicas out of DNS latency routing. Each box adds two columns to its registry when it starts (`routes.replicas` and `routes.replica`). There is nothing to migrate, and a box rolled back ignores them.", |
| + "branches": [ |
| + "edge-replica-placements" |
| + ] |
| }, |
| { |
| - "md": "**Long polls show up in Availability.** A visitor who gives up after waiting five seconds or more, before the instance has sent response headers, is filed as an outage. On a sleeping route the five seconds include the wake. An endpoint that holds its headers longer by design, like a long poll, shows a standing row, and that row is expected. A visitor who leaves sooner is no longer filed at all, so expect fewer one-off 502s on static files." |
| + "md": "**Long polls show up in Availability.** A visitor who gives up after waiting five seconds or more, before the instance has sent response headers, is filed as an outage. On a sleeping route the five seconds include the wake. An endpoint that holds its headers longer by design, like a long poll, shows a standing row, and that row is expected. A visitor who leaves sooner is no longer filed at all, so expect fewer one-off 502s on static files.", |
| + "branches": [ |
| + "availability-tab" |
| + ] |
| }, |
| { |
| - "md": "**Tunnelled failures are filed once.** On a route a box reaches over a tunnel or a fleet, the front box files only its own hop failures, and the box serving the app files the rest." |
| + "md": "**Tunnelled failures are filed once.** On a route a box reaches over a tunnel or a fleet, the front box files only its own hop failures, and the box serving the app files the rest.", |
| + "branches": [ |
| + "availability-tab" |
| + ] |
| }, |
| { |
| - "md": "**Instance moves.** A box cutting a move now fences the instance so a scheduled wake cannot start it again mid-move. A move past ready can no longer be aborted, and a replica cannot be moved: delete it and create one in the new region. A later release's boxes rely on this console's abort rule, which is one more reason to roll the console first." |
| + "md": "**Instance moves.** A box cutting a move now fences the instance so a scheduled wake cannot start it again mid-move. A move past ready can no longer be aborted, and a replica cannot be moved: delete it and create one in the new region. A later release's boxes rely on this console's abort rule, which is one more reason to roll the console first.", |
| + "branches": [ |
| + "instance-moves-p0" |
| + ] |
| }, |
| { |
| "md": "**The edge-local door is off by default.** It stays off until a box sets `CARLOS_EDGE_DOOR=/run/carlos/edge-local.sock` and its agent restarts. The calling process's user must also be enrolled with `carlos bootstrap --socket-consumer <user>`. The console re-signs door keys every 12 hours, and a key whose record has not been re-signed for 72 hours stops working, so a console down that long closes every door.", |
| + "branches": [ |
| + "instance-moves-p7" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-bootstrap" |
| ] |
| }, |
| { |
| "md": "**Each instance gets its own `/dev/shm`.** Instance units no longer see the host's `/dev/shm`, so an instance cannot reach another's shared memory. They also no longer set `RestrictSUIDSGID=`, which broke `openat2` on systemd 257 and kept the isolated exec probe from reporting ready. `carlos system update` installs the new units, and a running instance picks them up when it next restarts.", |
| + "branches": [ |
| + "isolated-exec-probe-fixes" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-system" |
| ] |
| @@ -1509,10 +1687,16 @@ |
| "title": "", |
| "bullets": [ |
| { |
| - "md": "**A new address is never a dead end.** An edge that hasn't heard of a host under the deployment's own domains yet answers 503 with `Retry-After` and a page that tries again on its own for 90 seconds, instead of \"unknown host\". Edges also learn which box serves a new host within about 30 seconds, down from as much as six minutes." |
| + "md": "**A new address is never a dead end.** An edge that hasn't heard of a host under the deployment's own domains yet answers 503 with `Retry-After` and a page that tries again on its own for 90 seconds, instead of \"unknown host\". Edges also learn which box serves a new host within about 30 seconds, down from as much as six minutes.", |
| + "branches": [ |
| + "edge-unknown-host-174" |
| + ] |
| }, |
| { |
| - "md": "**The release recipe is in the README.** \"Releasing the platform\" now walks through tagging, building, signing, publishing, Homebrew and apt." |
| + "md": "**The release recipe is in the README.** \"Releasing the platform\" now walks through tagging, building, signing, publishing, Homebrew and apt.", |
| + "branches": [ |
| + "docs-release-recipe" |
| + ] |
| } |
| ] |
| }, |
| @@ -1575,6 +1759,10 @@ |
| "bullets": [ |
| { |
| "md": "**Named deploy destinations.** `carlos config destinations` keeps the places an app deploys to in `.carlos/config`, and `carlos deploy -d <name>` picks one.", |
| + "branches": [ |
| + "config-destinations-cli", |
| + "carlos-deploy-targets" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-config", |
| "/docs/cli#carlos-deploy" |
| @@ -1582,27 +1770,45 @@ |
| }, |
| { |
| "md": "**Sidecars.** `carlos sidecars` runs a process beside each enrolled member of an app, such as a document converter. it takes enable, create, ls, wake, rm and logs.", |
| + "branches": [ |
| + "sidecar-members", |
| + "sidecar-engine-capability" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-sidecars" |
| ] |
| }, |
| { |
| "md": "**Email you can follow.** `carlos email events` shows what happened to each message an app sent (delivered, bounced, complained about or delayed) for 30 days. `carlos email events push` posts those events to the app itself, signed. `carlos email domains rm` stops an app sending from a domain and frees the domain for another account once no app uses it.", |
| + "branches": [ |
| + "mail-events-log", |
| + "mail-events-push" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-email" |
| ] |
| }, |
| { |
| "md": "**A store per environment.** `carlos store create --environment <e>` gives one named environment its own bucket and credential, delivered into that environment's config. an app can now have each instance get its own environment and store.", |
| + "branches": [ |
| + "per-instance-store", |
| + "412-storage-isolation-config" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-store" |
| ] |
| }, |
| { |
| - "md": "**Purge every host of an account at once.** `Carlos-Purge-Host` now takes a list of up to 32 hosts, each checked against the same app and account. a host the box doesn't serve is skipped. The control socket's `POST /purge` takes `{\"path\":\"*\"}` to clear the whole host." |
| + "md": "**Purge every host of an account at once.** `Carlos-Purge-Host` now takes a list of up to 32 hosts, each checked against the same app and account. a host the box doesn't serve is skipped. The control socket's `POST /purge` takes `{\"path\":\"*\"}` to clear the whole host.", |
| + "branches": [ |
| + "edge-purge-hosts-173" |
| + ] |
| }, |
| { |
| - "md": "**Faster edge start on distant boxes.** certificates now load in parallel at boot, so a box far from its bucket no longer fails its update check." |
| + "md": "**Faster edge start on distant boxes.** certificates now load in parallel at boot, so a box far from its bucket no longer fails its update check.", |
| + "branches": [ |
| + "fix-edge-boot-refresh" |
| + ] |
| } |
| ] |
| }, |
| @@ -2166,6 +2372,9 @@ |
| "bullets": [ |
| { |
| "md": "**Self-serve object storage.** `carlos store create` is the whole path once an account is approved; accounts apply and operators decide through the console.", |
| + "branches": [ |
| + "worktree-store-self-serve" |
| + ], |
| "docs": [ |
| "/docs/cli#carlos-store" |
| ] |
| @@ -2174,10 +2383,17 @@ |
| "md": "**Console instance pages.** Instance status, activity and live logs are wired up in the console." |
| }, |
| { |
| - "md": "**Edge reads `s-maxage`**, so an origin can address the edge cache and the browser cache separately." |
| + "md": "**Edge reads `s-maxage`**, so an origin can address the edge cache and the browser cache separately.", |
| + "branches": [ |
| + "edge-s-maxage" |
| + ] |
| }, |
| { |
| - "md": "**Maintenance worker.** Pinned maintenance subprocesses are supervised; a launch context codec is shared with standalone consumers." |
| + "md": "**Maintenance worker.** Pinned maintenance subprocesses are supervised; a launch context codec is shared with standalone consumers.", |
| + "branches": [ |
| + "feat-maintenance-worker", |
| + "feat-launchctx-module" |
| + ] |
| } |
| ] |
| } |