Skip to content

build(deps): bump freenet-stdlib 0.3.5 → 0.6.0 with V10 delegate migration#214

Merged
sanity merged 3 commits intomainfrom
stdlib-0.6
Apr 15, 2026
Merged

build(deps): bump freenet-stdlib 0.3.5 → 0.6.0 with V10 delegate migration#214
sanity merged 3 commits intomainfrom
stdlib-0.6

Conversation

@sanity
Copy link
Copy Markdown
Contributor

@sanity sanity commented Apr 15, 2026

Problem

River pins freenet-stdlib = "0.3.5" and is three minor versions behind the just-released 0.6.0 (see freenet-core 0.2.44 for the matching host release). Running without this bump means:

  • River delegates cannot receive MessageOrigin::Delegate(..) attestations (no scenario today, but the API surface is there);
  • The #[non_exhaustive] hardening on InboundDelegateMsg, UpdateData, MessageOrigin, etc. from 0.6.0 means any future variant addition would silently break river at the source level without a wildcard arm;
  • River keeps accumulating stdlib debt.

The 0.2.44 freenet-core release is already running clean on nova + vega gateways (~4 hour soak, no regressions, VisitedPeers bloom filter populating correctly).

Approach

Mechanical stdlib bump with wildcard arms where the 0.6.0 non_exhaustive attribute now requires them. No behavioural change for known variants.

Source fixes (5 sites)

  • contracts/room-contract/tests/common/test_utils.rs — 0.4.0 terminology rename seeding_contractshosting_contracts.
  • delegates/chat-delegate/src/lib.rs — three exhaustive matches get wildcard arms. The MessageOrigin match gains an explicit Some(MessageOrigin::Delegate(caller_key)) arm that rejects inter-delegate calls: chat-delegate is driven exclusively by the River web app via WebApp, and there is no scenario today where another delegate should be able to invoke it.
  • contracts/room-contract/src/lib.rs — replaced _ => unreachable!() on the UpdateData match with _ => Err(InvalidUpdate). A panic inside contract WASM kills the runtime for the room; returning InvalidUpdate is recoverable. With 0.6.0 making the enum non_exhaustive, future variants could actually reach this arm.
  • ui/src/components/app/freenet_api/response_handler/update_notification.rs_ => warn!("unknown UpdateData variant, ignored") wildcard matching the existing Related* arms.

Drive-by

  • common/tests/migration_test.rs — fixes a pre-existing clippy manual_is_multiple_of lint. Eight other pre-existing clippy warnings in ui/src/ are out of scope for this PR — river's CI has clippy disabled (.github/workflows/clippy.yml.disabled) so none of these gate the build. Follow-up issue to be filed for re-enabling clippy CI + cleaning up.

Migration (V10)

Added V10 entry to legacy_delegates.toml before any WASM-altering edits landed in the working tree, per .claude/rules/river-publish.md. The V10 entry records the CURRENT deployed V9-era delegate's hash:

```
[[entry]]
version = "V10"
description = "Before freenet-stdlib 0.6.0 bump (non_exhaustive hardening + MessageOrigin::Delegate)"
delegate_key = "c2c96b97ab5e2f38bfdfb1ea697906fb947a5cdbaea25747f4670561b3edb61b"
code_hash = "9ef1e02d5c99d9a9743972c5e618e061e0e2278658cfa178900b31a2834313c1"
```

When users' River clients update after this merges and is republished, they will migrate room data from the old V9-era delegate key to the new V10+ delegate key via the LEGACY_DELEGATES array generated from this TOML by ui/build.rs.

Migration-chain risk — assessed low

  • The stdlib 0.5.0 and 0.6.0 CHANGELOG is explicit that bincode discriminants for existing variants are unchanged; wire format is preserved. Wire-format pin tests at freenet-stdlib/rust/src/delegate_interface.rs:1205+ lock the InboundDelegateMsg::ApplicationMessage tag. The V4-V6 removal cause (bincode wire mismatch) does not apply here. V10 migration probe should succeed.
  • Will still grep nova logs post-publish for de/serialization error.*Invalid size (V4-V6 failure signature) as a belt-and-suspenders check.

Pre-existing V9 entry drift (noted by skeptical reviewer, not addressed in this PR)

V9 lists code_hash=3a794f5e… but git history shows the actual deployed chat_delegate.wasm since commit 7456f51b has been 9ef1e02d…. Nothing in the TOML corresponds to 3a794f5e… being on disk. Either V9 was recorded from an ephemeral local build or the real "V9.5" deployed state was never recorded until now as V10. This PR's V10 entry is correct for what's actually been deployed, so users are protected. The stale V9 entry is out of scope here; follow-up issue will be filed.

WASMs rebuilt

