Phase 11: hubs on the LAN, desktop Connect mode, Machines page #19

Merged
alex merged 41 commits from refactor/phase-11-discovery into refactor/phase-10-nodes 2026-10-29 18:45:55 +01:00
Owner

Phase 11: hubs on the LAN, desktop Connect mode, Machines page

Other computers can now find a Kanbot hub on the LAN, pair over HTTPS with the hub's certificate pinned, and either run agents for it (kanbot-node) or connect the desktop app to it.

What's new

  • Hub mode is a setting (hub.enabled, hub.port 7433), switched live from the Machines page or the setup.
    • It opens a second listener: HTTPS on every interface with the data dir's hub certificate.
    • Every request on it is another machine (device token required, even from loopback). The loopback HTTP listener for this computer's window is unchanged.
    • It announces _kanbot._tcp as kanbot-<machine id>.local, never the Mac's own name.
  • Discovery:
    • Discovery.hubs browses only on demand, for 60 s after it is asked.
    • Entries are grouped by fingerprint, conflicting claims are shown, and announced fingerprints are labelled unverified.
    • kanbot-node discover.
  • Pinning:
    • kanbot-node pair reads the certificate first (--fingerprint, or a confirmation on a TTY) and stores the pin; every wss dial verifies it.
    • The desktop app pins the window and its own calls.
  • Desktop pairing:
    • A code offered for a desktop pairs a browser device and a node device as companions (migration 0022_device_companions).
    • Revoking one revokes both.
  • Desktop Connect mode (electron):
    • The connect screen lists hubs found on the LAN or takes a typed address, shows the TLS fingerprint, and pairs.
    • The app then restarts with no local server. It runs the bundled node as a utility process and loads the hub.
    • It has an offline page and tray entries ("Connected to…", Disconnect).
    • Packaging ships the node and its dependencies.
  • Host actions follow the person:
    • From this machine they run here.
    • From a desktop's window they go to that desktop's node over NodeHost, and only presets are sent.
    • Anyone else is refused (error.opensOnYourComputer).
    • A project's folder opens only from the machine it is on.
  • Machines page (/machines): computers (online, last seen, running agents, capacity), sharing as a hub (addresses, fingerprint, status), hubs on the network, Connect.
  • Projects:
    • The project form has a machine picker.
    • Projects.update can move a project when no session is open (checked again inside the transaction).
  • Setup step "This computer and others" (since: 2): existing installs see only it.
  • Local only: the hub and launchers settings can be changed only from this computer.

Security

  • An adversarial review found no auth bypass. It found 10 smaller issues, all fixed with tests:
    • discovery spoofing;
    • handshake and connection limits on the listener (10 s handshake, 256 connections, 32 per address);
    • browsing on demand;
    • the shell's list capped;
    • launchers to nodes limited to presets;
    • hub and launchers settings Local-only;
    • the project move race;
    • one environment variable dropped for the node;
    • openExternal limited to http(s)/mailto;
    • IPv6 pin matching.
  • By design (CLAUDE.md):
    • A hub can run code on the desktops connected to it: connect only to hubs you control.
    • Pairing is trust on first use: compare the fingerprint with the hub's own Settings › Devices.
    • All paired devices are equally trusted.

Bug found by the exit check

bonjour-service announced under os.hostname(), and macOS renamed the computer on every announcement. It now announces under kanbot-<id>.local, and a test checks the name heard over real mDNS. This Mac's LocalHostName was bumped to Alexs-Mac-mini-1061 while testing; restore it with sudo scutil --set LocalHostName Alexs-Mac-mini.

Checks

  • npm run lint, npm run typecheck, npx vitest run: 1387 passed, 1 skipped, twice.
  • End to end (test/hub/desktopEndToEnd.test.ts), with the shell's own modules:
    • discovery and fingerprint agree;
    • desktop pairing is refused under a wrong pin and works under the right one;
    • the node runs a session on its project;
    • "open in editor" reaches the desktop's node, while a browser paired alone is refused;
    • an impostor-pinned node never links;
    • Disconnect revokes both devices.
  • Production builds:
    • hub mode over HTTPS;
    • real mDNS discover;
    • a node pairs with its fingerprint and connects;
    • a desktop code redeemed (Secure cookie plus node token);
    • over https, no token gets 401, the cookie gets 200, and the node token gets 403.
  • Packaged macOS app:
    • The bundled node finds the hub, pairs and connects over pinned wss.
    • desktop:verify passes, and the Windows x64 cross-build succeeds.

