# pack-io v0.7.0 — Hardening + API freeze
**The hardening release.** v0.7.0 ships zero new public API and zero wire-format changes — what it adds is the proof that the v0.6 surface is production-ready. Three new infrastructure pieces land together: an 8-target `cargo-fuzz` continuous harness wired into CI, a 29-test cross-platform byte-equivalence golden vector suite, and a 17-test hand-crafted hostile-input sweep. Plus the formal public-API freeze recorded as the authoritative v1.0 contract in [`docs/API.md`](https://github.com/jamesgober/pack-io/blob/main/docs/API.md#frozen-public-surface).
## API status: FROZEN
The complete public surface — every type, trait, free function, inherent method, derive macro, schema attribute, `SerialError` variant, and feature flag — is enumerated in [`docs/API.md`](https://github.com/jamesgober/pack-io/blob/main/docs/API.md#frozen-public-surface) with the version each was frozen at. **That list is the v1.0 contract.** Source-breaking changes are deferred to v2.0.
Pre-1.0 minor releases (v0.7.x → v0.9.x) ship:
- Bug fixes
- Hardening passes (more fuzz targets, more hostile-input cases)
- Performance work (against the existing surface only)
- Strictly *additive* changes (new `SerialError` variants under the existing `#[non_exhaustive]` enum; new derive macro support for new field types)
Anything not in those four categories waits for v2.0.
## What landed
### 1. cargo-fuzz harness — 8 targets, CI-integrated
New [`fuzz/`](https://github.com/jamesgober/pack-io/tree/main/fuzz) crate, workspace-excluded so stable / MSRV builds never touch it (`libfuzzer-sys` requires nightly plus AddressSanitizer instrumentation). One target per public decode entry point:
| Target | 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 deserialiser + variant-index varint |
| `decode_versioned` | schema-evolution body-length cap |
Every push to `main` runs every target for 30 seconds on Ubuntu nightly via the new CI `fuzz` job. Catches regressions fast. Longer continuous fuzzing (hours / days) happens out-of-band on dedicated infrastructure; post-1.0 the plan is ossfuzz integration.
Reproduce locally:
```bash
cd fuzz
cargo +nightly fuzz run decode_string -- -max_total_time=30
```
The contract every target asserts: decoder MUST NOT panic, MUST NOT read past the input, MUST NOT allocate above `Config::max_alloc`. Any failure is a bug filed with the offending input attached.
### 2. Cross-platform byte-equivalence — 29 golden vectors
[`tests/byte_equivalence.rs`](https://github.com/jamesgober/pack-io/blob/main/tests/byte_equivalence.rs) asserts known input → known exact bytes. Runs on every CI matrix cell — **Linux × macOS × Windows × stable × MSRV = six platform/toolchain combinations.** Passing on all six *is* the cross-platform byte-equivalence proof: if any platform produced different bytes for the same input, the assertions would fail there but not elsewhere.
Coverage:
- Every primitive integer type (u8/u16/u32/u64/u128, i8/i64) with edge values (0, 42, 128, MAX) and ZigZag corners (−1, 1, −2).
- `bool`, `f32`, `f64`, `()`, `String`, `Vec<u8>`.
- Every compound type: `Option`, `Result`, tuples, arrays.
- `BTreeMap` canonical-ordering verification with the exact byte sequence proving "beta" sorts before "alpha" / "gamma" (because the length-prefix `0x04` < `0x05`).
- `HashMap` ≡ `BTreeMap` byte equivalence over the same logical data.
- A nested-struct end-to-end round-trip with the exact expected byte concatenation laid out in the test.
Treat each `assert_eq!` as the wire-format spec asserting itself. Any byte sequence that changes is a wire-format-breaking change requiring a corresponding [`docs/WIRE_FORMAT.md`](https://github.com/jamesgober/pack-io/blob/main/docs/WIRE_FORMAT.md) update.
### 3. Hostile-input sweep — 17 hand-crafted attack vectors
[`tests/hostile_inputs.rs`](https://github.com/jamesgober/pack-io/blob/main/tests/hostile_inputs.rs) complements the existing `proptest` random-byte coverage with targeted adversarial cases:
- **Length-prefix corners:** `varint(u64::MAX)` as the length prefix across `String`, `Vec<u8>`, `HashMap<String, u64>`, and `Vec<Vec<u8>>`. Each must surface `SerialError::InvalidLength` before allocating.
- **Tight `max_alloc` enforcement:** a configured 64-byte cap rejects a payload declaring length 1024 — well under the default 1 GiB but above the tight cap — before touching the heap.
- **Varint corners:** 11-byte overlong `u64` varint and 20-byte overlong `u128` rejected as `VarintOverflow`; tenth byte of a `u64` varint with bit 1 set rejected (only bit 0 of byte 10 is legal under LEB128).
- **Recursion bombs:** deeply-nested `Option<Option<Option<...>>>` payloads at modest depth (8 levels) decode or error without stack overflow; longer payloads than the type permits surface as `TrailingBytes`.
- **`decode_view` attack surface:** the zero-copy path enforces the same `InvalidLength` and `InvalidUtf8` checks as owning decode.
- **Truncation sweep:** every prefix length of a known-good nested-struct encoding either decodes to a valid prefix value or errors. Never a panic.
- **Trailing-garbage rejection:** both `decode` and `decode_view` reject any byte past the value they consumed.
### Plus
- README status updated from "pre-1.0, in active development" to "pre-1.0, API frozen as of v0.7.0".
- Roadmap entry for v0.7 marked shipped.
## Breaking changes
**None.** Every v0.6 source file compiles unchanged, every v0.6 wire payload decodes identically.
## 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
# Fuzz crate (nightly, syntax-only on platforms without libfuzzer):
cd fuzz && cargo +nightly check
# Fuzz smoke run (Linux, requires `cargo install cargo-fuzz`):
cd fuzz && cargo +nightly fuzz run decode_string -- -max_total_time=30
```
Test counts at this tag (stable, `--all-features`): **266 total**, all passing.
| Suite | Count | Note |
|----------------------------------|------:|-----------------------------------|
| Unit tests | 71 | unchanged from v0.6 |
| roundtrip / determinism / adversarial proptests | 25 / 21 / 20 | unchanged from v0.6 |
| collections / streaming | 19 / 11 | unchanged from v0.6 |
| derive / schema_evolution | 14 / 15 | unchanged from v0.6 |
| **byte_equivalence** *(new)* | **29** | golden vectors on every platform |
| **hostile_inputs** *(new)* | **17** | hand-crafted attack vectors |
| Doctests | 24 | unchanged from v0.6 |
## What's next
- **v0.8.0 → v0.9.x — Alpha → Beta → RC.** Real-consumer integration. `network-protocol`, `wire-codec`, and Hive DB pick pack-io up. Bugs they surface get fixed. No new public API; the v0.7 freeze holds. Beta enters when a stable stretch passes with no outstanding bugs; RC enters when the audit checklist is fully resolved.
- **v1.0.0 — Stable.** API + wire format frozen for the entire 1.x line. Published to crates.io; tag pushed; release note in `docs/release/v1.0.0.md`.
## Installation
```toml
[dependencies]
pack-io = { version = "0.7", features = ["schema"] } # everything
# Or à la carte:
pack-io = { version = "0.7", features = ["derive"] } # derive macros only
pack-io = "0.7" # in-memory + streaming codec only
pack-io = { version = "0.7", 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) — now with `Frozen public surface` section listing the v1.0 contract**
- [Wire Format Spec](https://github.com/jamesgober/pack-io/blob/main/docs/WIRE_FORMAT.md) (v1.2 — unchanged from v0.5)
- [Performance](https://github.com/jamesgober/pack-io/blob/main/docs/PERFORMANCE.md) — unchanged from v0.6
- [CHANGELOG](https://github.com/jamesgober/pack-io/blob/main/CHANGELOG.md)
---
**Full diff:** [`v0.6.0...v0.7.0`](https://github.com/jamesgober/pack-io/compare/v0.6.0...v0.7.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/pack-io/blob/main/CHANGELOG.md#070---2026-06-04).