remove origin validation

ALLOW_ANY_ORIGIN, ALLOW_ANY_ORIGIN_MUSIC, and everything they gated. The flag
defaulted to ON, so none of it ran on a real install — what comes out is
documented defence in depth that was already switched off. The file said so
itself: "Both flags and their call sites come out once the tailnet is the
perimeter."

Origin was never authentication here in any case. An app's `officer://<hex>`
origin is chosen by the client, forgeable outside a browser, and extractable from
a shipped binary.

Gone: the two flags, isOriginAllowed, isOriginCheckDisabled, isMusicOriginExempt,
originValidationMiddleware, ORIGIN_RULES and the whole OFFICER_<APP>_ORIGIN
scheme, PUBLIC_URL's origin/host derivation, and origin-validation.test.ts, which
existed only to pin them. CORS now echoes whatever Origin it is given, which is
what every install already did.

What SURVIVES is the reason this needed care. origin-validation.ts held two
unrelated things, and the second was the global authorization gate — a valid
non-owner token reaches only what its role grants, deliberately NOT under the
flag because it is account-based rather than origin-based. Its own comment called
it "the airtight half". Deleting the file wholesale would have deleted
authorization.

So it moves to _middlewares/capability-gate.ts as capabilityGateMiddleware, with
the name matching what it does: nothing in it reads an Origin header any more.
hono.ts mounts it in the same position, ahead of every router.

origin-middleware.ts stays and is untouched — it extracts the Origin for six auth
handlers that log it, and for passkeys. Extraction, not validation.

Also updates every claim that rested on the old model: CLAUDE.md's security
section and repo map, docs/secret-store.md, docs/mobile-api-keys.md, and five
messages in machine-setup's Tailscale section which told the owner to set
ALLOW_ANY_ORIGIN=false when declining a tailnet. That advice is now impossible to
follow, and the honest version is different: with no tailnet the token is the
whole lock, so put a proxy in front and restrict who can reach it.