For you to try by hand (no GUI was launched)

  • The connect window, from the tray and from the Machines page.
  • Pairing a second Mac with a desktop code, and the hub's board loading with the pinned certificate.
  • A different certificate being refused.
  • The offline page and Disconnect.
  • Tray and notifications against the hub.
  • The Windows build on real Windows.

Base: refactor/phase-10-nodes (PR #18).

🤖 Generated with Claude Code

## Phase 11: hubs on the LAN, desktop Connect mode, Machines page Other computers can now find a Kanbot hub on the LAN, pair over HTTPS with the hub's certificate pinned, and either run agents for it (`kanbot-node`) or connect the desktop app to it. ### What's new - **Hub mode** is a setting (`hub.enabled`, `hub.port` 7433), switched live from the Machines page or the setup. - It opens a second listener: HTTPS on every interface with the data dir's hub certificate. - Every request on it is another machine (device token required, even from loopback). The loopback HTTP listener for this computer's window is unchanged. - It announces `_kanbot._tcp` as `kanbot-<machine id>.local`, never the Mac's own name. - **Discovery:** - `Discovery.hubs` browses only on demand, for 60 s after it is asked. - Entries are grouped by fingerprint, conflicting claims are shown, and announced fingerprints are labelled unverified. - `kanbot-node discover`. - **Pinning:** - `kanbot-node pair` reads the certificate first (`--fingerprint`, or a confirmation on a TTY) and stores the pin; every wss dial verifies it. - The desktop app pins the window and its own calls. - **Desktop pairing:** - A code offered for a desktop pairs a browser device and a node device as companions (migration `0022_device_companions`). - Revoking one revokes both. - **Desktop Connect mode** (electron): - The connect screen lists hubs found on the LAN or takes a typed address, shows the TLS fingerprint, and pairs. - The app then restarts with no local server. It runs the bundled node as a utility process and loads the hub. - It has an offline page and tray entries ("Connected to…", Disconnect). - Packaging ships the node and its dependencies. - **Host actions follow the person:** - From this machine they run here. - From a desktop's window they go to that desktop's node over `NodeHost`, and only presets are sent. - Anyone else is refused (`error.opensOnYourComputer`). - A project's folder opens only from the machine it is on. - **Machines page** (`/machines`): computers (online, last seen, running agents, capacity), sharing as a hub (addresses, fingerprint, status), hubs on the network, Connect. - **Projects:** - The project form has a machine picker. - `Projects.update` can move a project when no session is open (checked again inside the transaction). - **Setup step "This computer and others"** (`since: 2`): existing installs see only it. - **Local only:** the `hub` and `launchers` settings can be changed only from this computer. ### Security - An adversarial review found no auth bypass. It found 10 smaller issues, all fixed with tests: - discovery spoofing; - handshake and connection limits on the listener (10 s handshake, 256 connections, 32 per address); - browsing on demand; - the shell's list capped; - launchers to nodes limited to presets; - hub and launchers settings Local-only; - the project move race; - one environment variable dropped for the node; - `openExternal` limited to http(s)/mailto; - IPv6 pin matching. - **By design** (CLAUDE.md): - A hub can run code on the desktops connected to it: connect only to hubs you control. - Pairing is trust on first use: compare the fingerprint with the hub's own Settings › Devices. - All paired devices are equally trusted. ### Bug found by the exit check bonjour-service announced under `os.hostname()`, and macOS renamed the computer on every announcement. It now announces under `kanbot-<id>.local`, and a test checks the name heard over real mDNS. **This Mac's LocalHostName was bumped to `Alexs-Mac-mini-1061` while testing; restore it with `sudo scutil --set LocalHostName Alexs-Mac-mini`.** ### Checks - `npm run lint`, `npm run typecheck`, `npx vitest run`: 1387 passed, 1 skipped, twice. - **End to end** (test/hub/desktopEndToEnd.test.ts), with the shell's own modules: - discovery and fingerprint agree; - desktop pairing is refused under a wrong pin and works under the right one; - the node runs a session on its project; - "open in editor" reaches the desktop's node, while a browser paired alone is refused; - an impostor-pinned node never links; - Disconnect revokes both devices. - **Production builds:** - hub mode over HTTPS; - real mDNS `discover`; - a node pairs with its fingerprint and connects; - a desktop code redeemed (Secure cookie plus node token); - over https, no token gets 401, the cookie gets 200, and the node token gets 403. - **Packaged macOS app:** - The bundled node finds the hub, pairs and connects over pinned wss. - `desktop:verify` passes, and the Windows x64 cross-build succeeds. ### For you to try by hand (no GUI was launched) - The connect window, from the tray and from the Machines page. - Pairing a second Mac with a desktop code, and the hub's board loading with the pinned certificate. - A different certificate being refused. - The offline page and Disconnect. - Tray and notifications against the hub. - The Windows build on real Windows. Base: `refactor/phase-10-nodes` (PR #18). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
alex added 36 commits 2026-10-16 20:44:12 +02:00
Phase 11 contract: hub settings, Discovery, Shell, NodeHost
Some checks failed
CI / check (push) Has been cancelled
fcd67bfc76
The hub section (off, port 7433) switches on the LAN listener and the
announcement; Discovery lists the hubs mDNS sees; Shell asks the desktop
shell to switch to Connect; NodeHost is a node's HostOS, for the desktop a
person sits at. The groups are wired by the tracks that implement them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add bonjour-service 1.4.4 for LAN discovery
Some checks failed
CI / check (push) Has been cancelled
3d534b496b
Pure JS (multicast-dns); announces and finds hubs as _kanbot._tcp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hub mode's HTTPS listener, opened and moved live from the hub settings
Some checks failed
CI / check (push) Has been cancelled
0f5c61efdd
A second listener on hub.port with the data dir's certificate serves the
same app (pages, RPC, upgrades through crossws behind the same guard);
every socket it accepts is another machine's to the auth check. Hub.status
and Discovery.hubs over RPC; the LAN behind a Lan port (bonjour-service).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A code offered for a desktop (Devices.offerPairing({ kind: 'desktop' }))
pairs two devices in one transaction: the window's browser device (its
token is the kanbot_device cookie, Secure over HTTPS) and the node it
runs (its token in the answer: { deviceId, nodeDeviceId, nodeToken }).
devices.companion_id (migration 0022) points each at the other.
Revoking either revokes both and cuts both off. A code for another kind
fails like a wrong one. Settings › Devices offers the three kinds and
shows a desktop as one entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Host actions follow the person: NodeHost on the node, routing on the hub
Some checks failed
CI / check (push) Has been cancelled
a69d68ee22
A node serves NodeHost over its link, from a HostOS that reaches no
database (the local machine's, with English text); HostOSNone is gone.
On the hub, Host.* and openProject/openTask ask hostOfCaller: this
machine opens on its own desktop, a window paired together with a
linked node opens on that node's, anyone else is refused
(error.opensOnYourComputer). A project opens only from the machine it
is on. The test RPC client carries a caller (loopback by default).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shell.openConnect asks the desktop shell for its connect screen
Some checks failed
CI / check (push) Has been cancelled
8e37b69758
Over the utility process's parent port ({ type: 'kanbot-shell',
action: 'open-connect' }), only from the desktop app's own window (a
Local caller) and only when the shell started the server to talk over
that port; anything else is ShellUnavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Desktop shell: connect to a hub
Some checks failed
CI / check (push) Has been cancelled
be25b37386
shell.json says whether this computer runs its own board or a hub's. The
connect screen (a local page) lists hubs mDNS finds, shows the certificate
fingerprint an address presents, and pairs as a desktop over a connection
pinned to it: the window's device cookie goes into the session, the node's
pairing into userData/node/node.json, and the app restarts in connect mode,
with no local server, the hub's board in a window whose session accepts only
the pinned certificate for the hub's host, and the bundled node forked as a
utility process. An offline page offers Retry and Use this computer only;
the tray says which hub this is and can disconnect (best-effort revoke).
desktop-prepare ships the node's external packages and a bundled
bonjour-service; desktop-verify checks they are in the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project picks its machine, and moves to another when idle
Some checks failed
CI / check (push) Has been cancelled
96d1cbbf59
The project form has a machine picker (reachable machines, this one by
default) for a new project, and Projects.update takes machineId: the
project moves only to a machine that is reachable (MachineUnavailable),
has its folder (NotADirectory, checked there), and while none of its
sessions is open (ProjectBusy, error.projectBusy). Its tasks forget their
resume ids (those were the old machine's). ProjectChanged with machineId
tells the screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md: Desktop modes
Some checks failed
CI / check (push) Has been cancelled
4eb5af018a
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nodes pin the hub's certificate; kanbot-node discover
Some checks failed
CI / check (push) Has been cancelled
f1401b65d9
kanbot-node pair reads the https hub's certificate first: --fingerprint must
match it, or a person confirms it at a terminal; the code goes over a
connection pinned to it and node.json keeps the pin. Every wss dial is
pinned (undici's WebSocket over an Agent whose connector checks
fingerprint256), KANBOT_HUB_FINGERPRINT overrides. Tests over real HTTPS:
remote treatment on the hub listener (dev too), Secure cookies, pairing,
the node over pinned wss end to end, discovery over a fake and real mDNS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One hub certificate for callers at the same moment; CLAUDE.md on hub mode
Some checks failed
CI / check (push) Has been cancelled
d4c72deed5
The identity and hub mode's listener both ensure the certificate as the
runtime starts: on a first boot each made a pair, and one went on with a
fingerprint the files no longer had. CLAUDE.md: hub mode, remote treatment
on its listener, LAN discovery, node pinning; the real-mDNS test is skipped
under CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Machines page: the computers, sharing this one as a hub, the hubs around
Some checks failed
CI / check (push) Has been cancelled
89532433fd
/machines lists the machines (role, reachable, last seen, agents
running from Machines.activity, each one's limit), switches the hub
sharing (hub.enabled, port) with where the listener stands, lists the
hubs on the network, and offers Connect to a hub through the desktop
shell (off, and saying why, outside the desktop app's own window).
Linked from Settings › General and the app menu.

Hub.status and Discovery.hubs are track A's: useHubStatus and
useDiscoveredHubs are stand-ins (settings only, no hubs) to swap at
the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setup step Machines: this computer alone, shared as a hub, or connected
Some checks failed
CI / check (push) Has been cancelled
da5c0abcd9
A core step (since 2, right after welcome), so a fresh install sees it
in order and an install set up before (version 1) sees it once with the
end. Three choices: this computer only (default; sharing off), share as
a hub (sharing on, with its addresses and fingerprint), connect to a hub
(the desktop shell's connect screen; off in a browser, saying why). The
summary at the end says which. The story fixture for a step no screen
knows is now 'backups'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ESLint leaves .worktrees alone
Some checks failed
CI / check (push) Has been cancelled
ad5b34b2e0
Agents' checkouts of the repo live there; linted from the main checkout,
their files missed the suppressions baseline and failed lint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Machines page and setup read the real Hub.status and Discovery.hubs
Some checks failed
CI / check (push) Has been cancelled
d1793d9e2c
The stand-ins go: useHubStatus reads Hub.status (refreshed on
settings.changed and every 3 s on screen, since the listener follows the
setting by itself) and shows its hubMode.error key; useDiscoveredHubs
polls Discovery.hubs. A test pins the desktop shell's Disconnect
(Devices.revoke as the window's Bearer, no Origin) revoking both devices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test/hub/desktopEndToEnd.test.ts runs it over real sockets with the shell's own
modules (discovery, hubPin, pairing, hubLink) and the node process's layer: a
desktop code over pinned HTTPS, the node over pinned wss, a project on the
node's machine in a real git repo, a scripted session answered from the window,
Host.openProject reaching the node's (recording) desktop and refused for a
browser paired alone, an impostor pin never linking, and Disconnect revoking
both devices. nodeLayer takes a host desktop for tests; startNode keeps its logs,
so nodeOverHub waits on the refusal instead of sleeping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hub mode no longer renames the Mac: announce under the hub's own host name
Some checks failed
CI / check (push) Has been cancelled
343d497b46
bonjour-service publishes the SRV target with A/AAAA records for
os.hostname() by default, a name macOS's mDNSResponder owns; answering for it
from a second responder is a conflict macOS settles by renaming the computer
(Alexs-Mac-mini-691 -> -746 on turning hub mode on, and again on every run of
the real-mDNS test). The announcement now points at kanbot-<machine id>.local,
which only this hub answers for; hubs are reached by address, so nothing
else changes. The real-mDNS test checks the heard host name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md: the hub announces under its own host name
Some checks failed
CI / check (push) Has been cancelled
1e306a1608
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
desktopEndToEnd: type the shell's pairing outcome
Some checks failed
CI / check (push) Has been cancelled
f9bb98b9e5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Discovery keeps a hub's announcements apart from one posing as it
Some checks failed
CI / check (push) Has been cancelled
f8c5b0f3b9
Announcements are kept per service identity (fqdn, port, addresses), not
per the machine id they claim; one id announced with different
fingerprints is listed once per fingerprint, each marked conflict, on the
Machines card, the shell's connect list and kanbot-node discover. The
announced fingerprint is labelled unverified, and discover no longer says
to pair with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hub listener bounds handshakes and connections, and closes at once
Some checks failed
CI / check (push) Has been cancelled
6dfc18c6a7
A 10 s TLS handshake timeout, header and request timeouts, at most 256
connections and 32 per address; every TCP connection is tracked from
'connection', so closing the listener ends those still handshaking instead
of waiting up to 120 s for them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Browse the LAN for hubs only while someone looks for them
Some checks failed
CI / check (push) Has been cancelled
96d983161c
Discovery.hubs starts browsing and it stops 60 s after the last ask, instead
of every server process browsing mDNS from boot; LanBonjour closes its
multicast socket once nothing announces or browses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge origin/refactor/phase-11-discovery into wip/p11-security
Some checks failed
CI / check (push) Has been cancelled
c75f195bb7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shell keeps at most 64 announced hubs and tells changes in batches
Some checks failed
CI / check (push) Has been cancelled
a7388e4336
electron/discovery.js drops the announcement heard longest ago past 64 (the
server's cap) and publishes a burst of changes as one list after 250 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Another desktop opens folders for the hub only with a preset
Some checks failed
CI / check (push) Has been cancelled
8d7da23a06
The hub's launcher settings are hub-wide and any paired device can change
them, so a custom command or picked app sent over NodeHost would run on
someone else's computer. remoteHost sends the node platform's default
preset instead, and NodeHost refuses a custom or app choice from the wire
(error.remoteLauncherRefused). This machine keeps its custom launchers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only this computer may change hub sharing and Open with
Some checks failed
CI / check (push) Has been cancelled
8165d9b93a
Settings.update refuses the hub and launchers sections to a paired device
(mayChangeSettings -> SettingOnlyHere, error.settingOnlyHere), so a device
cannot switch hub mode off, move its port, or choose the command folders
open with. The sharing card and Settings > Open with show locked, with the
reason, when the page is not on this computer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project move checks again for open sessions inside its transaction
Some checks failed
CI / check (push) Has been cancelled
c18e588bd2
The check before the target's networked folder call could go stale: the
scheduler's sweep may start a session meanwhile. The transaction that
moves the project now re-reads its open sessions (SQL only) and refuses
ProjectBusy, writing nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The desktop's node never inherits KANBOT_HUB_FINGERPRINT
Some checks failed
CI / check (push) Has been cancelled
2521ef0814
It overrides node.json's pin, so nodeEnvironment drops it with the hub URL
and token variables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window hands only web and mail links to the system
Some checks failed
CI / check (push) Has been cancelled
51aa30e897
setWindowOpenHandler and will-navigate passed any scheme to
shell.openExternal; in connect mode the page is the hub's. They now go
through mayOpenExternally (electron/navigation.js): http, https, mailto,
and VS Code's file links in local mode only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window's pin matches an IPv6 hub's host name
Some checks failed
CI / check (push) Has been cancelled
12f04da7ef
Chromium hands setCertificateVerifyProc the bare address (fd00::1) while
new URL(pin.url).hostname keeps the brackets, so an IPv6 hub's pinned
certificate was left to Chromium and refused. Both sides are compared
without brackets, case-insensitively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Connect to a hub" looks disabled when it is
Some checks failed
CI / check (push) Has been cancelled
67604691c2
Outside the desktop app the Machines card's button was disabled but kept
the primary fill (Nuxt UI only dims it to 75%); it now turns neutral and
subtle. In the setup step a disabled choice's card is dimmed too, and
from a paired device the share / alone choices are disabled (the server
refuses the hub section to a device since the previous commits).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Connect mode leaves out "Open in browser"; CLAUDE.md says who trusts whom
Some checks failed
CI / check (push) Has been cancelled
CI / check (pull_request) Has been cancelled
60d4d12b10
In connect mode the system browser would reach a self-signed hub with no
pin, so the tray and the Help menu offer it in local mode only. CLAUDE.md
records the trust model: a connected hub runs code on the desktop, pairing
is trust on first use (the fingerprint on the hub itself is the only check
against a relay), all devices are equally trusted, a moved project sends
its task text to the node, and a LAN neighbour can burn the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KANBOT_HUB_ENABLED / KANBOT_HUB_PORT force the hub settings section (the
environment wins, Settings.update refuses it with SettingFromEnvironment,
Hub.status says setByEnvironment and the card shows it locked).
KANBOT_LOCAL_AGENTS=0 gives this machine a capacity of 0 (Machine.agentsOff).
kanbot-hub offer/status/health/backup talks to the server over loopback,
bundled on its own to .output/hub-cli (npm run build:hub-cli), sharing
desktop-prepare's externals vendoring (scripts/bundleExternals.mjs). The
log says how to get a first code while hub mode listens with no browser paired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Headless hub image: Dockerfile, compose example, docs
Some checks failed
CI / check (push) Has been cancelled
3c4391d718
Multi-stage build on node:24.19.0-bookworm (pinned by digest), runtime on
bookworm-slim with git, ca-certificates and tini, as uid 1000, data in the
/data volume, hub mode forced on 7433 (the one EXPOSE), the plain listener
on the container's loopback, no local agents, kanbot-hub on PATH and as the
HEALTHCHECK. docs/headless-hub.md covers building, pairing browsers, nodes
and desktops, backups (kanbot-hub backup), upgrades, limits and security.
test/hub/dockerImage.test.ts keeps the files consistent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md: Headless hub
Some checks failed
CI / check (push) Has been cancelled
75a3072b3f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KANBOT_HUB_PUBLIC_URL: where a containerised hub is reached
Some checks failed
CI / check (push) Has been cancelled
CI / check (pull_request) Has been cancelled
2387d3e94f
Inside a container the hub only sees its Docker address (172.17.0.x), which
it gave out in Hub.status, the pairing link, the first-pairing hint and
kanbot-hub offer. The variable (https origins, comma-separated) is given out
instead; the compose example reads it from .env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge pull request 'Phase 12: a headless hub in Docker' (#20) from refactor/phase-12-headless into refactor/phase-11-discovery
Some checks failed
CI / check (push) Failing after 2m18s
CI / check (pull_request) Failing after 2m18s
5079b2df8a
Reviewed-on: https://git.selene.acl.sh/alex/Overlight/pulls/20
alex merged commit df6b9a4b93 into refactor/phase-10-nodes 2026-10-29 18:45:55 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Overlight/App!19
No description provided.