Skip to content

feat: freenet-updates crate (decentralized version-pointer for tooling) #281

Description

@sanity

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

  1. riverctl: warn on startup if outdated, with a hint to cargo install riverctl --force.
  2. fdev: same shape.
  3. freenet itself: long-term, could underpin auto-update for the daemon.
  4. 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]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions