home-hosted 0.8.3 → 0.9.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 CHANGED
@@ -10,8 +10,8 @@ level: `settings.json` (listener, auth, TLS policy, host vitals, backups, revers
10
10
  `.ui/`, `.logs/` (the panel console), `.proxy/` (the engine, its generated config and nanny state), and
11
11
  `run.json` (the live daemon's pid/url/token, 0600). Every workspace owns `.hh/<id>/`: `settings.json`
12
12
  (server defaults, log retention, notifications, DDNS), `servers.config.json`, `.secrets.json` (0600:
13
- Telegram token, plus DDNS credentials sealed with AES-256-GCM under `HHOSTED_DDNS_SECRET` — only DDNS
14
- is sealed, because it is the one thing replayed to a third party), `.logs/` (per server, `<id>.log`
13
+ Telegram bot tokens, one per destination, plus DDNS credentials sealed with AES-256-GCM under
14
+ `HHOSTED_DDNS_SECRET` — only DDNS is sealed, because it is the one thing replayed to a third party), `.logs/` (per server, `<id>.log`
15
15
  plus `<id>.log.1` when it rotates), and `.state/` (nanny state, its 0600 spawn spec until read,
16
16
  `ddns.json`). A home still on the 0.6.x layout is **refused** by `ensureLayout()`, which names the file
17
17
  and tells the person to install the latest 0.7.x; 0.8.0 does not relocate it. **The package ships no
@@ -39,7 +39,7 @@ Where knowledge lives, and when to reach for each:
39
39
  | [`.agentDocs/COMPATIBILITY.md`](.agentDocs/COMPATIBILITY.md) | config and UI compatibility, migrations |
40
40
  | [`.agentDocs/RELEASING.md`](.agentDocs/RELEASING.md) | how a release is cut, and which version to dispatch |
41
41
  | [`docs/SERVERS.md`](docs/SERVERS.md) | entries, port conflicts, persistent entries |
42
- | [`docs/NOTIFICATIONS.md`](docs/NOTIFICATIONS.md) | Telegram events |
42
+ | [`docs/NOTIFICATIONS.md`](docs/NOTIFICATIONS.md) | notification destinations: Telegram, webhooks, shared defaults |
43
43
  | [`docs/DDNS.md`](docs/DDNS.md) | dynamic-DNS providers and hostnames |
44
44
  | [`docs/REVERSE_PROXY.md`](docs/REVERSE_PROXY.md) | the Caddy engine, routes, certificates |
45
45
  | [`docs/UI_CREATION.md`](docs/UI_CREATION.md) | the contract for a new UI |
@@ -56,6 +56,8 @@ pnpm run start # up --foreground (systemd, docker, a foregro
56
56
  pnpm exec tsx src/cli.ts logs # the panel's own console output (--lines, --follow, --json)
57
57
  pnpm dev # tsx-watch panel :6000 + the stock UI's Vite :6001, state in .dev-state/
58
58
  pnpm dev --ui noc-console # the same, for another UI; --port/--ui-port override
59
+ pnpm dev --home /tmp/hh-dev # state elsewhere; HHOSTED_HOME is IGNORED here, and the real
60
+ # ~/.home-hosted is refused. Dev password: `dev`
59
61
  pnpm run build # dist/cli.js + the stock UI (uis/stock/dist)
60
62
  pnpm run build:uis # every UI under uis/, zipped into uis/dist/ (release assets)
61
63
  pnpm run build:locales # i18n/sheets/*.csv -> the git-ignored i18n/generated/*.json (also
@@ -69,7 +71,8 @@ pnpm run set-password # non-interactive through HHOSTED_PASSWORD
69
71
  pnpm run set-token # --generate prints a new API token once
70
72
  pnpm run migrate # bring the config up to this release's schema
71
73
  pnpm exec tsx src/cli.ts init # scaffold a project (interactive; --yes for defaults)
72
- pnpm run media # regenerate docs/media (mockups, both served UIs, tour.gif)
74
+ pnpm run media # regenerate docs/media; needs `pnpm exec playwright install chromium`
75
+ # once (browser only; `gif` alone does not)
73
76
  ```
74
77
 
75
78
  The published bin is `home-hosted`, with an `hh` alias: both names run the same CLI.
@@ -128,7 +131,8 @@ These prevent defects if broken. The *why* for the non-obvious ones is in
128
131
  **refuses** a commit that changed a shipped UI without raising it (a UI's `test/` and its own
129
132
  `README.md` are exempt — neither is in the built asset).
130
133
  `src/shared/**` is a UI build input, so a runtime contract needs the bump too — the hook does not
131
- inspect that commit.
134
+ inspect that commit. **`tag` and `unix` are build provenance**, stamped into `dist/ui.json` by
135
+ `build-uis.mjs`; never committed to `public/ui.json`.
132
136
  - **Paths.** `dataRoot` is state, under `dataRoot/.hh`; `projectDir` is the base for relative entry
133
137
  paths. `{…}` and `${ENV}` expand in config — the full list is the table in
134
138
  [`docs/SERVERS.md`](docs/SERVERS.md), which is what to keep in step. No package-relative state.
package/README.md CHANGED
@@ -277,7 +277,7 @@ can be told what to be: *"Help me build a UI for home-hosted: nostalgic game the
277
277
  | 📈 **Resources** | CPU and RSS of the whole process tree, with an optional memory ceiling that triggers a restart. |
278
278
  | 🌡️ **Host vitals** | Load, memory, swap, disk and CPU temperature, with thresholds that notify once and again on recovery. |
279
279
  | 🤖 **Token API** | Scripts and agents drive it with `Authorization: Bearer` — no browser, no session. [↑](#-agents-scripts-and-tools) |
280
- | 🔔 **Notifications** | Telegram on crash, unhealthy, forced restart, recovery, host thresholds and DNS changes — [setup here](./docs/NOTIFICATIONS.md). |
280
+ | 🔔 **Notifications** | One list of **destinations** — Telegram chats and webhooks (ntfy, Discord, Slack, your own endpoint) — on crash, unhealthy, forced restart, recovery, host thresholds and DNS changes. The event toggles and throttle are set once, and each destination may inherit them or carry its own. [Setup here](./docs/NOTIFICATIONS.md). |
281
281
  | 🌐 **Dynamic DNS** | Keep hostnames pointed at your public IP — Cloudflare, Namecheap, Spaceship, Porkbun, GoDaddy, Gandi and more. [DDNS.md](./docs/DDNS.md) |
282
282
  | 🔀 **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) |
283
283
  | 💾 **Backups** | Global settings, TLS, workspaces, and inside each one its settings, servers, secrets and declared data — plain `.zip`, or AES-256 with a password. Each secret is picked separately, and a restore can be customized. |
@@ -467,10 +467,15 @@ Everything binds `127.0.0.1` until you say otherwise.
467
467
 
468
468
  ## 🔔 Notifications
469
469
 
470
- Telegram, when something happens while you are not looking: a server that gave up restarting, a
470
+ A message when something happens while you are not looking: a server that gave up restarting, a
471
471
  failing health check, a forced restart, a recovery, a host threshold (disk, memory, swap, load,
472
- temperature), or a dynamic DNS change. Opt-in, rate-limited per server *and* reason, and the bot token
473
- stays in the secrets file. **Two minutes of setup: [NOTIFICATIONS.md](./docs/NOTIFICATIONS.md).**
472
+ temperature), or a dynamic DNS change.
473
+
474
+ Every alert goes to a list of **destinations** — a Telegram chat, a webhook, or several of each. The
475
+ event toggles and the throttle are set **once**, and each destination either inherits them or carries
476
+ its own. Rate-limited per destination, server *and* reason. Bot tokens and webhook credentials stay in
477
+ the secrets file, never in a config. **Two minutes of setup:
478
+ [NOTIFICATIONS.md](./docs/NOTIFICATIONS.md).**
474
479
 
475
480
  ---
476
481
 
@@ -495,6 +500,12 @@ ordinary `.zip`, or WinZip AES-256 with a password, restored per path. Known bui
495
500
  dependency directories (`node_modules`, `dist`, `.next`, framework caches) are skipped per entry;
496
501
  `backupIgnoreGenerated: false` captures them anyway.
497
502
 
503
+ **Automatic backups** write one archive on a schedule you set — the page asks in hours; the config
504
+ stores `intervalMs` — with a retention of its own (`Keep automatic`). The two counts are independent: `Keep` bounds the archives you take by hand,
505
+ and a scheduled pass never prunes one of yours to make room for its own. Both live in the same
506
+ directory, and an automatic archive is named `auto-…` so you can tell them apart — a name you drop
507
+ in by hand with that prefix is treated as one, so do not use it.
508
+
498
509
  <details>
499
510
  <summary><b>🔑 Restoring a secret</b></summary>
500
511
 
@@ -690,7 +701,7 @@ plus types; `pnpm test` is vitest; `pnpm run media` regenerates the GIF above.
690
701
  | if you want to… | read |
691
702
  | --- | --- |
692
703
  | declare a server: every field, placeholders, port conflicts | [SERVERS.md](./docs/SERVERS.md) |
693
- | get Telegram alerts working end to end | [NOTIFICATIONS.md](./docs/NOTIFICATIONS.md) |
704
+ | get Telegram or webhook alerts working end to end | [NOTIFICATIONS.md](./docs/NOTIFICATIONS.md) |
694
705
  | expose the stack behind one hostname, with HTTPS | [REVERSE_PROXY.md](./docs/REVERSE_PROXY.md) |
695
706
  | build a UI against the API | [UI_CREATION.md](./docs/UI_CREATION.md) |
696
707
  | change the internals: architecture and the rules | [AGENTS.md](./AGENTS.md) |