Move the browser to the home machine over the tailnet and delete it from the VPS #46
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Blocked by: #45, #44
Part of #38
What to build
The headless browser leaves the VPS. 471 MiB of working set is returned to a 2 GiB box that has no
swap, and the browser instead runs on the home machine as its own deployable unit, reached over the
existing tailnet. No fallback sidecar is left behind — the memory this reclaims must not be quietly
given back.
The backend needs no code change: the CDP endpoint is already a configuration seam and the fetcher
only ever holds the endpoint URL. Relocation is one environment variable, reversible in one line.
Implementation decisions
touching the API stack. The API stack drops the service, its
depends_on, and the dedicatedbrowser network along with it.
BROWSER_WS_URLmust name the tailnet IP address, never a MagicDNS hostname: Chrome'sDevTools HTTP handler answers
/json/versionwith a 500 for any Host header that is not an IP orlocalhost. This is the same trap already documented for the Docker service name.0.0.0.0. CDP hasno authentication of its own — anything that reaches it has full control of that browser and a
foothold on that host. Today the safety comes from Docker network membership; on a machine with a
real LAN, a
0.0.0.0bind is a hole punched into the home network. Tailscale identity plus aper-device ACL is the access control. No bearer-token proxy is added: it would only defend against
a device already inside the tailnet.
the browser is the newcomer, not the incumbent:
pressure instead of taking memory from the runner and cold pages go to that box's 5.9 GiB of
SATA swap. Sizing evidence: a tuned Chrome survived a 350 MiB cap with 114 MiB headroom and zero
kills, while untuned Chrome peaked at 645 MiB cgroup — more than is free on that machine, so the
cap is load-bearing, not decorative.
at half a CPU, immaterial against a 45-second challenge budget.
BROWSER_WS_URLalready does: plain-TLSlibraries are unaffected, kagane logs and skips, the series waits out its cooldown, and stored
covers keep serving. A power outage costs chapter freshness, never the appearance of the library.
all currently describe a same-host sidecar and must describe a remote one.
Acceptance criteria
depends_onon it, or the browsernetwork, and starts clean.
only — a connection attempt from the LAN address is refused.
BROWSER_WS_URLset to the tailnet IP.proving the challenge clears from the home machine's residential egress. A red run here means
"not clearing from this address right now", which is a fact to re-check rather than
necessarily a defect.
the plain-TLS sites.
tailnet-IP requirement.
Blocked by
blanks the library.
there first is two deploys and a window where the CI runner is squeezed.
Implementation landed in PR #52 (branch
feat/46-remote-browser). Setup and deployment stay with the operator, as agreed — everything in the repo that the move needed is written.Acceptance criteria
depends_onon it, or the browser network, and starts clean. Brought the stack up: two services,/healthz200. One thing the spec did not anticipate: dropping thebrowsernetwork leftbookmark-apiondbalone, which isinternal: true— that meant no published port and no egress for the poller at all.bookmark-apinow takes thedefaultnetwork. Only found by running it.BROWSER_BIND_ADDRhas no default, so an unset value fails the deploy rather than publishing CDP to the LAN.BROWSER_WS_URLis now empty by default and documented as the tailnet IP; the MagicDNS/hostname trap is called out at every point of action.TestSmokeKaganeImagereturned 56710 bytes ofimage/webp,TestSmokeKaganeGeta 200 with a real chapter list. The challenge cleared with the reduced 128 MiB shm and the 512 MiB cap in force (321 MiB peak, zero OOM kills, zero restarts). That is the challenge clearing from a residential CGNAT egress, not from the home machine's — re-run it there after deploying.REDEPLOY.md§8 has the restart/OOM check;DEPLOY.md§7 now takes a VPSfree -mreading before and after, which criterion 7 previously had no way to measure..envcommentary, both runbooks and ADR-0006 all describe the remote browser, the two-unit topology and the tailnet-IP requirement.Two things the spec asserted but did not instruct, both now steps in
DEPLOY.md§7:free -m.Deploy order:
DEPLOY.md§7 (home machine first, thenBROWSER_WS_URLon the VPS), then re-run the smoke check from the VPS against the tailnet endpoint. Updating the browser afterwards isREDEPLOY.md§8 and touches nothing on the VPS.Moving to
ready-for-human: what remains is the deploy and the multi-day observation.