Skip to content

feat(engine): expose generated HTTP functions as worker groups#1640

Open
rohitg00 wants to merge 34 commits into
mainfrom
generated-http-functions
Open

feat(engine): expose generated HTTP functions as worker groups#1640
rohitg00 wants to merge 34 commits into
mainfrom
generated-http-functions

Conversation

@rohitg00

@rohitg00 rohitg00 commented May 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • track generated worker groups for externally registered functions without starting generated worker processes
  • expose converted OpenAPI/MCP sources as normal engine-runtime worker groups in engine::workers::list
  • keep generated functions directly triggerable through normal iii function ids
  • consume and strip the private metadata.iii.generatedWorker hint before public function metadata is stored
  • accept the older private metadata.iii.virtualWorker key as a compatibility alias
  • trust local generated-function bridges only when both the registering worker session and invocation URL are loopback
  • enable iii-http-functions by default so converted OpenAPI/MCP calls can register without extra config
  • cover the generated HTTP worker-group path from the Node, Python, and Rust SDK test suites

Validation

  • cargo fmt --all -- --check
  • cargo test -p iii --test generated_worker_bridge_e2e
  • cargo test -p iii generated_worker --lib
  • cargo test -p iii test_list_worker_infos_includes_generated_group_without_private_public_shape --lib
  • cargo test -p iii http_functions --lib
  • cargo check -p iii --tests
  • cargo test -p iii --test http_e2e_security
  • cargo test -p iii --test http_e2e_error_handling
  • cargo fmt -p iii-sdk -- --check
  • pnpm install --filter iii-sdk --frozen-lockfile
  • pnpm --filter iii-sdk exec tsc --noEmit
  • python3 -m compileall -q sdk/packages/python/iii/src sdk/packages/python/iii/tests/test_http_external_functions_integration.py
  • cargo test -p iii-sdk --test http_external_functions exposes_generated_http_functions --no-run
  • spec-to-worker repo: cargo fmt, cargo test, cargo clippy --all-targets --all-features -- -D warnings

Spec-to-Worker E2E Contract

The paired worker PR is iii-experimental/spec-to-worker#13. It now uses generic fixtures and docs instead of Context7-specific examples. The expected contract is:

  • OpenAPI conversion registers normal iii functions for each operation
  • MCP HTTP conversion registers normal iii functions for each discovered tool
  • MCP stdio conversion registers normal iii functions for each discovered tool
  • engine::workers::list shows the generated source as a normal engine-runtime worker group
  • public worker/function payloads do not expose private routing metadata or generated process details
  • no generated worker process is started; routing remains engine-owned through HTTP invocation

Notes

spec-to-worker::convert registers normal iii functions backed by HTTP invocation. The engine exposes a user-facing worker grouping for discovery, while routing remains engine-owned and no backing worker process is started for that group. Other workers see ordinary worker/function shapes, not private generated routing details.

Summary by CodeRabbit

  • New Features

    • Generated HTTP worker groups are exposed as normal engine workers with stable inventory and metadata.
  • Improvements

    • Session- and URL-based trust gating to prevent remote creation of loopback-targeted functions.
    • New trusted_internal flag lets trusted endpoints skip outbound URL validation.
    • Ownership-safe registration with rollback to avoid partial registrations on failure.
  • Tests

    • Added unit, integration, and end-to-end tests covering generated-worker behavior, trust rules, rollback, and listings.
  • Chores

    • CI workflow tweak and workspace dependency added.

Review Change Stack

@vercel

vercel Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
iii-website Skipped Skipped May 26, 2026 4:02pm

Request Review

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a generated-worker registry and trust gating for generated HTTP function registration, integrates claim/rollback ownership into engine registration/unregistration, skips URL validation for trusted internal endpoints, updates worker discovery, and adds e2e/sdk tests and CI/dependency tweaks.

Changes

Generated Worker Registration and Trust Gating

