`CorsLayer::allow_origin` replaces rather than appends, so folding over the
configured list left only `blackbeard.internal` allowed and silently refused
`blackbeard.observer` — the site's own public origin. It broke nothing, because
the frontend is served same-origin and never consults CORS, which is precisely
why it would have gone unnoticed until something else called the API.
Verified on the deployed vhost: both configured origins are now echoed back and
an unlisted one is refused.
Also records the deployment gotchas this session turned up (exact-argument
sudoers matching, the runas spec for the config check, the cross-site hop the
loopback probe cannot see, and why a WebSocket upgrade test needs --http1.1).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSDYiibCtELsrjQq6KXnoi