home-hosted 0.7.1 → 0.7.3

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 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: `docs/UI_CREATION.md`.
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), `workspaces.json`,
10
- `.control-secrets.json` (0600: password hash, API token hash), `.tls/`, `.backups/`, `.ui/`, `.logs/`
11
- (the panel console), and `run.json` — the live daemon's pid/url/token, 0600. Every workspace owns
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
@@ -109,12 +111,13 @@ a minor without one.
109
111
  `identity` (which port holder is this entry's own successor: marker first, then resolved image +
110
112
  argv), `nanny` (a persistent entry's spec/state files and the liveness rules for one),
111
113
  `log-tail` (an offset reader that survives rotation and truncation), `ddns/` (one `DdnsProvider` per
112
- registrar over one HTTP call each, plus `ip` and `zone` helpers), health-check, host, telegram,
113
- archive.
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.
114
117
  - `src/services/` — stateful orchestration: `panel` (one `WorkspaceRuntime` per workspace, workspace
115
118
  CRUD, and the single aggregate state frame — every per-workspace service hangs off it), supervisor,
116
119
  control-server, config-watch, state, auth + exposure, dependencies, history, log-buffer/log-files,
117
- 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
118
121
  `home-hosted init`: a manifest, a `.gitignore`, and the prompts stay in the CLI), `nanny` (the
119
122
  process a persistent entry runs under) and `log-relay` (the tailer that feeds its lines to the
120
123
  supervisor). It names no server — the scaffold must stay as neutral as the supervisor.
@@ -132,23 +135,28 @@ a minor without one.
132
135
  `repo`/`tag`/`asset` (which release carries it, for `ui-update`) and `unix` (when it was built).
133
136
  `UiService` carries those fields into the installed `$HHOSTED_HOME/.ui/ui.json` unchanged and adds
134
137
  `uploadedAt`/`files` of its own.
135
- - **Bumping a UI ships a new asset, so its `ui.json` is part of the change.** Any commit that alters
136
- a UI under `uis/<name>/` bumps that `ui.json`'s `version` and sets `unix` to the commit's own epoch
137
- seconds: patch for a fix, minor for a feature, and **major only for a rewrite or a restyle**. The
138
- number is what tells a person how big the change is — `ui-update` prints it and holds it against
139
- the release it is offering. `tag` is what pairs an official UI with its panel, and it is **stamped
140
- by `build-uis.mjs`**, not trusted from the source file: a UI zip is built before its release is cut,
141
- so the committed value is always a release behind the asset it ends up inside. The release workflow
142
- passes `HHOSTED_UI_TAG`; anywhere else the build stamps `v<package.json version>`. Do not hand-edit
143
- `tag` to chase a release — a stale one makes `ui-update` re-install the same UI on every boot.
138
+ - **Bumping a UI ships a new asset, so its `ui.json` is part of the change — and it is bumped once,
139
+ when the commit is made, not on every edit.** A commit that alters what a UI *ships*
140
+ (`uis/<name>/src`, `public`, its build inputs — not `test/`, which is not in the asset) raises that
141
+ `ui.json`'s `version`: patch for a fix, minor for a feature, and **major only for a rewrite or a
142
+ restyle**. The number is what tells a person how big the change is — `ui-update` prints it and holds
143
+ it against the release it is offering. `unix` is that commit's own epoch, never a leftover from
144
+ whenever the file was edited: `scripts/stamp-uis.mjs` runs from the pre-commit hook, stamps it, and
145
+ refuses a commit that changed a shipped UI without raising its version. `tag` is what pairs an
146
+ official UI with its panel, and it is **stamped by `build-uis.mjs`**, not trusted from the source
147
+ file: a UI zip is built before its release is cut, so the committed value is always a release behind
148
+ the asset it ends up inside. The release workflow passes `HHOSTED_UI_TAG`; anywhere else the build
149
+ stamps `v<package.json version>`. Do not hand-edit `tag` to chase a release — a stale one makes
150
+ `ui-update` re-install the same UI on every boot.
144
151
  - `bin/home-hosted.mjs` — the published bin: `dist/cli.js`, or `src/cli.ts` through tsx when the
145
152
  build is missing (a linked checkout).
146
153
  - `scripts/` — `build-uis.mjs` (build one UI, optionally zip it), `typecheck-uis.mjs`,
