Minor Changes
-
jalth servenow handles a mesh with no custom domain gracefully instead of dead-ending (or attempting an admin-only inline domain-set) mid-publish. It checks for a domain up front and branches by role: (#364)- SSO-admin device → an inline zone picker: it lists the account’s active Cloudflare zones (new admin-only
mesh.listZonesendpoint), you pick one, it sets the mesh domain and continues the managed publish. A “Skip — use a quick tunnel instead” option is offered alongside the zones. - Headless / non-admin device → an “ask an admin to run
jalth domain set <domain>” message plus the option to expose the service through an ephemeral*.trycloudflare.comquick tunnel. - Non-interactive (
--output json/ no TTY) without--quick→ a structuredconfigerror pointing atjalth domain set. It never prompts.
A new
--quickflag forces the quick-tunnel path explicitly (even when a domain exists). Quick tunnels are public, HTTP/HTTPS-only, carry no Access control (--allowis ignored with a warning), and their URL changes each run.Adds
mesh.listZones(admin-only) to the registry router /AppRouter. Removes the old free-text inline domain prompt from the publish saga (it only ever worked for an SSO admin, and a headless device hit a role error mid-publish). Input validation is hoisted to fail fast before any network I/O. - SSO-admin device → an inline zone picker: it lists the account’s active Cloudflare zones (new admin-only
Patch Changes
-
jalth auth-key createnow prints the “Use on a headless device” join command with the real token inlined —jalth join <url> --auth-key <actual-token>— instead of a<token>placeholder, so it can be copied and run directly. The token is the same one already shown above and is still only displayed once. (#364) -
Fix
jalth destroyandjalth leavefailing during teardown — three issues surfaced by real-device QA: (#364)- Timing out (“The operation timed out”). The registry fetch applied a blanket 10s budget to every tRPC call, including the heavy multi-step setup/teardown mutations (
init.configure,admin.destroyFinalize,admin.upgrade,devices.leave,devices.remove), which routinely run longer and were aborted mid-flight. These now get a 120s ceiling while ordinary queries keep the 10s fail-fast. A timed-out mutation (POST) also no longer silently retries against the workers.dev fallback, which could duplicate the work and collide with the first attempt’s own leave-lock (“A device leave is in progress — retry shortly.”). - Tunnel delete rejected with Cloudflare error 1022 (“This tunnel has active connections”). The local
cloudflaredconnector is now stopped before the Worker deletes this device’s tunnel (previously it was uninstalled after the Worker call, or — fordestroy— not at all). The Worker additionally cleans up the tunnel’s lingering connections and retries the delete with backoff, absorbing the few seconds Cloudflare takes to register the dropped connector. - Unhelpful errors. Teardown failures surfaced as
Unhandled exception: <message>(thebetter-resultwrapper) instead of the real cause; the underlying error is now unwrapped and classified for a correct message and exit code.jalth destroyfrom a non-admin device fails fast with a clear permissions error instead of a confusing Worker round-trip.
- Timing out (“The operation timed out”). The registry fetch applied a blanket 10s budget to every tRPC call, including the heavy multi-step setup/teardown mutations (
-
Fix the
jalth init/jalth join“Device name” prompt rejecting an empty submit instead of accepting the shown default. The default (your sanitized hostname) is now pre-filled as editable text, so pressing Enter uses it; clearing the field still requires a valid name. Non-interactive runs (--output json, non-TTY,--yes) fall back to the default and surface a clear error if the hostname can’t yield a valid name. (#364) -
jalth doctor --fixno longer prompts for — and attempts — the same fix more than once when several checks share it. Previously the Cloudflare WARP install was offered twice (once for the missingwarp-cli, once for the missing daemon). Each distinct fix now runs at most once per--fixrun, and its outcome is reused for the remaining checks that depend on it. (#364) -
Fix
jalth servesilently publishing to an unserved hostname (Cloudflare error 1033). Two issues: (#364)- cloudflared self-heal:
ensureCloudflaredtreated any installed cloudflared system service as ready, even a bare/foreign one with no tunnel token (or a stale token from a previous tunnel) — so the daemon ran no tunnel and the published hostname returned 1033. It now checks the installed service actually runs the current tunnel token (the token is embedded in the launchd plist / systemd unit) and reinstalls it when it does not, then re-verifies the token landed after install. - Readiness probe: after a successful publish,
servenow polls the public URL and reports whether the edge is actually serving it — distinguishing a not-yet-connected tunnel (1033 / HTTP 530), a down origin (502/503/504 → “is your local service running?”), and a live service. It warns rather than failing (the publish already succeeded) and includes areadinessfield in--output json. TCP services are skipped (not HTTP-probeable).
- cloudflared self-heal:
-
jalth initnow stops with anAlready initialized as "<device>" (mesh "<team>")error when this device is already set up, instead of silently re-prompting for the Cloudflare API token and re-running every step. Runjalth statusto inspect the existing mesh, orjalth init --freshto re-initialize (which reuses the existing Cloudflare resources). A run that failed partway through still resumes from its saved checkpoint as before. (#367) -
jalth joinnow offers to install Cloudflare WARP whenwarp-clior the WARP daemon is missing, matchingjalth init. Previously join failed the prerequisite check outright and pointed you atjalth doctor --fix. In an interactive terminal it prompts before installing; in--output jsonor non-interactive runs it installs without prompting (or surfaces a structuredpermissionserror when sudo can’t be obtained). (#364) -
Fix
jalth leaveandjalth destroynot fully de-enrolling Cloudflare WARP from this device: they removed the WARP MDM profile and restarted the daemon but never deleted the local WARP registration. Because current WARP versions persist the registration independently of the managed config, the device stayed logged in to the (now-removed) Zero Trust org — reinstalling or re-running WARP came back already connected to a dead mesh, andjalth leave’s confirmation prompt claimed it removed the registration when it did not. Teardown now deletes the WARP registration before removing the MDM profile, and is idempotent so a retriedleave/destroyno longer errors. (#367) -
jalth serve,jalth up, andjalth downnow offer to install their missing prerequisite instead of dead-ending — matchingjalth initandjalth join.serveoffers to installcloudflaredwhen it’s not on PATH (previously it failed withFailed to check existing services: cloudflared not found on PATH);up/downoffer to install Cloudflare WARP whenwarp-cliis missing. In an interactive terminal you’re prompted before installing; in--output json/ non-interactive runs the install is attempted non-interactively (or a structuredpermissionserror is surfaced when sudo isn’t available).up/downalso now route this through the normal error path, so failures are JSON-formatted consistently. (#364) -
jalth sshnow explains the crypticPermission denied (none)failure instead of leaving you to guess. When Cloudflare Access declines to issue a short-lived SSH certificate, the command appends an actionable hint covering both causes: your identity isn’t on the target’s allow-list (runjalth ssh allow <email>), or the connecting device joined the mesh headlessly with an auth-key and therefore has no user identity — headless devices can be SSH targets but not SSH clients to certificate-protected devices, since Cloudflare Access for Infrastructure issues certs against user identity only. (#364) -
Fix
jalth ssh enable(and the SSH step ofjalth init/jalth join) failing on macOS 13+ withFailed to enable sshd service: ... launchctl bootstrap system /System/Library/LaunchDaemons/ssh.plist exited 5: Bootstrap failed: 5: Input/output error. On modern macOS the SSH daemon is already loaded at boot, solaunchctl bootstrapalways returns that benign “already loaded” error — jalth now verifies the daemon is actually registered and continues to start it instead of aborting setup. (#364) -
Fix
jalth init/jalth joinfailing to install Cloudflare WARP on Ubuntu 26.04 (“Resolute Raccoon”) and any release newer than the latest LTS, whereapt-get updateerrored withThe repository 'https://pkg.cloudflareclient.com resolute Release' does not have a Release file. The apt source now maps the host to the newest Cloudflare-published codename for its family (Ubuntu →noble, Debian →trixie) and prefers the upstreamUBUNTU_CODENAMEon Ubuntu derivatives (Linux Mint, Pop!_OS) instead of writing the raw host codename. (#364) -
Updated dependencies [
9f29d80]:- @jalth/contracts@0.8.0