22 Commits
Author SHA1 Message Date
sir_melonchenandClaude Opus 5.5 cd69ff3aaf test(agent): dynamic records against real BIND
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>
2026-10-17 05:49:35 +02:00
sir_melonchenandClaude Opus 5.5 b461fedab3 feat(agent): DNS role applies managed records of dynamic zones (ADR 0026)
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>
2026-10-17 05:47:33 +02:00
sir_melonchenandClaude Opus 5.5 3e957d7b33 feat(agent): command runner with stdin and optional role logger
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>
2026-10-17 05:47:32 +02:00
sir_melonchenandClaude Opus 5.5 2555028956 fix: agents enrolled by hand run as systemd services
ci / Protobuf (push) Successful in 12s
ci / Web (lint, test, audit, build) (push) Successful in 2m44s
ci / Shell and licence headers (push) Successful in 21s
ci / Go (lint, test, vuln) (push) Successful in 6m22s
ci / Release build (dry run) (push) Successful in 3m51s
ci / Integration (real BIND and Kea) (push) Successful in 2m33s
ci / Development environment (compose) (push) Successful in 17s
ci / End-to-end (server, agents, BIND, Kea) (push) Successful in 4m14s
install / Installation (debian:12) (push) Successful in 4m54s
install / Installation (debian:13) (push) Successful in 5m6s
install / Installation (ubuntu:22.04) (push) Successful in 5m16s
install / Installation (rocky:9) (push) Successful in 7m28s
install / Installation (ubuntu:24.04) (push) Successful in 8m58s
release / release (push) Successful in 4m11s
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>
2026-10-17 02:44:03 +02:00
sir_melonchenandClaude Opus 5.5 9a6c3f5159 fix(kea): read the HA user from a file as Kea 3.x requires
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>
2026-10-14 20:28:18 +02:00
sir_melonchenandClaude Opus 5.5 06c3f3f7f6 test(agent): compare the state directory independently of the platform
ci / Protobuf (push) Successful in 8s
ci / Web (lint, test, audit, build) (push) Successful in 1m24s
ci / Shell and licence headers (push) Successful in 9s
ci / Go (lint, test, vuln) (push) Successful in 3m43s
ci / Release build (dry run) (push) Successful in 2m30s
ci / Integration (real BIND and Kea) (push) Failing after 2m2s
ci / Development environment (compose) (push) Successful in 13s
ci / End-to-end (server, agents, BIND, Kea) (push) Failing after 3m50s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:07:44 +02:00
sir_melonchenandClaude Opus 5.5 8c7f7e8bfc test(agent/dns): integration with keys file and a waiting secondary zone
named.conf includes ddi-suite-keys.conf; a revision without keys file
gets one without keys, and a revision with TSIG keys and a secondary
zone whose primary is unreachable deploys while the zone waits for its
transfer. The secondary directory gets the owner and mode of zone_dir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:37:58 +02:00
sir_melonchenandClaude Opus 5.5 5c9f2baa0a feat(agent/dhcp): install the Kea HA password file
A DHCP revision may carry ddi-suite-ha-password next to
kea-dhcp4.conf. The role writes it to the one path the configuration
references (control socket clients and HA peers must agree, the file
name must be ddi-suite-ha-password, the directory must exist) before
the configuration, restores it with the configuration when the reload
fails and removes the previous revision's file when HA goes away.
kea-dhcp4 -t checks a temporary copy that refers to the revision's
password file, and the health check notices a changed password file
without showing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:36:11 +02:00
sir_melonchenandClaude Opus 5.5 70643f8146 feat(agent/dns): keys file, secondary zones and their health
The DNS role installs ddi-suite-keys.conf (a keys file without keys for
revisions from before M5, so that named.conf can always include it),
includes it before ddi-suite.conf in the validation named.conf, accepts
secondary zone files in <zone_dir>/secondary/ and creates that
directory with the mode and owner of zone_dir without ever touching its
content. The health check queries secondary zones too; a zone still
waiting for its first transfer is reported but does not fail the
deployment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:30:40 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 9a4046e3e2 feat(agent): push deployments by polling, apply each deployment once
- GetDesiredStateResponse.deployment_id set: the desired revision is
  applied as a push with that ID, also in manual mode (ADR 0022).
