feat(dispatch): unblock one-click CLI install and add model config sync - #1884
Merged
bobleer merged 2 commits intoJul 30, 2026
Merged
Conversation
Preparing an SSH dispatch target failed at two gates that had no path forward from the UI. **One-click CLI install was blocked by a missing trust root.** The release signing key was only injected at build time from `BITFUN_RELEASE_PUBKEY`, so any build without that secret refused to install with "this build has no BitFun release signing key". Embed the official minisign public key (`50F47CBE6CC0A376`) as the default trust root — it is public data that every release already ships as its `minisign.pub` asset, and downloads stay pinned to the official repository, so this does not widen what a build will execute. `BITFUN_RELEASE_PUBKEY` still takes precedence for forks publishing their own releases. The CLI self-updater uses the same default, which upgrades its previous checksum-only fallback to mandatory signature verification. Releases published before the CLI checksum sidecars were signed return 404 for `.tar.gz.sha256.sig`. Treat only a definite 404 as "unsigned sidecar" and keep every other failure fatal, so a flaky network cannot silently downgrade verification. The archive's own minisign signature stays mandatory — the install path never degrades to checksum-only. **No way to give the target a model.** Add `dispatch_sync_model_config`, which merges only the four `ai` model keys into the target's `app.json`, preserves every other target setting, aborts rather than overwrite an unreadable or unparseable config, and writes owner-only via a temp-file rename. The payload carries API credentials, so the UI gates it behind its own explicit confirmation that says so, and it is offered only when the target CLI answered but reported no usable model. The new command is registered in the Desktop invoke handler and the Server Host route table, and added to all three peer-host keep-local tables so the existing cross-language contract test keeps it off the wire.
`every_registered_command_declares_a_remote_workspace_policy` failed on all three platforms: `dispatch_sync_model_config` was registered in `generate_handler!` but missing from `REMOTE_WORKSPACE_COMMAND_POLICIES`. It is `WorkspaceAgnostic` like every other dispatch command — the target is addressed by its own connection id, never by the currently open workspace.
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.
Preparing an SSH dispatch target failed at two gates that had no path forward from the UI: the CLI showed "not installed or unreachable" while one-click install refused to run, and the model check reported "no model configuration available on the target" with no way to supply one.
One-click
bitfun-cliinstallThe install UI already existed; it was blocked at two fail-closed points in the backend.
1. Missing trust root. The release signing key was only injected at build time from
BITFUN_RELEASE_PUBKEY, so any build without that secret refused to install, reportingthis build has no BitFun release signing key; refusing to install executable code.This embeds the official minisign public key (key ID
50F47CBE6CC0A376) as the default trust root. It is public data — every release already ships it as theminisign.pubasset — and downloads stay pinned to the official repository, so trusting the matching official key here does not widen what a build will execute.BITFUN_RELEASE_PUBKEYstill takes precedence, so forks publishing their own releases are unaffected.The CLI self-updater in
self_update.rsuses the same default, which upgrades its previous "no key, fall back to checksum-only" behaviour to mandatory signature verification.Before embedding it, the published v0.2.14 CLI archive signature was verified against this key with a local
minisignbinary to confirm it matches.2.
.tar.gz.sha256.sigreturns 404 on existing releases. The checksum sidecars were not yet signed when those releases were published. Only a definite 404 now degrades to reading the unsigned.sha256(used solely to show the digest for consent); every other failure stays fatal, so a flaky network cannot silently be taken as "unsigned sidecar". The archive's own minisign signature remains mandatory — the install path never degrades to checksum-only.One-click model configuration sync
Adds
dispatch_sync_model_config, wired from the Rust transport through to the web UI:~/Library/Application Support/bitfun/config, Linux${XDG_CONFIG_HOME:-$HOME/.config}/bitfun/config)aimodel keys (models,default_models,agent_model_defaults,func_agent_models); every other target setting is preservedapp.jsoncannot be read or parsedmv -f, withchmod 600The payload carries API credentials, so the UI gates it behind its own explicit confirmation that says so. The entry point appears only when the target CLI answered but reported no usable model, and the dialog re-probes the target once the sync completes.
The new command is registered in the Desktop invoke handler and the Server Host route table, and added to all three peer-host keep-local tables so the existing cross-language contract test keeps it off the wire.
Verification
bitfun-services-integrationsremote_ssh: 101 passed (including the new trust-root test and two model-config merge tests)bitfun-server: 14 passed, including the contract testclaims_only_the_narrow_dispatch_contractbitfun-clipeer_host: 71 passed; self_update: 10 passedbitfun-desktop/bitfun-core:cargo checkcleantsc --noEmitclean, dispatch suite 34 passed (3 new cases covering when the sync card appears, that declining the confirmation writes nothing, and that a late sync acknowledgement is discarded after the dialog closes)Notes
dispatch/README.mdgains invariant 18, recording the boundary for model-config sync: separate explicit confirmation, merges only theaimodel keys, aborts on an unreadable target config, and writes owner-only via a temp-file rename.