home-hosted 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +103 -15
- package/NOTIFICATIONS.md +69 -0
- package/README.md +208 -179
- package/SERVERS.md +101 -0
- package/UI_CREATION.md +104 -77
- package/dist/cli.js +1197 -142
- package/dist/cli.js.map +1 -1
- package/package.json +12 -4
- package/uis/stock/dist/assets/index-DOD80X00.css +2 -0
- package/uis/stock/dist/assets/index-x_wy1ycR.js +47 -0
- package/uis/stock/dist/index.html +2 -2
- package/uis/stock/dist/assets/index-CmkrtNU0.css +0 -2
- package/uis/stock/dist/assets/index-DqcdQi-W.js +0 -47
package/README.md
CHANGED
|
@@ -6,52 +6,31 @@
|
|
|
6
6
|
|
|
7
7
|
<sub>The harness for your servers.</sub>
|
|
8
8
|
|
|
9
|
-
Point it at the things you run — a gateway, a media server, a bot, a database — and it
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
specialized panel that fits you exactly.
|
|
9
|
+
Point it at the things you run — a gateway, a media server, a bot, a database — and it starts
|
|
10
|
+
them, watches them, restarts what dies, and shows you one page of what is going on. Or
|
|
11
|
+
[BYOU](#-bring-your-own-ui-byou), for a specialized UI that fits you exactly.
|
|
13
12
|
|
|
14
13
|
[](https://www.npmjs.com/package/home-hosted)
|
|
15
14
|
[](https://www.npmjs.com/package/home-hosted)
|
|
15
|
+
[](https://github.com/NamesMT/home-hosted/actions/workflows/quickcheck.yml)
|
|
16
16
|
[](./LICENSE)
|
|
17
17
|
[](https://nodejs.org)
|
|
18
18
|
|
|
19
|
-
[🚀 Quick start](#-quick-start) · [✨ Features](#-features) · [🧩
|
|
19
|
+
[🚀 Quick start](#-quick-start) · [🤖 Agents & API](#-agents-scripts-and-tools) · [✨ Features](#-features) · [🧩 Servers](./SERVERS.md) · [🛠 CLI](#-cli) · [🔔 Notifications](./NOTIFICATIONS.md) · [🎨 BYOU](#-bring-your-own-ui-byou)
|
|
20
20
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Running services on a home machine usually means one of two extremes: `tmux` sessions you
|
|
28
|
-
forget about, or a hand-written systemd unit per service (times six).
|
|
25
|
+
<div align="center">
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
| --- | --- |
|
|
32
|
-
| ❌ **"Is it still running?"** | You check with `ps`, then `curl`, then hope. |
|
|
33
|
-
| ❌ **Silent deaths** | Something crashes at 3am and you notice days later. |
|
|
34
|
-
| ❌ **One terminal per service** | Logs scroll away in tabs you closed. |
|
|
35
|
-
| ❌ **Fragile restarts** | The box reboots and half the stack is gone. |
|
|
36
|
-
| ✅ **home-hosted** | Declare it once, watch it forever, one command to stop it all. |
|
|
27
|
+

|
|
37
28
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
│ Vue panel + JSON API + SSE logs │
|
|
42
|
-
└───────────────┬──────────────────────┘
|
|
43
|
-
│ supervises
|
|
44
|
-
┌──────────────────────────┼──────────────────────────┐
|
|
45
|
-
▼ ▼ ▼
|
|
46
|
-
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
47
|
-
│ gateway │ │ files │ │ bot │
|
|
48
|
-
│ :4000 │ │ :4010 │ │ ... │
|
|
49
|
-
└─────────┘ └─────────┘ └─────────┘
|
|
50
|
-
health ✓ health ✓ restarts ↻
|
|
51
|
-
```
|
|
29
|
+
<sub>The stock panel, the [NOC-console](./uis/noc-console) that ships alongside it (for TUI and
|
|
30
|
+
shortcuts wizards), and UI directions you could build yourself —
|
|
31
|
+
<a href="#-bring-your-own-ui-byou">BYOU</a>.</sub>
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
what you run — a server is a command, some arguments, and the environment you give it.
|
|
33
|
+
</div>
|
|
55
34
|
|
|
56
35
|
---
|
|
57
36
|
|
|
@@ -60,10 +39,11 @@ what you run — a server is a command, some arguments, and the environment you
|
|
|
60
39
|
```bash
|
|
61
40
|
npx home-hosted # start it — detached, it stays running
|
|
62
41
|
npx home-hosted status # where is it, is it healthy
|
|
42
|
+
# pnpm instead of npx: `pnpm dlx home-hosted …`
|
|
63
43
|
```
|
|
64
44
|
|
|
65
|
-
That is it. The panel is on **<http://127.0.0.1:3999
|
|
66
|
-
|
|
45
|
+
That is it. The panel is on **<http://127.0.0.1:3999>** and keeps running after the terminal
|
|
46
|
+
closes. <sub>Needs Node 24 or newer.</sub> Stop it whenever you like:
|
|
67
47
|
|
|
68
48
|
```bash
|
|
69
49
|
npx home-hosted down # stops the panel *and* everything it started
|
|
@@ -71,42 +51,34 @@ npx home-hosted down # stops the panel *and* everything it started
|
|
|
71
51
|
|
|
72
52
|
> [!NOTE]
|
|
73
53
|
> The first boot writes a default password (`hh`) so the panel is never unprotected. Change
|
|
74
|
-
> it under **Settings →
|
|
54
|
+
> it under **Settings → Authentication** — binding beyond `127.0.0.1` stays refused until you do.
|
|
75
55
|
|
|
76
56
|
<details>
|
|
77
57
|
<summary><b>📦 Install it instead of npx-ing it</b></summary>
|
|
78
58
|
|
|
79
59
|
```bash
|
|
80
|
-
npm install -g home-hosted
|
|
60
|
+
npm install -g home-hosted # or: pnpm add -g home-hosted
|
|
81
61
|
home-hosted up
|
|
82
|
-
|
|
83
|
-
# or keep a project with its own servers *and* its own state directory
|
|
84
|
-
cd ~/my-servers
|
|
85
|
-
HHOSTED_HOME=./state npx home-hosted up
|
|
86
62
|
```
|
|
87
63
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Everything home-hosted owns — config, secrets, logs, TLS, backups — lives in one directory:
|
|
92
|
-
`$HHOSTED_HOME`, defaulting to `~/.home-hosted`. Delete it and nothing of yours is left behind.
|
|
64
|
+
Everything it owns — config, secrets, logs, TLS, backups — lives in `$HHOSTED_HOME`, default
|
|
65
|
+
`~/.home-hosted`. Delete that and nothing of yours is left behind.
|
|
93
66
|
|
|
94
67
|
</details>
|
|
95
68
|
|
|
96
69
|
<details>
|
|
97
|
-
<summary><b>📁 Keep
|
|
70
|
+
<summary><b>📁 Keep a whole setup in a project you can take anywhere</b></summary>
|
|
98
71
|
|
|
99
|
-
|
|
100
|
-
setup:
|
|
72
|
+
Commit the project and it *is* the setup. `init` scaffolds it:
|
|
101
73
|
|
|
102
74
|
```bash
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
npx home-hosted up --home ./state # state lives inside the project
|
|
75
|
+
npx home-hosted init # or: pnpm dlx home-hosted init
|
|
76
|
+
# project directory, package name, package manager, install, git — every step has a default
|
|
77
|
+
# `--yes` takes them all, for an agent or a CI job
|
|
108
78
|
```
|
|
109
79
|
|
|
80
|
+
It writes a manifest whose scripts all pass `--home ./state`:
|
|
81
|
+
|
|
110
82
|
```jsonc
|
|
111
83
|
// package.json
|
|
112
84
|
{
|
|
@@ -114,19 +86,23 @@ npx home-hosted up --home ./state # state lives inside the proje
|
|
|
114
86
|
"up": "home-hosted up --home ./state",
|
|
115
87
|
"down": "home-hosted down --home ./state",
|
|
116
88
|
"status": "home-hosted status --home ./state"
|
|
89
|
+
// …and restart, set-password, set-token, migrate
|
|
117
90
|
}
|
|
118
91
|
}
|
|
119
92
|
```
|
|
120
93
|
|
|
121
94
|
```gitignore
|
|
122
|
-
|
|
123
|
-
|
|
95
|
+
node_modules/
|
|
96
|
+
state/* # secrets, logs, TLS keys and archives stay local…
|
|
97
|
+
!state/servers.config.json # …but the server definitions are committed
|
|
98
|
+
data/ # per-server data directories, declared through dataEnvs
|
|
124
99
|
```
|
|
125
100
|
|
|
126
|
-
|
|
101
|
+
One clone, `pnpm install --frozen-lockfile`, `pnpm run up` — the setup is up on any machine with Node.
|
|
102
|
+
Worked example, with per-server data inside the project:
|
|
103
|
+
**[hhosted-9router-dsh](https://github.com/NamesMT/hhosted-9router-dsh)**.
|
|
127
104
|
|
|
128
|
-
|
|
129
|
-
**Settings → Backups** exports the same thing as one archive for someone else to restore.
|
|
105
|
+
<sub>Call them as `pnpm run up` — `pnpm up` is pnpm's own update, not your script.</sub>
|
|
130
106
|
|
|
131
107
|
</details>
|
|
132
108
|
|
|
@@ -152,76 +128,101 @@ WantedBy=multi-user.target
|
|
|
152
128
|
|
|
153
129
|
---
|
|
154
130
|
|
|
131
|
+
## 🤖 Agents, scripts and tools
|
|
132
|
+
|
|
133
|
+
Everything the panel does, a script or an agent can do: the API takes a **long-lived token** in place
|
|
134
|
+
of the browser cookie. One command for a credential, then plain HTTP to start, stop, inspect, restart
|
|
135
|
+
and read logs.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
home-hosted set-token --generate
|
|
139
|
+
# hh_9uA2… (printed once; only its hash is kept, mode 0600)
|
|
140
|
+
|
|
141
|
+
curl -H "Authorization: Bearer hh_9uA2…" http://127.0.0.1:3999/api/state
|
|
142
|
+
curl -H "Authorization: Bearer hh_9uA2…" -X POST http://127.0.0.1:3999/api/servers/9router/restart
|
|
143
|
+
curl -N -H "Authorization: Bearer hh_9uA2…" 'http://127.0.0.1:3999/api/events?logs=1' # SSE
|
|
144
|
+
home-hosted status --json # machine-readable: pid, url, health, paths
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A token has the same authority as a signed-in browser and outlives restarts; `set-token --clear`
|
|
148
|
+
revokes it instantly.
|
|
149
|
+
|
|
150
|
+
<details>
|
|
151
|
+
<summary><b>🌐 The endpoints worth knowing</b></summary>
|
|
152
|
+
|
|
153
|
+
| | |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `GET /api/state` | the full snapshot: config, live status, host vitals |
|
|
156
|
+
| `GET /api/events` | SSE: the live state, plus logs (`?logs=0`, `?serverId=…`) |
|
|
157
|
+
| `GET /api/servers/:id/stream` | SSE: one server's state and logs |
|
|
158
|
+
| `POST /api/servers/:id/{start,stop,restart}` | lifecycle |
|
|
159
|
+
| `POST /api/servers/:id/free-port` | ask whatever holds that server's port to stop |
|
|
160
|
+
| `PATCH /api/servers/:id`, `PATCH /api/settings` | edit configuration |
|
|
161
|
+
| `GET /api/logs`, `/api/backups`, `/api/notifications` | logs, archives, Telegram |
|
|
162
|
+
| `GET /healthz` | no session needed — the one an external monitor wants (its per-server detail needs a credential) |
|
|
163
|
+
| `GET /api/metrics` | Prometheus text |
|
|
164
|
+
|
|
165
|
+
`GET /openapi/spec.json` describes all of it, `/openapi/ui` is the browsable version, and every error
|
|
166
|
+
comes back as one envelope (`{ message, code, detail }`) with a stable `code` a tool can branch on.
|
|
167
|
+
|
|
168
|
+
</details>
|
|
169
|
+
|
|
170
|
+
<details>
|
|
171
|
+
<summary><b>🧑💻 Pointing an agent at it</b></summary>
|
|
172
|
+
|
|
173
|
+
Give the agent four things and it can run your home server without guessing:
|
|
174
|
+
|
|
175
|
+
1. the token (`home-hosted set-token --generate`),
|
|
176
|
+
2. `http://127.0.0.1:3999/openapi/spec.json` — the API it may call,
|
|
177
|
+
3. `home-hosted status --json` — where things are,
|
|
178
|
+
4. [SERVERS.md](./SERVERS.md) — how an entry is declared when it needs a new server.
|
|
179
|
+
|
|
180
|
+
For a UI rather than the API, [UI_CREATION.md](./UI_CREATION.md) is the whole contract, and the panel
|
|
181
|
+
can be told what to be: *"Help me build a UI for home-hosted: nostalgic game theme, including …"*.
|
|
182
|
+
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
155
187
|
## ✨ Features
|
|
156
188
|
|
|
157
189
|
| | |
|
|
158
190
|
| --- | --- |
|
|
159
191
|
| 🚦 **Lifecycle** | Start, stop, restart from the panel or the API; `autostart` entries come up with it. |
|
|
160
192
|
| ♻️ **Auto-restart** | Exponential backoff on crash, with the counter reset once a process stays up. |
|
|
161
|
-
| 🩺 **Health that acts** | TCP or HTTP probes per server: warn on the card, force a restart after a timeout,
|
|
193
|
+
| 🩺 **Health that acts** | TCP or HTTP probes per server: warn on the card, force a restart after a timeout, check ports before starting — and [follow or replace](./SERVERS.md#when-a-program-restarts-itself) a program that restarts itself. |
|
|
162
194
|
| 🔗 **Ordered startup** | `dependsOn` waits for a dependency to be *healthy* — not merely spawned — and stops in reverse. |
|
|
163
195
|
| 📜 **Logs** | Live per-server stream, buffer plus rotated files on disk, search, download, one click to clear. |
|
|
164
196
|
| 📈 **Resources** | CPU and RSS of the whole process tree, with an optional memory ceiling that triggers a restart. |
|
|
165
197
|
| 🌡️ **Host vitals** | Load, memory, swap, disk and CPU temperature, with thresholds that notify once and again on recovery. |
|
|
198
|
+
| 🤖 **Token API** | Scripts and agents drive it with `Authorization: Bearer` — no browser, no session. [↑](#-agents-scripts-and-tools) |
|
|
199
|
+
| 🔔 **Notifications** | Telegram on crash, unhealthy, forced restart, recovery and host thresholds — [setup here](./NOTIFICATIONS.md). |
|
|
166
200
|
| 💾 **Backups** | One click for config, secrets, TLS and your declared data directories — plain `.zip`, or AES-256 with a password, restored per path. |
|
|
167
|
-
|
|
|
168
|
-
|
|
|
169
|
-
|
|
|
170
|
-
| 🔐 **Security** | httpOnly cookie sessions, scrypt hashes, per-IP lockout, optional TLS, and a refusal to expose itself without a password. |
|
|
171
|
-
| 🧩 **Server-agnostic** | `command` + `args` + `env` + `cwd`. Nothing in the code knows what you run. |
|
|
201
|
+
| 🎨 **BYOU — Bring Your Own UI** | Upload a static build, `home-hosted ui-revert` to go back. [UI_CREATION.md](./UI_CREATION.md) |
|
|
202
|
+
| 🔐 **Security** | Cookie sessions, API tokens, scrypt hashes, per-IP lockout, optional TLS, and a refusal to expose itself without a password. |
|
|
203
|
+
| 🧩 **No special treatment** | A server is `command` + `args` + `env` + `cwd`; nothing is built in for any particular app. |
|
|
172
204
|
| 🖥 **Cross-platform** | Linux, macOS and Windows: `/proc`, `ps` or Win32_Process, process groups or `taskkill /T`, no shell dependencies. |
|
|
173
205
|
|
|
174
206
|
---
|
|
175
207
|
|
|
176
|
-
## 🧩
|
|
208
|
+
## 🧩 Servers
|
|
177
209
|
|
|
178
|
-
|
|
210
|
+
An entry is a few lines. Add one with **➕ Add server**, or write it into `servers.config.json`:
|
|
179
211
|
|
|
180
212
|
```json
|
|
181
213
|
{
|
|
182
214
|
"id": "myapp",
|
|
183
215
|
"command": "node",
|
|
184
216
|
"args": ["server.js"],
|
|
185
|
-
"cwd": "./apps/myapp",
|
|
186
217
|
"port": 8080,
|
|
187
218
|
"autostart": true,
|
|
188
|
-
"
|
|
189
|
-
"health": { "mode": "http", "http": { "path": "/healthz" } }
|
|
219
|
+
"dataEnvs": { "DATA_DIR": "{projectDir}/data/myapp" }
|
|
190
220
|
}
|
|
191
221
|
```
|
|
192
222
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
cd ~/my-servers
|
|
198
|
-
HHOSTED_HOME=./state npx home-hosted up
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
<details>
|
|
202
|
-
<summary><b>🧾 Field reference</b></summary>
|
|
203
|
-
|
|
204
|
-
| field | what it does |
|
|
205
|
-
| --- | --- |
|
|
206
|
-
| `command`, `args`, `cwd` | what to run, with `{placeholders}` resolved per entry |
|
|
207
|
-
| `env`, `dataEnvs`, `envFile` | environment; `dataEnvs` also marks data directories for backups, `envFile` keeps secrets out of the config |
|
|
208
|
-
| `port`, `bind` | enables the readiness wait, health checks and the conflict preflight; `local` keeps it on `127.0.0.1` |
|
|
209
|
-
| `health.mode` | `port` (TCP connect) or `http` (path, expected status, expected body) |
|
|
210
|
-
| `restart.*` | backoff: `maxRetries`, `baseDelayMs`, `factor`, `maxDelayMs`, `resetAfterMs` |
|
|
211
|
-
| `stop.*` | signal, `killGroup`, grace period, and whether to sweep leftover port holders |
|
|
212
|
-
| `dependsOn` | ids that must be healthy first; stopped in reverse order |
|
|
213
|
-
| `resources.maxRssBytes` | restart when the process tree grows past a limit |
|
|
214
|
-
| `bootstrap` | one command to run once before the first start (migrations, warmups) |
|
|
215
|
-
| `backupPaths` | extra paths this entry owns, included in backups |
|
|
216
|
-
|
|
217
|
-
Placeholders: `{id}` `{label}` `{port}` `{host}` `{displayHost}` `{bind}` `{lanIp}` `{cwd}`
|
|
218
|
-
`{projectDir}` `{dataRoot}` `{home}`, plus `${VAR}` from the environment. Unknown placeholders
|
|
219
|
-
stay visible instead of silently becoming empty.
|
|
220
|
-
|
|
221
|
-
Every supervised process also gets `HHOSTED_SERVER_ID` and `HHOSTED_CONTROL_PORT`, so a
|
|
222
|
-
service can tell which entry it is and how to reach the panel.
|
|
223
|
-
|
|
224
|
-
</details>
|
|
223
|
+
`dataEnvs` declares a data directory once: it is exported to the process *and* picked up by Backups.
|
|
224
|
+
**Every field, every placeholder, the port-conflict policies (including adopting a server that
|
|
225
|
+
restarts itself), and how hand-edits are validated: [SERVERS.md](./SERVERS.md).**
|
|
225
226
|
|
|
226
227
|
---
|
|
227
228
|
|
|
@@ -234,6 +235,9 @@ service can tell which entry it is and how to reach the panel.
|
|
|
234
235
|
| `home-hosted restart` | `down`, then `up` |
|
|
235
236
|
| `home-hosted status` | pid, URL, health, uptime, state and log paths (`--json` for scripts) |
|
|
236
237
|
| `home-hosted set-password` | set the panel password without opening a browser |
|
|
238
|
+
| `home-hosted set-token` | set the API token scripts and agents use (`--generate`, `--clear`) |
|
|
239
|
+
| `home-hosted migrate` | bring `servers.config.json` up to this release's schema (`--dry-run`, `--yes`) |
|
|
240
|
+
| `home-hosted init` | scaffold a project that keeps `state/` and its data in the repo |
|
|
237
241
|
| `home-hosted ui-revert` | go back to the stock panel UI after uploading your own |
|
|
238
242
|
|
|
239
243
|
<details>
|
|
@@ -253,96 +257,107 @@ service can tell which entry it is and how to reach the panel.
|
|
|
253
257
|
|
|
254
258
|
</details>
|
|
255
259
|
|
|
260
|
+
<details>
|
|
261
|
+
<summary><b>🧭 Upgrading, and why the panel sometimes refuses to start</b></summary>
|
|
262
|
+
|
|
263
|
+
`servers.config.json` records what wrote it: `meta.writtenBy` (the release) and `meta.schema` (the
|
|
264
|
+
config shape). That buys two guarantees:
|
|
265
|
+
|
|
266
|
+
- **A newer home-hosted always reads an older config** — every existing key keeps its meaning.
|
|
267
|
+
- **Keys a newer release added are ignored, not fatal.** The panel names them in its log, leaves them
|
|
268
|
+
in the file, and never resets the settings around them.
|
|
269
|
+
|
|
270
|
+
What it will not do is run a config it cannot read. A wrong value, a duplicate id, an unreadable file
|
|
271
|
+
or a config whose schema is newer than the running release stops `up` with the exact problem, rather
|
|
272
|
+
than starting with defaults that quietly differ from your file. Fix the file, or install the release
|
|
273
|
+
that wrote it.
|
|
274
|
+
|
|
275
|
+
When a release changes the shape itself, `home-hosted migrate` applies the steps it ships:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
home-hosted migrate --dry-run # print the steps, write nothing
|
|
279
|
+
home-hosted migrate # ask, then write — keeps servers.config.json.bak
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Unattended, consent comes from `--yes` or `HHOSTED_MIGRATE=allow`; without it the command stops.
|
|
283
|
+
|
|
284
|
+
</details>
|
|
285
|
+
|
|
256
286
|
---
|
|
257
287
|
|
|
258
288
|
## 🔐 Security
|
|
259
289
|
|
|
260
290
|
Everything binds `127.0.0.1` until you say otherwise.
|
|
261
291
|
|
|
262
|
-
- **A password is required to expose the panel.** Replace the default, then bind to `lan` —
|
|
263
|
-
|
|
264
|
-
- **Sessions** live in memory only; the cookie is `HttpOnly` and `SameSite=Strict`, and the
|
|
265
|
-
|
|
266
|
-
- **
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
- **A password is required to expose the panel.** Replace the default, then bind to `lan` — in the
|
|
293
|
+
UI, in the config, or with `--host lan`. The same guard applies in all three places.
|
|
294
|
+
- **Sessions** live in memory only; the cookie is `HttpOnly` and `SameSite=Strict`, and the login
|
|
295
|
+
route locks out repeated failures per IP.
|
|
296
|
+
- **API tokens** for scripts and agents: `home-hosted set-token --generate` prints one once, and a
|
|
297
|
+
request proves itself with `Authorization: Bearer …` — the same access as a signed-in browser,
|
|
298
|
+
stored as a SHA-256 hash, revoked with `set-token --clear`.
|
|
299
|
+
- **Port conflicts** are named — `port 4010 is already in use (pid 4242)` — and can be resolved from
|
|
300
|
+
a confirmation popover on that banner or card. The process is looked up again at that moment,
|
|
301
|
+
never taken from the message, and anything the panel supervises is refused, not killed. A server
|
|
302
|
+
that [restarts itself](./SERVERS.md#when-a-program-restarts-itself) can be followed, or replaced
|
|
303
|
+
with a supervised copy.
|
|
304
|
+
- **Secrets never enter the config**: the password hash, the API token hash, the Telegram bot token
|
|
305
|
+
and the TLS key live in `$HHOSTED_HOME/.control-secrets.json` with mode `0600`.
|
|
306
|
+
- **Behind a proxy** turn on `trustProxy` and let `cookieSecure: auto` add `Secure` on https, or
|
|
307
|
+
upload a PEM pair and let home-hosted terminate TLS itself.
|
|
270
308
|
|
|
271
309
|
---
|
|
272
310
|
|
|
273
|
-
##
|
|
274
|
-
|
|
275
|
-
**Settings → Backups** archives the config, the secrets file, the TLS pair and the data
|
|
276
|
-
directories your entries declare — as an ordinary `.zip`:
|
|
311
|
+
## 🔔 Notifications
|
|
277
312
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
-
|
|
281
|
-
|
|
313
|
+
Telegram, when something happens while you are not looking: a server that gave up restarting, a
|
|
314
|
+
failing health check, a forced restart, a recovery, or a host threshold (disk, memory, swap, load,
|
|
315
|
+
temperature). Opt-in, rate-limited per server *and* reason, and the bot token stays in the secrets
|
|
316
|
+
file. **Two minutes of setup: [NOTIFICATIONS.md](./NOTIFICATIONS.md).**
|
|
282
317
|
|
|
283
|
-
|
|
318
|
+
---
|
|
284
319
|
|
|
285
|
-
|
|
286
|
-
open **Settings → Backups**, upload the archive and restore: the server definitions come
|
|
287
|
-
back, their data lands where *this* machine's config says it should, and the entries marked
|
|
288
|
-
`autostart` are brought up right away. Nothing to re-declare by hand.
|
|
320
|
+
## 💾 Backups
|
|
289
321
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
machine — so `{home}` stays this user's home. A restore never writes anywhere a config does
|
|
293
|
-
not declare: yours, or the one inside the archive you chose to restore.
|
|
322
|
+
**Settings → Backups** archives the config, secrets, TLS pair and every data directory your entries
|
|
323
|
+
declare — an ordinary `.zip`, or WinZip AES-256 with a password, restored per path.
|
|
294
324
|
|
|
295
325
|
<details>
|
|
296
|
-
<summary><b
|
|
326
|
+
<summary><b>🚚 One archive is a whole setup</b></summary>
|
|
297
327
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"backupPaths": ["{home}/.myapp/uploads"]
|
|
302
|
-
}
|
|
303
|
-
```
|
|
328
|
+
Start a **blank** home-hosted anywhere — another machine, another user, a fresh container — upload the
|
|
329
|
+
archive and restore. Definitions come back, data lands where *this* machine's config says, and
|
|
330
|
+
`autostart` entries come up immediately.
|
|
304
331
|
|
|
305
|
-
|
|
306
|
-
|
|
332
|
+
It works because an archive carries its own `servers.config.json` and paths are matched by the
|
|
333
|
+
**declaration** (`9router:DATA_DIR`), not by an absolute path from the source machine. A restore never
|
|
334
|
+
writes where no config declares.
|
|
307
335
|
|
|
308
336
|
</details>
|
|
309
337
|
|
|
310
338
|
---
|
|
311
339
|
|
|
312
|
-
Two UIs live in this repo: **stock** (the panel you get by default) and **noc-console**, an
|
|
313
|
-
operations console over the same API. `pnpm run build:uis` builds and zips every one of them, and
|
|
314
|
-
each release attaches them as `home-hosted-ui-<name>.zip` — install one from Settings → Interface.
|
|
315
|
-
|
|
316
340
|
## 🎨 Bring your own UI (BYOU)
|
|
317
341
|
|
|
318
|
-
The panel is a static site
|
|
319
|
-
|
|
320
|
-
|
|
342
|
+
The panel is a static site: `$HHOSTED_HOME/.ui` overrides the packaged one, and **Settings →
|
|
343
|
+
Interface** takes a zip. No restart, no fork — and `home-hosted ui-revert` brings back the stock
|
|
344
|
+
panel if yours breaks.
|
|
321
345
|
|
|
322
|
-
|
|
323
|
-
home-hosted
|
|
324
|
-
|
|
346
|
+
Two ship in this repo: `uis/stock`, and `uis/noc-console` for TUI and shortcuts wizards; a release
|
|
347
|
+
attaches both as `home-hosted-ui-<name>.zip`. Yours can be anything that compiles to static files —
|
|
348
|
+
the server never cares what built it.
|
|
325
349
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
just the first client. [UI_CREATION.md](./UI_CREATION.md) has the contract, the rules and a
|
|
329
|
-
worked example.
|
|
350
|
+
<details>
|
|
351
|
+
<summary><b>🤖 Or have an agent build the UI you actually want</b></summary>
|
|
330
352
|
|
|
331
|
-
|
|
353
|
+
The whole contract fits in one file, so a coding agent can do this. Point it at this repo and be
|
|
354
|
+
specific:
|
|
332
355
|
|
|
333
|
-
|
|
356
|
+
> Help me build a UI for `home-hosted`: nostalgic game theme, including … features.
|
|
334
357
|
|
|
335
|
-
|
|
358
|
+
[UI_CREATION.md](./UI_CREATION.md) has the endpoints, the SSE frames, the auth rules and a checklist.
|
|
336
359
|
|
|
337
|
-
|
|
338
|
-
| --- | --- |
|
|
339
|
-
| `GET /api/state` | the full snapshot: config, live status, host vitals |
|
|
340
|
-
| `GET /api/servers/:id/stream` | SSE stream of state and log lines |
|
|
341
|
-
| `POST /api/servers/:id/{start,stop,restart}` | lifecycle |
|
|
342
|
-
| `PATCH /api/servers/:id`, `PATCH /api/settings` | edit configuration |
|
|
343
|
-
| `GET /api/logs`, `/api/backups`, `/api/notifications` | logs, archives, Telegram |
|
|
344
|
-
| `GET /healthz` | no session needed — the one an external monitor wants |
|
|
345
|
-
| `GET /api/metrics` | Prometheus text |
|
|
360
|
+
</details>
|
|
346
361
|
|
|
347
362
|
---
|
|
348
363
|
|
|
@@ -365,9 +380,12 @@ the same way: every supervised process tree is stopped before the panel exits.
|
|
|
365
380
|
</details>
|
|
366
381
|
|
|
367
382
|
<details>
|
|
368
|
-
<summary><b>
|
|
383
|
+
<summary><b>Nothing starts and the port is busy</b></summary>
|
|
369
384
|
|
|
370
|
-
|
|
385
|
+
A supervised server whose port is taken is reported rather than started over — the panel names the
|
|
386
|
+
holder and offers to free it, and a program that restarts itself can be followed or reclaimed instead
|
|
387
|
+
([SERVERS.md](./SERVERS.md#a-busy-port)). The control port itself is checked before the listener is
|
|
388
|
+
opened.
|
|
371
389
|
|
|
372
390
|
</details>
|
|
373
391
|
|
|
@@ -377,9 +395,9 @@ Just the control panel, `3999` by default. Supervised servers use the ports you
|
|
|
377
395
|
`$HHOSTED_HOME`, default `~/.home-hosted`:
|
|
378
396
|
|
|
379
397
|
```text
|
|
380
|
-
servers.config.json your servers
|
|
398
|
+
servers.config.json your servers, plus meta: which release and schema wrote it
|
|
381
399
|
servers.config.schema.json regenerated on every start, for editor autocomplete
|
|
382
|
-
.control-secrets.json password hash + Telegram token (mode 0600)
|
|
400
|
+
.control-secrets.json password hash + API token hash + Telegram token (mode 0600)
|
|
383
401
|
.logs/ rotated per-server logs + history
|
|
384
402
|
.tls/ an uploaded PEM pair
|
|
385
403
|
.backups/ zip archives
|
|
@@ -391,38 +409,49 @@ run.json the running panel (pid, url, token, mode 0600)
|
|
|
391
409
|
</details>
|
|
392
410
|
|
|
393
411
|
<details>
|
|
394
|
-
<summary><b>
|
|
412
|
+
<summary><b>Which ports does it use?</b></summary>
|
|
395
413
|
|
|
396
|
-
|
|
397
|
-
shipped examples avoid POSIX-only commands. CPU temperature and swap are best-effort where the
|
|
398
|
-
OS does not expose them to an unprivileged process.
|
|
414
|
+
Just the control panel, `3999` by default. Supervised servers use the ports you give them.
|
|
399
415
|
|
|
400
416
|
</details>
|
|
401
417
|
|
|
402
418
|
<details>
|
|
403
|
-
<summary><b>
|
|
419
|
+
<summary><b>Windows support, really?</b></summary>
|
|
404
420
|
|
|
405
|
-
|
|
406
|
-
|
|
421
|
+
Yes. Process trees are sampled from Win32_Process, termination uses `taskkill /T`, and the shipped
|
|
422
|
+
examples avoid POSIX-only commands. CPU temperature and swap are best-effort where the OS does not
|
|
423
|
+
expose them to an unprivileged process, and adopting a self-restarted process is Linux/macOS only.
|
|
407
424
|
|
|
408
425
|
</details>
|
|
409
426
|
|
|
410
427
|
---
|
|
411
428
|
|
|
412
|
-
## 🗂
|
|
429
|
+
## 🗂 Working on it
|
|
413
430
|
|
|
414
431
|
```text
|
|
415
432
|
src/ control plane: config, supervisor, API, providers, services
|
|
416
|
-
src/cli.ts the command line (up/down/status/restart/set-password)
|
|
433
|
+
src/cli.ts the command line (up/down/status/restart/set-password/set-token/migrate)
|
|
417
434
|
src/index.ts the control plane itself, used by `up --foreground`
|
|
418
|
-
uis/ UIs: `stock` (shipped) and alternatives
|
|
435
|
+
uis/ UIs: `stock` (shipped) and alternatives — any framework, static output
|
|
419
436
|
bin/ the published entry point
|
|
420
|
-
vite.server.config.ts bundles src/cli.ts into dist/cli.js
|
|
421
437
|
```
|
|
422
438
|
|
|
423
|
-
|
|
424
|
-
`.dev-state/`)
|
|
425
|
-
types
|
|
439
|
+
`pnpm dev` runs the panel with `tsx watch` plus the stock UI's dev server (state goes to
|
|
440
|
+
`.dev-state/`); `pnpm build` produces `dist/` and `uis/stock/dist/`; `pnpm quickcheck` is lint plus
|
|
441
|
+
types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
|
|
442
|
+
|
|
443
|
+
<details>
|
|
444
|
+
<summary><b>📚 The docs, and which one you want</b></summary>
|
|
445
|
+
|
|
446
|
+
| file | for |
|
|
447
|
+
| --- | --- |
|
|
448
|
+
| [SERVERS.md](./SERVERS.md) | declaring a server: every field, placeholders, port conflicts |
|
|
449
|
+
| [NOTIFICATIONS.md](./NOTIFICATIONS.md) | Telegram alerts, end to end |
|
|
450
|
+
| [UI_CREATION.md](./UI_CREATION.md) | building a UI against the API |
|
|
451
|
+
| [AGENTS.md](./AGENTS.md) | the architecture and the rules worth knowing before changing anything |
|
|
452
|
+
| [/openapi/ui](http://127.0.0.1:3999/openapi/ui) | the live API, on your own panel |
|
|
453
|
+
|
|
454
|
+
</details>
|
|
426
455
|
|
|
427
456
|
---
|
|
428
457
|
|