- The reports of the last 20 push deployments are kept in state.json.
  A resent push with a handled deployment ID is not applied again; its
  report is delivered again unless it is still queued.
- A push already queued with the same deployment ID is not queued twice.
- Reports the server refuses for good (InvalidArgument, NotFound,
  AlreadyExists, FailedPrecondition, OutOfRange) are dropped instead of
  blocking the report queue.
- Tests for polling pushes, resent pushes, mode changes via a new
  Welcome, persistence and bounds of handled deployments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:07:35 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 d3e5807a09 test: retry image pulls, executable e2e scripts, fix truncation test
ci / Protobuf (push) Successful in 18s
ci / Web (lint, test, audit, build) (push) Successful in 1m16s
ci / Shell and licence headers (push) Successful in 20s
ci / Release build (dry run) (push) Successful in 2m9s
ci / Go (lint, test, vuln) (push) Successful in 4m19s
ci / End-to-end (server, agents, BIND, Kea) (push) Successful in 1m49s
ci / Development environment (compose) (push) Successful in 12s
ci / Integration (real BIND and Kea) (push) Successful in 5m12s
Docker Hub timed out on the shared runner; pulls now retry and fall back
to a local image. The e2e scripts were committed without the executable
bit. The truncation test checked letters that the marker itself contains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:37:53 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 56347ec5ff test: end-to-end test of the agent path, fix first CI findings
ci / Web (lint, test, audit, build) (push) Successful in 1m1s
ci / Shell and licence headers (push) Successful in 7s
ci / Go (lint, test, vuln) (push) Failing after 2m40s
ci / Protobuf (push) Successful in 6s
ci / Release build (dry run) (push) Successful in 2m27s
ci / End-to-end (server, agents, BIND, Kea) (push) Failing after 1m26s
ci / Development environment (compose) (push) Successful in 13s
ci / Integration (real BIND and Kea) (push) Failing after 1m58s
test/e2e runs server, PostgreSQL, BIND and Kea with both agents in
containers (make test-e2e, CI job End-to-end): enrollment with the
command from the API, revision 1 and 2 applied and served by BIND and
Kea, unchanged data creates no revision, revoking disconnects the agent
while BIND keeps serving.

Fixes from the first CI run: quote a spec description containing a
comma, exempt the agent lock-out routes in the route protection test,
whole-second certificate lifetimes, a push replacing queued pulls, the
truncation marker containing the letter checked, and curl|grep -q under
pipefail in the server smoke test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:29:58 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 56892631be feat: wire BIND and Kea roles into the agents, allow revoking agents in read-only mode
ci / Protobuf (push) Successful in 2m18s
ci / Go (lint, test, vuln) (push) Failing after 3m11s
ci / Shell and licence headers (push) Successful in 8s
ci / Web (lint, test, audit, build) (push) Successful in 1m14s
ci / Integration (real BIND and Kea) (push) Successful in 1m42s
ci / Release build (dry run) (push) Failing after 2m24s
ci / Development environment (compose) (push) Successful in 38s
Revoking or deleting an agent and deleting enrollment tokens lock agents
out, a security function that must stay available when the license is
read-only. Translations for the new problem codes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:19:31 +02:00
beug.k@beugsiud.de 31fb8285eb Merge branch 'worktree-agent-a423f65773305d370' 2026-10-13 19:16:01 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 7bbcd28aa6 test(agent/role): run the DNS and DHCP roles against real BIND and Kea
A driver is cross-compiled for the Docker daemon's architecture and
copied with fixture revisions into BIND 9.18/9.20 and Kea 2.6.5/3.2.1
containers (docker cp, no bind mounts or published ports). It runs the
core's pipeline: two revisions apply, broken revisions are rejected,
a failed live check and a failed health check roll back, a failed Kea
reload restores the previous file, and leases added with lease4-add
are listed page by page. make test-integration runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:14:35 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 562f35c10c feat(agent/dhcp): Kea DHCPv4 role over the UNIX control socket
Validate requires exactly kea-dhcp4.conf with this agent's control
socket and runs kea-dhcp4 -t. Activate writes the file atomically and
sends config-reload; on failure the previous file is restored and
reloaded. Health uses status-get (pid, uptime, last reload, socket
state) and checks that the installed file is the expected revision.
Leases pages through lease4-get-page and maps Kea lease states
(default, declined, expired-reclaimed, released) to core lease states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:14:34 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 d61b738a8c feat(agent/dns): BIND role with validation, activation and SOA health check
Validate maps ddi-suite*.conf to conf_dir and zones/db.<zone> to
zone_dir, rejects other files and a zone directory that differs from
dns.zone_dir, and runs named-checkconf on a temporary named.conf plus
strict named-checkzone per zone. Activate backs up all touched files,
writes zone files then includes atomically (unchanged files are kept),
never touches existing create-only files, removes files dropped since
the previous revision and the stale journal of a zone that became
managed, runs named-checkconf -z on the live named.conf and then
rndc reconfig and rndc reload; failures restore the backup (and reload
again). Health runs rndc status and compares the SOA serial of every
managed zone (UDP with TCP fallback, authoritative answers only).

golang.org/x/net becomes a direct dependency (dnsmessage).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:14:34 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 b5e57c3cb7 feat(agent/role): command runner and atomic file helpers for the roles
Shared, role-internal helpers: programs run without a shell, with a
timeout and captured, truncated output quoted in errors; configuration
files are replaced atomically (temp file, fsync, rename, directory sync)
with explicit mode and the owner of the existing file or its directory;
an in-memory backup restores touched files after a failed activation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:14:23 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 3065175673 feat(agent): enrollment, server connection and apply pipeline
- CLI: enroll, run, status, version, help (core.Main with a role
  factory); the agent binaries use a placeholder factory until the
  BIND and Kea roles are wired in.
- Enrollment: local ECDSA P-256 key and CSR, TLS 1.3 to the agent API
  with the server chain pinned to the CA fingerprint.
- ServerSource: mTLS gRPC with keepalive, Connect stream (Hello,
  Welcome, heartbeats, apply and renew commands), reconnect with
  exponential backoff and jitter, GetDesiredState polling as fallback,
  certificate renewal after half of the lifetime.
- Apply pipeline: fetch, bundle verification with replay protection,
  store, Validate, Activate, Health with retries, rollback to the last
  good revision; outcomes succeeded/rejected/rolled_back/failed are
  queued in state.json and reported; failed revisions are retried only
  by a newer revision or an explicit push.
- Lease reporting with change detection, persistent sequence and full
  snapshots at start, per connection and periodically.
- Tests with fake roles, a fake source and an in-process fake
  AgentService over real mTLS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:06:05 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 c96eb33e58 feat(agent): configuration file and file-based agent state
Agent configuration (agent-<role>.yaml, strict decoding, role section
passed undecoded to the role factory) and the state directory of ADR
0011: identity with interrupted-renewal recovery, verified revisions
under revisions/<n>/, state.json with apply results, lease sequence and
queued deployment reports, atomic writes, pruning and a lock file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:05:16 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 e843502cbf feat: M3 contracts for agents, revisions and bundles
Shared Apache-2.0 packages for the signed bundle format and the agent
certificate conventions, the agent role interface, REST API spec for
agents, enrollment tokens, revisions, deployments and leases, and ADR
0021. Generator settings now hold the target paths on the managed hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:54:30 +02:00
beug.k@beugsiud.deandClaude Opus 5.5 14dde4bfe4 feat: add server, agent and shared code foundation
- internal/brand: central product, binary and path names (ldflags-overridable)
- internal/buildinfo, internal/logging (slog, secret redaction)
- server: env config, HTTP server with /healthz, /readyz, request IDs,
  access log, panic recovery, security headers, graceful shutdown
- web: embedded SPA handler with fallback page when UI is not built
- agent: ddi-agent-dns and ddi-agent-dhcp binaries on a shared core CLI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:59:11 +02:00