Repository navigation
Serve HTTPS on loopback so Safari can reach the runtime - #1
Merged
Merged
Conversation
Safari blocks an HTTPS page from calling http://127.0.0.1 as mixed content; Chrome and Firefox allow it. Measured on macOS 2026-09-19 with a test page on a public HTTPS origin: Chrome 152 and Firefox 155 passed against http://127.0.0.1:9477, Safari 26.6 failed ("Load failed"), and Safari passed against https://127.0.0.1:9478 with a trusted self-signed loopback certificate. - `agent-runtime tls setup` creates that certificate in <runtime dir>/tls (key 0600, dir 0700): RSA 2048, SAN 127.0.0.1 / ::1 / localhost, serverAuth only, CA:FALSE, 397 days. On macOS it adds it to the login keychain trusted for SSL only (macOS asks for the password). It reuses a valid certificate and renews one within 30 days of expiry. `tls status` shows expiry and trust; `tls remove` untrusts and deletes it. - `agent-runtime serve` also listens on https://127.0.0.1:9478 (--https-port, --no-https, AGENT_RUNTIME_HTTPS_PORT) when the certificate exists. Both listeners serve the same app, so pairing and tokens are shared and enforced alike. One signal handler stops both: uvicorn servers each install their own, and the last one would win. It falls back to HTTP only if the port is taken or the certificate expired, and says why. --debug keeps the single auto-reload listener. - Docs: the `targetAddressSpace: "local"` hint is removed. Chrome 151 rejects it for 127.0.0.1, which is in the loopback address space, and no annotation is needed. integration.md has the measured browser table and an HTTPS-then-HTTP detection example; security.md covers the certificate; the installer mentions `tls setup`. - New dependency: cryptography>=42. Tests: certificate properties, reuse and renewal, the macOS keychain commands (mocked), the CLI, and an end-to-end `serve` run. It checks HTTP, verified HTTPS for 127.0.0.1 and localhost, and pairing enforced on HTTPS, and that one SIGINT stops both listeners. 132 passed; ruff, format and mypy clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Safari blocks HTTPS pages from calling
http://127.0.0.1(mixed content). Chrome and Firefox allow it. This adds an HTTPS listener on loopback with a narrow, locally generated certificate.Measured on macOS, 2026-09-19. The test page ran from a public HTTPS origin (the SciMigo CDN) and paired, ran checks, streamed output and interrupted.
http://127.0.0.1:9477https://127.0.0.1:9478, trusted certTypeError: Load failedWhat's in it
agent-runtime tls setup | status | remove<runtime dir>/tls/localhost.pemand its key (key0600, dir0700).127.0.0.1,::1andlocalhostonly,serverAuthonly,CA:FALSEand nokeyCertSign. It's valid for 397 days, andsetuprenews it within 30 days of expiry.setupadds it to the login keychain with SSL-only trust (security add-trusted-cert -r trustRoot -p ssl), which asks for the user's password.agent-runtime servehttps://127.0.0.1:9478when the certificate exists (--https-port,--no-https,AGENT_RUNTIME_HTTPS_PORT).serving.py). Each uvicorn server installs its own handlers, and the last one would win.--debugkeeps the single auto-reload listener.targetAddressSpace: "local"hint. Chrome 151 rejects it for 127.0.0.1, which is in theloopbackaddress space, and no annotation is needed.integration.mdgets the browser table and an HTTPS-then-HTTP detection example.security.mdgets a certificate section; the installer mentionstls setup.cryptography>=42.Tests
tests/test_tls.py: certificate names and constraints, validity, key permissions, reuse and renewal, the macOS keychain commands (mocked), and the CLI.tests/test_serving.py: an end-to-endagent-runtime servesubprocess. It checks HTTP, verified HTTPS for127.0.0.1andlocalhost, that pairing is enforced on HTTPS (401 without a token), and that one SIGINT stops both listeners with exit 0.Not tested here
The real keychain trust (
tls setupon a Mac) hasn't been run through this code. The manual version it automates (same certificate profile, samesecurity add-trusted-certflags) is what made Safari pass. Please runagent-runtime tls setupon a Mac before merging.🤖 Generated with Claude Code