feat(cli): --signing-key-file flag overrides signing identity per command#282
Open
sanity wants to merge 1 commit into
Open
feat(cli): --signing-key-file flag overrides signing identity per command#282sanity wants to merge 1 commit into
sanity wants to merge 1 commit into
Conversation
…mand Adds a top-level `--signing-key-file <PATH>` flag (with `RIVER_SIGNING_KEY_FILE` env var fallback) that reads a raw 32-byte Ed25519 secret key and uses it in place of the room's stored `signing_key_bytes` for the current command. The override is in-memory only; `rooms.json` on disk is never modified. # Why `rooms.json` only stores ONE `signing_key_bytes` per room, but this machine often has multiple identities for the same room (room owner, invite bot, alt accounts). The UI's chat-delegate sync periodically overwrites `rooms.json[room].signing_key_bytes` with whatever the delegate has stored — silently leaving owner ops broken without a manual swap of the on-disk key. Pattern as documented in `river-official-room` skill: swap key → run command → optionally swap back. Fragile and recurring. The flag formalizes the "I have multiple identities, pick at command time" model: nominate the right identity per command, no rooms.json mutation, the existing identity stays loaded. Distinct from `message send --signing-key` which takes a base64- encoded inline key — the global file-based flag is preferred for non-test use because the key doesn't appear in shell history. # Tests - `signing_key_override_is_returned_and_not_persisted` in `cli/src/storage.rs` pins the contract: with override, `get_room` returns the override; without override (fresh Storage), the ORIGINAL stored bytes come back — proving the override is not written back to rooms.json. # Manual verification Sent the river#275/#276/#278 release announcement to the official Freenet River room as Room Owner via `--signing-key-file ~/.config/freenet-river-official/room_owner_signing_key.bin`, no rooms.json swap. Confirmed via `riverctl message list` that the message landed signed by Room Owner. # Follow-up #281 tracks the "perfect world" decentralized-version-pointer follow-up (a `freenet-updates` crate that uses a Freenet contract for tooling version checks). Separate PR. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
`rooms.json` only stores ONE `signing_key_bytes` per room, but this machine often has multiple identities for the same room (room owner, invite bot, alt accounts). The UI's chat-delegate sync periodically overwrites `rooms.json[room].signing_key_bytes` with whatever the delegate has stored — silently leaving owner ops (announcements, bans, room config edits) broken without a manual swap of the on-disk key.
The current mitigation in the `river-official-room` skill is: stash the existing `rooms.json`, copy the room-owner backup key into it, run the command, optionally swap back. Fragile, recurring, and easy to forget.
Approach
Add a top-level `--signing-key-file ` flag (with `RIVER_SIGNING_KEY_FILE` env var fallback) that reads a raw 32-byte Ed25519 secret key and uses it in place of the room's stored `signing_key_bytes` for the current command. The override is in-memory only; `rooms.json` on disk is never modified.
Implementation:
Distinct from `message send --signing-key` which takes a base64-encoded inline key — the global file-based flag is preferred for non-test use because the key doesn't appear in shell history.
Testing
Unit test `signing_key_override_is_returned_and_not_persisted` in `cli/src/storage.rs` pins the contract: with override, `get_room` returns the override; without override (fresh Storage from same data dir), the original stored bytes come back — proving the override is not written back to `rooms.json`.
Manual end-to-end: sent the river#275/#276/#278 release announcement to the official Freenet River room as Room Owner via `--signing-key-file ~/.config/freenet-river-official/room_owner_signing_key.bin`, no `rooms.json` swap. Confirmed via `riverctl message list` that the message landed signed by Room Owner. This is the use case that motivated the PR.
Notes
[AI-assisted - Claude]