diff --git a/Cargo.lock b/Cargo.lock index d558265b..37b882fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3670,7 +3670,7 @@ dependencies = [ [[package]] name = "tw-api" -version = "0.81.0" +version = "0.82.0" dependencies = [ "serde", "serde_json", @@ -3681,7 +3681,7 @@ dependencies = [ [[package]] name = "tw-bedrock" -version = "0.81.0" +version = "0.82.0" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -3702,7 +3702,7 @@ dependencies = [ [[package]] name = "tw-breaker" -version = "0.81.0" +version = "0.82.0" dependencies = [ "serde", "serde_json", @@ -3710,7 +3710,7 @@ dependencies = [ [[package]] name = "tw-config" -version = "0.81.0" +version = "0.82.0" dependencies = [ "blake3", "libc", @@ -3736,7 +3736,7 @@ dependencies = [ [[package]] name = "tw-control" -version = "0.81.0" +version = "0.82.0" dependencies = [ "axum", "base64 0.22.1", @@ -3782,7 +3782,7 @@ dependencies = [ [[package]] name = "tw-dialect" -version = "0.81.0" +version = "0.82.0" dependencies = [ "memchr", "serde", @@ -3791,7 +3791,7 @@ dependencies = [ [[package]] name = "tw-engine" -version = "0.81.0" +version = "0.82.0" dependencies = [ "chrono", "serde", @@ -3805,7 +3805,7 @@ dependencies = [ [[package]] name = "tw-gateway" -version = "0.81.0" +version = "0.82.0" dependencies = [ "arc-swap", "async-stream", @@ -3856,7 +3856,7 @@ dependencies = [ [[package]] name = "tw-guard" -version = "0.81.0" +version = "0.82.0" dependencies = [ "aho-corasick", "base64 0.22.1", @@ -3874,7 +3874,7 @@ dependencies = [ [[package]] name = "tw-handover" -version = "0.81.0" +version = "0.82.0" dependencies = [ "libc", "serde", @@ -3887,7 +3887,7 @@ dependencies = [ [[package]] name = "tw-link" -version = "0.81.0" +version = "0.82.0" dependencies = [ "serde", "serde_json", @@ -3901,7 +3901,7 @@ dependencies = [ [[package]] name = "tw-observe" -version = "0.81.0" +version = "0.82.0" dependencies = [ "tokio", "tracing", @@ -3910,7 +3910,7 @@ dependencies = [ [[package]] name = "tw-pack" -version = "0.81.0" +version = "0.82.0" dependencies = [ "age", "blake3", @@ -3934,7 +3934,7 @@ dependencies = [ [[package]] name = "tw-plugin" -version = "0.81.0" +version = "0.82.0" dependencies = [ "libc", "rand 0.10.2", @@ -3950,7 +3950,7 @@ dependencies = [ [[package]] name = "tw-pricing" -version = "0.81.0" +version = "0.82.0" dependencies = [ "arc-swap", "flate2", @@ -3963,14 +3963,14 @@ dependencies = [ [[package]] name = "tw-secret" -version = "0.81.0" +version = "0.82.0" dependencies = [ "thiserror 2.0.20", ] [[package]] name = "tw-store" -version = "0.81.0" +version = "0.82.0" dependencies = [ "blake3", "bytes", @@ -3990,7 +3990,7 @@ dependencies = [ [[package]] name = "tw-sync" -version = "0.81.0" +version = "0.82.0" dependencies = [ "age", "axum", @@ -4018,7 +4018,7 @@ dependencies = [ [[package]] name = "tw-types" -version = "0.81.0" +version = "0.82.0" dependencies = [ "serde", "serde_json", @@ -4027,7 +4027,7 @@ dependencies = [ [[package]] name = "tw-watch" -version = "0.81.0" +version = "0.82.0" dependencies = [ "libc", "notify", @@ -4038,7 +4038,7 @@ dependencies = [ [[package]] name = "tw-yaml" -version = "0.81.0" +version = "0.82.0" dependencies = [ "saphyr-parser", "serde_yaml_ng", @@ -4048,7 +4048,7 @@ dependencies = [ [[package]] name = "twcore" -version = "0.81.0" +version = "0.82.0" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 9ebedc58..89dbfb48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ categories = ["network-programming", "web-programming::http-server"] # 二进制走 CalVer,crate 走 SemVer —— 两者是两回事,卖给不同的人。 # 这里是 crate 的版本。 -version = "0.81.0" +version = "0.82.0" [workspace.dependencies] # ── 内部 crate ─────────────────────────────────────────── diff --git a/release-notes/0.82.0.md b/release-notes/0.82.0.md new file mode 100644 index 00000000..46e2636a --- /dev/null +++ b/release-notes/0.82.0.md @@ -0,0 +1,21 @@ +This release lets core be replaced without closing the gateway port or cutting requests that are under way: the running core hands its listening sockets and its state to the core that replaces it. + +**Upgrade notes** + +- The control-plane protocol version goes from 51 to 52. The request store's schema is unchanged; nothing is cleared. +- No configuration key is added, renamed or removed. +- Upgrading to this release still restarts core the previous way. Upgrades from this release onward are seamless. + +**Upgrades no longer drop requests.** A desktop app that is about to restart for an update now calls `POST /handover/arm`. Core stops following its parent process, opens a handover endpoint in the data directory, and waits for its successor. + +- The successor finds the lock held by an armed core, connects, and takes over the control plane, the gateway listeners and the remote control port. The same kernel sockets are passed over, so connections that were queued but not yet accepted are served by the new core instead of being reset. +- The old core finishes the requests it already has (for up to 30 minutes) and exits. Responses WebSocket sessions are closed with code 1012 once their current turn ends, and clients reconnect to the new core. +- The new core starts from the old core's state: conversation affinity, circuit breaker and rate-limit pauses, latency and speed figures, load-balancing turns, OAuth access tokens, and key and upstream usage. While both cores run, slots and usage are settled between them, so concurrency and usage limits are not doubled. +- OAuth renewals are paused while the two cores overlap, because renewing a refresh token invalidates the previous one. +- Anything that fails before the new core commits leaves the old core serving. If the two cores speak different handover versions, the old core drains and exits and the new core starts as before. +- If the new version cannot read the configuration, it takes over only the control plane; the gateway keeps forwarding with the previous configuration and the takeover completes once the file is fixed. +- If no successor arrives within the wait, core reopens the desktop app once. If the app is still running, the handover is called off. Otherwise core drains and exits. +- `GET /status` reports `handover` (`idle`, `armed`, `taking_over` or `draining`) and `draining`, the number of requests the previous core is still finishing. +- New codes: `control.handover.busy`, `control.handover.endpoint_failed`, `control.handover.login_refused`, `control.handover.remote`, `control.handover.safe_mode`, `gw.oauth.held`. + +**Dependencies.** `tokio` and `ctutils` are updated past RUSTSEC-2026-0386 and RUSTSEC-2026-0385.