v1.0.0
The first stable release. v0.10.0 ran on both production hosts for three days while a second full-tree review of the whole crate, the tests, the scripts and the workflows was applied in five batches and read twice. The compatibility promise stated in v0.10.0 starts here: configuration keys, their defaults and the command line are stable within 1.x, a key is removed only after a deprecation warning in at least one minor release, and log line shapes and the library API are outside the promise.
Added
volto --diagnosticsprints the running service's descriptor limits, not only the caller's. The twoRLIMIT_NOFILElines are those of the process running the command, which from an SSH shell is the shell; the bundle now says so on the line under them and, on Linux, walks/procfor every running volto process and prints itsMax open filessoft and hard limits with its pid. Nothing is asked ofsystemctl.volto-deploy --forcereinstalls a release the host already reports. The convergence check compares version strings, so a release deleted and published again under the same tag left a host on the first upload's bytes.--forcetakes the download, verify, install and restart path anyway, with the rollback guard unchanged, and needs--tag.- A password still equal to the shipped example's placeholder draws a startup warning, naming the
[auth].usersentry by index and never echoing the password. The manual installation indocs/deployment.mdnow enables the unit, has the operator edit the file, and starts the service as a separate step, so the port stays shut until the file is right. - A reload says when a socket buffer key did not apply.
limits.socket_recv_bufferandlimits.socket_send_bufferbind with the socket, likeserver.listen; a SIGHUP that moves either now warns once per direction (log_idom0np2q6) instead of applying nothing silently.
Fixed
- The SNI gate drops an Initial with a short connection ID before decrypting it. The rule sat behind the key derivation, so a 1200 byte datagram with a four-byte connection ID and a payload nobody sealed was passed through to quinn and answered with a 77 byte CONNECTION_CLOSE, at no cryptographic cost to its sender. It is now judged on the header, where quinn judges it, and nothing legitimate is refused: RFC 9000 section 7.2 gives a client's first Initial a floor of eight bytes.
- A reload no longer stalls the accept loop. With a gate list configured, the reload read and parsed the certificate while holding the guard every accepted connection takes. The file read and PEM parse now happen before the guard.
- A reload re-checks the descriptor budget.
limits.max_connectionsandlimits.max_targets_per_connare both reloadable and the product was checked at bind only, so numbers raised during an incident drew no warning. - Process exit is bounded by the grace in force, not the one the process started on. A reload that raised
server.shutdown_gracelet the drain wait the new value and then gave the blocking pool the old one. expected_snirefuses an IP address literal. RFC 6066 section 3 does not permit one inHostName, so a dotted quad on that list could never match and the gate's refusals are silent by construction. Startup and reload now refuse it.- The duplicate-username refusal no longer echoes the name to stderr and the journal, which
Restart=on-failurerepeats. The two indices name both entries. - A CONNECT 200 that fails to land resets the target it was opened for, as RFC 9114 section 4.4 asks, instead of closing it with a FIN; the lapse and the failure now take the same path.
- A reset CONNECT-UDP session stops the peer's sending half with the same code it reset the stream with, instead of the implicit STOP_SENDING with code 0 that dropping the reader sent, which RFC 9114 section 8.1 has a peer read as no error.
- A peer resetting its own control stream takes the same teardown exemption as the QPACK streams, so a connection that has already ended does not report H3_CLOSED_CRITICAL_STREAM into the log.
- The deploy script's rollback restores the unit as well as the binary, and runs
daemon-reloadbefore the restart. The unit carries the version-dependent hardening directives and is the half most likely to fail on an older host; the old guard put the old binary under the new unit and reported a rollback. - The self-signed installer cannot exit silent after regenerating the certificate. Two pipelines in its report stage failed under
set -o pipefailon a hand-edited user line or a host withouthostname -I, at the moment every pinned fingerprint had just gone stale.
Changed
- Release binaries are asserted stripped before they are published.
readelfrefuses a packaged binary that names.symtabor.debug_info, in both the release and the cross workflow, after the first v0.10.0 release shipped an unstripped aarch64 asset. - The interop job judges the server log. The independent check of
src/h3printed the log for a human and read nothing; it now fails on an unexpected WARN or ERROR, after stripping the colour escapes that had made the local filter match nothing at all. - Every tracked shell script is linted, and the MSRV check builds every target with every feature.
- The rest of the review's simplify items, across the gate, the endpoint, the configuration, the HTTP/3 layer, the tunnels and the test harness, with no behaviour moved: one helper for the three critical streams, one place that decides what a lapsed response leaves behind, five duplicated test helpers shared, and every reader of a tree, a log or a script asserting a floor before it judges.
Testing
- Every fix above carries a test proven red first. Targeted mutation over the functions batches 1 and 2 changed: 0 missed.
- The local SNI gate probe ran on the release commit: with
expected_sni = ["localhost"]aioquic passes all seven items, and a ClientHello for another name gets silence. - Three-second sleeps in the suite became condition waits, the replay's log-derived assertions and the authentication storm have floors under their bounds, and the documentation gate holds the manual installation and every default to one spelling across the page, the example and the unit.
Dependencies
- quinn-proto stays pinned to the upstream 0.11.x branch head
48455d3under[patch.crates-io]; no 0.11.x release carries the MTU and datagram fixes yet.
Upgrading: a configuration whose expected_sni names an IP address literal no longer loads; replace it with the host name the client sends. On the wire, only refusal paths moved: an Initial with a connection ID under eight bytes is dropped silently by a gated server where it drew a CONNECTION_CLOSE, a reset CONNECT-UDP session's STOP_SENDING carries the stream's own code, and a target whose CONNECT 200 failed to land gets a reset instead of a FIN. A client that does none of these sees no difference. volto-deploy --tag behaves as before; add --force to replace a binary that already reports the wanted version.
Static musl builds for x86_64 and aarch64. Verify with SHA256SUMS.
Each archive carries the binary, LICENSE, README.md and the script/ and docs/ directories. Installing and updating is described in docs/deployment.md.
Full Changelog: v0.10.0...v1.0.0