Member node onboarding (phase 1)
Goal: add a member node to an AdaOS hub using a short one-time join-code (no long-lived tokens in CLI args), then start python -m adaos api serve via OS autostart/service when possible.
For the target-state architecture beyond this phase-1 flow, including supervisor-owned member-hub lifecycle, restart-aware connectivity semantics, QR-based onboarding, and unified device access policy for members and browsers, see Member-Hub Connectivity, Device Access and Browsers, and Device Access Roadmap.
Prereqs
- Python 3.11
adaosinstalled (or run via repo bootstraps)- Hub node is running
python -m adaos api serve(or bootstrap-installed autostart) and has rolehub
1) On the hub: create a join-code (Root session)
Run on the hub machine:
This prints a short code like ABCD-EFGH (one-time, TTL). The code is stored on Root and can be consumed by the member via Root.
Notes:
- Root mode does not require a public hub URL: members connect via the Root proxy path
/hubs/<subnet_id>/.... - Hub must be bootstrapped on Root (mTLS hub cert/key + Root CA). If missing, run:
python -m adaos dev root init(requiresROOT_TOKEN).
1b) Offline/LAN-only join-code (no Root)
If the subnet has no Internet access / no Root connectivity, create a local join-code on the hub:
2) On the member: bootstrap + join + autostart
RootUrl/--root-url is the join entrypoint:
- Online mode: Root (default:
https://api.inimatic.com) - Offline mode: Hub URL (the hub node accepts join directly)
Zone-aware bootstrap is supported too:
- Repo bootstrap scripts accept
--zone <id>on Bash or-ZoneId <id>on PowerShell. - Use only a two-letter country or region code such as
ru. - With the default public Root URL, national zones follow the
[zone].api.inimatic.comrule. - Right now this is active for
ru, which resolves tohttps://ru.api.inimatic.com; the other zones still usehttps://api.inimatic.com. - This zone selection affects hub bootstrap, owner login, member join via join-code, and hub join-code creation.
For offline/LAN-only setups you can still override the hub address explicitly via adaos node join --hub-url http://<HUB_LAN_IP>:8777 (advanced).
Windows (PowerShell, pip bootstrap)
powershell -ExecutionPolicy Bypass -File tools/bootstrap.ps1 -JoinCode <CODE>
# example for RU Root:
# powershell -ExecutionPolicy Bypass -File tools/bootstrap.ps1 -JoinCode <CODE> -ZoneId ru -Dev
Linux/macOS (pip bootstrap)
bash tools/bootstrap.sh --join-code <CODE>
# example for RU Root:
# bash tools/bootstrap.sh --join-code <CODE> --zone ru --dev
Windows/Linux/macOS (uv bootstrap)
powershell -ExecutionPolicy Bypass -File tools/bootstrap_uv.ps1 -JoinCode <CODE>
# example for RU Root:
# powershell -ExecutionPolicy Bypass -File tools/bootstrap_uv.ps1 -JoinCode <CODE> -ZoneId ru -Dev
bash tools/bootstrap_uv.sh --join-code <CODE>
# example for RU Root:
# bash tools/bootstrap_uv.sh --join-code <CODE> --zone ru --dev
Offline/LAN member bootstrap (using --local join-code)
When using a local hub join-code, you must point join to the hub URL explicitly:
powershell -ExecutionPolicy Bypass -File tools/bootstrap.ps1 -JoinCode <CODE> -RootUrl http://<HUB_HOST>:8777
Note: joining on the same machine as the hub
If the hub is already running on 127.0.0.1:8777, the member node must use a different local API port.
Bootstraps now auto-pick 8778+ when --join-code/-JoinCode is provided, but you can override explicitly:
powershell -ExecutionPolicy Bypass -File tools/bootstrap.ps1 -JoinCode <CODE> -RootUrl http://127.0.0.1:8777 -ServePort 8778 -ControlPort 8778
3) Verify status
On the member machine:
Expected (example):
roleismemberreadyistrueroute_modeiswswhen connected to hub via/ws/subnet
Current phase-1 note:
adaos node joinpersists the membership contract immediately- live
member -> hubactivation may still depend on the running runtime picking up the new configuration - the target design is to make join self-activating in dev and supervisor-managed in production; see the roadmap in Member-Hub Connectivity
Where config is stored
- Bootstrap identity:
$ADAOS_BASE_DIR/node.yaml(default in bootstraps:<repo>/.adaos/node.yaml). This file is intentionally minimal and keeps only bootstrap/static identity such aszone_id,node_id,subnet_id,role,root.base_url, and key/cert paths. - Runtime connection state:
$ADAOS_BASE_DIR/state/node_runtime.json. Dynamic fields such as the currenthub_url, local control token, and runtime NATS credentials are stored here, not innode.yaml. - Durable structured state: local SQLite durable state.
Long-lived structured data such as root auth cache, subnet alias metadata, and capacity projections are persisted there instead of
node.yaml. - Join-code store: Root server (Root mode) or
$ADAOS_BASE_DIR/join_codes.json(hub local mode)
Idempotency
- Re-running bootstrap without
--join-codeshould keep existingnode_idand config. - Join-codes are one-time; reusing the same code fails.
Default role behavior
- If
--join-code/-JoinCodeis provided, bootstrap defaults role tomember. - If no join-code is provided, bootstrap defaults role to
hub.
Windows note: prefer python -m adaos (or wrapper script)
If adaos console-script wrapper is broken (e.g. ModuleNotFoundError: No module named 'adaos'), run via:
For the hub API on Windows, prefer: