Skip to content

feat(api): auto-derive CORS origins from cao-server --host/--port#261

Merged
haofeif merged 4 commits into
awslabs:mainfrom
call-me-ram:feat/issue-151-cors-from-port
May 29, 2026
Merged

feat(api): auto-derive CORS origins from cao-server --host/--port#261
haofeif merged 4 commits into
awslabs:mainfrom
call-me-ram:feat/issue-151-cors-from-port

Conversation

@call-me-ram

Copy link
Copy Markdown
Contributor

Summary

  • Adds add_local_cors_origins(host, port) in constants.py that extends CORS_ORIGINS in place from the cao-server listen address.
  • Calls it from cao-server's main() after argparse, so a custom --host/--port no longer requires the operator to also set CAO_CORS_ORIGINS for same-host browser access.
  • This is the second half of [feat] Make CORS origins configurable #151 — the CAO_CORS_ORIGINS env-var override shipped in fix(api): make network allowlists configurable via env vars #255; auto-derivation from --port was the remaining follow-up.

Behaviour

--host Origins added
127.0.0.1, localhost, 0.0.0.0, :: http://localhost:<port>, http://127.0.0.1:<port>
anything else (e.g. cao.internal) http://<host>:<port>

Idempotent — repeat calls (or hitting an already-present default like 5173) do not duplicate entries.

Implementation note

Starlette's CORSMiddleware stores allow_origins by reference and re-reads it per request (starlette/middleware/cors.py lines 66, 104). Mutating CORS_ORIGINS in place after the middleware is installed but before uvicorn.run is therefore sufficient — no need to defer app construction or reorder middleware wiring. TrustedHostMiddleware does copy its allowed-hosts list at init, so ALLOWED_HOSTS is intentionally left alone here; that surface stays under operator control via CAO_ALLOWED_HOSTS as today.

Test plan

  • pytest test/test_constants.py — 6 new tests in TestAddLocalCorsOrigins cover: loopback host, 0.0.0.0 wildcard, explicit hostname, idempotency, no-duplicate for default ports, and that mutation is visible via a previously-captured list reference (the contract that makes the in-place approach work).
  • Start cao-server --port 9999, open the UI in a browser, confirm API calls succeed without setting CAO_CORS_ORIGINS.

Closes #151.

Operators running cao-server on a non-default port previously had to
also set CAO_CORS_ORIGINS for the browser-side UI to reach the API,
even though the listen address was already known via --host/--port.

Wire a helper that extends CORS_ORIGINS in place after argparse:
loopback binds (127.0.0.1, localhost, 0.0.0.0, ::) yield both
http://localhost:<port> and http://127.0.0.1:<port>; an explicit
host yields http://<host>:<port>. Starlette's CORSMiddleware reads
the list by reference, so mutating it before uvicorn.run reaches
the already-installed middleware without re-wiring app construction.

Closes awslabs#151.

Copilot AI 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.

Pull request overview

Adds automatic CORS origin derivation from the cao-server listen host/port so custom local ports work without requiring CAO_CORS_ORIGINS.

Changes:

  • Adds add_local_cors_origins(host, port) to mutate CORS_ORIGINS in place.
  • Calls the helper from api.main.main() after parsing CLI arguments and before starting uvicorn.
  • Adds unit tests covering loopback, wildcard, custom host, idempotency, and in-place mutation behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/cli_agent_orchestrator/constants.py Adds runtime CORS origin derivation helper.
src/cli_agent_orchestrator/api/main.py Wires the helper into cao-server startup.
test/test_constants.py Adds tests for derived CORS origins and mutation semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cli_agent_orchestrator/constants.py Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/cli_agent_orchestrator/constants.py Outdated
Comment thread src/cli_agent_orchestrator/api/main.py
…in()

The CORS helper added in 43e14f7 produced unbracketed IPv6 origins
(e.g. http://::1:9999) which can never match a real browser Origin
header — browsers serialize IPv6 literals as http://[::1]:9999 — so
same-host access still failed when cao-server ran on ::1 or any other
v6 literal. Treat ::1 as a loopback alias alongside localhost and
127.0.0.1 so any of the three works, and bracket non-loopback IPv6
literals.

Also asserts main() actually calls add_local_cors_origins with the
resolved host/port before uvicorn.run; without this the helper could
be silently disconnected from the CLI and every existing test would
still pass.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@7906e83). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #261   +/-   ##
=======================================
  Coverage        ?   92.47%           
=======================================
  Files           ?       69           
  Lines           ?     6274           
  Branches        ?        0           
=======================================
  Hits            ?     5802           
  Misses          ?      472           
  Partials        ?        0           
Flag Coverage Δ
unittests 92.47% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread test/api/test_api_endpoints.py Outdated
Comment on lines +1105 to +1107
assert mock_add.call_count == 1
assert mock_uvicorn.call_count == 1
assert mock_add.call_args_list[0].args == ("0.0.0.0", 9999)
@haofeif haofeif added the enhancement New feature or request label May 28, 2026
call-me-ram and others added 2 commits May 28, 2026 18:07
Address the two follow-ups Copilot left on the previous push:

- The CI `Code Quality` job runs `black --check` on the whole tree;
  the helper added in 5a19d23 was not black-clean. Reformat.
- The wiring test asserted that both `add_local_cors_origins` and
  `uvicorn.run` ran, but not their relative order. Since `uvicorn.run`
  blocks, mutating CORS_ORIGINS after it would never reach the first
  request. Attach both patches to a parent mock and assert
  `parent.mock_calls` shows the helper call first, the uvicorn call
  second.
@haofeif haofeif merged commit 21baa89 into awslabs:main May 29, 2026
call-me-ram added a commit to call-me-ram/cli-agent-orchestrator that referenced this pull request Jun 3, 2026
Bring the event-driven architecture branch up to date with main (98
commits) and reconcile the rewrite with features that landed after it
forked: eager inbox delivery (awslabs#251), the OpenCode poller, env-var
forwarding (awslabs#259), memory curation (awslabs#254/awslabs#262), CORS auto-derive (awslabs#261),
DNS host validation (awslabs#124), and the self-send guard (awslabs#24).

Highlights:
- Providers adopt the async initialize() + get_status(buffer) contract;
  copilot_cli/opencode_cli converted; kiro keeps colour-only ANSI
  stripping so carriage-return-redraw permission prompts aren't misread
  as idle.
- Event-driven InboxService.deliver_pending with the awslabs#251 eager gate and
  message-sender attribution; OpenCode poller retained as a status-driven
  method; the watchdog (PollingObserver/LogFileHandler) is removed.
- terminal_service.create_terminal is async (FIFO + StatusMonitor wiring);
  session_service.create_session, flow_service.execute_flow, the API
  endpoints, and `cao flow run` updated to await.
- memory_service curated path and the flow CLI fixed to the new contract.

Full unit suite green (1908 passed); black + isort clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] Make CORS origins configurable

4 participants