Skip to content

feat(iii-worker): replace static registry with per-worker API#1460

Merged
andersonleal merged 2 commits intomainfrom
feat/worker-registry-api
Apr 10, 2026
Merged

feat(iii-worker): replace static registry with per-worker API#1460
andersonleal merged 2 commits intomainfrom
feat/worker-registry-api

Conversation

@andersonleal
Copy link
Copy Markdown
Contributor

@andersonleal andersonleal commented Apr 10, 2026

Summary

  • Replace the GitHub-hosted static registry index (registry/index.json) with a per-worker API at https://api.workers.iii.dev/download/:worker-name?version=<version>
  • New fetch_worker_info(name, version) replaces fetch_registry() + resolve_image()
  • Binary downloads use API-provided URLs with inline SHA256 checksums — no more URL construction or separate .sha256 file fetches
  • Simplify download_and_install_binary() from 6 parameters to 2 (worker_name, &BinaryInfo)
  • Checksums are now always verified (removes the has_checksum flag that defaulted to false)
  • Env var change: III_REGISTRY_URLIII_API_URL
  • Net change: -326 lines (329 added, 655 removed)

API contract

Binary worker: GET /download/image-resize?version=0.1.2 returns per-platform URLs + sha256 checksums
OCI worker: GET /download/todo-worker returns image_url for direct pull
Passthrough: Full OCI refs (ghcr.io/org/image:tag) bypass the API entirely

Test plan

  • cargo test -p iii-worker passes (426 tests)
  • cargo clippy -p iii-worker has no new warnings
  • iii worker add image-resize resolves via new API and downloads binary
  • iii worker add image-resize@0.1.2 resolves specific version
  • iii worker add ghcr.io/org/image:tag still works as OCI passthrough
  • iii worker add ./local-path still works for local workers
  • iii worker add w1 w2 w3 multi-add works (sequential API calls)
  • III_API_URL=file:///path/to/fixture.json iii worker add test-worker works in debug builds

Summary by CodeRabbit

  • Refactor

    • Switched to a unified worker-info API for resolving binaries and OCI images; installations now use API-provided binary info (direct download URL + checksum).
    • Simplified add/start flows, removed registry branches, and added direct OCI-image passthrough for image-style references.
    • CLI call sites updated for the reduced add API arity and default API endpoint.
  • Bug Fixes

    • Improved error messages for missing workers, invalid names, and platform selection; consistent checksum verification for downloads.
  • Tests

    • Updated and added tests for API shapes, platform lookup, checksum verification, and routing.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
iii-website Ready Ready Preview, Comment Apr 10, 2026 10:49pm
motia-docs Ready Ready Preview, Comment Apr 10, 2026 10:49pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

Replaces registry/index resolution with a worker-info API, consolidates binary metadata into BinaryInfo (url + sha256), updates CLI handlers to use fetch_worker_info(), and simplifies binary download to fetch and verify directly from BinaryInfo.url/sha256. Tests and call sites updated accordingly.

Changes

Cohort / File(s) Summary
Registry & API model
crates/iii-worker/src/cli/registry.rs
Replaced registry/index V2 with fetch_worker_info(name, version) and new response types: BinaryInfo, WorkerConfig, BinaryWorkerResponse, OciWorkerResponse, WorkerInfoResponse. Removed fetch_registry(), resolve_image(), validate_repo(), and DEFAULT_REGISTRY_URL; added DEFAULT_API_URL.
Binary download
crates/iii-worker/src/cli/binary_download.rs
download_and_install_binary signature changed to accept &BinaryInfo; downloads directly from binary_info.url and always verifies via sha256. Removed binary_download_url() and checksum_download_url() and their unit tests.
CLI handlers
crates/iii-worker/src/cli/managed.rs
Refactored handle_managed_add / handle_binary_add to use fetch_worker_info() responses; removed cached_registry/registry-routing branches; added direct OCI passthrough for inputs containing / or :; updated config generation to use response config.
Entry points
crates/iii-worker/src/main.rs
Updated call sites to match new handle_managed_add arity (removed None argument).
Tests
crates/iii-worker/tests/...
Removed URL-format and checksum-download tests; updated tests to pass BinaryInfo; added tests for platform-key lookup and verify_sha256 success/failure; adjusted managed/config integration tests to new routing and argument lists.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant API as Worker-Info API
  participant Downloader
  participant Verifier
  participant FS as Filesystem

  CLI->>API: fetch_worker_info(name, version?)
  API-->>CLI: WorkerInfoResponse { Binary | Oci }

  alt Binary response
    CLI->>CLI: select platform key from response.binaries
    CLI->>Downloader: GET binary_info.url
    Downloader-->>CLI: binary_data
    CLI->>Verifier: verify_sha256(binary_data, binary_info.sha256)
    Verifier-->>CLI: ok / mismatch
    alt ok
      CLI->>FS: install/unpack binary -> PathBuf
      FS-->>CLI: installed path
    else mismatch
      CLI-->>CLI: error (checksum mismatch)
    end
  else Oci response
    CLI->>CLI: handle_oci_pull_and_add(image_url)
    CLI->>Downloader: pull OCI image
    Downloader-->>FS: store image / layer
    FS-->>CLI: installed image
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • ytallo
  • sergiofilhowz

Poem

🐇 I hopped through JSON fields and found a single stream,
A BinaryInfo with URL and sha, tidy as a dream.
I fetched the info, nibble-checked each byte,
Verified the hash, installed by moonlight.
Hop hop hooray — new routes to run and rite! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main architectural change: replacing a static registry with a per-worker API approach, which is the primary purpose of this changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/worker-registry-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Replace the GitHub-hosted static registry index (registry/index.json)
with a per-worker API at https://api.workers.iii.dev/download/:worker-name.

- Add fetch_worker_info(name, version) replacing fetch_registry()
- Binary downloads use API-provided URLs + inline SHA256 checksums
- Simplify download_and_install_binary() from 6 params to 2
- Checksums are now always verified (no more has_checksum flag)
- OCI passthrough preserved for full refs with '/' or ':'
- Env var: III_REGISTRY_URL → III_API_URL
- Comprehensive test coverage: deserialization edge cases,
  error paths, platform selection, routing logic
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/iii-worker/src/cli/registry.rs (1)

117-122: Consider URL encoding for the worker name.

If a worker name contains characters that are valid per validate_worker_name (alphanumeric, dash, underscore, dot) but have special meaning in URLs (like .), the current string interpolation should be safe. However, for defense-in-depth, consider using proper URL encoding for the name segment.

♻️ Optional: URL-encode worker name
-        let url = format!("{}/download/{}", base_or_file, name);
+        let url = format!(
+            "{}/download/{}",
+            base_or_file,
+            urlencoding::encode(name)
+        );

This would require adding urlencoding as a dependency, so it may not be worth it given validate_worker_name already restricts the character set.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/iii-worker/src/cli/registry.rs` around lines 117 - 122, The code
builds the download URL by interpolating base_or_file and name into url without
encoding; update the construction so the worker name is percent-encoded before
interpolation (encode the variable name used to build url =
format!("{}/download/{}", base_or_file, name)) to defend against characters with
special URL meaning, e.g., use a percent-encoding utility (percent-encoding or
urlencoding crate) to encode name, keep the query handling with
HTTP_CLIENT.get(&url) and request.query(&[("version", v)]) unchanged, and add
the chosen crate to Cargo.toml if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/iii-worker/src/cli/registry.rs`:
- Around line 117-122: The code builds the download URL by interpolating
base_or_file and name into url without encoding; update the construction so the
worker name is percent-encoded before interpolation (encode the variable name
used to build url = format!("{}/download/{}", base_or_file, name)) to defend
against characters with special URL meaning, e.g., use a percent-encoding
utility (percent-encoding or urlencoding crate) to encode name, keep the query
handling with HTTP_CLIENT.get(&url) and request.query(&[("version", v)])
unchanged, and add the chosen crate to Cargo.toml if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: df2b61bd-c93c-4bb0-9f0e-d37f86861105

