Archived
Prepare KarlDNS production deployment
This commit is contained in:
1 parent
5a90bd9d87
commit
ce5b4e0ff2
101 files changed
+3364
-2018
No files matched your search
@@ -11,19 +11,19 @@ If you want a lightweight DuckDNS / Dynu / No-IP style service that you control
|
||||
## Why KarlDNS?
|
||||
|
||||
- **Self-hosted Dynamic DNS provider** for home labs, small ISPs, communities, and private infrastructure.
|
||||
- **FRITZ!Box compatible**: copy the generated `Update-URL`, `Domainnamen`, `Benutzername`, and `Kennwort` straight into the German FRITZ!Box DynDNS form.
|
||||
- **FRITZ!Box compatible**: copy the generated `Update-URL` and `Domainnamen` straight into the German FRITZ!Box DynDNS form.
|
||||
- **CNAME-friendly hostnames**: users can use the generated hostname directly, or point `home.example.com` to it with a CNAME.
|
||||
- **No registration required**: generated hostnames use private magic links, with no accounts or login flow.
|
||||
- **No registration required**: generated hostnames use private bearer links, with no accounts or login flow.
|
||||
- **CNAME alias support**: users create one KarlDNS hostname, then point `home.example.com` to it at their own DNS provider.
|
||||
- **Real DNS publishing** through **Cloudflare** or **RFC 2136** with A and AAAA support.
|
||||
- **Small-server friendly**: FastAPI, SQLite WAL, simple Docker deployment, and no heavy background platform.
|
||||
- **API first**: OpenAPI, Swagger UI, ReDoc, and JSON endpoints under `/api/v1`.
|
||||
- **API first**: small built-in API guide, OpenAPI JSON, and endpoints under `/api/v1`.
|
||||
|
||||
## Screenshots
|
||||
|
||||
### Create Router Credentials
|
||||
### Create A Hostname
|
||||
|
||||

|
||||

|
||||
|
||||
## Logo And Social Preview
|
||||
|
||||
@@ -31,12 +31,12 @@ KarlDNS ships web-ready brand assets so self-hosted instances and shared links l
|
||||
|
||||
| Asset | Purpose |
|
||||
| --- | --- |
|
||||
| `router_dyndns/assets/logo-nav.webp` | Small navigation logo. |
|
||||
| `router_dyndns/assets/logo.webp` | Optimized homepage logo. |
|
||||
| `router_dyndns/assets/logo-192.png` and `logo-512.png` | PWA, app icon, and manifest icons. |
|
||||
| `router_dyndns/assets/favicon.ico`, `favicon-16x16.png`, `favicon-32x32.png`, `apple-touch-icon.png` | Browser and mobile icons. |
|
||||
| `router_dyndns/assets/social-preview.png` | Open Graph and Twitter large-card preview for reliable crawlers. |
|
||||
| `router_dyndns/assets/social-preview.webp` | Optimized WebP social preview for modern clients. |
|
||||
| `karldns/assets/logo-nav.webp` | Small navigation logo. |
|
||||
| `karldns/assets/logo.webp` | Optimized homepage logo. |
|
||||
| `karldns/assets/logo-192.png` and `logo-512.png` | PWA, app icon, and manifest icons. |
|
||||
| `karldns/assets/favicon.ico`, `favicon-16x16.png`, `favicon-32x32.png`, `apple-touch-icon.png` | Browser and mobile icons. |
|
||||
| `karldns/assets/social-preview.png` | Open Graph and Twitter large-card preview for reliable crawlers. |
|
||||
| `karldns/assets/social-preview.webp` | Optimized WebP social preview for modern clients. |
|
||||
|
||||
## What It Does
|
||||
|
||||
@@ -44,10 +44,11 @@ KarlDNS lets you host your own managed DynDNS service:
|
||||
|
||||
1. A user opens your KarlDNS URL.
|
||||
2. They generate a random hostname like `a1b2c3d4.karldns.de`.
|
||||
3. KarlDNS shows the exact router settings for the DynDNS form.
|
||||
4. The router periodically calls the update URL with its current public IPv4/IPv6 address.
|
||||
5. KarlDNS publishes the matching DNS records through your configured DNS backend.
|
||||
6. If the user wants their own DNS name, they can create a CNAME to the generated hostname.
|
||||
3. KarlDNS shows the exact router settings and one-time secrets.
|
||||
4. The user confirms they saved the values, which registers the hostname.
|
||||
5. The router periodically calls the update URL with its current public IPv4/IPv6 address.
|
||||
6. KarlDNS publishes the matching DNS records through your configured DNS backend.
|
||||
7. If the user wants their own DNS name, they can create a CNAME to the generated hostname.
|
||||
|
||||
That means users always have a current hostname for VPN, remote access, self-hosted apps, home servers, NAS devices, and lab networks.
|
||||
|
||||
@@ -64,13 +65,13 @@ That means users always have a current hostname for VPN, remote access, self-hos
|
||||
|
||||
| Area | Support |
|
||||
| --- | --- |
|
||||
| Router update URLs | FRITZ!Box custom provider URL, `/u/<slug>`, and `/nic/update` compatibility |
|
||||
| Router update URLs | FRITZ!Box custom provider URL under `/api/v1/router/update/<secret>` |
|
||||
| DNS records | A and AAAA |
|
||||
| DNS providers | Cloudflare API and RFC 2136 dynamic DNS |
|
||||
| Generated hostnames | Random provider-owned names that can be used directly or as CNAME targets |
|
||||
| User-owned DNS names | CNAME only. KarlDNS publishes the generated provider-owned hostname; the user's DNS provider serves the alias |
|
||||
| Persistence | SQLite with WAL mode |
|
||||
| API | FastAPI, OpenAPI, Swagger UI, ReDoc |
|
||||
| API | FastAPI, small API docs page, OpenAPI JSON |
|
||||
| Auth model | No user accounts. Private bearer links for hostnames and dashboards |
|
||||
| Operations | Rate limiting, admin audit views, update logs, cleanup jobs, Docker, Caddy example |
|
||||
|
||||
@@ -100,12 +101,12 @@ Open:
|
||||
|
||||
- Web UI: `http://localhost:8080/`
|
||||
- Admin UI: `http://localhost:8080/admin`
|
||||
- Swagger UI: `http://localhost:8080/docs`
|
||||
- ReDoc: `http://localhost:8080/redoc`
|
||||
- API guide: `http://localhost:8080/docs`
|
||||
- OpenAPI JSON: `http://localhost:8080/openapi.json`
|
||||
|
||||
Admin auth uses HTTP Basic auth. The username can be any value; the password is `DDNS_ADMIN_PASSWORD`.
|
||||
|
||||
The public OpenAPI schema documents the developer API under `/api/v1`. Admin/operator endpoints are intentionally omitted from Swagger UI and ReDoc.
|
||||
The API guide documents the public endpoints plus the authenticated operator API. OpenAPI JSON remains available for tools.
|
||||
|
||||
## Production Setup
|
||||
|
||||
@@ -144,35 +145,23 @@ export DDNS_TTL=60
|
||||
|
||||
## FRITZ!Box DynDNS Setup
|
||||
|
||||
Create a hostname in the web UI. For the normal accountless flow, the generated `Update-URL` already contains the secret update slug. Put that URL into the router and use the generated hostname as `Domainnamen`.
|
||||
|
||||
KarlDNS also shows optional compatibility fields for routers that insist on separate credentials:
|
||||
Create a hostname in the web UI, copy the generated values, then click **Register hostname**. The generated `Update-URL` contains the secret update slug. Put that URL into the router and use the generated hostname as `Domainnamen`.
|
||||
|
||||
- `Update-URL`
|
||||
- `Domainnamen`
|
||||
- `CNAME target`
|
||||
- `Benutzername` (optional)
|
||||
- `Kennwort` (optional)
|
||||
- `Private dashboard`
|
||||
- `Register URL`
|
||||
|
||||
Copy the router password and full update URL when they are generated or rotated. They are intentionally not recoverable from the dashboard later.
|
||||
Copy the private URLs when they are generated or rotated. They are intentionally hidden by default on the dashboard to reduce shoulder-surfing risk.
|
||||
|
||||
The generated FRITZ!Box URL uses the native placeholders:
|
||||
|
||||
```text
|
||||
https://karldns.de/u/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
|
||||
https://karldns.de/api/v1/router/update/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
|
||||
```
|
||||
|
||||
FRITZ!Box replaces `<ipaddr>` and `<ip6addr>` with the current WAN IPv4/IPv6 address and sends an HTTP GET request to that URL whenever it detects an address change. KarlDNS returns DynDNS-style responses such as `good 203.0.113.10`, `nochg 203.0.113.10`, `badauth`, or `nohost`.
|
||||
|
||||
This URL is enough for routers that only expose a single update URL field and do not send separate credentials. KarlDNS also shows a `Full update URL` that embeds `hostname`, `username`, and `password` as query parameters for clients that require credential-style DynDNS URLs but do not expose separate credential fields. Treat both URLs as secrets.
|
||||
|
||||
The compatibility endpoint is also available:
|
||||
|
||||
```text
|
||||
https://karldns.de/nic/update?hostname=<generated-hostname>&myip=<ipaddr>&myipv6=<ip6addr>&username=<username>&password=<pass>
|
||||
```
|
||||
|
||||
Prefer `/u/<slug>` for managed service use because the router request cannot change the hostname.
|
||||
FRITZ!Box replaces `<ipaddr>` and `<ip6addr>` with the current WAN IPv4/IPv6 address and sends an HTTP GET request whenever it detects an address change. KarlDNS returns simple DynDNS-style responses such as `good 203.0.113.10`, `nochg 203.0.113.10`, `badip`, or `911 dns publish failed`.
|
||||
|
||||
## Manual Updates And Automation
|
||||
|
||||
@@ -180,24 +169,26 @@ Every generated hostname can be updated with curl, cron, a shell script, Home As
|
||||
|
||||
```bash
|
||||
# Explicit IPv4 update
|
||||
curl -sS 'https://karldns.de/u/<random-update-slug>?myip=203.0.113.10'
|
||||
curl -sS 'https://karldns.de/api/v1/router/update/<random-update-slug>?myip=203.0.113.10'
|
||||
|
||||
# Explicit IPv6 update
|
||||
curl -sS 'https://karldns.de/u/<random-update-slug>?myipv6=2001:db8::10'
|
||||
curl -sS 'https://karldns.de/api/v1/router/update/<random-update-slug>?myipv6=2001:db8::10'
|
||||
|
||||
# Use the request source IP
|
||||
curl -sS 'https://karldns.de/u/<random-update-slug>'
|
||||
```
|
||||
|
||||
The `/nic/update` compatibility endpoint also works for clients that expect hostname and credentials in the URL:
|
||||
|
||||
```bash
|
||||
curl -sS 'https://karldns.de/nic/update?hostname=a1b2c3d4.karldns.de&username=router&password=<password>&myip=203.0.113.10'
|
||||
curl -sS 'https://karldns.de/api/v1/router/update/<random-update-slug>'
|
||||
```
|
||||
|
||||
### Linux Cron Installer
|
||||
|
||||
For Linux servers, Proxmox hosts, NAS boxes, and small home-lab machines that are always on, KarlDNS ships a cron updater. It creates a hostname if you do not pass one, installs a tiny updater script, schedules it with cron, runs the first update, and prints the hostname plus private dashboard URL.
|
||||
For Linux servers, Proxmox hosts, NAS boxes, and small home-lab machines that are always on, KarlDNS ships a cron updater. It creates and registers a hostname if you do not pass one, installs a tiny updater script, schedules it with cron, runs the first update, and prints the hostname plus private dashboard URL.
|
||||
|
||||
The installer is POSIX `sh` and works on common Linux distributions including Debian, Ubuntu, Fedora, Arch, Alpine, and Proxmox hosts. It needs `curl` and cron. On Alpine, install those first if they are not present:
|
||||
|
||||
```bash
|
||||
apk add curl
|
||||
rc-service crond start
|
||||
rc-update add crond
|
||||
```
|
||||
|
||||
System-wide install:
|
||||
|
||||
@@ -211,17 +202,11 @@ The same script is hosted in GitHub and can be used directly:
|
||||
curl -fsSL https://raw.githubusercontent.com/StasonJatham/karldns.de/main/scripts/install-linux.sh | sudo sh
|
||||
```
|
||||
|
||||
User-level install without sudo:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://karldns.de/install.sh | sh -s -- --mode user
|
||||
```
|
||||
|
||||
Use an existing KarlDNS update URL:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://karldns.de/install.sh | sudo sh -s -- \
|
||||
--update-url 'https://karldns.de/u/<random-update-slug>' \
|
||||
--update-url 'https://karldns.de/api/v1/router/update/<random-update-slug>' \
|
||||
--hostname 'home.karldns.de'
|
||||
```
|
||||
|
||||
@@ -229,23 +214,29 @@ Common options:
|
||||
|
||||
```bash
|
||||
--every 5 # cron interval in minutes, default 5
|
||||
--mode user # install without root into ~/.local, ~/.config, and the user's crontab
|
||||
--ipv4 auto # auto, off, or a static IPv4
|
||||
--ipv6 auto # auto, off, or a static IPv6
|
||||
--no-cron # install files only
|
||||
--no-run # skip the first update
|
||||
```
|
||||
|
||||
The updater discovers the public address through `https://karldns.de/ip` and sends it to the private update URL. If explicit discovery fails, it falls back to calling the update URL without IP parameters so KarlDNS can use the request source address. Re-running the installer is safe: it replaces the managed cron block and keeps unrelated cron entries intact.
|
||||
The updater calls the private update URL. By default KarlDNS uses the request source address; explicit `--ipv4` or `--ipv6` values can be configured when needed. Re-running the installer is safe: it reuses an existing `updater.env`, refreshes the updater script, replaces only the managed cron block, and keeps unrelated cron entries intact.
|
||||
|
||||
Installed files in system mode:
|
||||
|
||||
```text
|
||||
/usr/local/bin/karldns-update
|
||||
/etc/karldns/updater.env
|
||||
/etc/cron.d/karldns-updater
|
||||
/var/cache/karldns/last-ip
|
||||
```
|
||||
|
||||
Cron is installed where the OS expects it:
|
||||
|
||||
- Debian/Ubuntu/Fedora/Arch style: `/etc/cron.d/karldns-updater`
|
||||
- Alpine/BusyBox style: managed block in `/etc/crontabs/root`
|
||||
- Fallback: managed block in root's `crontab`
|
||||
|
||||
Installed files in user mode:
|
||||
|
||||
```text
|
||||
@@ -254,7 +245,7 @@ Installed files in user mode:
|
||||
~/.cache/karldns/last-ip
|
||||
```
|
||||
|
||||
`updater.env` is written with mode `600` because it contains the private update URL and dashboard URL. The hostname itself is not secret. The update URL and dashboard URL are secrets.
|
||||
`updater.env` is written with mode `600` because it contains private URLs. The hostname itself is not secret. The update URL, registration URL, and dashboard URL are secrets.
|
||||
|
||||
Uninstall, keeping the private config by default:
|
||||
|
||||
@@ -262,7 +253,7 @@ Uninstall, keeping the private config by default:
|
||||
curl -fsSL https://karldns.de/uninstall.sh | sudo sh
|
||||
```
|
||||
|
||||
Remove the private config and last-IP cache too:
|
||||
Clean uninstall, including saved private URLs:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://karldns.de/uninstall.sh | sudo sh -s -- --delete-config
|
||||
@@ -282,31 +273,52 @@ To use your own domain, create a KarlDNS hostname first, then create a CNAME at
|
||||
home.example.com. CNAME a1b2c3d4.karldns.de.
|
||||
```
|
||||
|
||||
Put the KarlDNS `Update-URL`, `Domainnamen`, `Benutzername`, and `Kennwort` into the router. The router updates `a1b2c3d4.karldns.de`; `home.example.com` follows through the CNAME.
|
||||
Put the KarlDNS `Update-URL` and `Domainnamen` into the router. The router updates `a1b2c3d4.karldns.de`; `home.example.com` follows through the CNAME.
|
||||
|
||||
Use a subdomain such as `home.example.com`. Root/apex domains like `example.com` usually cannot be plain CNAME records unless your DNS provider supports CNAME flattening or ALIAS/ANAME records.
|
||||
|
||||
KarlDNS does not verify, claim, or publish records inside user-owned domains. Your DNS provider keeps serving `home.example.com`; KarlDNS only maintains the generated `*.karldns.de` target.
|
||||
|
||||
The generated hostname is not a secret. Treat it like any public DNS name. The update URL slug, router password, private dashboard URL, and full credentialed update URL are the secrets. Dashboard forms use CSRF tokens, and dashboard password unlock cookies last 24 hours by default unless the user chooses the 30-day stay-unlocked option.
|
||||
The generated hostname is not a secret. Treat it like any public DNS name. The update URL slug, registration URL, and private dashboard URL are the secrets. Dashboard forms use CSRF tokens, and dashboard password unlock cookies last 24 hours by default unless the user chooses the 30-day stay-unlocked option.
|
||||
|
||||
## HTTP API
|
||||
|
||||
The JSON API is available under `/api/v1`.
|
||||
The JSON API is available under `/api/v1`. The public API has three surfaces: admin data, user dashboard data with history, and router registration/update calls.
|
||||
|
||||
| Endpoint | Purpose |
|
||||
| --- | --- |
|
||||
| `POST /api/v1/hostnames/magic` | Create an anonymous provider-owned hostname. |
|
||||
| `GET /api/v1/management/{management_slug}` | Inspect a hostname by private management link. |
|
||||
| `DELETE /api/v1/management/{management_slug}` | Delete a hostname and its DNS records. |
|
||||
| `GET /api/v1/updates/{update_slug}` | JSON update endpoint for routers or automation. |
|
||||
| `POST /api/v1/hostnames` | Create an anonymous provider-owned hostname in pending state. |
|
||||
| `GET /api/v1/hostnames/{dashboard_secret}` | Inspect a hostname and recent update history by private dashboard secret. |
|
||||
| `POST /api/v1/hostnames/{dashboard_secret}/rotate-update-url` | Replace the private router update URL. |
|
||||
| `DELETE /api/v1/hostnames/{dashboard_secret}` | Delete a hostname and its DNS records. |
|
||||
| `GET or POST /api/v1/router/register/{dashboard_secret}` | Register a pending hostname after secrets are saved. |
|
||||
| `GET or POST /api/v1/router/update/{update_secret}` | Update a hostname from FRITZ!Box, another router, curl, or a cron script. |
|
||||
| `GET /api/v1/admin/dashboard` | Authenticated operator summary, metrics, cleanup state, and recent history. |
|
||||
| `GET /api/v1/admin/events` | Authenticated paginated update history. |
|
||||
| `GET /api/v1/admin/hostnames` | Authenticated paginated hostname inventory. |
|
||||
| `GET /api/v1/admin/records` | Authenticated paginated DNS record inventory. |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -sS https://karldns.de/api/v1/hostnames/magic \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{"username":"home-router"}'
|
||||
curl -sS -X POST https://karldns.de/api/v1/hostnames
|
||||
```
|
||||
|
||||
The create response includes `registration_url`, `update_url`, and `dashboard_url`. Save the returned secrets, then `POST` the registration URL before using the update URL. Registration publishes the initial DNS record from the request IP; scripts may also pass `myip` or `myipv6` on the registration URL. Pending hostnames that are never registered are cleaned up automatically.
|
||||
|
||||
Use the dashboard secret to rotate the update URL:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST https://karldns.de/api/v1/hostnames/<dashboard-secret>/rotate-update-url
|
||||
```
|
||||
|
||||
The response contains a replacement `update_url`. The old update URL stops working immediately.
|
||||
|
||||
Operator API calls use the same HTTP Basic auth password as the admin UI:
|
||||
|
||||
```bash
|
||||
curl -sS -u admin:<admin-password> https://karldns.de/api/v1/admin/dashboard
|
||||
curl -sS -u admin:<admin-password> 'https://karldns.de/api/v1/admin/events?limit=25'
|
||||
```
|
||||
|
||||
## Docker
|
||||
@@ -320,7 +332,7 @@ docker compose up -d --build
|
||||
|
||||
The example environment is intentionally short: admin password/path, dashboard cookie signing secret, public URL, managed hostname suffix, Cloudflare token, Cloudflare zone ID, and the SQLite path. The app creates the SQLite parent directory and all tables on first run. Re-running the container or starting the app twice is idempotent; schema creation uses `CREATE TABLE IF NOT EXISTS`, and the cleanup scheduler keeps a single durable task row.
|
||||
|
||||
The image and compose file default `DDNS_DATABASE` to `/data/ddns.sqlite3`, and the compose volume persists `/data`.
|
||||
The image uses the current Python 3.14 Alpine base image and installs production dependencies from `requirements.txt`. The compose file defaults `DDNS_DATABASE` to `/data/ddns.sqlite3`, and the compose volume persists `/data`.
|
||||
|
||||
The default container command runs one Uvicorn worker. Keep that deployment shape unless you have tested multi-worker update behavior for your traffic pattern; rate-limit counters and per-host update locks are process-local.
|
||||
|
||||
@@ -333,10 +345,11 @@ The default container command runs one Uvicorn worker. Keep that deployment shap
|
||||
- Use a long random value for `DDNS_ADMIN_PASSWORD`.
|
||||
- Use a separate long random value for `DDNS_DASHBOARD_COOKIE_SECRET` on public instances.
|
||||
- Change `DDNS_ADMIN_PATH` if you do not want the operator UI at `/admin`.
|
||||
- Set `DDNS_PUBLIC_BASE_URL` to the real public URL. KarlDNS uses it for generated links, code examples, API docs, canonical URLs, and the default trusted host.
|
||||
- Use a least-privilege DNS API token or TSIG key.
|
||||
- Set `DDNS_TRUSTED_HOSTS` only when the app should answer for hostnames beyond `DDNS_PUBLIC_BASE_URL`.
|
||||
- Configure `DDNS_TRUSTED_PROXY_IPS` only for reverse proxies that strip user-supplied forwarding headers.
|
||||
- Keep `/docs` and `/redoc` available for API users if you want a public developer surface. Admin routes are hidden from OpenAPI and still require Basic auth.
|
||||
- Keep `/docs` available if you want the small developer surface. Operator API routes are documented and require Basic auth; admin UI form routes stay hidden.
|
||||
- Leave cleanup enabled. The cleanup scheduler is stored in SQLite with a task row, next run time, status, last error, and a database lease so only one app process runs a due cleanup job.
|
||||
- Back up `/data/ddns.sqlite3`.
|
||||
- Run one Uvicorn worker with SQLite unless you have tested your deployment under load. Scheduled cleanup is lease-protected across workers, but rate-limit state and per-host update locks are process-local.
|
||||
@@ -349,11 +362,13 @@ Security and cleanup defaults:
|
||||
| `DDNS_RATE_LIMIT_PER_MINUTE` | `60` | Public update/API/form requests allowed per source IP and path. |
|
||||
| `DDNS_ADMIN_RATE_LIMIT_PER_MINUTE` | `20` | Admin/operator requests allowed per source IP and path. |
|
||||
| `DDNS_MAX_REQUEST_BODY_BYTES` | `16384` | Maximum accepted request body for form/API endpoints. |
|
||||
| `DDNS_ADMIN_PATH` | `/admin` | Private operator UI path. Admin routes are hidden from OpenAPI and require Basic auth. |
|
||||
| `DDNS_ADMIN_PATH` | `/admin` | Private operator UI path. Admin UI form routes stay hidden from OpenAPI and require Basic auth. |
|
||||
| `DDNS_PUBLIC_BASE_URL` | `http://localhost:8080` | Public URL used for generated links, public examples, API docs, canonical URLs, and the default trusted host. |
|
||||
| `DDNS_DASHBOARD_COOKIE_SECRET` | `DDNS_ADMIN_PASSWORD` fallback | HMAC signing secret for dashboard unlock cookies. Use a separate 32+ character random value in production. |
|
||||
| `DDNS_REDACTED_ACCESS_LOG` | `0` | Enable KarlDNS request logs with bearer paths and update query strings redacted. |
|
||||
| `DDNS_TRUSTED_HOSTS` | from `DDNS_PUBLIC_BASE_URL` | Host header allowlist. Add every public hostname that should serve the app. |
|
||||
| `DDNS_CLEANUP_UNUSED_ACCOUNT_HOURS` | `1080` | Delete generated hostnames that never received a router update after this many hours. |
|
||||
| `DDNS_CLEANUP_PENDING_ACCOUNT_HOURS` | `24` | Delete generated setup intents that were never registered after this many hours. |
|
||||
| `DDNS_CLEANUP_UNUSED_ACCOUNT_HOURS` | `1080` | Delete registered generated hostnames that never received a router update after this many hours. |
|
||||
| `DDNS_CLEANUP_EVENT_HISTORY_HOURS` | `4320` | Delete old raw update/audit events after this many hours. Rollups and recent events remain visible. |
|
||||
| `DDNS_CLEANUP_INTERVAL_SECONDS` | `3600` | Store the cleanup task cadence in SQLite and schedule the next cleanup run at this interval. |
|
||||
| `DDNS_DASHBOARD_UNLOCK_SECONDS` | `86400` | Default dashboard password unlock cookie lifetime. |
|
||||
@@ -375,22 +390,21 @@ Backend layout:
|
||||
|
||||
| Module | Purpose |
|
||||
| --- | --- |
|
||||
| `router_dyndns/app.py` | FastAPI app factory, lifespan, middleware and router registration. |
|
||||
| `router_dyndns/ddns.py` | Compatibility exports for existing imports and CLI users. |
|
||||
| `router_dyndns/routers/` | Module-level `APIRouter` files for API, public web, management, admin, assets, and DynDNS update routes. |
|
||||
| `router_dyndns/dependencies.py` | Shared FastAPI dependencies for app state, admin auth, update auth, and router update responses. |
|
||||
| `router_dyndns/schemas.py` | Pydantic request and response models used by the JSON API. |
|
||||
| `router_dyndns/web/` | HTML rendering, form parsing, and security-header/body-limit support code. |
|
||||
| `router_dyndns/ddns_service.py` | Domain logic for account creation and IP updates. |
|
||||
| `router_dyndns/store/` | SQLite persistence, rate limits, durable scheduled tasks, cleanup, and event history. |
|
||||
| `router_dyndns/ddns_store.py` | Compatibility export for `DdnsStore`. |
|
||||
| `router_dyndns/dns/` | Cloudflare and RFC 2136 DNS publishing. |
|
||||
| `router_dyndns/ddns_dns.py` | Compatibility exports for DNS provider helpers. |
|
||||
| `karldns/app.py` | FastAPI app factory, lifespan, middleware and router registration. |
|
||||
| `karldns/settings.py` | Pydantic settings loaded from environment variables. |
|
||||
| `karldns/models.py` | Shared domain models such as update results. |
|
||||
| `karldns/routers/` | Module-level `APIRouter` files for API, public web, management, admin, assets, and DynDNS update routes. |
|
||||
| `karldns/dependencies.py` | Shared FastAPI dependencies for app state, admin auth, update auth, and router update responses. |
|
||||
| `karldns/schemas.py` | Pydantic request and response models used by the JSON API. |
|
||||
| `karldns/web/` | Layout, reusable HTML components, scripts, CSS, form parsing, and security-header/body-limit support code. |
|
||||
| `karldns/services/ddns.py` | Domain logic for account creation and IP updates. |
|
||||
| `karldns/store/` | SQLite persistence, rate limits, durable scheduled tasks, cleanup, and event history. |
|
||||
| `karldns/dns/` | Cloudflare and RFC 2136 DNS publishing. |
|
||||
|
||||
```bash
|
||||
ruff check router_dyndns tests
|
||||
ruff check karldns tests
|
||||
pytest
|
||||
python -m compileall router_dyndns
|
||||
python -m compileall karldns
|
||||
```
|
||||
|
||||
## SEO Keywords
|
||||
|
||||
Reference in new issue
Block a user