fix: make onboarding installs prefer release artifacts with source fallback (#323)

* fix: make onboarding installs prefer release artifacts with source fallback

* fix: harden extension fallback errors and surface setup warnings

* fix: validate registry artifacts and harden fallback errors

* fix: address review feedback on installer fallback

- Add upfront validate_manifest_install_inputs() in
  install_with_source_fallback so bad manifests fail fast without
  relying on inner methods to catch them
- Document ALLOWED_ARTIFACT_HOSTS as GitHub-only by design
- Document intentional url omission from DownloadFailed Display
- Add channel manifest validation tests (wrong prefix rejected,
  correct prefix accepted)

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: require SHA256 checksum for artifact downloads

Reject artifact installs when the manifest has sha256: null instead of
warning and proceeding. This prevents installing unverified pre-built
binaries during onboarding. The check runs before downloading to avoid
wasting bandwidth.

Since InvalidManifest blocks source fallback, manifests with URLs but
no checksums will hard-fail rather than silently falling back to source
build — forcing the manifest to be fixed.

The release CI already computes SHA256 for each bundle; the manifests
just need to be populated with the actual values.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: enforce SHA256 checksums and auto-patch manifests in CI

- Fix cargo fmt on SHA256 check code
- Reorder release CI: build WASM extensions before binary so manifests
  can be patched with computed SHA256 before build.rs embeds them
- Add "Patch manifests with WASM checksums" step in build-local-artifacts
  that reads checksums.txt and updates registry JSON files before building
- Add update-registry-checksums job that commits patched manifests back
  to main after release, keeping the repo in sync with released artifacts

This closes the integrity gap where all manifests had sha256: null and
artifact downloads were unverified. The binary now embeds correct SHA256
values and the installer hard-rejects null checksums.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Bowen Wang <[email protected]>
This commit is contained in:
firat.sertgoz
2026-02-23 18:49:18 +00:00
committed by GitHub
co-authored by Claude Opus 4.6 Bowen Wang
parent cbf5c93578
commit 3e552e0e8e
5 changed files with 606 additions and 30 deletions
+5 -4
View File
@@ -258,7 +258,7 @@ key first, then falls back to the standard env var.
6c. Build channel options: discovered + bundled + registry catalog
6d. Multi-select: CLI/TUI, HTTP, all available channels
6e. Install missing bundled channels (copy WASM binaries)
6f. Install missing registry channels (build from source)
6f. Install missing registry channels (download artifacts, fallback to source build)
6g. Initialize SecretsContext (for token storage)
6h. Setup HTTP webhook (if selected)
6i. Setup each WASM channel (secrets, owner binding)
@@ -267,7 +267,7 @@ key first, then falls back to the standard env var.
**Channel sources** (priority order for installation):
1. Already installed in `~/.ironclaw/channels/`
2. Bundled channels (pre-compiled in `channels-src/`)
3. Registry channels (`registry/channels/*.json`, built from source)
3. Registry channels (`registry/channels/*.json`, download-first with source fallback)
**Tunnel setup** (`setup_tunnel`):
- Options: ngrok, Cloudflare Tunnel, localtunnel, custom URL
@@ -305,8 +305,9 @@ key first, then falls back to the standard env var.
4. Discover already-installed tools in `~/.ironclaw/tools/`
5. Multi-select: show all registry tools with display name, auth method,
and description. Pre-check tools tagged `"default"` and already installed.
6. For each selected tool not yet installed, build from source via
`RegistryInstaller::install_from_source()`
6. For each selected tool not yet installed, install via
`RegistryInstaller::install_with_source_fallback()` (download-first,
fallback to source build)
7. Print consolidated auth hints (deduplicated by provider, e.g. one hint
for all Google tools sharing `google_oauth_token`)
+12 -6
View File
@@ -1493,7 +1493,6 @@ impl SetupWizard {
any_installed = true;
}
// Then try registry channels (build from source for any still missing)
let installed_from_registry = install_selected_registry_channels(
&channels_dir,
&selected_wasm_channels,
@@ -1685,9 +1684,12 @@ impl SetupWizard {
continue; // Already installed, skip
}
match installer.install_from_source(tool, false).await {
match installer.install_with_source_fallback(tool, false).await {
Ok(outcome) => {
print_success(&format!("Installed {}", outcome.name));
for warning in &outcome.warnings {
print_info(&format!("{}: {}", outcome.name, warning));
}
installed_count += 1;
// Track auth needs
@@ -2657,8 +2659,6 @@ fn load_registry_catalog() -> Option<crate::registry::catalog::RegistryCatalog>
/// Install selected channels from the registry that aren't already on disk
/// and weren't handled by the bundled installer.
///
/// This builds channels from source using `cargo component build`.
async fn install_selected_registry_channels(
channels_dir: &std::path::Path,
selected_channels: &[String],
@@ -2703,8 +2703,14 @@ async fn install_selected_registry_channels(
channels_dir.to_path_buf(),
);
match installer.install_from_source(manifest, false).await {
Ok(_) => {
match installer
.install_with_source_fallback(manifest, false)
.await
{
Ok(outcome) => {
for warning in &outcome.warnings {
crate::setup::prompts::print_info(&format!("{}: {}", name, warning));
}
installed.push(name.clone());
}
Err(e) => {