Layer / File(s) Summary
CI workflow and dependency
.github/workflows/ci.yml, engine/Cargo.toml
Adds cache-bin: false to rust-cache steps and adds workspace url dependency.
Generated Worker Registry
engine/src/generated_workers.rs, engine/src/lib.rs
New registry types and helpers for claiming/removing function→generated-worker mappings and extracting generated worker names from metadata.
Session Trust Infrastructure & Engine registration
engine/src/engine/mod.rs, engine/src/workers/worker/rbac_session.rs, engine/src/invocation/http_function.rs, engine/src/invocation/http_invoker.rs, engine/src/workers/http_functions/mod.rs
Adds trusted_internal to auth/session and HTTP configs/params, detects loopback/trusted-bridge sessions, gates URL validation for trusted internal endpoints, integrates registration-specific ownership claim/rollback and generated-worker mapping updates across RegisterFunction/UnregisterFunction/cleanup, and adds unit tests.
Worker Discovery and Listing
engine/src/workers/engine_fn/mod.rs
Filters generated-owned functions from normal worker lists and emits generated worker group entries with deterministic metadata.
Engine e2e tests & SDK integration
engine/tests/generated_worker_bridge_e2e.rs, engine/tests/http_e2e_*.rs, sdk/packages/node/iii/tests/http-external-functions.test.ts, sdk/packages/python/iii/tests/test_http_external_functions_integration.py, sdk/packages/rust/iii/tests/http_external_functions.rs
Adds and updates tests exercising generated-worker registration, invocation, discovery exposure, and security restrictions; updates test helpers to set trusted_internal: false where appropriate.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • iii-hq/iii#1502: Modifies the same engine registration/unregistration lifecycle paths for external-function ownership and rollback behavior, related to the ownership/rollback changes here.

Suggested reviewers

  • guibeira
  • sergiofilhowz
  • ytallo

Poem

🐰 I nibble hints from metadata sweet,

claim and release so functions can meet,
Loopback gates let trusted bridges hum,
Generated workers march—one, two, then some.
Tests thump their feet — hop, feature, done!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(engine): expose generated HTTP functions as worker groups' accurately describes the main change: exposing generated HTTP functions as visible worker groups in the engine.
Description check ✅ Passed The pull request description comprehensively covers the changes with clear sections on what was done, why, validation steps, and contract expectations, matching the template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch generated-http-functions

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
engine/src/engine/mod.rs (1)

1231-1243: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Failed external re-registration can orphan an existing live function.

These rollback paths always drop external_function_owners, even when the same function_id already had a valid registration before this attempt. If the new registration fails before replacing the handler, the old engine.functions/http_functions entry stays installed but loses its owner, so cleanup_worker will later skip teardown and leave a stale function behind.

Also applies to: 1263-1272

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@engine/src/engine/mod.rs` around lines 1231 - 1243, The rollback branch in
the re-registration path drops ownership unconditionally, which can orphan an
existing live function: ensure that when a new registration attempt fails (e.g.,
in the branches around
service_registry.get_service::<HttpFunctionsWorker>("http_functions") and the
other similar branch), you only remove or release ownership if this worker
actually became the owner; change the logic around external_function_owners and
the call to release_external_function_if_owner(&worker.id, &reg_id) to check
current owner or previous registration state before removing it, and avoid
clearing external_function_owners or calling release_external_function_if_owner
when an earlier valid registration (engine.functions / http_functions) remains
installed so cleanup_worker can still teardown correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@engine/src/engine/mod.rs`:
- Around line 1212-1215: The registry and HTTP config are using different trust
logic: compute the trust predicate once (e.g., use the result of
trusted_generated_worker_name(..., &mut reg_metadata) or an explicit
is_trusted_generated_worker boolean based on the same session+loopback checks)
and reuse that single boolean for both constructing
HttpFunctionConfig.trusted_internal and for
generated_workers.claim_function(..., trusted_generated_worker ...); update the
code around trusted_generated_worker_name / reg_metadata and the
HttpFunctionConfig creation so they read the same variable (remove any
duplicated trust checks at the HttpFunctionConfig site) and ensure the same
boolean is passed into generated_workers.claim_function to keep registry and
HTTP config consistent.

In `@engine/src/generated_workers.rs`:
- Around line 124-126: The code currently uses
iii_object.remove("generatedWorker").or_else(||
iii_object.remove("virtualWorker"))? which only removes one of the two private
hint keys; update the logic so both private hint keys ("generatedWorker" and
"virtualWorker") are removed from iii_object regardless of order, then select
the alias from whichever value existed (prefer "generatedWorker" if present,
otherwise fall back to "virtualWorker"); ensure the variable hint still holds
the chosen value and that both keys are cleared from iii_object to avoid leaking
private hints into public metadata.

In `@engine/src/workers/engine_fn/mod.rs`:
- Line 351: The generated-worker entries currently recompute connected_at_ms on
each engine::workers::list call using chrono::Utc::now(), which misrepresents
actual registration time; add a timestamp field (e.g., registered_at: i64) to
the GeneratedWorkerInfo struct, set that field once when the worker is created
in claim_function, and then change the list serialization to use
generated_worker.registered_at as u64 (instead of
chrono::Utc::now().timestamp_millis()) so the reported connected_at_ms reflects
the original registration time.

---

Outside diff comments:
In `@engine/src/engine/mod.rs`:
- Around line 1231-1243: The rollback branch in the re-registration path drops
ownership unconditionally, which can orphan an existing live function: ensure
that when a new registration attempt fails (e.g., in the branches around
service_registry.get_service::<HttpFunctionsWorker>("http_functions") and the
other similar branch), you only remove or release ownership if this worker
actually became the owner; change the logic around external_function_owners and
the call to release_external_function_if_owner(&worker.id, &reg_id) to check
current owner or previous registration state before removing it, and avoid
clearing external_function_owners or calling release_external_function_if_owner
when an earlier valid registration (engine.functions / http_functions) remains
installed so cleanup_worker can still teardown correctly.
🪄 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: a2e08645-a255-40fc-af31-cdfbab57371d

📥 Commits

Reviewing files that changed from the base of the PR and between f446118 and 4ec6f4c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/ci.yml
  • engine/Cargo.toml
  • engine/src/engine/mod.rs
  • engine/src/generated_workers.rs
  • engine/src/invocation/http_function.rs
  • engine/src/invocation/http_invoker.rs
  • engine/src/lib.rs
  • engine/src/worker_connections/mod.rs
  • engine/src/workers/engine_fn/mod.rs
  • engine/src/workers/http_functions/mod.rs
  • engine/src/workers/worker/rbac_session.rs
  • engine/tests/generated_worker_bridge_e2e.rs
  • engine/tests/http_e2e_error_handling.rs
  • engine/tests/http_e2e_security.rs
  • engine/tests/rbac_infrastructure_e2e.rs
  • sdk/packages/node/iii/tests/http-external-functions.test.ts
  • sdk/packages/python/iii/tests/test_http_external_functions_integration.py
  • sdk/packages/rust/iii/tests/http_external_functions.rs

Comment thread engine/src/engine/mod.rs
Comment thread engine/src/generated_workers.rs Outdated
Comment thread engine/src/workers/engine_fn/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
engine/src/engine/mod.rs (1)

1104-1127: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep external unregister teardown symmetric with the cleanup path.

If unregister_http_function() fails here, we only log and continue after already clearing generated_workers. cleanup_worker() handles the same failure by calling remove_function(...), so this branch can leave an orphaned engine.functions entry behind while discovery state is gone.

Suggested fix
-                    self.generated_workers.remove_function(&resolved_id);
                     if let Some(http_module) = self
                         .service_registry
                         .get_service::<HttpFunctionsWorker>("http_functions")
@@
                             Err(err) => {
                                 tracing::error!(
                                     worker_id = %worker.id,
                                     function_id = %id,
                                     error = ?err,
                                     "Failed to unregister external function"
                                 );
+                                self.remove_function(&resolved_id);
                             }
                         }
