Documentation · Releases · v.G.1.4
Iris
Iris — v.G.1.4
One server, many hostnames. Each container picks its own.
The pre-v.G.1.4 model pinned exactly one DNS hostname per managed server — every chainweb container on that box shared it. v.G.1.4 unpins that. A managed server can now carry as many hostnames as the operator wants, mixing manual A-records and DuckDNS-backed dynamic hostnames in any combination, and each chainweb container at install time picks which hostname it uses for its --p2p-hostname advertisement and TLS subject. Existing single-DNS operators do nothing — the upgrade backfills their existing hostname into the new table marked PRIMARY, and the chainweb container's FK is auto-pointed at it. DuckDNS systemd-timer continuity, certificate issuance via DNS-01-DuckDNS, and parent-walk inheritance for Tunnelees + self-containers are all preserved end-to-end.
What landed
Multiple hostnames per server
The Server DNS card on a non-Tunnelee, non-self-container node becomes a list view. The operator can attach any number of hostnames to a server — manual A-records and DuckDNS-backed dynamic hostnames in any mix. Adding a second hostname does not remove the first.
One row per hostname is shown with its kind badge (DuckDNS auto-updated or Manual A-record) and the per-row actions: Set primary, Remove, and (for DuckDNS rows) the inline DuckDnsInstallPanel the previous version already shipped — now scoped per-row instead of per-node.
Gold PRIMARY pill chip
Exactly one row at a time carries the PRIMARY pill chip — gold on gold-tinted background, aligned right of the hostname. The primary row is what the InstallWizard pre-selects for new chainweb containers, and what the cluster listing surfaces as the "server hostname" column. Clicking Set primary on another row promotes it and demotes the old primary in a single transaction; the chip moves with it.
Per-container DNS picker at install time
InstallWizard Step-6 (Identity) replaces its free-text --p2p-hostname input with a <select> picker populated from the canonical parent server’s hostname list. Each option shows the hostname plus its kind badge; the row marked PRIMARY is pre-selected. The operator can override the default and pick a non-primary hostname for the new container.
An info row underneath the picker spells out the cert path so the operator sees it at install time: “cert will be issued for <hostname> via HTTP-01 / DNS-01-DuckDNS”. The picked hostname becomes the cert subject (CN) and the value passed into --p2p-hostname; both are threaded through body.p2pHostname on the install POST.
DuckDNS continuity guarantee
Operators who configured a DuckDNS hostname before the multi-DNS upgrade keep their setup working unchanged. No operator action required. The systemd refresh timer at /etc/ah-duckdns/ continues to run, the subdomain continues to update, and certificate issuance continues to use DNS-01 via DuckDNS. The backfill migration converts the existing nodes.dns_hostname + nodes.duckdns_subdomain + nodes.duckdns_token_id triple into a single row in the new node_dns_hostnamestable, marked PRIMARY, with the same vault-sealed token. Each chainweb container’s new chainweb_dns_hostname_id FK is auto-pointed at that backfilled row.
Inheritance preserved for Tunnelees + self-containers
Only standalone-master / Tunneler / self-container-parent rows own hostname lists. Tunnelees and self-containers leave their own list empty and READ the canonical parent’s list — the same parent-walk semantics the v.G.0.2 (Prometheus) era introduced, just over a list now instead of a single column. On a child node’s Server DNS card, rows render read-only with an (inherited from <parent label>) annotation and a deep-link up to the parent. The InstallWizard Step-6 picker on a child container shows the parent’s hostname list as the source.
Destructive migration drops legacy single-DNS columns
Migration 075_drop_legacy_dns_columns.sql drops the five legacy single-DNS columns from the nodes table: dns_hostname, dns_hostname_set_at, dns_hostname_set_by, duckdns_subdomain, duckdns_token_id. This is the second of a deliberate two-migration split: Migration A (074) added the node_dns_hostnames table and the chainweb_dns_hostname_id FK column on nodes, and backfilled both non-destructively. The read-path switch in Phase 2 routed every consumer through the new table. Migration B (075) is the cleanup. Pre-v.G.1.4 database dumps are not forward-compatible without re-running the migration chain in order — the legacy columns simply do not exist in the post-075 schema. The legacy singular endpoint /api/admin/nodes/[id]/dns-hostname is retired in the same release; the plural collection endpoints under /dns-hostnames are the only DNS write surface from v.G.1.4 forward.
High-level architecture
New node_dns_hostnames table
A 1:N child of nodes, modelled after the existing host_drives + firewall_rules precedents already in the schema. Each row owns one hostname on one node with these columns:
id— TEXT PKnode_id— FK intonodes(id)ON DELETE CASCADEhostname— the FQDNkind— CHECK-constrained enum, one of'manual-a-record'or'duckdns'(extensible to'cloudflare'/'route53'without further schema churn)is_primary— INT 0/1, the resolver’s default pickduckdns_subdomain+duckdns_token_id— populated whenkind='duckdns'; the token id points into the existing libsodium-sealed vault- actor + timestamp columns (
created_at,created_by,updated_at) for the audit trail UNIQUE(node_id, hostname)and indexes onnode_idand(node_id, is_primary)
Plus a parent-side FK column on nodes: chainweb_dns_hostname_id REFERENCES node_dns_hostnames(id)— the per-container pointer that says “this chainweb container picked that hostname.” Mirrors the existing nodes.data_drive_id precedent from the host-drives migration.
FK chain for cert + identity resolution
Cert issuance and --p2p-hostname emission both follow the same chain at install time: chainweb_dns_hostname_id → node_dns_hostnames.duckdns_token_id → unseal(). The install handler resolves the operator-picked row, threads its hostname into both flags['p2p-hostname'] and the certbot job’s domain payload field, and for DuckDNS-kind rows additionally unseals the vault token for DNS-01 challenge issuance. Each chainweb container gets its own cert subject — two containers picking the same hostname each get their own independent cert, preserving backup/restore symmetry.
Audit trail — three new actions
The audit log gains three new actions alongside the existing node.dns_hostname_change and node.duckdns_install:
node.dns_hostname_add— actor + node id + hostname + kindnode.dns_hostname_remove— same fields plus the deletion targetnode.dns_hostname_set_primary— actor + node id + the promoted hostname id (and the demoted one in the detail)
All three are info severity, permanent retention. The legacy actions stay registered for back-compat but stop firing once every reader has switched to the new table.
Operator notes
- No operator action required for the upgrade. Migration A (074) backfills your existing single-DNS hostname into the new table marked PRIMARY, and Migration B (075) drops the legacy columns once every reader has switched. Your existing chainweb containers continue running with their existing hostname unchanged.
- Removing the last hostname requires a confirmation step. A server with zero hostnames cannot host new chainweb containers — the install path requires a hostname. The UI surfaces a modal before allowing the last-hostname remove.
- Removing a hostname currently in use is refused. If any chainweb container’s
chainweb_dns_hostname_idstill references the hostname, the remove returns 409 with a deep-link list of the referencing containers. Either repoint those containers via the InstallWizard first (which writes a new install body with the new hostname id), or remove the containers, then remove the hostname. - Pre-v.G.1.4 database dumps need the migration chain. The 074 + 075 migration pair must run in order. Restoring an older dump bypasses the backfill and the column drops will fail. Run
npm run migrateafter a restore to bring the schema forward. - UFW / firewall is orthogonal. The Cerberus firewall rules from v.G.1.1 bind to ports + protocols, never to hostnames. Adding or removing a hostname does not touch the firewall ruleset; the
stoa-tunnel300-port envelope is independent of how many hostnames live on the server.
Out of scope (deliberately)
- Cert reuse via bind-mount when two containers share a hostname — deferred. Each container gets its own cert in its own data dir; this preserves backup/restore symmetry with no operator-visible downside.
- Cloudflare / Route53 provider integrations — the
kindenum reserves slots for them but no implementation lands in v.G.1.4. Manual A-records and DuckDNS are the two supported paths today. - Public-DNS A-record management. The hub declares which hostnames a managed server claims; the operator handles the actual A-record provisioning at their DNS provider out-of-band. DuckDNS is the one exception (the hub owns the dynamic update via the systemd timer), and that existed pre-v.G.1.4 too.
Further reading
- §6 The Stoic System — DNS hostnames — the operator-facing explainer for the multi-DNS model in the public hub docs.
- Per-version changelog (admin-only) — the full per-letter-suffix patch history within the Genesis-era release line, including the v.G.1.4 section.
- ← Back to all releases