diff --git a/CHANGELOG.md b/CHANGELOG.md index d8688fbe..89158aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This repository starts with a fresh history. Releases are listed here once tagge ## [Unreleased] +- Changed: the AutoGen (`autogen-agent-middleware`) and CrewAI (`crewai-agent-middleware`) integrations now default `base_url` to `https://api.agentmiddleware.dev` instead of `https://api.thisisatest.tech`. Pass `base_url` explicitly to keep the old host. - Upstream MCP: optional multi-tool discovery and exact-name allowlist with stable public IDs, per-tool permit scope, and unchanged per-call credits. - Initial import of the gateway (Fair Source, FSL-1.1-ALv2), the Python SDK and diff --git a/gateway/.env.example b/gateway/.env.example index 3ed81dd6..da84af25 100644 --- a/gateway/.env.example +++ b/gateway/.env.example @@ -14,7 +14,7 @@ PORT=8000 # --- Public URL (agent manifests, /llm.txt Base URL, OpenAPI servers) --- # Canonical hosted API (override for another deployment): -# PUBLIC_URL=https://api.thisisatest.tech +# PUBLIC_URL=https://api.agentmiddleware.dev PUBLIC_URL= # Public OpenAPI contact metadata. Leave blank until these are real, monitored diff --git a/gateway/app/core/public_contact.py b/gateway/app/core/public_contact.py index c924c2ef..68e2723d 100644 --- a/gateway/app/core/public_contact.py +++ b/gateway/app/core/public_contact.py @@ -17,6 +17,7 @@ _RESERVED_CONTACT_HOSTS = {"example", "invalid", "localhost", "test"} _BLOCKED_BOOKING_HOSTS = { "api.thisisatest.tech", + "api.agentmiddleware.dev", "agentmiddleware.dev", "www.agentmiddleware.dev", # The previous marketing host is still not a booking service. diff --git a/gateway/docs/constant-test-loop.md b/gateway/docs/constant-test-loop.md index 24e1867d..9c67c43a 100644 --- a/gateway/docs/constant-test-loop.md +++ b/gateway/docs/constant-test-loop.md @@ -45,7 +45,7 @@ deployment. ```bash python scripts/constant_test_loop.py \ - --api-url https://api.thisisatest.tech \ + --api-url https://api.agentmiddleware.dev \ --tool partner.echo \ --tool-args '{"text": "constant test loop"}' ``` @@ -75,7 +75,7 @@ export BOOTSTRAP_KEY="amw_live_..." # Disable shell tracing while handling credentials. Do not print this JSON. set +x PROVISION_JSON="$(python scripts/partner_api_key_bootstrap.py \ - --api-url https://api.thisisatest.tech \ + --api-url https://api.agentmiddleware.dev \ --agent-id ci-smoke-agent \ --key-name constant-test-loop \ --budget-credits 5000 \ @@ -89,7 +89,7 @@ unset PROVISION_JSON KEY_FILE="$(mktemp)" umask 077 python scripts/partner_api_key_bootstrap.py \ - --api-url https://api.thisisatest.tech \ + --api-url https://api.agentmiddleware.dev \ --agent-id ci-smoke-agent \ --key-name constant-test-loop \ --budget-credits 5000 \ @@ -101,7 +101,7 @@ export CI_SMOKE_KEY_ID="$(jq -r .key_id "$KEY_FILE")" rm "$KEY_FILE" # Clean up immediately # Run the constant test -API_URL=https://api.thisisatest.tech python scripts/constant_test_loop.py +API_URL=https://api.agentmiddleware.dev python scripts/constant_test_loop.py ``` ## Machine-Readable Bootstrap Output @@ -111,7 +111,7 @@ API_URL=https://api.thisisatest.tech python scripts/constant_test_loop.py ```bash # Pipe to jq to extract the agent API key BOOTSTRAP_KEY="..." python scripts/partner_api_key_bootstrap.py \ - --api-url https://api.thisisatest.tech \ + --api-url https://api.agentmiddleware.dev \ --agent-id ci-smoke-agent \ --key-name test-key \ --budget-credits 5000 \ @@ -119,7 +119,7 @@ BOOTSTRAP_KEY="..." python scripts/partner_api_key_bootstrap.py \ # Or pipe directly to gh secret set BOOTSTRAP_KEY="..." python scripts/partner_api_key_bootstrap.py \ - --api-url https://api.thisisatest.tech \ + --api-url https://api.agentmiddleware.dev \ --agent-id ci-smoke-agent \ --key-name constant-test-loop \ --budget-credits 5000 \ diff --git a/gateway/docs/partner-api-key-bootstrap.md b/gateway/docs/partner-api-key-bootstrap.md index 061aadcc..1e330269 100644 --- a/gateway/docs/partner-api-key-bootstrap.md +++ b/gateway/docs/partner-api-key-bootstrap.md @@ -17,7 +17,7 @@ Never commit `VALID_API_KEYS` to git. ## Operator flow (live API) ```bash -export API_URL="${PUBLIC_URL:-https://api.thisisatest.tech}" +export API_URL="${PUBLIC_URL:-https://api.agentmiddleware.dev}" export BOOTSTRAP_KEY=... # from secret manager / Railway variables — not a demo string # One-shot provisioner (prints agent key once to stdout): diff --git a/gateway/scripts/adversarial_battery.py b/gateway/scripts/adversarial_battery.py index 2c6a8c50..e0948810 100644 --- a/gateway/scripts/adversarial_battery.py +++ b/gateway/scripts/adversarial_battery.py @@ -400,7 +400,7 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument( "--confirm-production", action="store_true", - help="confirm intentional use of https://api.thisisatest.tech", + help="confirm intentional use of https://api.agentmiddleware.dev", ) args = parser.parse_args(argv) diff --git a/gateway/scripts/constant_test_loop.py b/gateway/scripts/constant_test_loop.py index fadc577b..338b9ebd 100755 --- a/gateway/scripts/constant_test_loop.py +++ b/gateway/scripts/constant_test_loop.py @@ -21,7 +21,7 @@ Run against production (once CI_SMOKE_AGENT_KEY is set in CI):: - API_URL=https://api.thisisatest.tech \\ + API_URL=https://api.agentmiddleware.dev \\ python scripts/constant_test_loop.py """ diff --git a/gateway/scripts/live_script_target.py b/gateway/scripts/live_script_target.py index 48d982e7..53f4d6bd 100644 --- a/gateway/scripts/live_script_target.py +++ b/gateway/scripts/live_script_target.py @@ -10,7 +10,10 @@ from urllib.parse import urlsplit API_URL_ENV_VAR = "AGENT_MIDDLEWARE_API_URL" -CANONICAL_PRODUCTION_ORIGIN = "https://api.thisisatest.tech" +CANONICAL_PRODUCTION_HOST = "api.agentmiddleware.dev" +LEGACY_PRODUCTION_HOSTS = ("api.thisisatest.tech",) +CANONICAL_PRODUCTION_ORIGIN = "https://api.agentmiddleware.dev" +LEGACY_PRODUCTION_ORIGINS = ("https://api.thisisatest.tech",) _DNS_LABEL = re.compile(r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$") @@ -114,7 +117,9 @@ def resolve_live_target( authority = f"{authority}:{port}" origin = f"{scheme}://{authority}" - if origin == CANONICAL_PRODUCTION_ORIGIN and not confirm_production: + if ( + host == CANONICAL_PRODUCTION_HOST or host in LEGACY_PRODUCTION_HOSTS + ) and not confirm_production: raise LiveTargetError( "the canonical production target requires --confirm-production" ) diff --git a/gateway/scripts/partner_api_key_bootstrap.py b/gateway/scripts/partner_api_key_bootstrap.py index f7692586..272edf00 100644 --- a/gateway/scripts/partner_api_key_bootstrap.py +++ b/gateway/scripts/partner_api_key_bootstrap.py @@ -181,7 +181,7 @@ def main() -> int: parser.add_argument( "--api-url", required=True, - help="Canonical public API origin, https://api.thisisatest.tech", + help="Canonical public API origin, https://api.agentmiddleware.dev", ) parser.add_argument( "--bootstrap-key", diff --git a/gateway/scripts/rotate_api_keys.py b/gateway/scripts/rotate_api_keys.py index 09591513..2897d8d5 100644 --- a/gateway/scripts/rotate_api_keys.py +++ b/gateway/scripts/rotate_api_keys.py @@ -7,7 +7,7 @@ python scripts/rotate_api_keys.py generate [--count 2] # 2. Smoke-check after host configuration and rollout are confirmed - export AGENT_MIDDLEWARE_API_URL=https://api.thisisatest.tech + export AGENT_MIDDLEWARE_API_URL=https://api.agentmiddleware.dev export OLD_API_KEY=... # exact old bootstrap key export NEW_API_KEY=... # exact intended replacement bootstrap key python scripts/rotate_api_keys.py verify diff --git a/gateway/scripts/stress_test_live.py b/gateway/scripts/stress_test_live.py index 23412942..c40b252c 100644 --- a/gateway/scripts/stress_test_live.py +++ b/gateway/scripts/stress_test_live.py @@ -647,7 +647,7 @@ async def main(argv: list[str] | None = None) -> int: parser.add_argument( "--confirm-production", action="store_true", - help="confirm intentional use of https://api.thisisatest.tech", + help="confirm intentional use of https://api.agentmiddleware.dev", ) args = parser.parse_args(argv) diff --git a/gateway/scripts/trust_plane_conformance.py b/gateway/scripts/trust_plane_conformance.py index 267aa300..8026a542 100644 --- a/gateway/scripts/trust_plane_conformance.py +++ b/gateway/scripts/trust_plane_conformance.py @@ -426,7 +426,7 @@ async def main(argv: list[str] | None = None) -> int: parser.add_argument( "--confirm-production", action="store_true", - help="confirm intentional use of https://api.thisisatest.tech", + help="confirm intentional use of https://api.agentmiddleware.dev", ) args = parser.parse_args(argv) diff --git a/gateway/tests/test_constant_test_loop.py b/gateway/tests/test_constant_test_loop.py index cdc5153c..4ba9ec76 100644 --- a/gateway/tests/test_constant_test_loop.py +++ b/gateway/tests/test_constant_test_loop.py @@ -122,7 +122,7 @@ def test_constant_test_loop_refuses_cleartext_http_non_loopback(): """Configuration error (exit 2) when API_URL uses HTTP for non-loopback host.""" env = { **os.environ, - "API_URL": "http://api.thisisatest.tech", + "API_URL": "http://api.agentmiddleware.dev", "CI_SMOKE_AGENT_KEY": "test_key_value", } result = subprocess.run( @@ -624,7 +624,7 @@ def test_off_loopback_runs_require_a_pinned_tool_and_approved_payload(): sys.executable, str(CONSTANT_TEST_SCRIPT), "--api-url", - "https://api.thisisatest.tech", + "https://api.agentmiddleware.dev", ], env=env, capture_output=True, @@ -641,7 +641,7 @@ def test_off_loopback_runs_require_a_pinned_tool_and_approved_payload(): sys.executable, str(CONSTANT_TEST_SCRIPT), "--api-url", - "https://api.thisisatest.tech", + "https://api.agentmiddleware.dev", "--tool", "partner.echo", ], diff --git a/gateway/tests/test_core_db_qa.py b/gateway/tests/test_core_db_qa.py index 8c7c123a..63468f4f 100644 --- a/gateway/tests/test_core_db_qa.py +++ b/gateway/tests/test_core_db_qa.py @@ -366,6 +366,14 @@ def test_public_contact_valid_triple(): ), "^PUBLIC_CONTACT_URL must point to a booking service$", ), + ( + _Contact( + name="Acme Operations", + email="ops@acme-corp.com", + url="https://api.agentmiddleware.dev/book", + ), + "^PUBLIC_CONTACT_URL must point to a booking service$", + ), ( _Contact( name="Agent Middleware API", diff --git a/gateway/tests/test_live_script_target_safety.py b/gateway/tests/test_live_script_target_safety.py index 04f47d63..22014e77 100644 --- a/gateway/tests/test_live_script_target_safety.py +++ b/gateway/tests/test_live_script_target_safety.py @@ -94,18 +94,42 @@ def test_multiple_trailing_dns_root_dots_are_rejected() -> None: def test_canonical_production_requires_confirmation() -> None: with pytest.raises(LiveTargetError, match="--confirm-production"): resolve_live_target( - "https://API.THISISATEST.TECH.:443/", + "https://API.AGENTMIDDLEWARE.DEV.:443/", confirm_production=False, environ={}, ) assert ( resolve_live_target( - "https://API.THISISATEST.TECH.:443/", + "https://API.AGENTMIDDLEWARE.DEV.:443/", confirm_production=True, environ={}, ) - == "https://api.thisisatest.tech" + == "https://api.agentmiddleware.dev" + ) + + +def test_production_hosts_require_confirmation_regardless_of_port() -> None: + with pytest.raises(LiveTargetError, match="--confirm-production"): + resolve_live_target( + "https://api.agentmiddleware.dev:8443", + confirm_production=False, + environ={}, + ) + with pytest.raises(LiveTargetError, match="--confirm-production"): + resolve_live_target( + "https://api.thisisatest.tech:8443", + confirm_production=False, + environ={}, + ) + + assert ( + resolve_live_target( + "https://api.agentmiddleware.dev:8443", + confirm_production=True, + environ={}, + ) + == "https://api.agentmiddleware.dev:8443" ) @@ -208,7 +232,7 @@ def test_target_normalization(target: str, normalized: str) -> None: id="multiple-trailing-root-dots", ), pytest.param( - ["--api-url", "https://api.thisisatest.tech"], + ["--api-url", "https://api.agentmiddleware.dev"], None, CANARY_KEY, id="production-without-confirmation", @@ -431,10 +455,15 @@ def fake_urlopen(request: object, timeout: float | None = None) -> object: pytest.param("", [], id="empty-target"), pytest.param("http://staging.example.test", [], id="remote-cleartext"), pytest.param( - "https://api.thisisatest.tech", [], id="production-without-confirmation" + "https://api.agentmiddleware.dev", [], id="production-without-confirmation" ), pytest.param( - "https://API.THISISATEST.TECH./", [], id="production-alias-unconfirmed" + "https://api.thisisatest.tech", + [], + id="legacy-production-without-confirmation", + ), + pytest.param( + "https://API.AGENTMIDDLEWARE.DEV./", [], id="production-alias-unconfirmed" ), pytest.param( "https://user:password@staging.example.test", @@ -499,9 +528,9 @@ def test_adversarial_battery_requires_bootstrap_key_before_any_request( id="cli-overrides-environment", ), pytest.param( - "https://api.thisisatest.tech", + "https://api.agentmiddleware.dev", ["--confirm-production"], - "https://api.thisisatest.tech", + "https://api.agentmiddleware.dev", id="confirmed-production", ), ], diff --git a/gateway/tests/test_mcp_discovery_wedge_gate.py b/gateway/tests/test_mcp_discovery_wedge_gate.py index 546a8d31..c354c336 100644 --- a/gateway/tests/test_mcp_discovery_wedge_gate.py +++ b/gateway/tests/test_mcp_discovery_wedge_gate.py @@ -166,11 +166,11 @@ async def test_awi_manifest_404_when_proof_off(client, proof_surfaces_off): @pytest.mark.anyio async def test_llm_txt_uses_public_url_and_wedge_quickstart(client, monkeypatch): settings = get_settings() - monkeypatch.setattr(settings, "PUBLIC_URL", "https://api.thisisatest.tech") + monkeypatch.setattr(settings, "PUBLIC_URL", "https://api.agentmiddleware.dev") resp = await client.get("/llm.txt") assert resp.status_code == 200 text = resp.text - assert "https://api.thisisatest.tech" in text + assert "**Base URL:** https://api.agentmiddleware.dev\n" in text assert "**Base URL:** http://localhost:8000" not in text assert "partner.notes.write" in text assert "POST /v1/telemetry/events" not in text diff --git a/gateway/tests/test_phase7_followups.py b/gateway/tests/test_phase7_followups.py index 87951879..11c9bf29 100644 --- a/gateway/tests/test_phase7_followups.py +++ b/gateway/tests/test_phase7_followups.py @@ -19,7 +19,7 @@ async def client(): @pytest.mark.anyio async def test_agent_json_canonical_api_from_public_url(client, monkeypatch): - public = "https://api.thisisatest.tech" + public = "https://api.agentmiddleware.dev" monkeypatch.setenv("PUBLIC_URL", public) get_settings.cache_clear() try: diff --git a/gateway/tests/test_production_trust_posture.py b/gateway/tests/test_production_trust_posture.py index eb4927d3..bbd79cdb 100644 --- a/gateway/tests/test_production_trust_posture.py +++ b/gateway/tests/test_production_trust_posture.py @@ -45,7 +45,7 @@ "DEBUG": "false", "WEBAUTHN_ALLOW_MOCK": "false", "TRUST_SIGNING_PRIVATE_KEY_B64": _TEST_SIGNING_PRIVATE_KEY_B64, - "PUBLIC_URL": "https://api.thisisatest.tech", + "PUBLIC_URL": "https://api.agentmiddleware.dev", # A production posture is not complete without the relational database the # wallets, permits, receipts, and ledger live in. It is PostgreSQL because # validate_trust_mode_config refuses SQLite here: SQLAlchemy silently drops diff --git a/gateway/tests/test_rotate_api_keys.py b/gateway/tests/test_rotate_api_keys.py index 2730d4ec..3649b1f5 100644 --- a/gateway/tests/test_rotate_api_keys.py +++ b/gateway/tests/test_rotate_api_keys.py @@ -132,7 +132,7 @@ def unexpected_probe(*args): ("target", "normalized"), [ ("https://api.example.test/", "https://api.example.test"), - ("https://api.thisisatest.tech", "https://api.thisisatest.tech"), + ("https://api.agentmiddleware.dev", "https://api.agentmiddleware.dev"), ("http://localhost:8000", "http://localhost:8000"), ("http://127.0.0.1:8000", "http://127.0.0.1:8000"), ("http://[::1]:8000", "http://[::1]:8000"), diff --git a/integrations/autogen-agent-middleware/src/autogen_b2a/tool.py b/integrations/autogen-agent-middleware/src/autogen_b2a/tool.py index 21ad5f9b..111ef31c 100644 --- a/integrations/autogen-agent-middleware/src/autogen_b2a/tool.py +++ b/integrations/autogen-agent-middleware/src/autogen_b2a/tool.py @@ -80,7 +80,7 @@ def __init__( self, api_key: str, wallet_id: str, - base_url: str = "https://api.thisisatest.tech", + base_url: str = "https://api.agentmiddleware.dev", permit_budget: Decimal = Decimal(100), permit_ttl_minutes: int = 30, ): diff --git a/integrations/crewai-agent-middleware/src/crewai_b2a/tool.py b/integrations/crewai-agent-middleware/src/crewai_b2a/tool.py index 8a75aee0..203a24f1 100644 --- a/integrations/crewai-agent-middleware/src/crewai_b2a/tool.py +++ b/integrations/crewai-agent-middleware/src/crewai_b2a/tool.py @@ -151,7 +151,7 @@ class CrewAIB2ATool(BaseTool): sync_client_factory: Callable[[], B2AClient] | None = Field( default=None, exclude=True, repr=False ) - base_url: str = "https://api.thisisatest.tech" + base_url: str = "https://api.agentmiddleware.dev" api_key: str = Field(repr=False, exclude=True) wallet_id: str permit_budget: Decimal = Decimal(100) @@ -177,7 +177,7 @@ def __init__( self, api_key: str, wallet_id: str, - base_url: str = "https://api.thisisatest.tech", + base_url: str = "https://api.agentmiddleware.dev", permit_budget: Decimal = Decimal(100), permit_ttl_minutes: int = 30, **kwargs: Any, diff --git a/integrations/crewai-agent-middleware/tests/test_boundaries.py b/integrations/crewai-agent-middleware/tests/test_boundaries.py index 34f5b283..c1f77573 100644 --- a/integrations/crewai-agent-middleware/tests/test_boundaries.py +++ b/integrations/crewai-agent-middleware/tests/test_boundaries.py @@ -453,7 +453,10 @@ async def handler(request: httpx.Request) -> httpx.Response: class _CaptureClient(real_client): def __init__( - self, api_key: str, base_url: str = "https://api.thisisatest.tech", **kwargs + self, + api_key: str, + base_url: str = "https://api.agentmiddleware.dev", + **kwargs, ): captured["constructed_key"] = api_key captured["constructed_url"] = base_url