+                        self.generated_workers.remove_function(&resolved_id);
                         self.service_registry
                             .remove_function_from_services(&resolved_id);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@engine/src/engine/mod.rs` around lines 1104 - 1127, The branch that handles a
failed unregister call leaves engine state inconsistent because
self.generated_workers.remove_function(&resolved_id) ran before attempting
http_module.unregister_http_function(&resolved_id). On the Err path, mirror
cleanup_worker by invoking the same engine-level removal used there (call the
engine's remove_function(&resolved_id) or equivalent method that cleanup_worker
uses) so the engine.functions entry is cleared when unregister_http_function
fails; keep the existing
service_registry.remove_function_from_services(&resolved_id) usage as
appropriate after ensuring engine-level removal.
♻️ Duplicate comments (1)
engine/src/engine/mod.rs (1)

1320-1325: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Ignore generatedWorker hints on non-HTTP registrations.

generated_worker_name only proves the session is trusted. This branch has no loopback URL to validate, but it still records the function as a trusted generated worker, so any trusted/local WS worker can spoof a generated worker group via metadata alone.

Suggested fix
-                if let Some(worker_name) = generated_worker_name {
-                    self.generated_workers
-                        .claim_function(worker_name, worker.id, true, &reg_id);
-                } else {
-                    self.generated_workers.remove_function(&reg_id);
-                }
+                self.generated_workers.remove_function(&reg_id);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@engine/src/engine/mod.rs` around lines 1320 - 1325, The code currently treats
any registration with generated_worker_name as trusted; update the branch
handling generated_worker_name so it only calls
self.generated_workers.claim_function(...) when the registration is an HTTP
registration with a valid loopback URL (i.e., the registration type/metadata
proves a loopback endpoint), otherwise call
self.generated_workers.remove_function(&reg_id). Locate the branch using
generated_worker_name, self.generated_workers.claim_function(..., worker.id,
true, &reg_id), and self.generated_workers.remove_function(&reg_id) and add a
guard that verifies the registration has an HTTP loopback URL (or equivalent
trusted indicator) before claiming the function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@engine/src/engine/mod.rs`:
- Line 3243: The seeded function IDs use a dot separator ("external.invalid_url"
and "external.cleanup") instead of the required "::" form; change the string
literals id: "external.invalid_url".to_string() and the other occurrence at the
later location to id: "external::invalid_url".to_string() (and
"external::cleanup") and update any tests/assertions or setup code that match
the old IDs to the new "external::..." values so all comparisons and registry
lookups use the "::" separator.

---

Outside diff comments:
In `@engine/src/engine/mod.rs`:
- Around line 1104-1127: The branch that handles a failed unregister call leaves
engine state inconsistent because
self.generated_workers.remove_function(&resolved_id) ran before attempting
http_module.unregister_http_function(&resolved_id). On the Err path, mirror
cleanup_worker by invoking the same engine-level removal used there (call the
engine's remove_function(&resolved_id) or equivalent method that cleanup_worker
uses) so the engine.functions entry is cleared when unregister_http_function
fails; keep the existing
service_registry.remove_function_from_services(&resolved_id) usage as
appropriate after ensuring engine-level removal.

---

Duplicate comments:
In `@engine/src/engine/mod.rs`:
- Around line 1320-1325: The code currently treats any registration with
generated_worker_name as trusted; update the branch handling
generated_worker_name so it only calls
self.generated_workers.claim_function(...) when the registration is an HTTP
registration with a valid loopback URL (i.e., the registration type/metadata
proves a loopback endpoint), otherwise call
self.generated_workers.remove_function(&reg_id). Locate the branch using
generated_worker_name, self.generated_workers.claim_function(..., worker.id,
true, &reg_id), and self.generated_workers.remove_function(&reg_id) and add a
guard that verifies the registration has an HTTP loopback URL (or equivalent
trusted indicator) before claiming the function.
🪄 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: 543d4793-7026-4767-b3da-eea2cb004915

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec6f4c and 0a92353.

📒 Files selected for processing (3)
  • engine/src/engine/mod.rs
  • engine/src/generated_workers.rs
  • engine/src/workers/engine_fn/mod.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • engine/src/workers/engine_fn/mod.rs
  • engine/src/generated_workers.rs

Comment thread engine/src/engine/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
engine/src/workers/http_functions/mod.rs (1)

90-104: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add a doc comment to register_http_function to document the trusted_internal privilege.

The only production code path setting trusted_internal: true is in engine/src/engine/mod.rs:1259–1260, which correctly enforces both loopback validation and session trust before calling register_http_function. However, since the method is pub on a service-discoverable worker, a doc comment should explicitly document that trusted_internal: true must only be set after upstream loopback + session trust gates have been confirmed. This prevents future refactors or callers from accidentally bypassing validation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@engine/src/workers/http_functions/mod.rs` around lines 90 - 104, Add a doc
comment above the public async function register_http_function documenting the
meaning and safety requirements of the trusted_internal flag: state that
trusted_internal == true grants bypass of URL validation and must only be set by
callers that have already enforced loopback/host-only checks and session trust
(i.e., the upstream gate that currently sets trusted_internal must be retained),
warn that this function is exposed by a service-discoverable worker and that
callers must not set trusted_internal to true without those checks, and include
guidance for future maintainers to audit callers before allowing
trusted_internal=true.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@engine/src/workers/http_functions/mod.rs`:
- Around line 127-137: Add two unit tests to lock behavior: (1) a test that
calls unregister_http_function("does.not.exist") and asserts it returns an Err
with code "function_not_found" to cover the not-found branch in
unregister_http_function; (2) a test that builds a FunctionConfig with
trusted_internal = true and an invalid URL (e.g. "://bad-url"), calls
register_http_function(config) and asserts it succeeds and that
module.http_functions contains the function key, verifying that
register_http_function bypasses URL validation when trusted_internal is set. Use
existing helpers like build_module, make_function_config and
ensure_default_meter to set up the tests.

---

Outside diff comments:
In `@engine/src/workers/http_functions/mod.rs`:
- Around line 90-104: Add a doc comment above the public async function
register_http_function documenting the meaning and safety requirements of the
trusted_internal flag: state that trusted_internal == true grants bypass of URL
validation and must only be set by callers that have already enforced
loopback/host-only checks and session trust (i.e., the upstream gate that
currently sets trusted_internal must be retained), warn that this function is
exposed by a service-discoverable worker and that callers must not set
trusted_internal to true without those checks, and include guidance for future
maintainers to audit callers before allowing trusted_internal=true.
🪄 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: b71b6592-ff9f-4428-9125-825eeb164638

📥 Commits

Reviewing files that changed from the base of the PR and between e30a937 and 1ce19eb.

📒 Files selected for processing (2)
  • engine/src/engine/mod.rs
  • engine/src/workers/http_functions/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • engine/src/engine/mod.rs

Comment thread engine/src/workers/http_functions/mod.rs
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.

1 participant