chore(release): 0.8.1 — duplicate adopt/open fix + fuzz harness bookkeeping

- version 0.8.0 -> 0.8.1 (bug-fix release; semver-checks 196 pass vs 0.8.0)
- CHANGELOG 0.8.1: the manager_routing-found duplicate adopt/open fix,
  the fuzz harness, and the verification summary
- publish exclude gains docs/research/ (internal research notes;
  fuzz/ was already excluded — package verified clean of both)
- fuzzing.md §7.9: standing no-hosted-CI policy — corpus replay is the
  release-verification fuzz gate; campaigns manual via the detached
  runner; OSS-Fuzz out; no workflow files in this repo
- AGENTS.md: corpus replay added to the verification checklist
- lockfiles: alkcall 0.8.1 (root + fuzz)

Verification: 684 tests; clippy -D warnings (main, wasm, fuzz/shared);
fmt clean; doc clean; wasm32-unknown-unknown check+clippy clean;
semver-checks 196 pass; publish --dry-run 116 files (no fuzz/research/
reviews/sdd/AGENTS in the tarball); fuzz corpus replay 5/5 green
This commit is contained in:
glm-5.3-flash committed 2026-09-28 06:56:11 +00:00
1 parent 77f7006e2e
commit 3bda29c419
6 files changed
+107 -10

No files matched your search

+1
View File
@@ -194,6 +194,7 @@ Run these before committing. All must pass.
```bash
cargo test # full suite
cargo test --manifest-path fuzz/shared/Cargo.toml # fuzz corpus replay (stable)
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo doc --no-deps # if docs changed
+60
View File
@@ -4,6 +4,65 @@ All notable changes to this crate are documented here. The format is
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
this crate adheres to [Semantic Versioning](https://semver.org/).
## [0.8.1] - 2026-09-28
Bug-fix release: a duplicate `adopt_channel`/`open_channel` no longer
destroys the live channel it collided with, found by the crate's new
fuzzing harness (`fuzz/` workspace — five cargo-fuzz targets, committed
seed corpora, three 10-minute campaigns clean). No API or wire-format
change; the error is still `Err(ChannelExists)` — the difference is
entirely in what the collision leaves behind.
### Fixed
- **Duplicate adopt/open no longer replaces the live channel's state
(found by the `manager_routing` fuzz target,
`docs/research/fuzzing.md` §7.8).** `ChannelManager::open_channel`
and `adopt_channel` used `HashMap::insert(...).is_some()` as the
collision check — but `insert` *replaces* an occupied entry and
returns the old value, so a duplicate on an in-use id installed the
new `ChannelState`, dropped the live channel's `demux_sender`
(spurious EOF to its readers; all subsequently routed chunks lost),
and *still* returned `Err(ChannelExists)`. The mux write half kept
framing onto the transport for a channel the demux no longer fed.
Both functions now check `contains_key` and return before any map
mutation — the live channel's routing state is untouched on
collision. Reachable whenever an open-op response is replayed or a
connection-owner race re-announces an id. Regression tests:
`adopt_channel_duplicate_id_leaves_live_channel_intact`,
`open_channel_duplicate_id_leaves_live_channel_intact` (both verify
routing survives a rejected duplicate; the first also verifies the
EOF sentinel remains the only EOF source).
### Added (development)
- **Fuzzing harness (`fuzz/` workspace, `docs/research/fuzzing.md`).**
Five cargo-fuzz targets over the two attacker-reachable wire formats
and the stateful channel-routing surface: `chunk_header` (8-byte
header no-panic/round-trip/accounting), `envelope_frame` (frame
decode error-shape partition + allocation bound), `manager_routing`
(`Arbitrary` op sequences over `ChannelManager` with exact counter
models and the parked-bytes bound), `envelope_semantic` (all six
event kinds: constructors → serde → framing round-trip), and
`spec_parse` (attacker-shaped op-register schemas compile or reject
cleanly at registration). Invariant logic lives in
`fuzz/shared/` — a stable-toolchain crate replayed as plain
`cargo test`, so the committed corpora stay executable without
nightly. Campaigns (10 min per target, detached runner): ~25M
executions total, zero crashes/hangs/OOMs. The `manager_routing`
campaign found the 0.8.1 fix above within minutes — exactly the
stateful interleaving class example-based tests cannot reach.
`fuzz/` is excluded from the workspace and from the published
package.
### Verified
- 684 tests pass; `clippy --all-targets -- -D warnings`, `fmt --check`,
`cargo doc`, wasm32-unknown-unknown check, `cargo semver-checks`
(no API changes vs 0.8.0), and `cargo publish --dry-run` clean;
corpus replay (`cargo test --manifest-path fuzz/shared/Cargo.toml`)
green.
## [0.8.0] - 2026-09-18
Review 008's remediation lands in full — the graduation upstream asks
@@ -678,6 +737,7 @@ Vendored core types (`Connection`, `ProtocolHandler`, `BiStream`,
(ADR-046), the channels protocol with openable-ALPNs-as-operations
(ADR-047), and the `ChannelClient` transport-agnostic client.
[0.8.1]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.8.1
[0.8.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.8.0
[0.7.1]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.1
[0.7.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.0
Generated
+1 -1
View File
@@ -27,7 +27,7 @@ dependencies = [
[[package]]
name = "alkcall"
version = "0.8.0"
version = "0.8.1"
dependencies = [
"async-trait",
"bytes",
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "alkcall"
version = "0.8.0"
version = "0.8.1"
edition = "2021"
rust-version = "1.88"
license = "MIT OR Apache-2.0"
@@ -9,7 +9,7 @@ readme = "README.md"
repository = "https://git.alk.dev/alkdev/alkcall"
keywords = ["rpc", "json-rpc", "multiplexing", "wire-format", "alpn"]
categories = ["network-programming", "asynchronous", "encoding"]
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/sdd_process.md", "fuzz/"]
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/sdd_process.md", "docs/research/", "fuzz/"]
[workspace]
members = ["."]
+42 -6
View File
@@ -3,7 +3,12 @@
**Status:** steps 1–3 of §7.4 adopted — `fuzz/` workspace, five targets,
committed seeds, detached runner, two-tier campaigns run; one real
finding (duplicate adopt/open destroying the live channel) fixed with
regression tests. CI deferred until a platform exists (§7.8)
regression tests. §7.2/§7.4's CI tiers are **not planned** — this repo
will not run hosted CI (the git host is a minimal gitea that serves git
only; see §7.9). CI's two deliverables are covered instead: the stable
side's corpus replay (`cargo test --manifest-path fuzz/shared/Cargo.toml`)
runs as part of every release's verification checklist (AGENTS.md), and
campaigns run manually via the detached runner (§7.6)
**Date:** 2026-09-27
**Research inputs:** web survey of the 2025–2026 Rust fuzzing ecosystem, survey
of fuzzing practice in comparable Rust protocol crates (rustls, quinn, quiche,
@@ -380,11 +385,15 @@ comparisons), and a `-dict` of JSON tokens for the envelope targets.
found; triage §6.2 candidates with targeted corpus entries.
**Done for targets 1–2 — see §7.7.**
3. Add targets 3–5, the smoke CI job, and `.gitignore` entries.
**Targets 3–5 done, campaigns clean — see §7.8. CI deferred (no CI
platform exists in this repo yet; the stable-side corpus replay
`cargo test --manifest-path fuzz/shared/Cargo.toml` is the drop-in
smoke gate when a platform is chosen).**
4. Scheduled campaign tier; then OSS-Fuzz application.
**Targets 3–5 done, campaigns clean — see §7.8. The CI job will not
exist — see §7.9 (no hosted CI policy); the stable-side corpus
replay `cargo test --manifest-path fuzz/shared/Cargo.toml` is in
the release verification checklist instead (AGENTS.md).**
4. ~~Scheduled campaign tier; then OSS-Fuzz application.~~ **Not
planned — see §7.9.** Long campaigns are run manually via the
detached runner (§7.6) when wanted; OSS-Fuzz is out (it requires
hosted infrastructure and a public repo posture this project does
not have).
### 7.5 Relationship to existing tests
@@ -733,6 +742,33 @@ artifacts directory — no crashes, hangs, OOMs, or leaks.**
and held; §6.2-3 confirmed bounded at the parser level (§7.7). The
stateful coverage §7.4 step 3 called for exists and is clean.
---
## 7.9 No hosted CI — the standing policy (2026-09-28)
§7.2's two CI tiers (per-push smoke, scheduled campaign) and the
OSS-Fuzz step assume a hosted CI platform. This repo has none and will
not get one: the git host is a minimal gitea that serves git and
nothing else, deliberately — gitea/gitlab have had full-compromise CVEs
(app.ini and any plaintext DB tokens exfiltrated in one real incident
elsewhere), so the attack surface stays minimal. All verification and
campaigns are **manual, run from the separate publishing server**:
- **Corpus replay is the fuzz gate, as plain `cargo test`:**
`cargo test --manifest-path fuzz/shared/Cargo.toml` replays every
committed seed through the same invariant functions the fuzzer runs
— on stable, no nightly, no cargo-fuzz. It is part of the release
verification checklist (AGENTS.md). This is CI tier 3's deliverable
(§7.2), minus the automation.
- **Campaigns** run via the detached runner (§7.6) when wanted —
e.g. before a release, or after touching `src/protocol/wire.rs`,
`src/channels/wire.rs`, the demux loop, or `ChannelManager`.
- **OSS-Fuzz is out**: it requires hosted infrastructure and a public
repo posture this project does not have.
- Do not add Actions/Gitea-Actions/workflow files anywhere in this
repo, and do not reintroduce "when CI exists" language into docs —
point at this section instead.
## 8. Answering the "if / how" directly
- **If?** Yes — justified by position in the dependency graph, two stable
+1 -1
View File
@@ -27,7 +27,7 @@ dependencies = [
[[package]]
name = "alkcall"
version = "0.8.0"
version = "0.8.1"
dependencies = [
"async-trait",
"bytes",