📥 Commits

Reviewing files that changed from the base of the PR and between 68078d5 and 7c9eb1d.

📒 Files selected for processing (7)
  • crates/iii-worker/src/cli/binary_download.rs
  • crates/iii-worker/src/cli/managed.rs
  • crates/iii-worker/src/cli/registry.rs
  • crates/iii-worker/src/main.rs
  • crates/iii-worker/tests/binary_worker_integration.rs
  • crates/iii-worker/tests/config_force_integration.rs
  • crates/iii-worker/tests/config_managed_integration.rs
💤 Files with no reviewable changes (1)
  • crates/iii-worker/src/main.rs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/iii-worker/src/cli/managed.rs (1)

178-243: ⚠️ Potential issue | 🟠 Major

Derive the effective OCI worker name once and reuse it for --force.

The force branch keys running checks, artifact deletion, and remove_worker() off the full image ref, but Lines 233-239 later normalize that same ref down to the last segment before adding it. iii worker add ghcr.io/org/image:tag --force --reset-config therefore will not clear or stop the existing worker named image, and digest refs can end up with names like image@sha256.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/iii-worker/tests/config_managed_integration.rs`:
- Around line 173-193: The test handle_managed_add_oci_ref_passthrough currently
only asserts result != 0 which can false-pass; change the test to verify the
failure source by examining the error output instead of only the exit code:
invoke iii_worker::cli::managed::handle_managed_add (and the other similar test)
in a way that captures its stderr/returned error message (or adjust
handle_managed_add to return a Result with an error string if needed), then
assert the message contains pull/OCI-related text (e.g. "pull" or "OCI" or
"failed to pull") and does NOT mention API resolution (e.g. "API", "resolve", or
"registry resolution"); apply the same stricter assertion to the other test
mentioned (the one covering the non-passthrough case) so both tests check
failure source, not just non-zero exit.
- Around line 221-229: The test mutates the process-global III_API_URL without
synchronization, causing races; add a file-level static ENV_LOCK: Mutex<()>
(same pattern as in registry.rs) and acquire the lock before each env var
mutation block around calls to iii_worker::cli::managed::handle_managed_add (and
other tests in this file) so the unsafe std::env::set_var / remove_var pairs
execute while holding let _guard = ENV_LOCK.lock().unwrap(); to serialize
modifications and prevent cross-test contamination.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 89b5aca9-2c3e-43b6-bc45-92917f2b8e38

📥 Commits

Reviewing files that changed from the base of the PR and between 7c9eb1d and c919737.

📒 Files selected for processing (7)
  • crates/iii-worker/src/cli/binary_download.rs
  • crates/iii-worker/src/cli/managed.rs
  • crates/iii-worker/src/cli/registry.rs
  • crates/iii-worker/src/main.rs
  • crates/iii-worker/tests/binary_worker_integration.rs
  • crates/iii-worker/tests/config_force_integration.rs
  • crates/iii-worker/tests/config_managed_integration.rs
💤 Files with no reviewable changes (1)
  • crates/iii-worker/src/main.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/iii-worker/tests/binary_worker_integration.rs

sergiofilhowz
sergiofilhowz previously approved these changes Apr 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
crates/iii-worker/src/cli/managed.rs (2)

225-241: OCI reference name extraction may produce unexpected results for certain image formats.

The name extraction logic uses rsplit('/').next() then split(':').next(), which works for standard formats like ghcr.io/org/image:tag. However, for edge cases like:

  • image:tag (no slashes) → correctly extracts image
  • org/image (no tag) → correctly extracts image
  • :tag (starts with colon, malformed) → extracts empty string

The fallback to image_or_name mitigates the empty-string case, but consider whether such malformed inputs should be validated earlier.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/iii-worker/src/cli/managed.rs` around lines 225 - 241, The name
extraction for OCI references can yield an empty or invalid name (e.g., ":tag");
update the logic around image_or_name -> name in managed.rs to validate the
extracted value before calling handle_oci_pull_and_add: after computing name
from rsplit('/') and split(':'), trim it and if it is empty or contains invalid
characters, emit a clear error/early return (or fallback to a safer validation
path) instead of passing an empty name into handle_oci_pull_and_add; ensure any
logging (e.g., the resolving messages) reflects the validation failure so
malformed inputs are rejected or handled deterministically.

90-96: Consider handling potential serialization failure more gracefully.

The unwrap_or_default() silently swallows YAML serialization errors. While rare, if response.config.config contains values that can't be serialized to YAML, this would produce an empty config without any indication of the problem.

💡 Proposed improvement
     let config_yaml = response
         .config
         .config
         .as_object()
-        .map(|_| serde_yaml::to_string(&response.config.config).unwrap_or_default());
+        .and_then(|_| {
+            serde_yaml::to_string(&response.config.config)
+                .map_err(|e| tracing::warn!("Failed to serialize worker config: {}", e))
+                .ok()
+        });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/iii-worker/src/cli/managed.rs` around lines 90 - 96, The current code
silently swallows YAML serialization errors by using
serde_yaml::to_string(...).unwrap_or_default() when building config_yaml from
response.config.config; change this to explicitly handle serialization failures:
call serde_yaml::to_string on the actual object (not using unwrap_or_default),
match on the Result and on Err either return or propagate an error from this
function (or log and skip append) so you don't pass an empty config to
super::config_file::append_worker; ensure config_yaml (used in the call to
super::config_file::append_worker(worker_name, config_yaml.as_deref())) is an
Option<String> only when serialization succeeded and use early return or Result
propagation to surface the serialization error instead of hiding it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/iii-worker/src/cli/managed.rs`:
- Around line 225-241: The name extraction for OCI references can yield an empty
or invalid name (e.g., ":tag"); update the logic around image_or_name -> name in
managed.rs to validate the extracted value before calling
handle_oci_pull_and_add: after computing name from rsplit('/') and split(':'),
trim it and if it is empty or contains invalid characters, emit a clear
error/early return (or fallback to a safer validation path) instead of passing
an empty name into handle_oci_pull_and_add; ensure any logging (e.g., the
resolving messages) reflects the validation failure so malformed inputs are
rejected or handled deterministically.
- Around line 90-96: The current code silently swallows YAML serialization
errors by using serde_yaml::to_string(...).unwrap_or_default() when building
config_yaml from response.config.config; change this to explicitly handle
serialization failures: call serde_yaml::to_string on the actual object (not
using unwrap_or_default), match on the Result and on Err either return or
propagate an error from this function (or log and skip append) so you don't pass
an empty config to super::config_file::append_worker; ensure config_yaml (used
in the call to super::config_file::append_worker(worker_name,
config_yaml.as_deref())) is an Option<String> only when serialization succeeded
and use early return or Result propagation to surface the serialization error
instead of hiding it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f4b00ce3-01f3-4b96-87c5-f7c0286a8a09

📥 Commits

Reviewing files that changed from the base of the PR and between c919737 and fe46669.

📒 Files selected for processing (5)
  • crates/iii-worker/src/cli/managed.rs
  • crates/iii-worker/src/cli/registry.rs
  • crates/iii-worker/tests/binary_worker_integration.rs
  • crates/iii-worker/tests/config_force_integration.rs
  • crates/iii-worker/tests/config_managed_integration.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/iii-worker/tests/config_managed_integration.rs

@andersonleal andersonleal merged commit 1e6c7a4 into main Apr 10, 2026
32 checks passed
@andersonleal andersonleal deleted the feat/worker-registry-api branch April 10, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants