Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ───────────────────────────────────────────
Expand Down
21 changes: 21 additions & 0 deletions release-notes/0.82.0.md
Original file line number Diff line number Diff line change
@@ -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.
Loading