File Purpose
ui/public/contracts/chat_delegate.wasm New chat delegate (code_hash 05bc461d…)
ui/public/contracts/room_contract.wasm New room contract
cli/contracts/room_contract.wasm Synced to match (byte-identical, verified sha256 ade1e4a0…)

Built via cargo make sync-wasm after the stdlib bump landed. Staged explicitly by name — never git add -A per .claude/rules/wasm-safety.md.

Testing

  • cargo make build — full workspace build succeeds in ~5 min
  • cargo test --package room-contract --features net — clean
  • cargo test -p river-core --test delegate_key_test — clean
  • cargo test -p river-core --test migration_test — 4 tests pass
  • cargo test -p chat-delegate10 tests pass (9 existing + 1 new regression for `MessageOrigin::Delegate` rejection path — addresses code-first + testing reviewer feedback)
  • cargo make check-migration — confirms V9 → V10 migration entry exists and matches committed WASM hashes
  • cargo check -p river-ui --target wasm32-unknown-unknown --features no-sync — clean

Not run locally

  • Playwright tests — the only UI code change is a trivial new warn! arm on the UpdateData wildcard; CI will exercise them automatically. Per AGENTS.md Playwright is "REQUIRED before publishing"; this PR does not publish, so the CI gate covers us. Will re-run locally if the CI run flags anything.

Review status

All four internal reviewers completed:

  • Code-first: Clean mechanical bump. Verified V10 migration hashes match parent-commit WASM bytes. WASM sync across 3 paths verified byte-identical. Only actionable finding: add a test for the new Delegate-origin rejection path — addressed in 4779211.
  • Testing: Same test gap — addressed. Flagged manual V9→V10 migration probe as a pre-publish (not pre-merge) requirement. Filing follow-up issues for: automated host-runtime migration harness, unknown UpdateData variant test, MessageOrigin bincode compat test.
  • Skeptical: "Ship it. Migration math is correct; wire format is preserved; WASMs are in sync. No correctness defects introduced by this PR." Pre-existing V9 entry drift noted above.
  • Big-picture: Goal-aligned, no removals, no anti-patterns, migration entry hygiene compliant with V7-V9 format. Safe to merge after CI green.

Post-merge publish plan

  1. Merge this PR to main (after CI green)
  2. Pull main locally
  3. Manual V9→V10 migration probe on a local node (register V9 chat_delegate.wasm from the parent commit, store a secret, swap to V10 WASM, verify the secret is readable). If the probe fails, same V4-V6 cleanup applies.
  4. cargo make publish-all — coordinated republish of BOTH River UI to Freenet AND riverctl to crates.io (they both embed room contract WASM and derive the contract key from it, so they MUST stay in lockstep)
  5. Smoke-test riverctl member list against nova
  6. Grep nova/vega logs for de/serialization error.*Invalid size — V4-V6 failure signature
  7. Move to delta and ghostkeys upgrades (same pattern)

Follow-up issues to file

  • Re-enable clippy CI and clean up 8 pre-existing ui/src/ lint errors
  • Automated host-runtime migration harness (cargo test that runs old delegate WASM on current host to verify migration chain)
  • Unknown UpdateData variant test for room-contract
  • MessageOrigin bincode compat test in river/common
  • V9 legacy_delegates.toml entry drift — annotate or remove

Related

  • Unblocks parallel stdlib bumps for delta and ghostkeys
  • Matches freenet-core 0.2.44 (merged + released earlier today)
  • freenet-stdlib 0.6.0 on crates.io

[AI-assisted - Claude]

sanity and others added 3 commits April 14, 2026 19:06
…ation

Catches river up three minor versions of freenet-stdlib, crossing:

- 0.4.0 seeding→hosting terminology rename (field `seeding_contracts`
  became `hosting_contracts` on `SystemMetrics`).
- 0.5.0 `MessageOrigin::Delegate(DelegateKey)` variant added so the
  runtime can attest inter-delegate callers; enum marked
  `#[non_exhaustive]`.
- 0.6.0 hardening: `#[non_exhaustive]` added to `InboundDelegateMsg`,
  `UpdateData`, `DelegateError`, `ContractError`, `APIVersion` plus
  wire-format pin tests.

## Source fixes (5 sites)

- `contracts/room-contract/tests/common/test_utils.rs` — rename
  `seeding_contracts` → `hosting_contracts` in the telemetry print.
- `delegates/chat-delegate/src/lib.rs` — three exhaustive matches
  needed wildcard arms: the `message_type` classifier (wildcard
  classifies future variants as "unknown message"), the `origin`
  routing (new explicit `MessageOrigin::Delegate(caller_key)` arm
  rejects inter-delegate calls — chat-delegate is driven exclusively
  by the River web app and has no scenario today where another
  delegate should be able to invoke it), and the message routing
  match (wildcard rejects unknown variants with a clear error).
