npm package: @hera-al/standardnode
Description: Hera StandardNode — remote execution node for Hera gateway
Author: TGP
Latest version: 1.0.7

# @hera-al/standardnode

> **Part of [Hera Artificial Life](https://github.com/hera-artificial-life/hera-al)** — an opinionated AI assistant platform that runs locally on your machine. This package is the remote execution agent that connects your machines to the Hera gateway.

Lightweight remote execution node that connects to a Hera gateway via WebSocket. Once paired, it can execute shell commands and proxy browser automation requests dispatched by the central server.

## Features

- **WebSocket connection** to Hera gateway (Nostromo) with automatic reconnect
- **Signature-based pairing** — each node has a unique cryptographic identity, approved by an admin
- **Shell execution** — run commands remotely with optional allowlist and timeout
- **Browser proxy** — forward requests to a local [`@hera-al/browser-server`](https://www.npmjs.com/package/@hera-al/browser-server) instance (optional)
- **Auto-detection** — discovers the gateway URL automatically when running inside the Hera project
- **YAML configuration** — auto-generated on first run, CLI flags override and persist
- **File logging** — append-only with automatic rotation at 10 MB
- **Multi-platform** — macOS, Linux, Windows (Node.js ≥ 18)

## Install

```bash
npm install @hera-al/standardnode
```

### With browser automation (optional)

```bash
npm install @hera-al/standardnode @hera-al/browser-server
```

## Quick start

### CLI

```bash
npx hera-stdnode --ws ws://localhost:3001/nostromo/ws/nodes
```

All CLI flags:

| Flag       | Default                | Description                          |
| ---------- | ---------------------- | ------------------------------------ |
| `--ws`     | *(auto-detected)*      | WebSocket URL of the Hera gateway    |
| `--name`   | *(OS hostname)*        | Display name for this node           |
| `--config` | `config.stdnode.yaml`  | Path to configuration file           |
| `--init`   | —                      | Copy example config to current directory and exit |
| `--help`   | —                      | Show help message                    |

Values passed via CLI are persisted to the config file.

### First run

```bash
# Option A: just launch — config is auto-generated with sensible defaults
hera-stdnode --ws wss://yourserver:3001/nostromo/ws/nodes

# Option B: generate a commented config first, edit it, then launch
hera-stdnode --init
# → creates config.stdnode.yaml in the current directory
vi config.stdnode.yaml
hera-stdnode
```

### From source

```bash
git clone <repo>
cd standardnode
npm install
npm start -- --ws ws://localhost:3001/nostromo/ws/nodes
```

## WebSocket URL format

```
ws[s]://<hostname>:<port><basePath>/ws/nodes
```

| Segment      | Description                                     | Default      |
| ------------ | ----------------------------------------------- | ------------ |
| `<hostname>` | Server address or Tailscale DNS name            | `localhost`  |
| `<port>`     | Nostromo UI port configured on the Hera server  | `3001`       |
| `<basePath>` | Nostromo base path configured on the Hera server| `/nostromo`  |

Use `wss://` when connecting through Tailscale Serve or any TLS-terminated proxy.

## Configuration

On first run a `config.stdnode.yaml` is created with a unique node ID and cryptographic signature. You can also copy the bundled `config.stdnode.example.yaml` as a starting point.

### node

| Key           | Type   | Default        | Description                          |
| ------------- | ------ | -------------- | ------------------------------------ |
| `id`          | string | *(generated)*  | Unique node identifier (UUID)        |
| `displayName` | string | *(hostname)*   | Human-readable name shown in Nostromo|

### gateway

| Key           | Type    | Default                                | Description                      |
| ------------- | ------- | -------------------------------------- | -------------------------------- |
| `enabled`     | boolean | `true`                                 | Enable gateway connection        |
| `url`         | string  | `ws://localhost:3001/nostromo/ws/nodes` | WebSocket endpoint               |
| `token`       | string  | `""`                                   | Authentication token (reserved)  |
| `signature`   | string  | *(generated)*                          | Node signature for pairing       |
| `reconnectMs` | number  | `5000`                                 | Reconnect delay in milliseconds  |

### commands.shell

| Key         | Type     | Default | Description                                   |
| ----------- | -------- | ------- | --------------------------------------------- |
| `enabled`   | boolean  | `true`  | Enable shell command execution                |
| `allowlist` | string[] | `[]`    | If non-empty, only these commands are allowed |
| `timeout`   | number   | `30000` | Command timeout in milliseconds               |

### browser

| Key              | Type     | Default | Description                                    |
| ---------------- | -------- | ------- | ---------------------------------------------- |
| `enabled`        | boolean  | `false` | Enable browser automation proxy                |
| `controlPort`    | number   | `3002`  | Port for the browser HTTP control server       |
| `headless`       | boolean  | `false` | Run Chrome in headless mode                    |
| `noSandbox`      | boolean  | `false` | Disable Chrome sandbox (CI/Docker)             |
| `attachOnly`     | boolean  | `false` | Only attach to an already-running Chrome       |
| `executablePath` | string   | —       | Custom Chrome/Brave/Edge executable path       |
| `allowProfiles`  | string[] | `[]`    | If non-empty, only these profiles are allowed  |

### logs

| Key   | Type   | Default          | Description                    |
| ----- | ------ | ---------------- | ------------------------------ |
| `dir` | string | `./logs-stdnode` | Directory for log files        |

Log files are written as `stdnode.log` with rotation at 10 MB (up to 9 rotated files).

## Pairing flow

```
┌──────────┐         ┌──────────┐         ┌───────────┐
│   Node   │──hello──▶│ Nostromo │         │   Admin   │
│          │         │ (gateway)│◀─approve─│  (web UI) │
│          │◀─status─│          │         │           │
└──────────┘         └──────────┘         └───────────┘
```

1. Node connects and sends a `hello` message with ID, signature, and capabilities
2. Nostromo shows the node as **pending** in the admin UI
3. An admin approves (or revokes) the node
4. Once approved, the node starts receiving and executing commands
5. If the connection drops, the node reconnects automatically after `reconnectMs`

## Supported commands

| Command         | Description                                               |
| --------------- | --------------------------------------------------------- |
| `shell.run`     | Execute a shell command (`cmd` + `args` array)            |
| `shell.which`   | Resolve a binary path                                     |
| `browser.proxy` | Forward an HTTP request to the local browser-server       |

### shell.run

```json
{
  "cmd": "/usr/bin/ls",
  "args": ["-la", "/tmp"],
  "cwd": "/home/user",
  "timeout": 10000,
  "env": { "NODE_ENV": "production" }
}
```

Returns `{ stdout, stderr, exitCode }`.

> **Security:** Commands are spawned with `shell: false` — no shell injection possible. Use the `allowlist` to restrict which binaries can be executed.

### browser.proxy

Requires `@hera-al/browser-server` installed and `browser.enabled: true` in config.

```json
{
  "method": "GET",
  "path": "/snapshot",
  "query": { "mode": "text" },
  "profile": "default",
  "timeoutMs": 30000
}
```

Returns the browser-server response. File results (screenshots, PDFs) are automatically base64-encoded.

## Architecture

```
┌─────────────────────────────────────────────────┐
│              Hera Gateway (Nostromo)             │
└────────────────────┬────────────────────────────┘
                     │ WebSocket
          ┌──────────┴──────────┐
          │    StandardNode     │
          │  ┌────────────────┐ │
          │  │  Gateway Link  │ │  ← connection, pairing, heartbeat
          │  └───────┬────────┘ │
          │    ┌─────┴─────┐    │
          │    │           │    │
          │  Shell    Browser   │
          │  Runner   Proxy     │
          │    │           │    │
          │    ▼           ▼    │
          │  spawn()   fetch()  │  ← local processes / browser-server
          └─────────────────────┘
```

- **Gateway Link** — WebSocket client with reconnect, heartbeat (30s), and command dispatch
- **Shell Runner** — secure process spawning with allowlist, timeout, env isolation
- **Browser Proxy** — HTTP proxy to local `@hera-al/browser-server` with file detection and profile filtering

## Auto-detection

When running inside the Hera project directory (i.e. `standardnode/` is a subfolder), the node reads `../config.yaml` on first run and auto-configures the gateway URL from the Nostromo settings. The `--ws` flag always takes priority.

## NPM scripts

| Script          | Description                                  |
| --------------- | -------------------------------------------- |
| `npm start`     | Run via tsx (development, no build needed)    |
| `npm run dev`   | Run with file watcher (hot reload)           |
| `npm run build` | Compile TypeScript to `dist/`                |
| `npm run node`  | Run the compiled build (`node dist/index.js`)|
| `npm run help`  | Show CLI help                                |

All scripts forward extra flags: `npm start -- --ws <url> --name <name>`.

## Requirements

- Node.js ≥ 18
- macOS, Linux, or Windows
- A running Hera gateway with Nostromo enabled

## License

[MIT](./LICENSE) — © 2026 TGP / Hera Artificial Life