Clusters (DDI_CLUSTER), Kea HA pairs (DDI_KEA_HA), dynamic zones
(DDI_DYNAMIC_ZONES) and SSO (DDI_SSO) now need the matching Keygen
entitlement; the trial grants all of them. Only taking a feature into
use is blocked (403 license_feature): existing clusters, HA pairs,
dynamic zones and enabled SSO keep working and stay editable.
maxAgents is checked when creating an enrollment token and, under an
advisory lock, at enrollment; revoked agents do not count. GET /license
reports available_features and the agent usage. The UI shows the
features on the license page and disables locked controls, and
keygen-setup.sh creates the entitlements and assigns them to licenses
(ADR 0027).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dcdiag /test:dns needs the Windows DNS service and stays red with BIND;
list the checks that work instead, explain the _msdcs delegation message
and the DFSR state that blocks a second DC. Fix the script examples.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sets the DNS servers, optionally makes a DHCP address static, disables
the Windows DNS server, re-registers the locator records and waits until
they answer at the DDI DNS server. Works on Server Core (PowerShell 5.1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Netlogon registers A records below _msdcs (gc._msdcs.<domain>), which the
default check-names of primary zones refuses; dynamic zones now get
check-names ignore. Troubleshooting for dcdiag in ACTIVE-DIRECTORY.md, an
end-to-end check of such an update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its container ends without output in CI; the path is covered by the agent
role's integration test against real BIND and by the end-to-end test.
Recorded as open point 19 in the project log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The output of the failing dynamic update test did not show up in the CI
log; escape control characters and log each line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
named -g logs "running as: named ..." right at the start, so waiting for
"running" returned at once and nsupdate met a server that did not listen
yet. Wait for the line that ends in " running".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The attached output of docker start can end early; the failure of the
dynamic update test was cut off mid-line in CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DNS agent now maintains DDI's records in dynamic zones (ADR 0026):
updated zone and record hints, the system key is marked and cannot be
deleted (key_system), API types regenerated. Project log updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Starts named with a revision whose dynamic zones still hold the previous
initial files, applies dynamic-records.json with nsupdate -k as the DNS
agent will, and checks the records with dig, the per-record delete next
to a foreign record, and that unsigned updates from 127.0.0.1 are still
refused. The cluster check now covers the update key on the primary only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A static address in a second network with domain ad.e2e.test reaches the
existing dynamic zone on the primary and, after the transfer, on the
secondary; after deleting it the name is gone while the records added by
nsupdate earlier stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server creates the system key ddi-agent-update (hmac-sha256, sealed
like every key) with the first dynamic zone and at start, marks it in the
new column tsig_keys.system (migration 00007) and puts it into the
generator input (Source version 4). System keys are listed, rotatable and
not deletable (key_system); their name is reserved (key_exists). The
address check no longer reports dynamic_zone (ADR 0026).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revision 1 adds managed records to a dynamic zone by update; revision 2
removes and adds one while records of a third party (also at a managed
name) stay; a revision whose update key named does not accept rolls
back; a revision without managed records deletes only DDI's records.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revisions for targets serving dynamic zones as primary carry
ddi-suite-update.key and dynamic-records.json. They stay in the revision
directory. Validate checks both strictly (one key matching the records
file and defined in ddi-suite-keys.conf; zones must be dynamic primary
zones of the revision; names inside their zone; types A/AAAA/PTR/CNAME/
MX/TXT/SRV/NS with presentation data on one line). After the reload,
Activate deletes exactly the records the previous revision managed and
the new one does not, adds all new ones with nsupdate -k (new option
dns.nsupdate), retries while a zone is not loaded yet and fails the
activation otherwise, so that the core rolls back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DNS role needs to feed nsupdate scripts on stdin and to log its
dynamic updates. command.InputRunner adds RunInput (errors never quote the
input); roles implementing core.LogSetter get the core's logger.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Targets that serve a dynamic zone as primary get the DNS agents' update
key (Source version 4, UpdateKeyID) in the update ACLs and two more
artifacts: ddi-suite-update.key (secret) and dynamic-records.json with
the records DDI owns in each dynamic zone (ADR 0026). Older sources and
targets without dynamic primary zones render byte-identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent installed without token and enrolled with the command of the web
interface never ran as a service: the installer left the unit disabled and
the root-run enrollment left an identity the service user could not read.
The installer now always enables the agent unit (it waits for the
identity via ConditionPathExists), enroll as root hands the state over to
the service user and enables and starts the unit, and rerunning or
upgrading the installer repairs agents enrolled by hand. The web interface
shows the matching installer command next to the enroll command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the user's server the check reported "socket: permission denied" for
ping and asked only the server's own resolver. Ping now falls back to a
raw ICMP socket (the server unit gets CAP_NET_RAW, like /bin/ping); DNS
asks the DNS agents outside clusters when no name server addresses are
configured, gives every server its own timeout, and explains missing
records (dynamic zone, system resolver, not deployed yet).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Covers expanding a network and paging its IPs, Shift-click and from/to
selection, range assignment and freeing with dry run, bulk dry run and
apply with per-address problems, the check dialog, adopting the lease
host name, the random host name hint, the network form without pools and
live reloads of the IP list. Test calls expose their query string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lease name moves into the lease column, long descriptions are cut with a
tooltip, row actions get some air and the IP list on the network page
uses the available height.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Go resolver reads /etc/hosts first, so a hosts entry could make a
missing DNS record look fine. Checks against configured servers now use a
small DNS client (UDP, TCP on truncation, random query IDs).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New problems dynamic_is_gateway, dynamic_ipv6_unsupported and check_size;
range and type errors with their parameters; pool problems that no longer
exist removed. Bulk dry runs report effects per code with a count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Networks expand inline (and on the detail page) to every host address from
GET /networks/{id}/ips with paging, checkbox and Shift-click selection and
from/to ranges. Selections can be assigned as dynamic, static or reserved
or freed via POST /networks/{id}/range after a dry run, changed in bulk
(dry run, then apply) and checked with POST /addresses/check. The pool
editor is gone; DHCP ranges are shown as derived. Dynamic entries hint at
the random host name and offer to adopt the lease host name. Live events
also refresh the IP lists. API types regenerated (ADR 0025).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Left sidebar navigation in groups, slim license/pending strips, page
headers with counts and one primary action, help texts behind a small
"?" instead of paragraphs, compact tables and panels, restrained palette
with small radii in light and dark mode, terse texts in both locales.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ARCHITECTURE §3.2 describes the type table and the DHCP range made of
dynamic entries, API.md the IP list, range operations, bulk dry runs and
the address check with their error codes, OPERATIONS the upgrade to
migration 00006 and what the check needs (ping_group_range, DNS ports).
ADR 0023 points to ADR 0025 for the lease adoption.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Networks are created without pools; the e2e and installation tests assign
the old pool range with POST /networks/{id}/range and check that Kea gets
the pools around the reservation and that a dynamic entry resolves (A and
PTR). The lease step now expects the lease to update the existing dynamic
entry (MAC, lease_hostname) without renaming it. The benchmark assigns the
/16 DHCP test range at setup (65,521 dynamic entries, noted in the report);
the /21 data networks have no range any more.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0025 on the server:
- Migration 00006 turns the free addresses of every pool into dynamic
entries with random host names (dyn- and 8 characters), names unnamed
dynamic entries, moves colliding lease names to the new column
lease_hostname, extends the unique FQDN index to dynamic entries and
drops dhcp_pools. Down rebuilds pools from runs of dynamic entries.
- Networks no longer take pools; Network.pools is derived from the
dynamic entries, also for the generator input.
- Dynamic entries without host name get a random one (create, update,
CSV import, bulk type change, range assign), drawn again on an FQDN
collision.
- Leases only update existing entries (lease fields, lease_hostname, the
MAC of dynamic entries) and never create entries or change host names.
- GET /networks/{id}/ips, POST /networks/{id}/range (assign/clear, dry
run, one audit entry) and POST /addresses/check (unprivileged ICMP echo,
forward and reverse lookup at the zone's servers).
- Bulk changes: set_description, regenerate_hostnames and dry runs with
summarized effects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derive.BuildDNS creates A/AAAA and PTR records for dynamic entries only for
sources of version 3 or later, so that stored older sources render byte
for byte as before (rollbacks; TestM4Compatibility now covers versions 0
and 2). Dynamic entries in networks without domain produce no warning,
which would otherwise repeat for every address of a large DHCP range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0025: dynamic entries publish A/PTR records and form the DHCP range of
their network; pools are no longer part of the network. Dynamic entries
need DHCP, IPv4 and must not be the gateway. Ranges() merges addresses into
runs of consecutive addresses, utilization counts dynamic entries, the
address map loses the pool state, previews report entering and leaving the
DHCP range by type. Helpers for the IP list and range operations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0025 and the REST API: per-address DHCP instead of pools, DNS for
dynamic entries, all IPs of a network, range operations, bulk dry runs
and ping/DNS checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-row network and address searches limited imports to ~300 rows/s, so a
file at the row limit outlasted the write timeout. The import now loads
networks and the affected entries once and gets a 10-minute deadline. The
master key mode in ADR 0024 follows the server's check (0400).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A refresh that fails because the primary is not up yet is retried only
after the zone's retry interval, longer than the test waits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/RELEASE.md describes the release flow, the packages and how to
create the Ed25519 signing key, the Gitea secret and the installer's
public key. ADR 0024 records the implementation details; ARCHITECTURE
lists the retention options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release dry run builds and inspects the packages and proves signing
and verification with a throwaway key; the release workflow signs
SHA256SUMS when RELEASE_SIGNING_KEY is set and checks the signature with
the installer's public key. The shell job runs the brand check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
make packages (or make release PACKAGES=1) packages the binaries from the
release tarballs with the unit rendered for /usr/bin and the license
text, and rewrites SHA256SUMS over tarballs and packages. nfpm 2.48.0 is
installed pinned with checksum by scripts/ci/install-tools.sh, which can
now install single tools. Make targets lint-brand and sign-release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Implements the check announced in ADR 0018. The installer may define
the names once between '# --- names (ADR 0018) ---' and
'# --- end names ---'; missing files are skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/sign-release.sh signs with a key file or the PEM key from
RELEASE_SIGNING_KEY (private temporary file, never printed), verifies
with openssl pkeyutl -rawin and has a selftest with a throwaway key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
retention.audit_days and retention.revisions_keep (DDI_RETENTION_*) drive
a cleanup five minutes after the start and then daily. The latest
revision, revisions agents run, fall back to or target, and revisions
with pending deployments are never deleted; deployment rows stay. A
cleanup that deletes anything records retention.cleanup in the audit log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Validates and activates the key like PUT /api/v1/license (same service
call, audited as the system actor) and prints the license state without
the key. Exit codes: 2 bad input, 3 key rejected, 4 license server
unreachable or untrusted (nothing stored).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DHCP agent writes the HA credentials only as ddi-suite-ha-password and
ddi-suite-ha-user; other names are now rejected when the settings are
saved instead of when a revision is generated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kea 3.x rejects clear-text users and passwords in HTTP authentication
("use of clear text 'user' is NOT SECURE"). Control socket clients now
use user-file and password-file, HA peers basic-auth-user-file and
basic-auth-password-file. The new secret artifact ddi-suite-ha-user
sits next to the password file (HAConfig.UserFile, set by derive);
keagen requires the file names ddi-suite-ha-password and
ddi-suite-ha-user. The DHCP agent handles both credential files alike:
consistent references in one directory, validation copy pointing at
the revision's files, written before the configuration, restored on a
failed reload, removed when HA goes away, compared by the health check.
Integration test and e2e read the user from the file; ADR 0023 updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second BIND joins as secondary, a second Kea forms an HA pair. The test
checks zone transfers, records, updates by address and by TSIG key (and a
refused one), that revisions keep dynamic records, the HA state over the
authenticated control socket and the adoption of a lease as address.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kea's load-balancing mode needs non-overlapping pools per server: every
pool is split into a lower half for the primary (HA_primary) and an upper
half for the secondary (HA_secondary). The Kea integration test now covers
both HA modes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generator targets replace the temporary server shim. The keys file header
carries no revision number, so that diffs only show it when a key changed.
API test of cluster targets with the real generator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The development and end-to-end named.conf include the TSIG keys file, which starts empty, so named starts before the first revision.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>