- `contracts/room-contract/src/lib.rs` — replaced `_ => unreachable!()`
  on the `UpdateData` match with `_ => return Err(InvalidUpdate)`. A
  panic inside contract WASM would kill the runtime for the room and
  surface as a generic execution error; returning `InvalidUpdate` is
  recoverable and gives a clearer diagnostic. Now that `UpdateData`
  is `#[non_exhaustive]`, future variants really could reach this arm.
- `ui/src/components/app/freenet_api/response_handler/update_notification.rs`
  — added `_ => warn!("unknown UpdateData variant, ignored")` wildcard
  arm to the UI's update-notification match, mirroring the existing
  `Related*` arms.

## Drive-by

- `common/tests/migration_test.rs` — fixed a pre-existing clippy
  `manual_is_multiple_of` lint (`hex.len() % 2 == 0` →
  `hex.len().is_multiple_of(2)`). Eight other pre-existing clippy
  warnings in `ui/src/` are out of scope for this PR — river's CI
  has clippy disabled (`.github/workflows/clippy.yml.disabled`) so
  none of these gate the build.

## Migration (V10)

Added V10 entry to `legacy_delegates.toml` BEFORE any WASM-altering
edits, per the migration workflow in `.claude/rules/river-publish.md`:

  [[entry]]
  version = "V10"
  description = "Before freenet-stdlib 0.6.0 bump (non_exhaustive \
                 hardening + MessageOrigin::Delegate)"
  delegate_key = "c2c96b97ab5e2f38bfdfb1ea697906fb947a5cdbaea25747f4670561b3edb61b"
  code_hash    = "9ef1e02d5c99d9a9743972c5e618e061e0e2278658cfa178900b31a2834313c1"

This records the CURRENT deployed V9-era delegate's hash so when this
lands and users' River clients update, they can migrate their room
data from the old delegate key to the new one.

## WASMs rebuilt

- `ui/public/contracts/chat_delegate.wasm`  (new code_hash: 05bc461d…)
- `ui/public/contracts/room_contract.wasm`
- `cli/contracts/room_contract.wasm`         (synced to match)

All rebuilt via `cargo make sync-wasm` after the stdlib bump landed
in the working tree. Migration check confirms the V10 entry matches
the old hash and the new WASM is distinct.

## Validation

- `cargo make build` — full workspace build succeeds (room contract +
  chat delegate + web-container contract + UI)
- `cargo test --package room-contract --features net` — clean
- `cargo test -p river-core --test delegate_key_test` — clean
- `cargo test -p river-core --test migration_test` — 4 tests pass
  (all entries have valid hex, delegate_key = BLAKE3(code_hash), no
  duplicate code_hashes, no duplicate delegate_keys)
- `cargo make check-migration` — confirms
  V9→V10 migration entry exists and matches committed WASM hashes
- `cargo check -p river-ui --target wasm32-unknown-unknown --features no-sync`
  — clean (UI compiles against the new `LEGACY_DELEGATES` const array
  generated by `ui/build.rs` from `legacy_delegates.toml`)

## Follow-up

- Playwright tests should be run locally before un-drafting (CI
  triggers them automatically).
- `cargo make publish-all` must be run post-merge on main to rebuild
  and publish both the River UI to Freenet AND riverctl to crates.io
  — they both embed the room contract WASM and the contract key is
  derived from it.

Closes the stdlib-3-minors-behind gap; unblocks delta and ghostkeys
parallel upgrades.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both code-first and testing reviewers of #214 flagged that the new
`MessageOrigin::Delegate(caller_key)` rejection arm added in the main
PR commit had zero test coverage. Per `.claude/rules/pr-quality.md`
every behavioral change needs a regression test; this pins the
"chat-delegate does not accept inter-delegate calls" policy so a
future refactor that reorders the `match origin` arms cannot
silently regress it.

The test synthesizes a `MessageOrigin::Delegate(caller)` with an
arbitrary DelegateKey (value doesn't matter — only the variant tag
does) and asserts that the chat delegate returns
`DelegateError::Other` containing the phrase
"does not accept inter-delegate calls".

Verified: 10/10 chat-delegate tests pass locally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The check-wasm-sync CI workflow explicitly flagged that the room
contract WASM changed but riverctl's crates.io version matched
local. Per the workflow's own guidance: "If the UI is published
with new WASM but riverctl isn't, they'll target different
contracts." This bump triggers the republish so the UI and CLI
stay in lockstep on the new contract key.

Will be published to crates.io via cargo make publish-all post-merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sanity sanity merged commit 9636cb3 into main Apr 15, 2026
6 checks passed
@sanity sanity deleted the stdlib-0.6 branch April 15, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant