Commit Graph
53 Commits
Author SHA1 Message Date
Ilker ManapandClaude Opus 5.5 2f708e8f69 Root rotation without re-joining: prepare, switch, retire
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
CI / go (push) Successful in 47s
The CA directory holds the active root and, during a rotation, the next
or the previous one; everything that trusts the region gets the trust
bundle (all of them). Rack controllers receive it in their state, signed
by the active root; the root update unit installs it only when the
signature verifies against the roots it already trusts, and binary
signatures verify against any of them. After switch racks renew their
client and server certificates from the new root by themselves and
report what they run on; switch and retire wait for every rack (-force).

/ca.pem stays one certificate, the root of that server's certificate, and
the PXE command line names that root, so agents in older images keep
working; /ca-bundle.pem is the bundle. Deployed machines get one file per
root and a daily timer that refreshes them from their rack over HTTPS.

Refs #12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 15:37:44 +01:00
Ilker ManapandClaude Opus 5.5 008fa3f66f Infoblox WAPI driver for the IPAM authority
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 41s
ipam.provider infoblox: one record:host per address, named by its DNS
name and configured for DNS in dns_view ("-" = IPAM only); ownership is
the rollcall_machine extensible attribute, changed with extattrs+ so a
host's other attributes stay. Anything else on the address (a host, a
lease, a fixed address, a DHCP or reserved range, network/broadcast) makes
it taken. Delegated networks carry the rollcall attribute. WAPI paging.

The reconcile no longer compares fields an authority does not keep
(Infoblox has no prefix length or status on a host). Built from the WAPI
reference and tested against a fake WAPI; no real grid yet.

Closes #11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:22:17 +01:00
Ilker ManapandClaude Opus 5.5 dc6eb55b26 Location changes kick the IPAM reconcile
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 42s
A machine's NetBox device follows a move at once, not at the next pass.

Refs #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:11:38 +01:00
Ilker ManapandClaude Opus 5.5 2e1a2176cd rollcall ipam sync: print the device report
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
CI / go (push) Successful in 42s
The previous commit's message promised this; the edit had not applied.

Refs #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:08:39 +01:00
Ilker ManapandClaude Opus 5.5 c3032683ab Device sync: v6 primary IP, DMI placeholders, CLI report
CI / site (push) Successful in 3s
CI / agent (push) Successful in 3s
CI / go (push) Successful in 41s
The primary IP of each family is the management address, else the first
address on the management interface (the v6 address next to a v4
management address). DMI placeholders ("Not Specified", "To Be Filled
By O.E.M.") are not written as serials. rollcall ipam sync prints the
device report.

Refs #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:07:22 +01:00
Ilker ManapandClaude Opus 5.5 133b5f38d9 NetBox device sync; lab NetBox container
CI / go (push) Successful in 43s
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
ipam.device_sync mirrors commissioned machines as dcim devices: serial,
device type and role (created when missing), status, interfaces with MAC
objects, Rollcall's addresses assigned to them and the management address
as primary IP. Site, location and rack are matched by name from the
machine's location and never created. Ownership by tag and
rollcall_machine; a foreign device of the same name is a conflict.

lab/netbox/install.sh sets up a NetBox on the lab segment so the drivers
are tested without writing to a real NetBox.

Refs #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:00:43 +01:00
Ilker ManapandClaude Opus 5.5 dbb0d93dc7 Kea: reservations in memory when there is no hosts database
CI / go (push) Successful in 41s
CI / agent (push) Successful in 2s
CI / site (push) Successful in 2s
Found against Kea 3.0.5 in the lab: reservation-add answers "Host
database not available" on a server that keeps its configuration in a
file. Then reservations go into the running configuration
(operation-target memory) and config-write saves them; with a hosts
database nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:13:05 +01:00
Ilker ManapandClaude Opus 5.5 482a9d5987 DHCP reservations on the site's DHCP server: Kea and OpenWrt
CI / go (push) Successful in 43s
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
On a VLAN in proxy mode the site's DHCP server hands out addresses and
Rollcall answers only PXE. With dhcp_reservations in config.json the
machines still get the addresses Rollcall planned: each address on a
proxy-mode IPv4 subnet becomes a fixed lease (MAC of its interface ->
IP, hostname) on that server, reconciled on every address change and
every sync_interval.

- kea: control API (Kea 3.0's HTTP socket or the Control Agent; the
  "service" field is learned) with the host_cmds hook, open source
  since Kea 3.0: reservation-add/-del/-get-all, subnet ids from
  config-get, ownership in user-context {"rollcall": machine},
  config-write afterwards (persist) for reservations kept in memory;
  a clear error without host_cmds (Debian's Kea 2.6 lacks it)
- openwrt: rpcd/ubus JSON-RPC, host sections in /etc/config/dhcp with
  option rollcall, uci commit and dnsmasq reload; relogin on expiry
- reservations others made are never touched; one holding the MAC or
  the address is a conflict

rollcall reservations [status] | sync [-dry]; API under
/api/v1/dhcp-reservations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:46:44 +01:00
Ilker ManapandClaude Opus 5.5 b2ad0210bf GRUB keeps the firmware's text console; phone-home back to HTTP
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 40s
Lab, Secure Boot VM: the deployed Debian booted under Secure Boot
through the chainload, but its console was garbled after the network
GRUB had switched the display mode; GRUB menus now use the firmware's
text console (terminal_output console), which also suits serial/SOL.

phone-home over HTTPS failed once on that first boot (TLS: bad record
MAC on the region's side; the same request works from the machine
since), and cloud-init spends its 10 tries within milliseconds. Its
content is not secret and its token only works from the machine's
address, so it goes over HTTP again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:51:22 +01:00
Ilker ManapandClaude Opus 5.5 c494c7b226 GRUB local boot chainloads the disk's signed loader; image sums
CI / go (push) Successful in 45s
CI / agent (push) Successful in 4s
CI / site (push) Successful in 2s
On the Secure Boot lab VM, GRUB's "exit" back to the firmware did not
reach the disk: OVMF went on to its other network boot options and
stopped in its own boot menu. The local-disk entry now finds the
installed OS's signed loader on the EFI partition (shimx64.efi under
EFI/debian, ubuntu, rocky, almalinux, redhat, centos; shim.efi for
SUSE; else EFI/BOOT/BOOTX64.EFI) and chainloads it, as MAAS does.

build-image.sh writes SHA256SUMS next to the image (the old one was
stale after a rebuild; bootstrap.sh --image-url checks it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 11:28:56 +01:00
Ilker ManapandClaude Opus 5.5 a0df7295ea UEFI PXE through signed shim and GRUB (Secure Boot)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 40s
UEFI machines were given syslinux.efi, which is unsigned: with Secure
Boot on (the default on much server hardware) the firmware refuses it
("Access Denied", seen on an OVMF lab VM), so such a machine could not
PXE-boot at all. They now get Debian's signed shim (bootx64.efi), which
loads the signed network GRUB (grubx64.efi); GRUB reads
grub/grub.cfg-<mac>, else grub/grub.cfg-default, and loads kernels
only as shim verifies them. Rack controllers write the GRUB menus from
the pxelinux menus they already get (same entries: kernel and initrd,
local disk via exit, power off via halt), so both always agree; BIOS
machines keep pxelinux. install.sh, install-rack.sh and bootstrap.sh
install shim-signed and grub-efi-amd64-signed and lay out the files.

Secure Boot covers the kernel; the initrd and the squashfs it fetches
are not covered by it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:25:54 +01:00
Ilker ManapandClaude Opus 5.5 47210a9e69 gofmt rackctl.go
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 40s
CI's gofmt step failed on it since a9ca326, so CI and the deploy that
follows it stopped running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 07:16:17 +01:00
Ilker ManapandClaude Opus 5.5 179fdcdec5 HTTPS for stateless overlays and phone-home
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
CI / go (push) Failing after 21s
The stateless overlay client (rollcall-overlay in the compute image)
takes rollcall.tls= and rollcall.ca= like the agents: the CA is accepted
only by its fingerprint and the overlay (munge.key and the like) comes
over HTTPS, with no fallback. A deployed OS calls home over HTTPS when
its rack serves it (it trusts the Rollcall CA from first boot).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 07:12:55 +01:00
Ilker ManapandClaude Opus 5.5 a9ca326dd2 Agents over HTTPS: rack server certificates, fingerprinted CA
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Failing after 20s
Rack controllers ask the region for a server certificate for their own
URL's host (POST /api/v1/rack/server-cert; CN rack-server:NAME,
ServerAuth only, never one of the region's names or addresses) and serve
the agents' endpoints and images over HTTPS too (tls_listen, default
<host>:443), reporting the HTTPS URL when they register (accepted only
on the rack's own host). The pxelinux command lines name it with the CA
fingerprint (rollcall.tls=, rollcall.ca=); the embedded rack uses the
region's listener. Agents fetch /ca.pem, accept it only by that
fingerprint and then use HTTPS with it, including the image download; a
job fetched over HTTPS gets an HTTPS image URL. No fallback to HTTP once
TLS was asked for; old agents ignore the new words. The CLI refuses a
rack controller's certificate as the region.

PXE itself (TFTP, kernel, initrd) stays unauthenticated: this protects
what agents send and receive from listeners and from an HTTP server
posing as the rack, not from an attacker rewriting boot files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:51:17 +01:00
Ilker ManapandClaude Opus 5.5 d0ea29d2a9 Power: say why a BMC command failed (CLI, UI); certificate changes are events
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 40s
Found in the lab test of certificate pinning: the CLI printed only
'ipmitool failed on 1 machine(s)'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:37:02 +01:00
Ilker ManapandClaude Opus 5.5 9ab081aa5a BMC certificates pinned on first use (Redfish, Proxmox)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 42s
The Redfish and Proxmox clients skipped TLS verification (BMCs ship
self-signed certificates), so a host posing as a BMC on the network got
the Basic-auth password. Before any credentials go to an HTTPS BMC the
region now reads its certificate with a handshake-only operation
(tls-fingerprint, run on the rack controller) and pins the SHA-256 on
the BMC record (extra tls_fingerprint); every later connection must show
the same certificate or nothing is sent. Changing the address, clearing
tls_fingerprint, or a BMC firmware update run by Rollcall (new firmware
often means a new certificate) pins anew. verify_tls=true still uses the
system CAs instead. The lab emulator keeps its certificate like a BMC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:32:38 +01:00
Ilker ManapandClaude Opus 5.5 dc093391a5 Machine tokens only from the machine's own address
CI / agent (push) Successful in 2s
CI / site (push) Successful in 2s
CI / go (push) Successful in 43s
Job and node tokens travel in the per-MAC pxelinux file, readable by any
host on the provisioning network over TFTP; with a job token one could
fetch the deploy seed (user-data, tag cloud-config) or report the job,
with a node token the stateless overlay (e.g. munge.key).

The region knows the address it gives each machine (static DHCP leases
from the network model): /jobs/self, job progress, phone-home and the
node overlay are now refused from anywhere else (403, an event on the
machine). Through a remote rack controller the address is the rack's
own X-Forwarded-For entry, believed only from an authenticated rack. A
machine without a fixed address (proxy DHCP) binds a job to the first
address that uses it. agent_source_check: false turns this off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:50:01 +01:00
Ilker ManapandClaude Opus 5.5 e474fbcf16 BMC hints: the password stays on the region
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 41s
GET /api/v1/bmc-hint answered with the BMC password to anyone with the
agent token (public on the PXE command line) and a MAC (visible on the
wire). It now returns the address and user only. When a machine reports
in, the region matches its MACs against bmc_hints itself, seals the
configured password into the record, and checks the BMC with a status
call from the rack controller (verified_oob). The agent skips its own
check when no password comes; old agents keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:28:00 +01:00
Ilker ManapandClaude Opus 5.5 a495c33b66 Rack certificate renewal: rack-owned state, atomic, grace period
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 41s
Renewal could not work: rack join writes the certificate and key as
root, and the rack (user rollcall) failed to overwrite them. A lost
renewal answer also locked the rack out, since the region accepted only
the new serial.

- the renewed certificate and key go into one file in
  /var/lib/rollcall/rack-cert (cert_state_dir), written atomically, and
  are preferred at start when they are this rack's, from the CA, newer
- after a renewal the region accepts the previous serial for 7 days; a
  new join does not keep it
- rollcall rack renew renews now (run as the service user)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:22:20 +01:00
Ilker ManapandClaude Opus 5.5 b01d8e2b4d Firmware updates over Redfish, per vendor
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 39s
A firmware repository on the region (firmware_dir, served under
/firmware/<id>/<file>, cached by rack controllers for BMCs to pull),
each machine's firmware inventory (FirmwareInventory, classified into
bios/bmc/nic/raid/cpld/psu/drive), and update jobs (kind "firmware")
that the region drives with short rack commands: fw-start hands the
package over, fw-poll watches the task (tolerating a BMC that restarts),
fw-finish releases what was claimed, fw-inventory verifies the version.
Running updates resume after a region restart; they cannot be cancelled
once the BMC has the package.

Standard methods (multipart, SimpleUpdate pull, HttpPushUri) with known
Content-Length, plus vendor plans from their documentation or their own
tools: Dell MultipartUpload (OnReset), HPE SimpleUpdate with .fwpkg
extracted at import, Lenovo /mfwupdate with HttpPushUriTargetsBusy,
Supermicro /upload with targets and preserve options, GIGABYTE/AMI/
ASRock Rack /upload with OemParameters ImageType, OpenBMC
update-multipart, xFusion upload to /tmp/web then SimpleUpdate, KAYTUS
upload then OEM SimpleUpdate with fixed tasks, Fujitsu iRMC OEM actions;
Cisco CIMC is refused clearly (SCP only). fw_params and fw_method on
the BMC record override a plan.

CLI rollcall firmware list|import|rm|show|update, API, a Firmware tab
and a repository card in the UI. The lab Redfish emulator gains an
UpdateService (BMC applies at once, BIOS at the next reset).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:22:01 +01:00
Ilker ManapandClaude Opus 5.5 3568c4866e Redfish vendor profiles from public documentation
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 37s
One DMTF Redfish implementation, plus a profile per vendor with what
its documentation (or Ironic/sushy, MAAS, bmclib field notes) says is
different, each with sources: Dell iDRAC, HPE iLO, Lenovo XCC,
Supermicro, xFusion/Huawei iBMC, KAYTUS/Inspur, Fujitsu iRMC, Cisco
CIMC, GIGABYTE, ASRock Rack, OpenBMC, AMI MegaRAC.

Generic rules: reset types and boot targets from AllowableValues or
ActionInfo; boot target, enable and mode in one PATCH, the mode kept,
omitted or tried per profile; If-Match from header or @odata.etag with
412 (strong, then none) and 428 handling; AMI future-state /SD and
@Redfish.Settings targets; busy retries (POST, 409, 503, "Retry in");
202 task following; chassis reset fallback; emulated power cycle;
session login; 400 answers that say Success.

Probe results (reset types, boot targets, mode, profile) are kept on
the BMC record and shown in the UI; the UI's driver list comes from the
server. Each profile has a fake BMC test built from its sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:52:13 +01:00
Ilker ManapandClaude Opus 5.5 a549f01ef5 Security review: fix traversal, identity, injection and takeover paths
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 34s
Findings from a full review (auth/API, CA and rack channel, config
generation, integrations, UI/agents/scripts), each verified in code:

- rack binary endpoint read any region file via arch=../.. (CA key)
- render endpoint traversal; machine-reported MACs used as file paths
- delegated intermediates could mint admin/rack identities and IPs;
  identities now require root-issued certs, intermediates are
  ServerAuth-only with both name types constrained
- rack certs were ServerAuth for rack-chosen SANs: now client-only;
  racks accept only the region's own cert; join checks the CA
  fingerprint; unbound join codes cannot take an existing name; job
  results bound to the rack that took them
- rack self-update: region signs binaries with the CA key, the root
  unit verifies against the join-time CA in a root-owned dir
- dnsmasq reads a checked, root-owned copy limited to generated options
- commissioning could overwrite deployed machines; MAC takeover of PXE
- injection into dnsmasq/cloud-init/PXE/udev/DNS via names and
  machine-reported strings; stored XSS in the UI; CSP and cross-origin
  protection; deploy agent shell injection and seed traversal
- Redfish requests confined to the BMC's /redfish/ tree
- admin_http defaults to loopback; body limits and timeouts; secrets
  off command lines

Also: IPAM claim/reconcile race, sync stopping on one error, watchdog
race, release wiping the wrong disk, SAN LUNs as auto targets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:58:42 +01:00
Ilker ManapandClaude Opus 5.5 07be25ac92 DNS provider: PowerDNS API and RFC 2136 with TSIG
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 1m34s
Rollcall writes its machines' A/AAAA records into the site's DNS when
no IPAM authority publishes them. Names follow the IPAM rule.

- powerdns: RRsets owned by comment account "rollcall"; others are
  reported as conflicts and left alone.
- rfc2136: TSIG-signed updates with a _rollcall.<name> TXT marker.
  Changing a record requires the marker, creating one requires the
  name to be free, so foreign records are never overwritten. Written
  records are kept in dns_records and queried on every sync, so
  out-of-band changes are repaired (marker present) or reported
  (someone else's record).
- Sync on allocation/release and every sync_interval; rollcall dns,
  rollcall dns sync [-dry], doctor check.

Tested against netops PowerDNS and BIND 9 (conflict, repair, wrong
TSIG secret).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:22:51 +01:00
Ilker ManapandClaude Opus 5.5 7a007ef048 Proxy DHCP for networks whose DHCP server stays in charge
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
CI / go (push) Successful in 36s
A VLAN in proxy mode leaves addresses to the site's DHCP server; the rack
only answers PXE (dnsmasq proxy range + pxe-service), machines keep DHCP
unless a static range is set aside. CLI -proxy-dhcp, web UI mode select,
bootstrap --proxy-dhcp, doctor/discover aware.

dnsmasq 2.91 (and 2.93) loops forever in its proxy path when the proxy
range carries a set: tag: option_filter appends a request's tags to the
context's persistent list, so the next request's stack interface tag
points at itself (found with gdb on the lab rack). Proxy ranges are
untagged and PXE services limited by interface; dhcp-boot/dhcp-match only
for served subnets; no dhcp-host lines for proxied ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:04:10 +01:00
Ilker ManapandClaude Opus 5.5 0c6f6557db cloud-init final_message: no $hostname (not expanded there; warned on every deploy)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:01:11 +01:00
Ilker ManapandClaude Opus 5.5 e2102e1741 Deployments trust the CA on RHEL-family systems too
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
cloud-init's ca_certs ran update-ca-certificates on Rocky and failed, so
those machines never trusted the Rollcall CA. The CA is now written into
both trust-anchor directories and a bootcmd rebuilds the store with
update-ca-trust or update-ca-certificates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:38:46 +01:00
Ilker ManapandClaude Opus 5.5 996ecbd89d Lost power commands are visible; jobs not started in job_start_timeout fail
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 37s
pxeBoot dropped the errors of its BMC calls, so a command lost while the
rack or region restarted produced an empty, success-looking event and the
job waited forever. Errors now go into the event (pxe boot failed), and a
watchdog fails jobs the machine has not started within job_start_timeout
(default 30m: real servers take 4-5 minutes to POST, longer in bulk).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:23:38 +01:00
Ilker ManapandClaude Opus 5.5 0ff96d7e74 Day-0 install without a network plan; rollcall net discover
CI / agent (push) Successful in 2s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
bootstrap.sh no longer needs the provisioning network: it installs and
starts the region on the management address; --iface later models the
subnet, serves DHCP and moves server_url. rollcall net discover shows what
each interface sees (LLDP, DHCP servers via a parallel probe, neighbours)
and suggests the next step. commission_append follows server_url. Doctor
treats a host that serves no DHCP yet as day 0. Fixes found on a clean VM:
os-release clobbered the rack name, install.sh not executable, slow DHCP
offers missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:28:37 +01:00
Ilker ManapandClaude Opus 5.5 01cc53120c From-scratch install: deploy/bootstrap.sh and rollcall doctor
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
bootstrap.sh sets up a region on a fresh Debian 13 host (packages,
PostgreSQL, TFTP tree, config, services, verified commissioning image,
provisioning subnet with ranges/DHCP, optional NAT, first OS image).
rollcall doctor checks an installation and says what to fix, including a
DHCPDISCOVER probe for foreign DHCP servers. pxelinux-default no longer
hard-codes the lab address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:08:30 +01:00
Ilker ManapandClaude Opus 5.5 ada6566534 admin-cert request: refresh the stored CA for the same region (it may have been rotated)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 33s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:48:40 +01:00
Ilker ManapandClaude Opus 5.5 53c05bb44d Admin client certificates as an alternative to the admin token
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 1m30s
rollcall admin-cert request NAME makes a key locally and sends a CSR; the
region signs admin:NAME (client auth) and records the serial; a verified,
recorded, unrevoked certificate is an admin. CLI/console read it from
client.json, a .p12 is written for the browser; the web UI no longer insists
on a token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:44:06 +01:00
Ilker ManapandClaude Opus 5.5 28a0c2952f Web UI over plain HTTP redirects to HTTPS when admin_http forbids HTTP
CI / agent (push) Successful in 3s
CI / site (push) Successful in 3s
CI / go (push) Successful in 35s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 14:16:36 +01:00
Ilker ManapandClaude Opus 5.5 15537b90f0 network-config: policy routing for addresses on secondary subnets with a gateway
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 14:03:02 +01:00
Ilker ManapandClaude Opus 5.5 bbe45804c7 Rack controllers update themselves to the region's version
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 38s
The region offers its binary (version, arch, sha256) in each rack's state and
serves it over mutual TLS; the rack stages it after checking checksum and
version; a root path unit installs it, restarts the rack and rolls back if it
does not register within a minute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:59:52 +01:00
Ilker ManapandClaude Opus 5.5 eb245b1d51 dnsmasq: forward outside names (systemd-resolved stub or resolv.conf)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 35s
Debian's dnsmasq looks for /run/dnsmasq/resolv.conf, which only resolvconf
writes; without it every name outside the cluster domain was refused, so
nodes could not resolve mirrors or site services.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:07:07 +01:00
Ilker ManapandClaude Opus 5.5 77894b7d57 CA delegation: name-constrained intermediates, root rotation, ACME node certificates
CI / site (push) Successful in 2s
CI / go (push) Successful in 35s
CI / agent (push) Successful in 3s
New roots carry path length 1. `rollcall ca intermediate` signs a CSR (or a
generated key) as an intermediate limited to the permitted domains and
address ranges; a root with path length 0 refuses and `rollcall ca rotate`
replaces it. deploy/tags/acme-cert.yaml gets a node certificate from an
ACME server under such an intermediate and renews it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:23:26 +01:00
Ilker ManapandClaude Opus 5.5 bcbb07721b Web UI: network topology drawing; richer Overview
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:16:57 +01:00
Ilker ManapandClaude Opus 5.5 9a11d03567 ipam: a subnet domain names records only inside the authority's zone
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 36s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:38:19 +01:00
Ilker ManapandClaude Opus 5.5 2706fd65e7 External IPAM authority: NetBox driver, ownership, reconcile
CI / agent (push) Successful in 2s
CI / site (push) Successful in 2s
CI / go (push) Successful in 35s
Every address Rollcall hands out is registered in the configured authority
first; addresses others hold there are skipped. Rollcall's records carry the
tag `rollcall` and the machine ID in the `rollcall_machine` custom field;
only those are changed or deleted. Releases trigger a reconcile that also
runs periodically. dns_name per address feeds the site's DNS sync. Delegated
prefixes can be listed and imported as subnets. `rollcall ipam`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:35:21 +01:00
Ilker ManapandClaude Opus 5.5 c8465c235a Region as certificate authority, BMC power drivers, IPv6
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 34s
CA (llmgw-style)
- region creates its CA (ECDSA P-256) and serves API/UI/images over HTTPS on tls_listen with a
  certificate for every name/address it has (reissued on change or a month before expiry);
  admin_http policy (allow|loopback|deny) for the admin API over plain HTTP; /ca.pem
- rack controllers enroll with one-time join codes (CA fingerprint + secret + expiry + address):
  rollcall controller join-code, rollcall rack join; mutual TLS from then on, identity from the
  certificate (CN rack:NAME, serial on record), removal revokes, renewal a month before expiry;
  the shared rack_secret stays as a legacy fallback
- clients: CA from the server config, ROLLCALL_CA, or trusted on first connect in the console after
  showing the fingerprint (kept in client.json); deployed machines trust it via cloud-init ca_certs
  (allowed on RHEL-family images), overlays get .CA; rollcall ca show|issue

Power drivers
- ipmi (cipher/privilege/interface), redfish (system discovery, ResetType from AllowableValues or
  ActionInfo, boot override with If-Match), idrac, ilo, supermicro, xcc, openbmc, proxmox (API
  token), manual; executed on the rack controller; Redfish auto-detection after commissioning;
  operator settings survive recommissioning; rollcall bmc, BMC editing in web UI and console
- lab: pve_redfish.py Redfish emulator per Proxmox VM (iDRAC, iLO, generic profiles)

IPv6
- IPv6 subnets and IPAM (netip), dual-stack default layout (all subnets of the boot VLAN),
  per-family default routes, accept-ra off where addresses are planned, dnsmasq RA/DHCPv6 with
  router lifetime 0 unless the rack is the gateway, AAAA host records from IPAM, IPv6 listen
  addresses and URLs, rack interfaces and region certificate with IPv6, agent reports IPv6

Tests for CA/join/pinning/CSR signing, Redfish (iDRAC, iLO fallbacks, probe), Proxmox, manual,
IPv6 candidates, dual-stack plans and dnsmasq IPv6.

Verified in the lab: rack2 enrolled and running on mutual TLS only, revocation and single-use
codes; iLO auto-detected through rack2, iDRAC and Proxmox drivers, wrong password not saved;
Debian 13 (bond+VLAN), Rocky 9 and AlmaLinux 9 dual-stack with Redfish-driven deployments; CA
trusted on Debian and AlmaLinux; admin CLI over HTTPS on IPv4 and IPv6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 07:44:13 +01:00
Ilker ManapandClaude Opus 5.5 736a7e38a3 Network model, IPAM, interface layouts and the region/rack split (MAAS-style)
CI / agent (push) Successful in 3s
CI / go (push) Successful in 32s
CI / site (push) Successful in 2s
- fabrics, VLANs, subnets, IP ranges (dynamic/reserved/static), IPAM with adoption of
  addresses machines already have; rollcall fabric|vlan|subnet|range|ip
- interface layouts (physical/bond/vlan, NICs by selector: boot, name, mac, fastest:N, ib:N)
  per machine or per tag; deployment allocates addresses and writes cloud-init network-config
  plus udev name rules (RHEL cloud images boot with net.ifnames=0); release frees them;
  rollcall layout, rollcall net show|set
- region/rack: rollcall rack long-polls its desired state (per-MAC PXE files kept in the
  database, dnsmasq configuration and host files) and a BMC command queue, caches boot and OS
  images (kernels/initrds prefetched for TFTP), proxies the node API and tags requests with its
  name; the region runs an embedded rack controller; install-rack.sh, rollcall-rack.service
- dnsmasq configuration generated per rack from the VLANs it serves (split pool for two racks);
  reload script restarts only on change and restores the last working config if dnsmasq does
  not come back (drilled in the lab)
- web: Network page, per-machine network tab, layout action, tag layout; console: Network page,
  network section in the machine view, layout action
- tests for candidates, plans (default, bond+VLAN, pinned, DHCP fallback), network-config,
  udev rules and dnsmasq rendering
- lab: pve-vbmc address script finds the interface per BMC address

Verified in the lab: embedded rack (10.213.77.0/24) and a remote rack controller on a second
SDN segment (10.213.78.0/24): enlistment, commissioning, BMC through the rack's queue, image
cache, deployment and phone-home through the rack; Debian 13, Ubuntu 24.04 and Rocky 9 with a
static boot NIC and with an active-backup bond carrying VLAN 200.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 23:58:40 +01:00
Ilker ManapandClaude Opus 5.5 e3d9373201 Terminal console: bare rollcall in a terminal opens a full-screen UI (llmgw style)
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 1m31s
- Bubble Tea + lipgloss with the same frame as llmgw: title bar, sidebar, bordered panel,
  key line, dialog overlay; keyboard only
- pages: overview, machines (filter, state filter, multi-select, bulk actions), machine
  detail, racks with elevation, jobs, events, tags, profiles, OS images
- actions: power, boot device, deploy OS, assign/unassign, tags, location, re-commission,
  release, cancel job, delete; confirmation for destructive ones; per-machine results
- connect screen when no server is configured; URL + token saved to
  ~/.config/rollcall/client.json (0600), also used by the CLI
- batched runes outside text fields are handled key by key
- rollcall tui -dump WxH renders one screen; first Go tests for rendering and keys
- web: unnamed machines show their id instead of the live image's hostname

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 20:00:51 +01:00
Ilker ManapandClaude Fable 5.1 5ee35037e5 ui: machines table scrolls horizontally on narrow screens; shorter model/serial cells
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 27s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 19:20:40 +01:00
Ilker ManapandClaude Fable 5.1 727d50177d ui: fix undefined location filter variable on the Machines page
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 27s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 19:18:55 +01:00
Ilker ManapandClaude Fable 5.1 cb7b6fd19e Physical location: building / room / rack / unit / asset tag
CI / go (push) Successful in 35s
CI / agent (push) Successful in 4s
CI / site (push) Successful in 3s
- machines.building/room/rack/rack_unit/asset_tag, racks table (height, comment)
- API: POST /machines/{id}/location, POST /locations/import (CSV or JSON, matched by
  id/serial/mac/node_name, ?dry=1), GET/POST/DELETE /racks, ?building/?room/?rack filters
- CLI: rollcall location ..., location import, location racks/rack, list -rack/-room/-building
- UI: location column and building > room > rack chips, bulk Set location…, Racks page with
  elevation views and CSV import dialog, location in machine detail
- .Node.Building/Room/Rack/Unit/AssetTag in overlay templates and tag match rules

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 15:32:26 +01:00
Ilker ManapandClaude Fable 5.1 0333c1e486 Web UI redesigned for datacenter fleets
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 27s
Sidebar navigation (Overview, Machines, Jobs, Tags, Profiles, OS images); machine list with
state/tag chips, search, sort, paging and multi-select bulk actions (power, deploy, assign,
tag, re-commission, release, boot device); machine detail with tabs (overview, hardware,
network, BMC, jobs, events, raw) and dialog-driven actions; fleet-wide /api/v1/events and
/api/v1/jobs; ?token= sign-in; GPU count in summaries counts real accelerators only;
OS catalog ignores dot-directories.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 14:48:29 +01:00
Ilker ManapandClaude Fable 5.1 0f798d8797 Machine tags (MAAS-style), changelog-driven release notes
CI / agent (push) Successful in 3s
CI / site (push) Successful in 2s
CI / go (push) Successful in 26s
- tags table + machines.tags/auto_tags; match rules as Go templates over .Node
  (GPUs, IB, CPUs, memory, disks, NVMe, PCI vendors, manufacturer/product, OS)
  re-evaluated at commissioning and on tag changes
- deployment: tag cloud-config fragments appended as MIME parts with additive merge;
  deploy/tags/gpu-nvidia.yaml installs the NVIDIA driver per distro family
- stateless: tag overlays appended to the profile's overlays
- CLI: tag, tags, list -tag, deploy/power tag:NAME fan-out; UI: tags column, per-machine
  editor, tag definitions panel; API: /api/v1/tags, /machines/{id}/tags, ?tag=
- CHANGELOG.md; release workflow publishes the tag's section as release notes and
  refuses tags without one

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 11:32:02 +01:00
Ilker ManapandClaude Fable 5.1 d4a5108878 usage: write the help text directly so go vet stops seeing a format directive
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 1m17s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 10:39:00 +01:00
Ilker ManapandClaude Fable 5.1 3fd926d6b0 rollcall version; fix go vet complaint in usage text
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Failing after 31s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 10:37:19 +01:00
Ilker ManapandClaude Fable 5.1 ee3a1a4787 deploy: cancelled job restores the previous machine state; site: deployment features
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 10:13:16 +01:00