Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "node-manager"
version = "6.0.0"
version = "6.1.0"
edition = "2021"

# No external dependencies — only std.
Expand Down
49 changes: 31 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The onboarding and management server that ships inside every Holo Node.

It is a single Rust binary with zero external dependencies — no Tokio, no Axum, no serde. It serves a browser UI over plain TCP on port 8080 and handles the full lifecycle of a node: first-time setup, SSH key management, AI agent configuration, hardware mode switching, and binary self-updates pulled from this repository's GitHub Releases.
It is a single Rust binary with zero external dependencies — no Tokio, no Axum, no serde. It serves a browser UI over plain TCP on port 8080 and handles the full lifecycle of a node: first-time setup, SSH key management, Unyt Agent ID linking for HoloFuel compensation, hardware mode switching, and binary self-updates pulled from this repository's GitHub Releases.

---

Expand All @@ -14,12 +14,10 @@ It is a single Rust binary with zero external dependencies — no Tokio, no Axum
4. [Repository structure](#repository-structure)
5. [Shipping a release](#shipping-a-release)
6. [Self-update mechanism](#self-update-mechanism)
7. [Switching OpenClaw forks](#switching-openclaw-forks)
8. [Routes reference](#routes-reference)
9. [File paths on the node](#file-paths-on-the-node)
10. [Security model](#security-model)
11. [Adding a new chat channel](#adding-a-new-chat-channel)
12. [Contributing](#contributing)
7. [Routes reference](#routes-reference)
8. [File paths on the node](#file-paths-on-the-node)
9. [Security model](#security-model)
10. [Contributing](#contributing)

---

Expand Down Expand Up @@ -62,17 +60,24 @@ On startup the binary generates a random 12-character password, writes its SHA-2

A three-step browser UI walks the operator through:

1. **Node identity & SSH** — node name (used as hostname slug) and optional SSH public key for the `holo` user
1. **Node identity & SSH** — node name (used as hostname slug), optional Unyt Agent ID for HoloFuel compensation, and optional SSH public key for the `holo` user
2. **Hardware mode** — initial container mode (EdgeNode or Wind Tunnel)
3. **Review & initialize** — summary before committing

When Wind Tunnel mode is selected, the WT client name is always `{node_name}-{suffix}`:
- **With Unyt Agent ID:** suffix is the full Agent ID
- **Without Agent ID:** suffix is a random 16-character hex string generated at setup (persisted in state as `wt_suffix`)

The combined name must fit within 63 characters. Each physical node should use a unique node name + Agent ID pair.

After the operator submits, the server configures everything, starts the appropriate container service, and redirects the browser to the management panel.

### Management panel (`/manage`)

After onboarding, `GET /` redirects to `/manage`. The panel (password-protected) lets the operator:

- Add and remove SSH public keys for the `holo` user without physical access
- Link or update a Unyt Agent ID for HoloFuel compensation
- Switch hardware mode between Standard EdgeNode and Wind Tunnel
- Change the node password
- Trigger an immediate software update check
Expand All @@ -85,6 +90,14 @@ A background thread wakes every hour, queries the GitHub Releases API for this r

## Building locally

### Branch workflow

| Branch | Purpose |
|--------|---------|
| `main` | Production-ready releases. Tag pushes trigger GitHub Actions release builds. |
| `develop` | Integration branch for in-progress features. Merge to `main` when ready to release. |
| `feature/*` | Short-lived branches off `develop`. |

### Prerequisites

- Rust stable (1.75 or newer)
Expand Down Expand Up @@ -131,7 +144,7 @@ To simulate an already-onboarded node (skip to /manage):

```bash
mkdir -p /etc/node-manager
echo "onboarded=true\nnode_name=test\nhw_mode=STANDARD\nagent_enabled=false" \
echo -e "onboarded=true\nnode_name=test\nhw_mode=STANDARD\nunyt_agent_id=" \
> /etc/node-manager/state
cargo run
# GET / will redirect to /manage
Expand Down Expand Up @@ -170,23 +183,23 @@ Every release publishes two binary assets:

### Step-by-step release process

1. Make your changes to `src/main.rs` (and/or `holo-node.md`).
1. Make your changes on a `feature/*` branch off `develop`, then merge into `develop`.

2. Update the version in **two places** — they must match exactly:
- `const VERSION: &str = "5.1.0";` in `src/main.rs`
- `version = "5.1.0"` in `Cargo.toml`
2. When ready to release, merge `develop` into `main` and update the version in **two places** — they must match exactly:
- `const VERSION: &str = "6.1.0";` in `src/main.rs`
- `version = "6.1.0"` in `Cargo.toml`

3. Commit:
```bash
git add src/main.rs Cargo.toml
git commit -m "release: v5.1.0 — <one line summary of changes>"
git commit -m "release: v6.1.0 — <one line summary of changes>"
```

4. Tag and push:
```bash
git tag v5.1.0
git tag v6.1.0
git push origin main
git push origin v5.1.0
git push origin v6.1.0
```

5. GitHub Actions (`.github/workflows/release.yml`) picks up the tag, builds both binaries using musl static linking, creates a GitHub Release, and attaches both binary assets automatically. No manual upload needed.
Expand Down Expand Up @@ -234,6 +247,7 @@ The `UPDATE_REPO` environment variable overrides the default (`holo-host/node-ma
| `GET` | `/manage/status` | session | JSON node state snapshot |
| `POST` | `/manage/ssh/add` | session | Add SSH public key |
| `POST` | `/manage/ssh/remove` | session | Remove SSH key by index |
| `POST` | `/manage/unyt` | session | Save or update Unyt Agent ID |
| `POST` | `/manage/hardware` | session | Switch STANDARD ↔ WIND_TUNNEL |
| `POST` | `/manage/password` | session | Change node password |
| `POST` | `/manage/update` | session | Trigger immediate update check |
Expand All @@ -246,9 +260,8 @@ Session tokens are stored in-memory and cleared on restart — operators will ne

| Path | Contents | Permissions |
|------|----------|-------------|
| `/etc/node-manager/state` | Key-value store of node state (node_name, hw_mode, agent_enabled, channel, provider, model) | 600 |
| `/etc/node-manager/state` | Key-value store of node state (`onboarded`, `node_name`, `hw_mode`, `unyt_agent_id`, `wt_suffix`) | 600 |
| `/etc/node-manager/auth` | Password hash: `sha256:<salt>:<hash>` | 600 |
| `/etc/node-manager/provider` | Provider credentials for agent re-enable | 600 |
| `/etc/containers/systemd/edgenode.container` | Podman Quadlet for the EdgeNode container | 644 |
| `/etc/containers/systemd/wind-tunnel.container` | Podman Quadlet for Wind Tunnel | 644 |
| `/home/holo/.ssh/authorized_keys` | SSH public keys for the holo user | 600 |
Expand Down
227 changes: 0 additions & 227 deletions holo-node.md

This file was deleted.

Loading