Repository navigation
feat(reachability): make kubo dialable from browsers - #208
Conversation
computeKuboConfig(kuboConfig, reachabilityConfig, enabled) is the one place that decides the kubo edits. planKuboConfig also returns a record of what changed, which tells our entries from identical operator ones and lets disable restore the touched fields byte for byte.
reachability/config.json holds the operator's choices and refuses to load a newer version rather than drop its fields. The daemon's check results go in a separate file so they cannot overwrite a setup in progress.
…e status Caddy keeps certificates for old addresses and other CAs until they expire, so status now filters to the configured CA and current names. A damaged runtime status file reads as no checks yet, and the text output prints domain and Caddy checks.
…le tests Caddy is downloaded on first use and checked against a sha256 pinned in source. The generated Caddyfile keeps Caddy off UDP 443 and port 80. The integration test issues a certificate for 127.0.0.1 from Pebble and completes a js-libp2p handshake with kubo through Caddy.
Discovers public addresses from interfaces, kubo and the router, obtains an IP certificate, applies the kubo config and restarts kubo. It checks what kubo really listens on, because kubo stays silent when it cannot bind UDP 443.
--domain gets a normal certificate when 443 is free, or prints a proxy snippet for the detected server when it is busy. --announce is added only after a libp2p handshake through it succeeds. A name that does not resolve here is refused unless --force is passed.
The daemon spawns Caddy and keeps it up, re-derives public IPs and verifies announced names every 5 minutes, and moves the certificate and announcement on an IP change. A stale name warns hourly and is dropped after 24 hours. Without a configuration it prints one line naming the setup command.
enable repairs from stored choices without asking. disable restores the kubo fields and keeps state; --purge moves it to a timestamped backup. Also: announced addresses no longer confirm an old IP, a dead Caddy no longer holds the CLI open, a domain typed at the prompt is checked like a flag, and mode keeps a proxy alongside a domain.
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec82dea084
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Checks permission for Caddy and kubo to bind port 443, including UDP 443 where kubo otherwise silently does not listen, what holds TCP 80, TCP 443 and UDP 443, active ufw and firewalld rules, and what kubo really listens on. Each result names its fix.
status took "Announced addresses" from reachability's own state, so it looked healthy after an address was removed from kubo's config, or while kubo ran from an older copy of it. A new kubo-announce check compares that state with kubo's config file and with running kubo's /id. enable could not repair the second case, because it restarted kubo only when the file changed. setup and enable now also restart kubo when it is not announcing the addresses, and warn if a restart does not help, as when Addresses.NoAnnounce hides one. The integration tests stand in 127.0.0.1 for a public address, which a repo created by the daemon hides through NoAnnounce, so they now clear that entry.
A host with a global IPv6 behind a router's firewall could never get a certificate for it, yet the address looked new on every check. The daemon asked the CA again about every 10 minutes, forever. It now waits per address family, from 1 hour doubling up to 24, and no longer reports a move or rewrites kubo's config when nothing moved. - status: skip the kubo-ws check on a node setup left without WSS, where the listener is absent on purpose and enable could not add it. - runtime: take what is dropped from the config. setup can announce an address again without touching the status file, which left a broken name announced forever, and a lost status file stopped a recovered name being restored. - runtime: stopping an adopted Caddy no longer hides the next crash's warning, since an adopted Caddy never reports an exit.
…tificate A rerun that could not issue any certificate, say on an IP change or a move to another CA, wiped the Caddyfile sites and announced addresses and set serveWss to false, turning a working node into WebTransport-only. It now leaves such a node exactly as it was, and refuses --announce. - checks: the Nginx HTTP and Apache ufw profiles open only port 80, so they no longer count as allowing TCP 443. - kubo-config: undoing our edits removes one copy of each entry, so an identical entry the operator added while enabled survives disable. - docs: reachability sets up two of the ways a browser can dial a node, not the only two.
On Windows CI the test's edit to Addresses.NoAnnounce was gone by the time kubo was checked. pkc-js sets kubo's Routing config in the background right after the daemon starts, which rewrites the same file, so the helper now repeats the edit until running kubo has it, and logs any retries. Commands run by this test now time out with their output, instead of leaving only the test's own timeout to report a hang.
…errors Stale domain: a setup rerun removed the Caddy site of a stored domain whose DNS no longer pointed here, and did not record a shorter outage as dropped. When DNS recovered, Caddy failed the TLS handshake for the announced name, or the daemon never announced the name again. The site is now kept and the address moves to droppedAnnounce. Certificate retries: when no certificate could be issued for a moved address, the daemon ordered again every other check, which is 6 attempts an hour against Let's Encrypt's limit of 5 failed validations. It now backs off. The stored old address no longer ends the wait, since only a newly held address proves that a certificate was issued. Proxy names: an IP change added a site for any domain with a matching proxy address, so Caddy kept ordering a certificate for a name that points elsewhere. An IP change now keeps the domain sites setup chose. Bind errors: each Caddy version is a new file without the setcap capability, which nothing here can grant. The daemon now prints the fix for the current binary when Caddy may not bind its port.
…hdog No public IP: a setup rerun that found no public address emptied the stored addresses and the Caddyfile of a node serving WSS. The daemon then stopped looking at IPs, so WSS stayed off until setup ran again. A router not answering says nothing about the address, so this now leaves the node as it was, and refuses --announce, the same as a rerun that gets no certificate. Kubo watchdog: the daemon waited for its first Caddy start before setting up the kubo watchdog and the repo GC scheduler, and that start can spend minutes downloading Caddy. If kubo's restart failed during the download, nothing retried it. Reachability now starts after both.
Shutdown: stopping the daemon waited for a certificate wait of up to 90 s, or a stalled Caddy download up to the 120 s shutdown limit. Both now end when the runtime stops. A stop during an IP change also restores the old Caddyfile, so the next start does not serve an address the config does not hold yet, and it is not counted as a failed issuance. ufw: a blanket Anywhere rule counted as allowing TCP 443 but not UDP 443, so status and setup warned about a port that was open.
…domain A setup run with a new --domain kept the old domain's address in droppedAnnounce. If that name later pointed here again, the daemon announced it on DNS alone, with no Caddy site behind it, so browser dials failed the TLS handshake. Setup now keeps only dropped addresses of the current domain or of a proxy.
Router forwards: a stop during the first Caddy download found nothing to clean up, then start() carried on, set its timers and asked the router for forwards that outlived the release in stop(). Both start() and refreshLeases() now check for a stop first. ufw: rules such as 80,443/tcp and 443:444/udp were not read as including 443, so status and setup warned about a port that was open. Outgoing rules, which print as ALLOW OUT, were counted as opening the port, and no longer are.
Closes #198
Closes #209
Closes #216
This replaces #202, #203, #204, #205, #206 and #207, which were one feature split across a stack. Their review comments stay on those PRs.
Summary
Evidence
libp2p.directwent away, and nothing reported it.After:
npm run test:cli: 64 files, 961 passed, 2 skipped.Real binaries, on Linux locally and on all three OSes in CI (Pebble as the CA, 127.0.0.1 as the "public" address):
Pure logic, with a fake clock where it matters:
Another Caddy on the machine, real binaries:
Fixed from review, each with a test:
Live evidence through this code: on a real server with a public IPv4,
reachability setupgot a staging and then a trusted production Let's Encrypt IP certificate. From another network, the WebSocket upgrade returned101with the certificate verified and a libp2p handshake matched the node's peer ID. Real Chromium, with default certificate checks, completed libp2p handshakes over WSS and over WebTransport on UDP 443. Details are on #198.Live evidence, by hand, not through this code: real staging and production issuance for a bare IP, with the same Caddy version and settings, is recorded on #198. A js-libp2p dial using this PR's dial code then reached that terminator from another network.
Not proven yet:
Known issues
modecan go stale when the daemon drops or restores a domain, or changes IPs: fix(reachability): update mode when the daemon drops or restores a domain #221.Where this differs from #198
pmpNatneeds the router's address. libp2p'supnp-natis also UPnP only.setcapon Caddy doesn't help it. kubo starts normally, logs nothing and just doesn't listen, so setup checksswarm/addrs/listenand prints the fix.kuboEdits,acmeDirectory,caddyAdminPort,domainForced,externalAnnounce,droppedAnnounce.Caddyfile choices that came from running it
Setup refuses the production Let's Encrypt directory when
CIis set.Merge Danger
Door: one-way, once released
config.jsonv1 becomes a format later versions must read. Everything else can be reverted, anddisablerestores the kubo fields.Blast Radius: daemon-startup
@achingbrain/nat-port-mapperbecome direct dependencies. The libp2p ones are already installed through pkc-js at the same versions.Summary by CodeRabbit