home-hosted 0.6.5 → 0.6.7

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/docs/DDNS.md ADDED
@@ -0,0 +1,80 @@
1
+ # Dynamic DNS
2
+
3
+ Keep a list of hostnames pointed at this machine's public IP. The panel checks the address on an
4
+ interval and calls a provider only when it actually changed — restart-safe, because the last
5
+ confirmed address per hostname is kept in `$HHOSTED_HOME/.state/ddns.json`.
6
+
7
+ **Settings → Dynamic DNS** is the whole setup: add an account, paste its credentials, add hostnames.
8
+ Nothing else is required, and provider credentials never enter `servers.config.json`.
9
+
10
+ ```json
11
+ {
12
+ "ddns": {
13
+ "enabled": true,
14
+ "intervalMs": 300000,
15
+ "ipv4": { "enabled": true, "url": "" },
16
+ "ipv6": { "enabled": false, "url": "" },
17
+ "ttl": 1,
18
+ "proxied": false,
19
+ "notify": true,
20
+ "accounts": [
21
+ { "id": "cf", "provider": "cloudflare", "label": "Home zone" }
22
+ ],
23
+ "domains": [
24
+ { "host": "home.example.com", "account": "cf", "types": ["A"] },
25
+ { "host": "vpn.example.com", "account": "cf", "types": ["A", "AAAA"], "proxied": true }
26
+ ]
27
+ }
28
+ }
29
+ ```
30
+
31
+ Credentials live in `.control-secrets.json` (0600), keyed by the account `id`. Header fields:
32
+
33
+ | Field | Default | Notes |
34
+ | --- | --- | --- |
35
+ | `enabled` | `false` | Nothing runs until this is on. |
36
+ | `intervalMs` | `300000` | How often the public address is re-checked. |
37
+ | `ipv4` / `ipv6` | on / off | Which families to detect. A family must be on for its record types. |
38
+ | `ipv4.url` / `ipv6.url` | `""` | Override the detector with one endpoint. |
39
+ | `ttl` | `1` | Default record TTL; `1` is automatic where supported. |
40
+ | `proxied` | `false` | Default for providers with a CDN flag (Cloudflare). |
41
+ | `notify` | `true` | Tell Telegram about a change, and about a failure. |
42
+ | `accounts[]` | — | `id` (keys the credentials), `provider`, optional `label`. |
43
+ | `domains[]` | — | `host`, `account`, `types` (`A`/`AAAA`, default `A`), optional `ttl`, `proxied`, `zone`, `enabled`. |
44
+
45
+ Per-hostname `zone` pins the registered domain — only needed where a provider has to be told the apex
46
+ and the guess is wrong (`home.example.co.uk` is handled; an unusual suffix may not be).
47
+
48
+ ## Providers
49
+
50
+ | Provider | Record types | Credentials | Notes |
51
+ | --- | --- | --- | --- |
52
+ | **Cloudflare** | A, AAAA | API token (`Zone → DNS → Edit`); or legacy email + global key | Zones and record ids are discovered, then cached. Updates use `PATCH`, so comments survive. |
53
+ | **Namecheap** | A | Dynamic DNS password | Router-style Dynamic DNS, not the XML API. The password is under Advanced DNS → Dynamic DNS — not the account password. IPv4 only. |
54
+ | **Spaceship** | A, AAAA | API key + secret | No DDNS endpoint; uses the DNS API's whole-record-set upsert. |
55
+ | **Porkbun** | A, AAAA | API key + secret key | Read-then-upsert by name and type. |
56
+ | **GoDaddy** | A, AAAA | API key + secret | Optional `baseUrl` points at the OTE test environment. GoDaddy has gated this API by account before; a 403 means the account is not eligible. |
57
+ | **Gandi** | A, AAAA | Personal access token (`pat_…`) | LiveDNS RRset update. |
58
+ | **DigitalOcean** | A, AAAA | API token | No upsert-by-name; lists records, then updates the one it found. |
59
+ | **deSEC** | A, AAAA | API token | `update.dedyn.io`, HTTP Basic with the hostname. |
60
+ | **DuckDNS** | A, AAAA | Account token | One token covers every subdomain. |
61
+ | **No-IP** | A, AAAA | Email + password | Sends a recognised `User-Agent`; override it if yours is registered. |
62
+ | **Dynu** | A, AAAA | Username + password | `password` may be the SHA-256 hash. |
63
+ | **Hurricane Electric** | A, AAAA | Per-host DDNS key | `dyn.dns.he.net`. |
64
+ | **FreeDNS (afraid.org)** | A, AAAA | The per-record "Direct URL" | Paste the whole URL or just its hash. |
65
+
66
+ Not supported: **Route 53** (AWS SigV4 signing, a much larger dependency), **DreamHost** (no public
67
+ DNS API), and the Namecheap XML API (a full zone rewrite per update).
68
+
69
+ ## Notifications
70
+
71
+ Telegram carries one message per pass that changed a record, and one for a pass that failed. Turn it
72
+ off under Settings → Notifications → *Dynamic DNS*, or with `ddns.notify: false`.
73
+
74
+ ## Adding a provider
75
+
76
+ `src/providers/ddns/<name>.ts` exports one `DdnsProvider`: its credential fields, the families it can
77
+ manage, and an `update()` that makes one HTTP call and returns `{ ok, changed, message }`. Register it
78
+ in `src/providers/ddns/index.ts`; the settings page, the credentials form and validation follow from
79
+ that metadata. Put the reasoning for `changed: false` in the provider — it is what makes the panel
80
+ skip a write when a record is already current.
@@ -47,8 +47,11 @@ curl -X POST http://127.0.0.1:3999/api/notifications/test \
47
47
  | a server was force-restarted (health timeout) | `onForcedRestart` | ✅ |
48
48
  | a server recovered | `onRecovered` | ⬜ |
49
49
  | host thresholds breached, and recovered | `onHost` | ✅ |
50
+ | a dynamic DNS record changed, or a pass failed | `onDdns` | ✅ |
50
51
 
51
52
  Host thresholds themselves — disk, memory, swap, load, temperature — are **Settings → Host**.
53
+ Dynamic DNS carries one message per pass that changed a record, and one per failed pass — see
54
+ [DDNS.md](./DDNS.md).
52
55
 
53
56
  ## Quiet periods
54
57
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "home-hosted",
3
3
  "type": "module",
4
- "version": "0.6.5",
4
+ "version": "0.6.7",
5
5
  "packageManager": "pnpm@12.5.1",
6
6
  "description": "Self-hosted control panel that keeps your home-server processes alive: supervision, restarts, port conflicts, logs — and a UI you can replace.",
7
7
  "author": "NamesMT <dangquoctrung123@gmail.com>",
@@ -55,6 +55,7 @@
55
55
  "README.md",
56
56
  "bin",
57
57
  "dist",
58
+ "docs/DDNS.md",
58
59
  "docs/NOTIFICATIONS.md",
59
60
  "docs/SERVERS.md",
60
61
  "docs/UI_CREATION.md",