Idea
Build a reusable freenet-updates crate that holds a small Freenet contract whose state is the canonical "current published version + release-key signature" pointer for a given tool. Tools (riverctl first; eventually fdev, river-publish, etc.) consume the crate to check for updates via the local Freenet node — no external HTTP, no crates.io polling, no GitHub API rate limits, no privacy footprint to a centralised registry.
Why now
We just discussed adding an HTTP-based crates.io version check to riverctl. That's the obvious-but-undecentralised approach — and adding a "phone home to a central registry" check to a tool whose purpose is decentralisation feels off. We should eat our own dogfood.
Shape
- New crate
freenet-updates in the freenet/ org (or under river/ workspace if scoped first to River — TBD).
- Contract:
VersionPointerV1 { tool_name: String, latest_version: SemVer, released_at: SystemTime, release_signature: Signature } signed by a known release key.
- One contract instance per tool (e.g., riverctl, fdev, river contract). Tools know their own contract key.
- Helper API:
freenet_updates::check_for_update(tool_name, current_version) -> Result<UpdateStatus> — GETs the pointer contract via the local Freenet node, validates the release signature, returns UpToDate | Outdated { latest, released_at } | NetworkError.
- Tools opt in by calling the helper on startup (or via a
self-update-check subcommand). Warn-and-continue on outdated. Opt-out env var.
Design questions
- Release key management: the simplest answer is one Ed25519 release key per tool, held by the release-cutter (today: Ian). Compromise of the release key = false-version attack. Mitigate via signature being verified locally against the bundled public key.
- Contract update mechanism: each tool publish bumps its version-pointer contract via a delegate or a release-script
riverctl-style invocation.
- Bootstrap: tools need to be able to read the contract via the local Freenet node. If the local node isn't reachable, fall back to a no-op (don't block).
- Cache: stale-while-revalidate — tools can use the most recent successful check (cached to local disk) for offline scenarios.
Use cases
- riverctl: warn on startup if outdated, with a hint to
cargo install riverctl --force.
- fdev: same shape.
- freenet itself: long-term, could underpin auto-update for the daemon.
- River UI: the UI could surface a "new River build available" toast once the UI bundles the freenet-updates crate.
Acceptance criteria
- Crate published to crates.io (or available as a git dep within the freenet org).
- riverctl uses it for its startup check.
- Public README documenting the release-key setup.
- Issue closed after the second tool starts using it (proves the API is genuinely reusable).
Related
- The "crates.io HTTP check" alternative was considered and rejected on dogfood grounds.
- Connection to the existing
legacy_delegates.toml migration story: not directly, but the release-signature key is the same kind of "centralised trust root we need to manage" problem.
[AI-assisted - Claude]
Idea
Build a reusable
freenet-updatescrate that holds a small Freenet contract whose state is the canonical "current published version + release-key signature" pointer for a given tool. Tools (riverctl first; eventually fdev, river-publish, etc.) consume the crate to check for updates via the local Freenet node — no external HTTP, no crates.io polling, no GitHub API rate limits, no privacy footprint to a centralised registry.Why now
We just discussed adding an HTTP-based crates.io version check to riverctl. That's the obvious-but-undecentralised approach — and adding a "phone home to a central registry" check to a tool whose purpose is decentralisation feels off. We should eat our own dogfood.
Shape
freenet-updatesin thefreenet/org (or underriver/workspace if scoped first to River — TBD).VersionPointerV1 { tool_name: String, latest_version: SemVer, released_at: SystemTime, release_signature: Signature }signed by a known release key.freenet_updates::check_for_update(tool_name, current_version) -> Result<UpdateStatus>— GETs the pointer contract via the local Freenet node, validates the release signature, returnsUpToDate | Outdated { latest, released_at } | NetworkError.self-update-checksubcommand). Warn-and-continue on outdated. Opt-out env var.Design questions
riverctl-styleinvocation.Use cases
cargo install riverctl --force.Acceptance criteria
Related
legacy_delegates.tomlmigration story: not directly, but the release-signature key is the same kind of "centralised trust root we need to manage" problem.[AI-assisted - Claude]