pack-io 1.0.1

Compact binary wire format with schema evolution and zero-copy deserialization for Rust. The serialization substrate under network-protocol and Hive DB.
Documentation
# pack-io v0.9.0 — Beta

**The beta release.** v0.9.x is the bug-fixes-only window before RC. v0.9.0 itself ships zero new public API and zero wire-format changes — what it adds is the broader-testing infrastructure that pre-RC software needs (5 new fuzz targets, CI runtime doubled per target) and the **v1.0 performance baseline** committed as the canonical reference that post-1.0 regression detection will diff against.

## Honest caveat

The roadmap defines the alpha → beta promotion as gated on "a stable stretch with no outstanding bugs from the v0.8.x line". No real-consumer bug reports have come in since v0.8.0 — there hasn't been a stretch *of any duration* yet. So v0.9.0 is the ceremonial promotion that opens the bug-fixes-only window; the substantive content is the testing + baseline work below. If a v0.8.x consumer surfaces a bug after this release, it gets fixed in v0.9.1 / v0.9.2 / ... within the bug-fixes-only window — that's exactly what beta is for.

## Status

| Field           | Value                                                                                  |
|-----------------|----------------------------------------------------------------------------------------|
| Crate status    | **beta — bug-fixes-only**                                                              |
| Public API      | **frozen since v0.7.0** (no source-breaking change until v2.0)                          |
| Wire format     | **frozen at v0.3.0** (spec version 1.2; no wire change in v0.9.x)                       |
| Performance     | **frozen baseline at v0.9.0** ([`docs/PERFORMANCE_BASELINE.md`](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE_BASELINE.md)) |
| RC target       | v0.9.5+, critical fixes + doc polish only                                              |
| Stable target   | v1.0.0                                                                                 |

## What landed

### 5 new fuzz targets — 13 total

Continuous fuzz coverage extends to the decode paths the v0.7 set didn't touch:

| Target *(v0.9 additions in bold)* | What it hardens |
|---|---|
| `decode_string` | varint length + UTF-8 validation |
| `decode_vec_u8` | byte-run fast path (`u8::deserialize_many`) |
| `decode_tuple` | mixed primitive + length-prefixed shape |
| `decode_collection` | `HashMap<String, Vec<u8>>` count cap + per-entry decode |
| `decode_view_str` | zero-copy `&str` decode + lifetime / UTF-8 validation |
| `decode_struct_derive` | derive-generated struct deserialiser |
| `decode_enum_derive` | derive-generated enum + variant-index varint |
| `decode_versioned` | schema-evolution body-length cap |
| **`decode_btreemap`** | `BTreeMap<u64, String>` ordered-map non-`std` path |
| **`decode_btreeset`** | `BTreeSet<String>` with per-element UTF-8 validation |
| **`decode_hashset`** | `HashSet<u32>` preallocation cap |
| **`decode_view_bytes`** | zero-copy `&[u8]` decode (no UTF-8 path) |
| **`decode_view_collection`** | `Vec<&str>` collection of borrows |

### CI runtime doubled

Per-target runtime in the CI fuzz job bumped from **30 s → 60 s**. Combined with the 5 new targets, every push to `main` now exercises **~13 minutes of continuous fuzzing** across all targets (was ~4 minutes in v0.7 with 8 targets × 30 s).

This is a smoke pass, not exhaustive fuzzing — exhaustive fuzzing happens out-of-band on dedicated infrastructure or via post-1.0 ossfuzz integration. The CI pass is sized to catch the obvious "decoder crashes on input X" regression within minutes of a push.

### v1.0 performance baseline — frozen reference

[`docs/PERFORMANCE_BASELINE.md`](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE_BASELINE.md) is the canonical v1.0 performance reference. Numbers are Criterion medians from **100 samples × 10 s measurement window × 2 s warmup** — high enough fidelity that the confidence interval on each median is ≤ 1 ns for the primitive paths and ≤ 1 % for the struct paths.

The 10 s window confirms the v0.6 / v0.7 quick-run (3 s) numbers within ~3 % on every row. There's no surprise behaviour at longer measurement times.

**Regression policy** (per [`REPS.md`](https://github.com/jamesgober/pack-io/blob/main/REPS.md) §Performance): any post-1.0 change that exceeds a **5 % regression** on any row of the baseline blocks the merge. A win exceeding 5 % updates the baseline file and the changelog.

CI does **not** run the comparative benchmark — measurement variance across CI runners is too high for it to be a useful regression gate. Performance checks happen on dedicated hardware between releases.

### Baseline summary

| Workload | pack-io | bincode | postcard | rkyv | Position |
|---|---:|---:|---:|---:|---|
| encode/log_record | **37.9 ns** | 39.4 ns | 232.6 ns | 112.6 ns | pack-io fastest |
| decode_owned/log_record | 158.9 ns | 165.1 ns | 285.1 ns | **154.6 ns** | rkyv 1.03× faster |
| decode_view/log_record | 34.7 ns | — | — | **12.0 ns** | rkyv 3× faster (by design) |
| `u64` round-trip | 22.3 ns | **20.6 ns** | — | — | bincode 1.08× faster |
| 64-byte `String` owning | **44.7 ns** | 48.8 ns | — | — | pack-io fastest |
| 64-byte `&str` view | **5.2 ns** | — | — | — | uncontested |
| 4 KiB `Vec<u8>` decode | **59.7 ns** | 63.0 ns | — | — | pack-io fastest |

pack-io is the fastest of the four on encode, owning `String`, view `&str`, and `Vec<u8>` decode. Tied within ~5 % on `u64` round-trip and owned struct decode. Only meaningful loss is rkyv's archived path (~3×), which is a design choice the wire format spec explicitly trades off — see [`docs/PERFORMANCE.md`](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE.md) for the analysis.

### Status markers

- README banner: "alpha — integration window open" → **"beta as of v0.9.0; v0.9.x is bug-fixes-only"**
- API.md status banner mirrors the beta marker + adds the performance-baseline freeze callout
- Roadmap row for v0.9 marked shipped; v0.9.5+ narrowed to RC; v1.0 next

## Breaking changes

**None.** Every v0.8 source file compiles unchanged. Every v0.8 wire payload decodes identically. v0.9 is purely additive (fuzz coverage + frozen reference doc) and status-marker work.

## What v0.9.x will track

| Allowed in v0.9.x                         | Deferred                                |
|-------------------------------------------|-----------------------------------------|
| Bug fixes from real consumers or fuzz hits | New public API (waits for v2.0)        |
| Tightening existing tests                 | Wire format anything                    |
| Doc improvements                          | Source-breaking refactors               |
| Performance fixes (against the baseline)  | Speculative additive features (YAGNI)   |
| New fuzz targets / hostile-input cases    | New feature flags                       |

Performance fixes are allowed if they preserve or improve every row of the baseline; any change that regresses any row by > 5 % requires an explicit justification and CHANGELOG callout.

## Verification

All gates green on **both stable and MSRV 1.85**:

```bash
cargo fmt --all -- --check
cargo +1.85 fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo +1.85 clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo +1.85 test --all-features
cargo build --no-default-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo audit
cargo deny check
cd fuzz && cargo +nightly check
```

Test counts at this tag (stable, `--all-features`): **274 total**, all passing — unchanged from v0.8 because v0.9 is bug-fixes-only and no consumer bugs have surfaced yet. All ten example programs run end-to-end. All 13 fuzz targets compile cleanly under nightly.

## What's next

- **v0.9.x point releases** track bugs surfaced by consumers or fuzz hits. No scheduled cadence — driven by reports.
- **v0.9.5+ — RC.** Critical fixes + doc polish only.
- **v1.0.0 — Stable.** API + wire format + performance baseline frozen for the entire 1.x line.

## Installation

```toml
[dependencies]
pack-io = { version = "0.9", features = ["schema"] }   # everything

# Or à la carte:
pack-io = { version = "0.9", features = ["derive"] }   # derive macros only
pack-io = "0.9"                                         # in-memory + streaming codec only
pack-io = { version = "0.9", default-features = false } # no_std
```

MSRV: Rust 1.85 (2024 edition).

## Documentation

- [README](https://github.com/jamesgober/pack-io/blob/main/README.md)
- [API Reference](https://github.com/jamesgober/pack-io/blob/main/docs/API.md)
- [Wire Format Spec](https://github.com/jamesgober/pack-io/blob/main/docs/WIRE_FORMAT.md) (v1.2 — unchanged from v0.5)
- [Performance overview](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE.md) — analysis + methodology
- **[v1.0 Performance baseline](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE_BASELINE.md) (new)** — frozen data table
- [CHANGELOG](https://github.com/jamesgober/pack-io/blob/main/CHANGELOG.md)

---

**Full diff:** [`v0.8.0...v0.9.0`](https://github.com/jamesgober/pack-io/compare/v0.8.0...v0.9.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/pack-io/blob/main/CHANGELOG.md#090---2026-06-04).