6.7 KiB
AI Hub fleet security
Code: makepad_network::fleet (platform/network/src/fleet/): auth.rs
(credentials), front.rs (the TLS front, shared by every fleet service),
lan.rs (the private-LAN bind rule); the hub's route policy is
libs/ai/hub/src/fleet_routes.rs; makepad_network::tls (TLS on each OS's
own stack, known hosts). The asset store's sync listener (store sync,
apps/commercial/engine/asset/store/src/host/sync.rs) uses the same front
and credentials: roles node and lan, read-only sync routes.
The model
-
Nodes are trusted the ssh way. Each node makes its own self-signed certificate on first start (
<cache>/fleet/tls). A client records the certificate fingerprint perhost:porton first contact, in~/.makepad/ai-hub/known_nodes. If it later changes, the client:- prints a loud warning (host, old and new fingerprint);
- appends it to
known_nodes.log; - keeps it in
makepad_network::tls::fingerprint_changes(), for status displays; - updates the record and continues.
Nothing about nodes is provisioned.
-
Clients are what is checked.
- A fleet key (32 random bytes) is on the admin Mac and on every node
(
<cache>/fleet/fleet.key, 0600). - A client credential is
mkc2.<id>.<role>.<expiry>.<secret>, where the secret is derived from the fleet key. - The credential is never sent. Each request carries
Authorization: MKC2 <id>.<role>.<expiry>.<proof>, where the proof is HMAC(secret,"mkfleet2 proof|" + certificate sha256) for the certificate the node actually presented. - Something in the middle with its own certificate only gets a proof for that certificate, which is useless against a real node.
- A fleet key (32 random bytes) is on the admin Mac and on every node
(
-
Roles and ownership.
lan(people's apps) reaches the whole job API.nodereaches model blobs,/v1/model_inventoryand/healthonly.devicereaches its own job routes only.- Jobs belong to the credential that submitted them. Only that client can
cancel them, keep their leases alive or say
/byefor them.
-
No open nodes. A node bound to a network address serves only through its TLS front, and refuses to start without
<cache>/fleet/fleet.key. A loopback-only node (the machine node, tests) is machine-local and needs none of this. -
Front limits.
- 10 s for TLS.
- A request head of at most 16 KiB, within 15 s.
- 5 failed authentications from one address within 10 min ban it for 15 min.
- 32 connections per address, 256 in total (8 and 64 on the edge).
-
The node's HTTP server. It listens on loopback only and trusts identity headers only when they carry the front's per-process secret.
-
Discovery. Beacons (
makepad-ai-hub-tls) only say where a node's TLS port is. A process with a credential reaches every node it hears; one without a credential ignores them (logged). -
Peers. A node fetches model blobs from fleet endpoints only:
- request
peer_sourcesmust be discovered fleet endpoints; MAKEPAD_AI_PEER_SOURCEShosts are mapped onto the discovered nodes.
- request
Admin tasks (ai-fleet, admin Mac only)
cargo build --release -p makepad-ai-hub --bin ai-fleet
./target/release/ai-fleet init # ~/.makepad/ai-hub/admin/fleet.key
./target/release/ai-fleet issue rik-mac lan --out ~/.makepad/ai-hub/client.credential
./target/release/ai-fleet node-files w203 --out /tmp/w203 # fleet.key + node.credential
./target/release/ai-fleet revoke <client id>
Enrol a node. Copy fleet.key and node.credential (from
node-files) into <cache>/fleet/ and peer-secret into <cache>/,
readable only by the node's account, and restart the node. peer-secret
signs model-blob tickets: without it the node serves no blobs to its peers.
Keep every node's models complete. models = all in the WM's
ai-hub.conf (or MAKEPAD_AI_PREFETCH=all for a bare hub) fetches every
registry model the node can run, from the peers that hold each file, then
Hugging Face; models_licences (default none,community) names the licence
classes it may fetch (non-commercial, restricted, gated are opt-in),
models_mbps paces it (default 60). Each pass writes <cache>/prefetch.status.
Enrol a person or app. Run ai-fleet issue <id> lan --out <file>. On
the client, the file becomes ~/.makepad/ai-hub/client.credential (mode
600), or MAKEPAD_AI_HUB_CREDENTIAL holds its text.
Revoke. ai-fleet revoke <id> adds the id to
~/.makepad/ai-hub/admin/revoked. Copy that file to every node's
<cache>/fleet/revoked and restart the nodes. Credentials expire after
--days (default 365); to rotate one, issue a new one and revoke the old
id. To rotate the fleet key itself, run ai-fleet init on a fresh admin
directory, then re-issue everything.
Scripts without the Rust client (for example Python):
- Open TLS without CA checking.
- Take sha256 of the server's DER certificate. Compare it with your own record if you keep one; warn on a change and continue.
- Send
Authorization: MKC2 <id>.<role>.<expiry>.<hex HMAC-SHA256(bytes.fromhex(secret), b"mkfleet2 proof|" + fingerprint_hex)>.
Going on the internet later (not done; for the user)
The hub on 165 can serve an edge: a second TLS front with device routes
only. Set MAKEPAD_AI_HUB_EDGE=0.0.0.0:8443 in its environment.
Router (OpenWrt at 10.0.0.1). Forward WAN tcp/8443 to the hub only:
uci add firewall redirect
uci set firewall.@redirect[-1].name='aihub-edge'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].src_dport='8443'
uci set firewall.@redirect[-1].dest='lan'
uci set firewall.@redirect[-1].dest_ip='10.0.0.165'
uci set firewall.@redirect[-1].dest_port='8443'
uci set firewall.@redirect[-1].proto='tcp'
uci commit firewall && /etc/init.d/firewall reload
Never forward the nodes' ports or :8384 (tunnels).
Address. For a dynamic home IP, set up DDNS on the router
(opkg install ddns-scripts plus a provider package; LuCI → Services →
Dynamic DNS) for a hostname of your choice, or use a static IP.
Devices.
- On the LAN, issue a credential per device:
ai-fleet issue <phone-name> device --out …. - Enrolment should carry it with the hub's address and certificate
fingerprint, e.g. a QR code with
makepad-hub://<host>:8443?fp=<sha256>&cred=<credential>. - On the edge, devices must pin the hub certificate strictly: refuse a change, unlike the LAN's warn-and-continue rule, because a device on the internet has no other way to tell the hub apart. This is still to be decided and built, with the enrolment UI.
Checks before opening the port. From outside the LAN (a phone hotspot):
- the forwarded port answers TLS only;
- a request without a credential gets 401, and repeated failures ban that address;
- only device routes work.