Files
settled-reach/docs/architecture/sprint-36-bincode-audit.md
T
jpmschweitzerandClaude Sonnet 4.6 e11a9c308a feat(engine): generate_brands pipeline — 10K minor brands from templates (#829)
New generate_brands binary reads 126 brand archetype templates
(wiki/economics/archetypes/brand_templates.toml), assigns halo+volume pairs
to all 48 hand-authored corps, outputs wiki/economics/corporations/generated_brands.toml.

Result: 10,000 brand_product rows, 26,750 brand_inputs, all 48 corps covered.
Brand structural validation V-B01..V-B06 passes. Generated file is gitignored
(regenerated on each `make economy-db` run).

make economy-db now runs generate-brands before import_economics.py.
import_economics.py merges generated_brands.toml alongside hand-authored brands.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:15:03 +02:00

7.4 KiB
Raw Blame History

title, description, type, status, ticket, author, created, updated
title description type status ticket author created updated
Bincode v1 → v2 Migration — Risk Audit (Sprint 36) Audit of bincode usage in the server crate, risk assessment, and migration recommendation for ticket #636. architecture final #636 Tyre 2026-04-19 2026-04-19

Bincode Migration — Risk Audit

Ticket: #636 (Migrate bincode v1.x to v2.x) Advisory: RUSTSEC-2025-0141 (bincode v1.3.3 unmaintained) Author: Tyre TL;DR: bincode is an orphan dependency — nothing in the server crate actually calls it. Remove it outright. The "migration" is a four-line change.


1. What the audit found

Grep-audit of the entire repository, not just server/src/:

grep -rn "use bincode\|bincode::" server/ tests/ tooling/ --include="*.rs"
# → 0 hits

Cargo manifest references:

grep -rn "bincode" server/ --include="*.toml" --include="*.lock"
# → server/Cargo.toml:19        bincode = "1"
# → server/Cargo.lock:329       [[package]] name = "bincode" version = "1.3.3"
# → server/Cargo.lock:1311      " bincode"," — under settled-reach-server deps
# → server/audit.toml:6          ignore = ["RUSTSEC-2025-0141"]

No Rust source file in any crate (server/, tests/, tooling/) contains the string bincode. The lockfile entry under tooling/test-client shows bincode transiting through rmp-serde or a sibling — not from direct use.

Conclusion: bincode = "1" in server/Cargo.toml was added in anticipation of save-load / Rust-Rust sync (see docs/workshops/v01-gap-analysis/round1-tyre.md, docs/workshops/save-load-architecture/workshop-brief.md) but the implementation path chose rmp-serde / MessagePack instead (see server/src/bridge/types.rs — tests use rmp_serde::to_vec_named and rmp_serde::from_slice, line 1052 onward).

Bincode is a dead dependency.

2.1 The actual changes

server/Cargo.toml — remove line 19:

-bincode = "1"

server/audit.toml — remove the ignore (lines 59):

-[advisories]
-# RUSTSEC-2025-0141: bincode v1.3.3 is unmaintained.
-# Migration to bincode v2 or an alternative is tracked in ticket #636.
-# This ignore can be removed once #636 is resolved.
-ignore = ["RUSTSEC-2025-0141"]

(If removing the whole [advisories] section leaves audit.toml empty, either delete the file or leave the file with just a header comment — check .config/cargo-audit/ or the Makefile for how cargo audit is invoked.)

server/Cargo.lock — regenerate by running cargo check in server/. Verify bincode no longer appears.

2.2 Verification

# 1. No source regressions:
grep -rn "bincode" server/ tests/ tooling/ --include="*.rs"
# Expected: 0 hits.

# 2. Clean build:
cargo check --workspace --all-features

# 3. Clean tests:
cargo test --workspace

# 4. Audit is green without the ignore:
cargo audit
# Expected: no RUSTSEC-2025-0141 mention.

# 5. cargo-deny (once #726 lands):
cargo deny check

2.3 Risk assessment

Risk Likelihood Impact Mitigation
Hidden use bincode I missed Near-zero Build break Covered by §2.2 step 1 grep + cargo check
Proc-macro or build.rs pulling bincode Near-zero Build break No build.rs in server crate; no proc-macro deps use it
Transitive need (some crate depends on it) Zero N/A Transitive deps come through lockfile without a manifest entry
Future save-load work expects it in manifest Low Re-add If save-load lands with bincode later, re-add bincode = "2" then — fresh v2 install, no migration

All four risks are trivially mitigated. Net risk: ~0.


3. If the team decides to keep bincode — the v1 → v2 cheat sheet

Included for completeness even though §2 is the recommendation. If save-load (#553, D-085) or a future Rust↔Rust server-sync feature decides to use bincode, adopt it fresh at v2 with these signature changes:

3.1 The core API difference

v1 (current, unmaintained):

// Relies on serde Serialize/Deserialize derives.
let bytes: Vec<u8> = bincode::serialize(&value)?;
let value: MyType = bincode::deserialize(&bytes)?;

v2 (stable):

// New "Encode"/"Decode" derives, explicit config.
use bincode::{config, encode_to_vec, decode_from_slice};

let cfg = config::standard();
let bytes: Vec<u8> = encode_to_vec(&value, cfg)?;
let (value, _used): (MyType, usize) = decode_from_slice(&bytes, cfg)?;

Derive change: v2 introduced its own #[derive(bincode::Encode, bincode::Decode)] traits. If the type must stay serde-compatible (required for us — we use rmp-serde and ron side-by-side), use the compat shim:

use bincode::serde::{encode_to_vec, decode_from_slice};
let bytes = encode_to_vec(&value, config::standard())?;
let (value, _) = decode_from_slice::<MyType, _>(&bytes, config::standard())?;

This keeps #[derive(Serialize, Deserialize)] as the only derives on the data types — no dual-derive required. That matters because the same types cross the MessagePack boundary via rmp-serde.

3.2 Config

v2 makes encoding config explicit. config::standard() uses variable-int, little-endian — matches v1 default for our types (no floats in the save shape today, so endian parity is not critical). For perfectly-byte-identical output to v1, use config::legacy(). Any new adoption should use config::standard() — don't inherit v1 quirks.

3.3 Known gotchas (for future reference)

  • v2 does not auto-handle untagged serde enums in the compat layer (pre-v2.0.1); if we adopt it and hit an untagged enum, use bincode::serde::Compat.
  • v2's decode_from_slice returns the byte count consumed — v1 silently ignored trailing bytes. Useful for streaming multi-message frames; irrelevant for one-shot save files.
  • The bincode::options() builder from v1 (with_fixint_encoding() etc.) is gone — replaced by config::Configuration.
  • Binary format is not compatible across v1 ↔ v2. Any v1-written blob is unreadable by v2. (This is moot for us — we have none.)

3.4 Touch points if we were actually migrating

None. Literally no source file imports or uses it.


4. For Dudley — execution checklist

  1. Delete bincode = "1" from server/Cargo.toml.
  2. Delete the RUSTSEC-2025-0141 ignore block from server/audit.toml.
  3. cargo check --workspace — regenerates Cargo.lock.
  4. cargo test --workspace — must pass.
  5. cargo audit — must not print RUSTSEC-2025-0141 anymore.
  6. Commit:
    fix(deps): remove unused bincode dependency (#636)
    
    RUSTSEC-2025-0141 no longer relevant — bincode was declared but
    never imported. Drop the crate and the audit ignore. Future
    save-load work that wants bincode should adopt v2 fresh.
    

Estimated effort: ~15 minutes including verification.

5. What this means for docs

One doc to update: docs/sprints/sprint-27/server.md line 93 mentions the audit ignore. Either leave it (it's historical notes) or strike through. Not blocking.


Audit status: Complete. Recommendation: remove bincode entirely. If the team prefers "migrate now, don't remove" (symbolic commitment to the migration path), say the word and I'll spec that instead — but it costs more with zero benefit given the usage survey.