Files
technitium-console/hub/README.md
T
DenozordecandClaude Opus 5.5 3f917c6bd2 Hub stage 4: its own image and pipeline
docker/Dockerfile.hub builds the console and ships it with the hub sources on
node:22-alpine, without node_modules, as a non-root user with a healthcheck.
.gitea/workflows/hub.yaml checks and pushes git.shx.one/denozord/technitium-hub
(latest from main, sha-*, X.Y.Z and X.Y from hub-v tags), independent of the console
pipeline, which now ignores hub-only changes. Compose example for hub.shnt.top and
hub/README.md, with the read-only permissions each server's token needs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:21:59 +07:00

2.8 KiB

Technitium hub

A small, dependency-free Node program that looks like ONE Technitium DNS Server to the console and answers from several real ones. It serves the console's static build and a read-only, Technitium-compatible /api/*:

  • node=<id> forwards the call to that server, with that server's token.
  • No node (or node=cluster) asks every server in parallel and merges the answers (statistics are summed, top lists and query logs are merged). If a server does not answer, the others are shown and the response carries hubWarnings.
  • Anything that would change data is refused with 403. The hub has its own sign-in.

The servers themselves are not changed: each only needs a read-only API token. Create it for a user whose group has View, and nothing else, on Dashboard, Logs, Apps (the logging apps Query Logs reads from) and Settings (the blocking state).

Configuration

Variable Default Meaning
HUB_SERVERS required Ordered id=url list, e.g. dns=https://dns.example.net,ndns=https://ndns.example.net. The first server is shown as the Primary node.
<ID>_TOKEN required API token of each server; the id is uppercased with non-alphanumerics as _ (dns gives DNS_TOKEN, my-dns gives MY_DNS_TOKEN).
HUB_USER, HUB_PASSWORD required The hub's own sign-in. It refuses to start without them.
HUB_PORT 8080 Listening port.
HUB_CACHE_SECONDS 15 How long successful answers of the servers are cached.
HUB_TIMEOUT_MS 8000 Per-request timeout towards a server.
HUB_DOMAIN hub Shown as the server's domain in the console.
HUB_STATIC ./dist The console's build to serve.

Endpoints

Public: status, user/login. After signing in: user/session/get, user/logout, user/checkForUpdate (always "no update") and these reads:

dashboard/stats/get, dashboard/stats/getTop, dashboard/metrics/json, apps/list, logs/query, logs/list, settings/get, admin/cluster/state (synthesized).

Everything else answers 403 The hub is read-only.

Run locally

npm run build
node dev/mock-api.mjs --port 5380 &
node dev/mock-api.mjs --port 5381 --scale 2 &
HUB_SERVERS=a=http://127.0.0.1:5380,b=http://127.0.0.1:5381 A_TOKEN=x B_TOKEN=y \
  HUB_USER=admin HUB_PASSWORD=admin HUB_PORT=5390 npm run hub

Then open http://localhost:5390. Tests: npx vitest run hub.

Docker

docker run --rm -p 8080:8080 \
  -e HUB_SERVERS=dns=https://dns.example.net,ndns=https://ndns.example.net \
  -e DNS_TOKEN=... -e NDNS_TOKEN=... -e HUB_USER=admin -e HUB_PASSWORD=... \
  git.shx.one/denozord/technitium-hub:latest

The image is built by docker/Dockerfile.hub and published by .gitea/workflows/hub.yaml (latest from main, sha-<7>, and X.Y.Z / X.Y from a hub-vX.Y.Z tag). A compose example with Traefik is in docs/compose.hub.example.yaml.