home-hosted 0.7.0 → 0.7.2
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 +31 -11
- package/README.md +30 -23
- package/dist/cli.js +4230 -2315
- package/dist/cli.js.map +1 -1
- package/docs/NOTIFICATIONS.md +6 -4
- package/docs/REVERSE_PROXY.md +161 -0
- package/docs/UI_CREATION.md +5 -2
- package/package.json +2 -1
- package/uis/stock/dist/assets/index-JGpABIqV.css +2 -0
- package/uis/stock/dist/assets/{index-Ocn66d5H.js → index-UtOMDd0A.js} +26 -26
- package/uis/stock/dist/index.html +2 -2
- package/uis/stock/dist/ui.json +3 -3
- package/uis/stock/dist/assets/index-S1nhkKEu.css +0 -2
package/AGENTS.md
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
`home-hosted` is a Node 24 / TypeScript harness for self-hosted servers: `up` starts a Hono/srvx
|
|
4
4
|
panel (default `127.0.0.1:3999`) that supervises the entries in each workspace's
|
|
5
5
|
`servers.config.json` and serves a UI. User docs: `README.md`, `docs/SERVERS.md` (entries and port
|
|
6
|
-
conflicts), `docs/NOTIFICATIONS.md`, `docs/DDNS.md`; UI authors:
|
|
6
|
+
conflicts), `docs/NOTIFICATIONS.md`, `docs/DDNS.md`, `docs/REVERSE_PROXY.md`; UI authors:
|
|
7
|
+
`docs/UI_CREATION.md`.
|
|
7
8
|
|
|
8
9
|
State lives only in `$HHOSTED_HOME/.hh` (default `~/.home-hosted/.hh`). Global files sit at its top
|
|
9
|
-
level: `settings.json` (listener, auth, TLS policy, host vitals, backups),
|
|
10
|
-
`.control-secrets.json` (0600: password hash, API token hash), `.tls/`,
|
|
11
|
-
(the panel console),
|
|
10
|
+
level: `settings.json` (listener, auth, TLS policy, host vitals, backups, reverse proxy),
|
|
11
|
+
`workspaces.json`, `.control-secrets.json` (0600: password hash, API token hash), `.tls/`,
|
|
12
|
+
`.backups/`, `.ui/`, `.logs/` (the panel console), `.proxy/` (the reverse-proxy engine, its generated
|
|
13
|
+
configuration and its nanny state), and `run.json` — the live daemon's pid/url/token, 0600. Every workspace owns
|
|
12
14
|
`.hh/<id>/`: `settings.json` (server defaults, log retention, notifications, DDNS), `servers.config.json`,
|
|
13
15
|
`.secrets.json` (0600: Telegram bot token and DDNS credentials sealed with AES-256-GCM under
|
|
14
16
|
`HHOSTED_DDNS_SECRET`), `.logs/`, and `.state/` (a persistent entry's nanny state plus its 0600 spawn
|
|
@@ -21,7 +23,8 @@ seed entry, or a path that names one.
|
|
|
21
23
|
```sh
|
|
22
24
|
pnpm run up|down|restart|status # detached; `down` asks /_hh/shutdown, signals are the fallback
|
|
23
25
|
pnpm run start # up --foreground (systemd, docker, a foreground shell)
|
|
24
|
-
pnpm dev # tsx-watch panel :
|
|
26
|
+
pnpm dev # tsx-watch panel :6000 + the stock UI's Vite :6001 (proxies /api), state in .dev-state/
|
|
27
|
+
pnpm dev --ui noc-console # the same, for another UI; --port/--ui-port override
|
|
25
28
|
pnpm run build # dist/cli.js + the stock UI (uis/stock/dist)
|
|
26
29
|
pnpm run build:uis # every UI under uis/, zipped into uis/dist/ (release assets)
|
|
27
30
|
pnpm run quickcheck # eslint + tsc + vue-tsc for every UI under uis/
|
|
@@ -36,6 +39,12 @@ pnpm run media # regenerate docs/media (mockups, both served
|
|
|
36
39
|
|
|
37
40
|
The published bin is `home-hosted`, with an `hh` alias: both names run the same CLI.
|
|
38
41
|
|
|
42
|
+
**Dev servers and test instances stay in the 6xxx range** — panel 6000, UI 6001, test-spawned panels
|
|
43
|
+
6100+ (`pnpm dev` takes `--ui`/`--port`/`--ui-port`, or `HHOSTED_DEV_PANEL_PORT` /
|
|
44
|
+
`HHOSTED_DEV_UI_PORT`). Never assume 3999 is free: an installed panel or another dev instance may hold
|
|
45
|
+
it, and the CLI's port preflight runs *before* its config guard, so a busy default port turns config
|
|
46
|
+
tests red for the wrong reason.
|
|
47
|
+
|
|
39
48
|
Releases are dispatched from `.github/workflows/release.yml` with a version (and a `dry-run` switch
|
|
40
49
|
that stops before pushing). It verifies the version, lints/types/tests, builds the CLI plus the stock
|
|
41
50
|
UI and every UI zip, lets changelogen write the changelog, bump `package.json`, commit and tag
|
|
@@ -102,12 +111,13 @@ a minor without one.
|
|
|
102
111
|
`identity` (which port holder is this entry's own successor: marker first, then resolved image +
|
|
103
112
|
argv), `nanny` (a persistent entry's spec/state files and the liveness rules for one),
|
|
104
113
|
`log-tail` (an offset reader that survives rotation and truncation), `ddns/` (one `DdnsProvider` per
|
|
105
|
-
registrar over one HTTP call each, plus `ip` and `zone` helpers),
|
|
106
|
-
|
|
114
|
+
registrar over one HTTP call each, plus `ip` and `zone` helpers), `proxy/` (the reverse-proxy
|
|
115
|
+
engines this build can drive: where the binary comes from and how to start it — the route model
|
|
116
|
+
stays the panel's), health-check, host, telegram, archive.
|
|
107
117
|
- `src/services/` — stateful orchestration: `panel` (one `WorkspaceRuntime` per workspace, workspace
|
|
108
118
|
CRUD, and the single aggregate state frame — every per-workspace service hangs off it), supervisor,
|
|
109
119
|
control-server, config-watch, state, auth + exposure, dependencies, history, log-buffer/log-files,
|
|
110
|
-
notifications, host-monitor, ddns, backups, tls, ui, plus `init` (the scaffold behind
|
|
120
|
+
notifications, host-monitor, ddns, proxy, backups, tls, ui, plus `init` (the scaffold behind
|
|
111
121
|
`home-hosted init`: a manifest, a `.gitignore`, and the prompts stay in the CLI), `nanny` (the
|
|
112
122
|
process a persistent entry runs under) and `log-relay` (the tailer that feeds its lines to the
|
|
113
123
|
supervisor). It names no server — the scaffold must stay as neutral as the supervisor.
|
|
@@ -139,9 +149,10 @@ a minor without one.
|
|
|
139
149
|
- `scripts/` — `build-uis.mjs` (build one UI, optionally zip it), `typecheck-uis.mjs`,
|
|
140
150
|
`capture-media.mjs` (the README's media), `check-release-version.mjs` and `release-notes.mjs`
|
|
141
151
|
(used by the release workflow), `dev.mjs` (`pnpm dev`).
|
|
142
|
-
- `docs/` — the topic docs (`SERVERS.md`, `NOTIFICATIONS.md`, `DDNS.md`, `
|
|
152
|
+
- `docs/` — the topic docs (`SERVERS.md`, `NOTIFICATIONS.md`, `DDNS.md`, `REVERSE_PROXY.md`,
|
|
153
|
+
`UI_CREATION.md`),
|
|
143
154
|
`mockups/*.html` (hand-written UI examples in four directions) and `media/*`: their screenshots plus
|
|
144
|
-
the README's `tour.gif`, all regenerated by `pnpm run media`. Only the
|
|
155
|
+
the README's `tour.gif`, all regenerated by `pnpm run media`. Only the five `.md` files are
|
|
145
156
|
published; the media is a build artifact.
|
|
146
157
|
|
|
147
158
|
## Conventions
|
|
@@ -286,6 +297,12 @@ either is a last resort, and never an accidental one.
|
|
|
286
297
|
`$HHOSTED_HOME/.hh/.ui` overrides the packaged UI at runtime.
|
|
287
298
|
- **A restore writes only where a config says** — archive allowlist, `origin` matching first,
|
|
288
299
|
symlinks never recreated.
|
|
300
|
+
- **The reverse proxy's engine is a process the panel owns, not a server entry.** Its generated
|
|
301
|
+
configuration is applied through the engine's admin API (atomic, never hand-edited), the engine runs
|
|
302
|
+
under the same nanny a `persistent: true` entry uses so a panel restart does not drop the socket,
|
|
303
|
+
and its admin endpoint stays panel-side — a unix socket in a `0700` directory, loopback plus the
|
|
304
|
+
engine's origin check on Windows. A route that points at the panel is exposure and obeys
|
|
305
|
+
`checkProxyExposure()`, the same bar as a non-loopback bind.
|
|
289
306
|
- **Cross-platform.** Linux/macOS/Windows: `/proc` vs `ps` vs Win32_Process, process groups vs
|
|
290
307
|
`taskkill /T`, graceful fallbacks, no shell utilities assumed.
|
|
291
308
|
- **Writes are atomic** (`writeFileAtomic`) and validated before commit.
|
|
@@ -338,6 +355,9 @@ either is a last resort, and never an accidental one.
|
|
|
338
355
|
file-only blocks on their own. The copy has to be **detached**, too: a shallow one leaves a nested
|
|
339
356
|
group (`health.http`) aliased to live state, so an edit to "Healthy below status" mutated the very
|
|
340
357
|
config the guard compared against and the next frame reset the whole editor (`cloneHealth`).
|
|
358
|
+
- **An absolutely positioned element inside a *static* scroll container resolves against the
|
|
359
|
+
page, not the scroller** — an `sr-only` label in a table row was enough to give the document a
|
|
360
|
+
second, empty scrollbar. The shell's `main` carries `relative` for that reason; keep it there.
|
|
341
361
|
- **A dialog's footer has to be a flex sibling of a scrolling body** (`uis/stock/src/components/ui/Modal.vue`):
|
|
342
362
|
the sheet is `flex flex-col` with `max-h-[88dvh]`, the body `min-h-0 flex-1 overflow-y-auto`. When
|
|
343
363
|
only the body carried a max-height, a tall form pushed its own save button below the clipped
|
|
@@ -393,5 +413,5 @@ either is a last resort, and never an accidental one.
|
|
|
393
413
|
## Publishing
|
|
394
414
|
|
|
395
415
|
`pnpm pack` runs `prepack` (a full build) and ships `bin/`, `dist/`, `uis/stock/dist`, `README.md`, `docs/DDNS.md`,
|
|
396
|
-
`docs/SERVERS.md`, `docs/NOTIFICATIONS.md`, `docs/UI_CREATION.md`, `AGENTS.md` and `LICENSE`. The bin falls back to tsx so `pnpm link` works before a
|
|
416
|
+
`docs/SERVERS.md`, `docs/NOTIFICATIONS.md`, `docs/REVERSE_PROXY.md`, `docs/UI_CREATION.md`, `AGENTS.md` and `LICENSE`. The bin falls back to tsx so `pnpm link` works before a
|
|
397
417
|
build; `vue`/`vue-router` are devDependencies because the UIs are prebuilt.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Point it at the things you run — a gateway, a media server, a bot, a database — and it starts
|
|
10
10
|
them, watches them, restarts what dies, and shows you one page of what is going on. Split them into
|
|
11
|
-
[workspaces](#-workspaces) when one panel holds more than one setup.
|
|
11
|
+
[workspaces](#-workspaces) when one panel holds more than one setup. And
|
|
12
12
|
[BYOU](#-bring-your-own-ui-byou), for a specialized UI that fits you exactly.
|
|
13
13
|
|
|
14
14
|
[](https://www.npmjs.com/package/home-hosted)
|
|
@@ -17,7 +17,7 @@ them, watches them, restarts what dies, and shows you one page of what is going
|
|
|
17
17
|
[](./LICENSE)
|
|
18
18
|
[](https://nodejs.org)
|
|
19
19
|
|
|
20
|
-
[🚀 Quick start](#-quick-start) · [🗂 Workspaces](#-workspaces) · [🧩 Servers](./docs/SERVERS.md) · [🤖 Agents & API](#-agents-scripts-and-tools) · [✨ Features](#-features) · [🛠 CLI](#-cli) · [🔔 Notifications](./docs/NOTIFICATIONS.md) · [🎨 BYOU](#-bring-your-own-ui-byou)
|
|
20
|
+
[🚀 Quick start](#-quick-start) · [🗂 Workspaces](#-workspaces) · [🧩 Servers](./docs/SERVERS.md) · [🤖 Agents & API](#-agents-scripts-and-tools) · [✨ Features](#-features) · [🛠 CLI](#-cli) · [🔔 Notifications](./docs/NOTIFICATIONS.md) · [🔀 Reverse proxy](./docs/REVERSE_PROXY.md) · [🎨 BYOU](#-bring-your-own-ui-byou)
|
|
21
21
|
|
|
22
22
|
</div>
|
|
23
23
|
|
|
@@ -213,19 +213,26 @@ default workspace.
|
|
|
213
213
|
<details>
|
|
214
214
|
<summary><b>🌐 The endpoints worth knowing</b></summary>
|
|
215
215
|
|
|
216
|
-
| | |
|
|
217
|
-
| --- | --- |
|
|
218
|
-
| `GET /api/state` | the full snapshot: global settings, every workspace with its servers, host vitals |
|
|
219
|
-
| `GET`/`POST /api/workspaces`, `PATCH`/`DELETE /api/workspaces/:id` | the registry: list, create, rename, remove |
|
|
220
|
-
| `GET /api/events` | SSE: the live state, plus logs (`?logs=0`, `?serverId=…`; a `server` payload carries its `workspaceId`) |
|
|
221
|
-
| `GET /api/servers
|
|
222
|
-
| `
|
|
223
|
-
| `POST /api/servers/:id/free-port` |
|
|
224
|
-
| `
|
|
225
|
-
| `GET /api/logs
|
|
226
|
-
| `PUT
|
|
227
|
-
| `
|
|
228
|
-
| `GET /api/
|
|
216
|
+
| | | `?workspace` |
|
|
217
|
+
| --- | --- | --- |
|
|
218
|
+
| `GET /api/state` | the full snapshot: global settings, every workspace with its servers, host vitals | ❌ |
|
|
219
|
+
| `GET`/`POST /api/workspaces`, `PATCH`/`DELETE /api/workspaces/:id` | the registry: list, create, rename, remove | ❌ |
|
|
220
|
+
| `GET /api/events` | SSE: the live state, plus logs (`?logs=0`, `?serverId=…`; a `server` payload carries its `workspaceId`) | ❌ |
|
|
221
|
+
| `GET`/`POST /api/servers`, `POST /api/servers/{start,stop}-all` | that workspace's entries, and its lifecycle for all of them | ✅ |
|
|
222
|
+
| `GET`/`PATCH`/`DELETE /api/servers/:id` | one entry | ✅ |
|
|
223
|
+
| `POST /api/servers/:id/{start,stop,restart}`, `POST /api/servers/:id/free-port` | one entry's lifecycle, and freeing its port | ✅ |
|
|
224
|
+
| `GET /api/servers/:id/logs`, `GET /api/servers/:id/stream` | that entry's buffered lines, and its SSE | ✅ |
|
|
225
|
+
| `GET /api/logs` | persisted history and files | ✅ |
|
|
226
|
+
| `GET`/`PUT /api/ddns`, `/api/ddns/credentials/:id`, `/api/ddns/check` | that workspace's Dynamic DNS policy, credentials and a check | ✅ |
|
|
227
|
+
| `PUT`/`DELETE /api/notifications/token`, `POST /api/notifications/{test,detect-chats}` | that workspace's bot credential and a test send | ✅ |
|
|
228
|
+
| `GET`/`PATCH /api/settings/workspace` | server defaults, logs, notifications | ✅ |
|
|
229
|
+
| `GET`/`PATCH /api/settings` | panel-wide: listener, authentication, TLS, host vitals, backups | ❌ |
|
|
230
|
+
| `GET`/`POST /api/backups`, restore | archives | ❌ |
|
|
231
|
+
| `GET /healthz` | no session needed — the one an external monitor wants (its per-server detail needs a credential) | ❌ |
|
|
232
|
+
| `GET /api/metrics` | Prometheus text (needs a token or session, like every `/api` route) | ❌ |
|
|
233
|
+
|
|
234
|
+
✅ takes `?workspace=<id>`; ❌ is panel-wide. Omitting it means the panel's default workspace — never
|
|
235
|
+
another one.
|
|
229
236
|
|
|
230
237
|
`GET /openapi/spec.json` describes all of it, `/openapi/ui` is the browsable version, and every error
|
|
231
238
|
comes back as one envelope (`{ message, code, detail }`) with a stable `code` a tool can branch on.
|
|
@@ -265,6 +272,7 @@ can be told what to be: *"Help me build a UI for home-hosted: nostalgic game the
|
|
|
265
272
|
| 🤖 **Token API** | Scripts and agents drive it with `Authorization: Bearer` — no browser, no session. [↑](#-agents-scripts-and-tools) |
|
|
266
273
|
| 🔔 **Notifications** | Telegram on crash, unhealthy, forced restart, recovery, host thresholds and DNS changes — [setup here](./docs/NOTIFICATIONS.md). |
|
|
267
274
|
| 🌐 **Dynamic DNS** | Keep hostnames pointed at your public IP — Cloudflare, Namecheap, Spaceship, Porkbun, GoDaddy, Gandi and more. [DDNS.md](./docs/DDNS.md) |
|
|
275
|
+
| 🔀 **Reverse proxy** | One engine in front of everything, with automatic HTTPS: `git.example.com` → a Gitea entry, `media.example.com` → a service on another box, `panel.example.com` → the panel. Caddy, installed and supervised by the panel. [REVERSE_PROXY.md](./docs/REVERSE_PROXY.md) |
|
|
268
276
|
| 💾 **Backups** | Two levels: pick global settings, global secrets, TLS or a whole workspace at the top, then the pieces inside it (settings, servers, secrets, data directories) — plain `.zip`, or AES-256 with a password, restored per path. |
|
|
269
277
|
| 🎨 **BYOU — Bring Your Own UI** | Upload a static build, `ui-update` to follow its releases, `ui-revert` to go back. [UI_CREATION.md](./docs/UI_CREATION.md) |
|
|
270
278
|
| 🔐 **Security** | Cookie sessions, API tokens, scrypt hashes, per-IP lockout, optional TLS, and a refusal to expose itself without a password. |
|
|
@@ -496,10 +504,7 @@ Two ship in this repo: `uis/stock`, and `uis/noc-console` for TUI and shortcuts
|
|
|
496
504
|
attaches both as `home-hosted-ui-<name>.zip`. Yours can be anything that compiles to static files —
|
|
497
505
|
the server never cares what built it.
|
|
498
506
|
|
|
499
|
-
<sub>Install a UI from the CLI: `home-hosted ui-switch`
|
|
500
|
-
built for this release. An **official** UI keeps itself paired with the panel: upgrade the panel and
|
|
501
|
-
the next `up` re-installs the matching asset. Someone else's UI declares its own `repo`/`asset` in
|
|
502
|
-
`ui.json`, and `home-hosted ui-update` offers its newer releases to pick from — `--old` for older ones.</sub>
|
|
507
|
+
<sub>Install a UI from the CLI: `home-hosted ui-switch` `--asset noc-console` selects an UI directly, no flags interactively show official assets. Official UIs's version auto-sync when you update `home-hosted`. For unofficial UIs, `home-hosted ui-update` offers its newer releases to pick from — or `--old` for older (author must set up `ui.json` and GH releases).</sub>
|
|
503
508
|
|
|
504
509
|
<details>
|
|
505
510
|
<summary><b>🤖 Or have an agent build the UI you actually want</b></summary>
|
|
@@ -605,9 +610,10 @@ scripts/ builds, typechecks, media capture, release helpers
|
|
|
605
610
|
test/ the vitest suite
|
|
606
611
|
```
|
|
607
612
|
|
|
608
|
-
`pnpm dev` runs the panel with `tsx watch` plus
|
|
609
|
-
|
|
610
|
-
|
|
613
|
+
`pnpm dev` runs the panel with `tsx watch` plus one UI's dev server on the 6xxx range — panel `6000`,
|
|
614
|
+
UI `6001` (`pnpm dev --ui noc-console`), state in `.dev-state/` — so a dev instance never fights an
|
|
615
|
+
installed panel's 3999. `pnpm build` produces `dist/` and `uis/stock/dist/`; `pnpm quickcheck` is lint
|
|
616
|
+
plus types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
|
|
611
617
|
|
|
612
618
|
<details>
|
|
613
619
|
<summary><b>📚 Which doc do I need?</b></summary>
|
|
@@ -616,6 +622,7 @@ types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
|
|
|
616
622
|
| --- | --- |
|
|
617
623
|
| declare a server: every field, placeholders, port conflicts | [SERVERS.md](./docs/SERVERS.md) |
|
|
618
624
|
| get Telegram alerts working end to end | [NOTIFICATIONS.md](./docs/NOTIFICATIONS.md) |
|
|
625
|
+
| expose the stack behind one hostname, with HTTPS | [REVERSE_PROXY.md](./docs/REVERSE_PROXY.md) |
|
|
619
626
|
| build a UI against the API | [UI_CREATION.md](./docs/UI_CREATION.md) |
|
|
620
627
|
| change the internals: architecture and the rules | [AGENTS.md](./AGENTS.md) |
|
|
621
628
|
| poke the live API on your own panel | [/openapi/ui](http://127.0.0.1:3999/openapi/ui) |
|
|
@@ -627,7 +634,7 @@ types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
|
|
|
627
634
|
<details>
|
|
628
635
|
<summary><b>🔗 Interesting resources</b></summary>
|
|
629
636
|
|
|
630
|
-
- [dsh-home-hosted](https://github.com/NamesMT/dsh-home-hosted) — home-hosted servers management with boot autostart from [DeepSeek Harness](https://github.com/deepseek-ai/dsh)
|
|
637
|
+
- [dsh-home-hosted](https://github.com/NamesMT/dsh-home-hosted) — home-hosted servers management with boot autostart from inside [DeepSeek Harness](https://github.com/deepseek-ai/dsh)
|
|
631
638
|
|
|
632
639
|
<sub><i>+ PR to add yours</i></sub>
|
|
633
640
|
|