147
154
  `capture-media.mjs` (the README's media), `check-release-version.mjs` and `release-notes.mjs`
148
155
  (used by the release workflow), `dev.mjs` (`pnpm dev`).
149
- - `docs/` — the topic docs (`SERVERS.md`, `NOTIFICATIONS.md`, `DDNS.md`, `UI_CREATION.md`),
156
+ - `docs/` — the topic docs (`SERVERS.md`, `NOTIFICATIONS.md`, `DDNS.md`, `REVERSE_PROXY.md`,
157
+ `UI_CREATION.md`),
150
158
  `mockups/*.html` (hand-written UI examples in four directions) and `media/*`: their screenshots plus
151
- the README's `tour.gif`, all regenerated by `pnpm run media`. Only the four `.md` files are
159
+ the README's `tour.gif`, all regenerated by `pnpm run media`. Only the five `.md` files are
152
160
  published; the media is a build artifact.
153
161
 
154
162
  ## Conventions
@@ -293,6 +301,12 @@ either is a last resort, and never an accidental one.
293
301
  `$HHOSTED_HOME/.hh/.ui` overrides the packaged UI at runtime.
294
302
  - **A restore writes only where a config says** — archive allowlist, `origin` matching first,
295
303
  symlinks never recreated.
304
+ - **The reverse proxy's engine is a process the panel owns, not a server entry.** Its generated
305
+ configuration is applied through the engine's admin API (atomic, never hand-edited), the engine runs
306
+ under the same nanny a `persistent: true` entry uses so a panel restart does not drop the socket,
307
+ and its admin endpoint stays panel-side — a unix socket in a `0700` directory, loopback plus the
308
+ engine's origin check on Windows. A route that points at the panel is exposure and obeys
309
+ `checkProxyExposure()`, the same bar as a non-loopback bind.
296
310
  - **Cross-platform.** Linux/macOS/Windows: `/proc` vs `ps` vs Win32_Process, process groups vs
297
311
  `taskkill /T`, graceful fallbacks, no shell utilities assumed.
298
312
  - **Writes are atomic** (`writeFileAtomic`) and validated before commit.
@@ -345,6 +359,9 @@ either is a last resort, and never an accidental one.
345
359
  file-only blocks on their own. The copy has to be **detached**, too: a shallow one leaves a nested
346
360
  group (`health.http`) aliased to live state, so an edit to "Healthy below status" mutated the very
347
361
  config the guard compared against and the next frame reset the whole editor (`cloneHealth`).
362
+ - **An absolutely positioned element inside a *static* scroll container resolves against the
363
+ page, not the scroller** — an `sr-only` label in a table row was enough to give the document a
364
+ second, empty scrollbar. The shell's `main` carries `relative` for that reason; keep it there.
348
365
  - **A dialog's footer has to be a flex sibling of a scrolling body** (`uis/stock/src/components/ui/Modal.vue`):
349
366
  the sheet is `flex flex-col` with `max-h-[88dvh]`, the body `min-h-0 flex-1 overflow-y-auto`. When
350
367
  only the body carried a max-height, a tall form pushed its own save button below the clipped
@@ -400,5 +417,5 @@ either is a last resort, and never an accidental one.
400
417
  ## Publishing
401
418
 
402
419
  `pnpm pack` runs `prepack` (a full build) and ships `bin/`, `dist/`, `uis/stock/dist`, `README.md`, `docs/DDNS.md`,
403
- `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
420
+ `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
404
421
  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. Or
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
  [![npm](https://img.shields.io/npm/v/home-hosted.svg)](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](https://img.shields.io/npm/l/home-hosted.svg)](./LICENSE)
18
18
  [![Node](https://img.shields.io/node/v/home-hosted.svg)](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/:id/stream` | SSE: one server's state and logs (`?workspace=…`) |
222
- | `POST /api/servers/:id/{start,stop,restart}` | lifecycle (workspace-scoped) |
223
- | `POST /api/servers/:id/free-port` | ask whatever holds that server's port to stop |
224
- | `PATCH /api/servers/:id`, `PATCH /api/settings`, `PATCH /api/settings/workspace` | edit configuration |
225
- | `GET /api/logs`, `/api/backups` | history and archives |
226
- | `PUT`/`DELETE /api/notifications/token`, `POST /api/notifications/{test,detect-chats}` | the bot credential, a test send |
227
- | `GET /healthz` | no session needed — the one an external monitor wants (its per-server detail needs a credential) |
228
- | `GET /api/metrics` | Prometheus text (needs a token or session, like every `/api` route) |
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` — with no flags it fetches the official asset
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>
@@ -617,6 +622,7 @@ plus types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
617
622
  | --- | --- |
618
623
  | declare a server: every field, placeholders, port conflicts | [SERVERS.md](./docs/SERVERS.md) |
619
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) |
620
626
  | build a UI against the API | [UI_CREATION.md](./docs/UI_CREATION.md) |
621
627
  | change the internals: architecture and the rules | [AGENTS.md](./AGENTS.md) |
622
628
  | poke the live API on your own panel | [/openapi/ui](http://127.0.0.1:3999/openapi/ui) |
@@ -628,7 +634,7 @@ plus types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
628
634
  <details>
629
635
  <summary><b>🔗 Interesting resources</b></summary>
630
636
 
631
- - [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)
632
638
 
633
639
  <sub><i>+ PR to add yours</i></sub>
634
640