Not typechecked (empty node_modules, frozen installs). Every changed file parses;
the setup section was run and writes four variables now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 00:15:25 +00:00
co-authored by Claude Opus 5
parent c5adb4aa08
commit f063fc0c08
22 changed files with 117 additions and 370 deletions
+16 -20
View File
@@ -14,11 +14,10 @@
#
# ── Why it matters to Officer specifically ──
#
# The platform's CLAUDE.md is explicit: the perimeter IS the tailnet.
# ALLOW_ANY_ORIGIN defaults ON, and that is only defensible because the machine is
# not reachable from the open internet in the first place — a valid token plus the
# tailnet is the lock. An Officer install with no tailnet is an Officer install
# with one fewer layer than it was designed around.
# The platform's CLAUDE.md is explicit: the perimeter IS the tailnet. Origin
# checking was removed outright on 2026-08-13 because the tailnet stands in its
# place, so a valid token plus the tailnet IS the lock — not one layer of two.
# An Officer install with no tailnet is missing the half the design assumes.
#
# ── Why the original hung ──
#
@@ -46,10 +45,9 @@ tailscale_help() {
echo " port forwarding, no exposed ports, no holes in the firewall."
echo ""
echo " For Officer it is not a convenience. The platform is built assuming"
echo " the tailnet IS the perimeter — ALLOW_ANY_ORIGIN defaults on, and"
echo " that is only defensible because the machine is not reachable from"
echo " outside in the first place. A valid token plus the tailnet is the"
echo " lock; without the tailnet it is one layer short of its design."
echo " the tailnet IS the perimeter, and there is no origin checking behind"
echo " it — a valid token plus the tailnet is the whole lock. Without the"
echo " tailnet you are running with half of it missing."
echo ""
echo " It is installed at this point in the run, before anything that can"
echo " lock you out of the machine, so there is always a second way in."
@@ -162,9 +160,8 @@ tailscale_networks_help() {
echo ""
echo " FOR OFFICER"
echo " Whichever you pick, the tailnet is what Officer treats as its"
echo " perimeter. ALLOW_ANY_ORIGIN defaults on, and that is only"
echo " defensible because the machine is not reachable from the open"
echo " internet in the first place. Installed at this point in the run,"
echo " perimeter, and it is not one layer of two — there is no origin"
echo " checking behind it. Installed at this point in the run,"
echo " before anything that can lock you out, so there is always a second"
echo " way in."
echo ""
@@ -219,15 +216,14 @@ tailscale_none_warning() {
echo " · This machine will be found. Anything listening on a public"
echo " address is scanned within minutes and attacked continuously."
echo ""
echo " For Officer specifically, one setting stops being safe:"
echo " For Officer specifically, this removes a layer that cannot be put"
echo " back from a setting:"
echo ""
echo " ALLOW_ANY_ORIGIN defaults ON, which means origin checking is off"
echo " unless it is explicitly set to false. That default is deliberate"
echo " and it is only defensible because the tailnet is the perimeter."
echo " With no tailnet you must set ALLOW_ANY_ORIGIN=false and put an"
echo " HTTPS reverse proxy in front of the platform, or it is running"
echo " with a check disabled that was disabled on the assumption you"
echo " are making false."
echo " There is no origin checking in the platform. It was removed"
echo " because the tailnet is the perimeter, so a valid token plus the"
echo " tailnet is the entire lock. With no tailnet, the token is the"
echo " only thing left. Put an HTTPS reverse proxy in front of the"
echo " platform and restrict who can reach it at the network layer."
}
tailscale_needs_logout() {
+2 -2
View File
@@ -491,8 +491,8 @@ if ! skip; then
if [[ "$TS_PLANE" == "none" ]]; then
warn "no private network — Tailscale is installed but not connected"
echo " Connect it later with: sudo tailscale up"
echo " Remember ALLOW_ANY_ORIGIN=false and an HTTPS proxy in front of Officer."
SUMMARY+=("Tailscale: NOT connected by choice — no private network, ALLOW_ANY_ORIGIN must be set false")
echo " Remember an HTTPS proxy in front of Officer, and restrict who can reach it."
SUMMARY+=("Tailscale: NOT connected by choice — no private network, so the token is the only lock")
TS_CONNECT=false
fi
-16
View File
@@ -474,25 +474,9 @@ if ! skip; then
ask_required ENV_PORT "Port Officer listens on" "${ENV_PORT:-9000}"
ENV_BROWSER_RELAY_PORT="${ENV_BROWSER_RELAY_PORT:-18792}"
# ── origin checking, decided by the machine rather than by a default ──
#
# ALLOW_ANY_ORIGIN defaults to ON inside the platform, which CLAUDE.md says is
# only defensible because the tailnet is the perimeter. So the value is written
# explicitly here, from whether this machine actually has one.
if tailnet_present; then
ENV_ALLOW_ANY_ORIGIN="true"
ORIGIN_WHY="tailscale0 is up, so the tailnet is the perimeter"
else
ENV_ALLOW_ANY_ORIGIN="false"
ORIGIN_WHY="no tailnet on this machine, so origin checking is left ON"
fi
echo ""
echo " ALLOW_ANY_ORIGIN=${ENV_ALLOW_ANY_ORIGIN}${ORIGIN_WHY}"
echo ""
echo " to write:"
echo " PORT=${ENV_PORT} BROWSER_RELAY_PORT=${ENV_BROWSER_RELAY_PORT}"
echo " ALLOW_ANY_ORIGIN=${ENV_ALLOW_ANY_ORIGIN}"
echo " POSTGRES_URL=${POSTGRES_URL%%:*}://…"
echo ""
echo " the install root is not written here — the platform derives it as the"
-10
View File
@@ -44,12 +44,6 @@ env_get() {
}' "$(env_file)"
}
# Origin checking is OFF unless this is explicitly false — CLAUDE.md is explicit
# that the inversion is deliberate and is only defensible because the tailnet is
# the perimeter. With no tailnet there is no perimeter, so the default stops
# being defensible and the value has to be written the other way.
tailnet_present() { ip link show tailscale0 &>/dev/null; }
write_env() {
local dest
dest="$(env_file)"
@@ -76,10 +70,6 @@ BROWSER_RELAY_PORT="${ENV_BROWSER_RELAY_PORT}"
POSTGRES_URL="${POSTGRES_URL}"
# Origin checking. Off by default in the platform, which is only safe behind a
# tailnet; written explicitly here so the machine's actual situation decides it.
ALLOW_ANY_ORIGIN="${ENV_ALLOW_ANY_ORIGIN}"
DISCORD_BUG_REPORT_WEBHOOK="${ENV_DISCORD_WEBHOOK}"
ENVF