CHG-0042: The Home Pi Becomes a Deevnet Pi, and the Devices Hold Both Sites

Date2026-10-26
Change typeMigration
StatusComplete
SystemsThe home backend Pi, 192.168.2.40 on DVNT-CORE, with a new card named dv01rpi001p01 (the card in it now calls itself dv02rpi003p01); turntable-power-01, cover-screen-01, lp-stand-01, respeaker-01; the tenant’s Terraform (one deevnet_certificate); the home network’s DNS
Howmake -C deploy/backend image fetch card; deevnet-kit cert request and cert install; terraform apply in infra/deevnet-tenant-eds; make pair spotify turntable drivers status devices; provision_tenant.py --site home --also mobile=terraform on each device
RiskMedium. Most likely to go wrong: a device refusing the broker’s new certificate. No ESP32 has yet verified a server through Deevnet’s Tenant Service CA to the Root
DecisionsADR-0033, ADR-0049; Deevnet ADR-0040 (site-less names) and ADR-0041 §2 (a Deevnet Pi)
RelatedCHG-0028, which built the card this replaces; Deevnet CHG-0054 and CHG-0056, which built the names and the kit’s cert commands

Summary

The home Pi runs EdS on a card whose certificate is signed by that card’s own CA, for names entered by hand in the home DNS. The devices dial its address and trust that CA, and hold one site’s settings.

After: a new card, whose certificate is Deevnet’s, from the Tenant Service CA, for the tenant’s site-less names (mqtt.eds.deevnet.net and its siblings) and for each of them at home and mobile. Everything on it trusts the Deevnet Root. The devices dial mqtt.eds.deevnet.net, trust the Root, and hold both sites: home in use, mobile beside it, for the switching firmware to come (ADR-0049).

Goal

  • sudo deevnet-kit status on the Pi names mqtt.eds.deevnet.net, and the certificate’s issuer is the Tenant Service CA.
  • make status says ok for every check.
  • /etc/eds/lightd.env on the Pi has MQTT_HOST=mqtt.eds.deevnet.net.
  • Each device’s presence is green on the console, from this Pi’s broker.
  • A record on the turntable lights the stand.

Scope

In scope: the new card, its certificate, the home DNS names, and the four devices’ creds partitions. Out of scope: the switching firmware (ADR-0049), the devices’ firmware, the tenant’s workload and the relay Pi.

Risk and impact

RiskWhereGuard
Home is dark from the swap until step 6The whole back endDone in one sitting; the old card is kept unchanged
A device refuses the new brokerThe first device movedThe beacon first: nothing is attached to it. Its old settings are kept to put back
eds-backend-setup on Deevnet names, never run on hardwareThe services on the Pimake status before any device is moved
The old card’s device settings overwrittendeploy/backend/devices.json, deevnet-kit-ca.pemCopied aside before make devices (step 7)

Procedure

Step 1: Build the image

Run: make -C deploy/backend image fetch

Verify: the build’s check of the base passes: its deevnet-kit has cert install (0.15.0).

Step 2: The card

Flash eds-pi-backend.img.xz with no customization, then:

Run: make -C deploy/backend card PI_HOSTNAME=dv01rpi001p01

Step 3: Swap and first boot

Power off the Pi, keep the old card, put in the new one, and power on. Allow a few minutes for both boots.

Verify: ssh eds@dv01rpi001p01.local sudo deevnet-kit status answers.

Step 4: The certificate

Run, on the Pi: sudo deevnet-kit cert request. Then a deevnet_certificate in the tenant’s Terraform with that request, name mqtt, names logs, grafana and console, and sites home and mobile; terraform apply. Copy the full chain and the Deevnet Root to the Pi:

sudo deevnet-kit cert install fullchain.pem deevnet-root-ca.pem
sudo systemctl restart eds-backend-setup

Verify: sudo deevnet-kit status names the Deevnet names, and /etc/eds/lightd.env has MQTT_HOST=mqtt.eds.deevnet.net.

Step 5: Drivers and checks

Run: make pair, make spotify, make turntable DEVICE=plughw:CODEC, make drivers DRIVERS=itunes,spotify,turntable, make status

Verify: every check ok.

Step 6: Home DNS

mqtt, logs, grafana and console, under eds.deevnet.net and eds.home.deevnet.net, answer 192.168.2.40 on the home network.

Verify: from a computer at home, https://console.eds.deevnet.net/ loads with no warning where the Deevnet Root is trusted.

Step 7: The devices

Copy deploy/backend/devices.json and deevnet-kit-ca.pem aside: they are the old card’s. Then make devices, and on each device, the beacon first, in its firmware directory:

python3 tools/provision_tenant.py -p <port> \
    --outputs-json ../../deploy/backend/devices.json \
    --ca ../../deploy/backend/deevnet-kit-ca.pem \
    --site home --also mobile=terraform

Verify: before each write, the two sites printed are home (DVNT-CORE, mqtt.eds.deevnet.net) in use and mobile beside it; after it, the device’s presence is green on the console. Only after the beacon is, the others.

Verification

Every check under Goal.

Undo

Before step 7: power off, put the old card back. Nothing else has changed but a certificate issued, which is left to expire.

After a device is moved: the old card back in, and that device provisioned from the copies kept in step 7 (provision_tenant.py with them, and no --site).

Outcome

Completed after the change has run.

WhenStepWhat happened
2026-10-271–3The image built, the card written and booted; make status answered
2026-10-274cert request on the Pi; deevnet_certificate.backend applied with provider 0.8.0, serial 55:c1:0d:cb:…:9d:0a, valid to 2027-10-28, for mqtt, logs, grafana, console at eds, eds.home and eds.mobile. It chains to the Deevnet Root and is for the request’s key
2026-10-29 02:20 UTC4The pending request’s key checked against the certificate’s, then cert install and restart eds-backend-setup. deevnet-kit status: “trusts the Deevnet Root (a Deevnet Pi)”, broker mqtts://mqtt.eds.deevnet.net:8883; lightd.env and nowplayd.env have MQTT_HOST=mqtt.eds.deevnet.net
2026-10-29 02:22 UTC4deevnet-kit selftest: 13 of 15. Grafana was still starting, and its log data sources still dialed https://localhost:8427, which the new certificate does not name. systemctl restart deevnet-kit-dashboards rewrote them as logs.eds.deevnet.net; then all 15 passed
2026-10-29 02:25 UTC5make status: every check ok but iTunes, whose PC at 192.168.2.104 did not answer (asleep or not running; the pairing is on file)
2026-10-29 02:27 UTC6Home DNS already answered mqtt, logs, grafana and console under eds.deevnet.net and eds.home.deevnet.net with 192.168.2.40. https://console.eds.deevnet.net/, https://console.eds.home.deevnet.net/ and Grafana answered 200 verified against the Deevnet Root alone, and the Mac’s keychain trusts it
2026-10-29 02:28 UTC5With iTunes playing Jack & Diane, the console showed it with the player’s cover, and lightd sent the stand dark at brightness 0. Fourteen seconds later the turntable driver heard the same song through the amplifier and became current: the beacon was still the tenant’s, so this Pi had never heard from it. A play_pause sent as the cover screen went to the turntable, which takes none
2026-10-29 02:39 UTC7The beacon (MAC 70:4b:ca:27:06:14): its tenant creds partition read and kept aside, the old card’s CA set aside, make devices (DVNT-CORE), a dry run, then provision_tenant.py with devices.json and the Deevnet Root. Online on this Pi’s broker at 02:39:35, six seconds after the write: an ESP32 verified a broker through the Tenant Service CA to the Root, with no firmware change
2026-10-29 02:41 UTC7Back on its outlet with the turntable off: on=false, and iTunes current at once
2026-10-29 02:43 UTC5play_pause again, iTunes playing: nowplayd to the iTunes driver to iTunes in 7 ms, and iTunes paused. The turntable heard the song again at 02:42:19 and was not made current
2026-10-29 02:46 UTC7The cover screen (MAC a0:a3:b3:ab:61:8c on /dev/cu.usbserial-2130): tenant creds kept aside, a dry run, then written. Online on this Pi’s broker within twenty seconds, and showed the cover when iTunes played. Its buttons did nothing: from 02:46:26 to 02:48:50 no press reached nowplayd
2026-10-29 02:48 UTC7Opening its serial port to watch restarted it. In that second start every touch registered and every press was carried out: pause, next, previous, previous, play, from cover-screen-01 to iTunes. Why the first start after provisioning sent nothing is not known: no log covers it
2026-10-29 02:55 UTC7The stand (MAC 70:4b:ca:26:b1:a0, on the port the beacon had used): tenant creds kept aside, a dry run, then written. Online within twenty seconds, and it reported applying the retained scene: solid, brightness 0, dark for iTunes
2026-10-29 02:56 UTC7The console’s stand switch set to all: the stand applied chase, four colors from American Fool’s cover, brightness 0.8. Set back to records: solid, brightness 0 again
2026-10-29 03:00 UTC7respeaker-01 left on the tenant, by decision: what it sends is heard by voiced and written down by scribe, and neither runs on this Pi. It goes on working as before, through the tenant’s workload
2026-10-29 02:58 UTCGoalThe turntable switched on and a record played: the beacon online, the turntable current, and the stand lit from the cover (chase, four colors, 0.8). Between tracks, standby at 0.15. Every goal met but the reSpeaker’s, which was left on the tenant by decision

Departures from the plan

  • The tenant’s provider pin went from ~> 0.7 to ~> 0.8: a certificate’s sites first came in 0.8.0.
  • The same day’s applies also created grafana_dashboard.covers, waiting since ADR-0050 was merged without one.
  • Before the certificate, the Pi warned that its card certificate did not name its IPv6 address. A Deevnet certificate names no address.
  • The card is named eds-backend, not dv01rpi001p01: it was written with make card’s default PI_HOSTNAME.
  • cert install restarted the services but not deevnet-kit-dashboards, so Grafana’s log data sources kept the card’s localhost until that was restarted by hand.
  • The beacon was provisioned without --site home --also mobile=terraform: the tenant’s state is reachable only from dvntm. Its tenant settings were read from its creds partition first, to write back as the undo.
  • respeaker-01 was not moved. The plan named it, but the backend Pi runs palette, lightd, nowplayd and the console, and not voiced or scribe, so its requests would reach a broker with nothing listening for them.
  • The Pi has two addresses on the home network, 192.168.2.40 (wired) and 192.168.2.228.

Follow-ups

  • Deevnet: deevnet-kit cert install should rerun the dashboards step (deevnet-provisioning-api#41), so Grafana’s data sources dial the new names.
  • Deevnet (deevnet-provisioning-api#42): on a Deevnet Pi, status and selftest warn that the certificate names none of the Pi’s addresses and suggest regen-certs, which then refuses. A Deevnet certificate names no address by design; the check should not run in that mode.
  • Find out why the cover screen’s first start after provisioning sent no presses while showing covers. Test each device’s controls straight after it is written, before anything restarts it. Not seen again on 2026-10-31: unplugged since the move, the screen was powered again with nothing on its serial port, came online by itself, and four presses in a row reached iTunes, each in a fraction of a second. If it happens again, it is right after a provisioning write, and the screen’s serial log is the place to look.
  • The resolver named Journey’s Wheel In the Sky, heard on Infinity, as from Alive in America, a live album, and lit the stand from its cover (named_by=original, ADR-0038). The record is not in the Discogs collection, so nothing named it first. The original album step should not choose a live album or a reissue over a studio original.
  • The reSpeaker, once this Pi runs voiced and scribe: until then it stays on the tenant. Its account is already in accounts.conf.
  • --also mobile=terraform for the beacon, the screen and the stand, on a day on dvntm: each holds this Pi’s site alone.
  • The switching firmware (ADR-0049), over the air to the stand, the screen and the beacon; by cable to the reSpeaker.
  • The certificate named for the host, not a service. Its CN is mqtt.eds.deevnet.net, from name = "mqtt". Wanted: name = "backend" and names = ["mqtt", "logs", "grafana", "console", "dv01rpi001p01"], sites home and mobile, for a CN of backend.eds.deevnet.net. The tenant’s CA signs only in its own zone, so the Deevnet host name appears as dv01rpi001p01.eds.deevnet.net, never dv01rpi001p01.home.deevnet.net. Changing name replaces the certificate, and the old one is left to expire. The round trip is this change’s: cert request at home, terraform apply on dvntm, cert install at home. Home DNS gains backend and dv01rpi001p01 under eds.deevnet.net and eds.home.deevnet.net, at 192.168.2.40; the .home one is the host’s own name, for SSH. At the next reflash, perhaps the card named to match (it is eds-backend).
  • nginx on the Pi’s 80 and 443: plain HTTP sent to HTTPS, and a name beginning grafana. reaching Grafana, so it needs no :3000, with Grafana on loopback behind it. From an earlier draft that this change superseded, kept as the branch archive/backend-names-nginx; its names were interim until the site-less ones this change put in place.
  • .local or the A record for SSH and make (PI ?= $(PI_HOSTNAME).local). Talked through on 2026-10-29: .local stays for now. It came with the take-home kit, which assumed no DNS at all; iTunes needs Bonjour only to pair, and never the Pi’s own name. Its one remaining merit is working when the router’s DNS does not, which is when SSH is wanted most. Revisit with the certificate’s rename: give the host a site-qualified A record, backend.eds.home.deevnet.net (a site-less name follows the tenant between sites, and a host does not move), use it with PI=backend.eds.home.deevnet.net, and decide then whether it becomes the default.
  • Deevnet (deevnet-provisioning-api#43): deevnet-kit cert install installs only for a pending request, and deletes its key once used, so a renewal Terraform issues for the same key cannot be installed. Every renewal is a new cert request. To note beside ADR-0053’s renewal job.