<feed xmlns='http://www.w3.org/2005/Atom'>
<title>luke/ansible-roles, branch main</title>
<subtitle>Ansible roles for nth.io infrastructure
</subtitle>
<id>https://src.nth.io/luke/ansible-roles/atom?h=main</id>
<link rel='self' href='https://src.nth.io/luke/ansible-roles/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/'/>
<updated>2026-08-11T02:11:03Z</updated>
<entry>
<title>openhop/repeater: gate service start on the data-dir mount</title>
<updated>2026-08-11T02:11:03Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-11T02:11:03Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=440f01ea7de8dabf01480c381ff53c67922720b2'/>
<id>urn:sha1:440f01ea7de8dabf01480c381ff53c67922720b2</id>
<content type='text'>
Add RequiresMountsFor to the systemd unit so the daemon does not start
before the storage backing the data dir (and thus the identity file) is
mounted, which would make it generate a fresh keypair onto the bare
mountpoint. Derives from the data dir path, so it resolves to the root
mount (a no-op) when the data dir lives on the SD card.
</content>
</entry>
<entry>
<title>nginx: add catch-all default_server returning 404 for unmatched hosts</title>
<updated>2026-08-10T14:37:37Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-10T14:36:28Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=3610744c6a2324cbffeb49bd2e8a2b7c58a2e9e6'/>
<id>urn:sha1:3610744c6a2324cbffeb49bd2e8a2b7c58a2e9e6</id>
<content type='text'>
Without an explicit default_server, nginx falls back to the first
alphabetically-loaded vhost for any request whose Host or SNI matches no
server_name. Add an explicit catch-all that returns a plain 404 over
HTTP and rejects the TLS handshake (ssl_reject_handshake, nginx &gt;=
1.19.4) so unmatched HTTPS requests are not served a mismatched cert.
</content>
</entry>
<entry>
<title>Removed mosh from base.</title>
<updated>2026-08-03T18:29:15Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-03T18:29:15Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=5ec681720eed1dde9f356d58cff6305b9620629c'/>
<id>urn:sha1:5ec681720eed1dde9f356d58cff6305b9620629c</id>
<content type='text'>
</content>
</entry>
<entry>
<title>openhop/repeater: fixes from first real deploy on ubuntu</title>
<updated>2026-08-02T00:24:23Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-02T00:24:23Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=254577c537e20dbd2e365c63773e2010f3c8374d'/>
<id>urn:sha1:254577c537e20dbd2e365c63773e2010f3c8374d</id>
<content type='text'>
- create spi/gpio groups and udev device rules (Raspberry Pi OS ships them, Ubuntu does not)
- hardware CS via spidev (cs_pin -1); a manual cs_pin on a kernel-owned CE line fails EBUSY
- align RAK6421 pin maps with upstream's hardware catalog: en_pins for the FEM enables,
  gpiod backend on chip 0 (catalog says chip 1, wrong for Pi 4)
- us RF preset is now MeshCore USA/Canada (Recommended): 910.525 / SF7 / BW62.5 / CR5
- set storage.storage_dir; omit mqtt_brokers entirely (any key enables the MQTT pusher)
- fetch radio-settings/radio-presets json into the data dir (pip omits them and the web
  setup wizard's hardware step is empty without them)
- config deployed 0660 with a vaultable repeater.security block so the daemon can persist
  credentials and re-templates no longer wipe them
- sudoers rule so the web UI can restart the service after config changes
</content>
</entry>
<entry>
<title>mattertimectl: hardcode the github user now that the shared var is gone</title>
<updated>2026-08-01T14:53:44Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-01T14:53:44Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=eab57497338b290cf10454a7272c6890de12e824'/>
<id>urn:sha1:eab57497338b290cf10454a7272c6890de12e824</id>
<content type='text'>
</content>
</entry>
<entry>
<title>soju: pass passwords via stdin, restart after sojudb writes</title>
<updated>2026-08-01T14:53:44Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-01T14:53:44Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=3bf3b8424de4ccabd0a97faa6d6a99b1ffb558f8'/>
<id>urn:sha1:3bf3b8424de4ccabd0a97faa6d6a99b1ffb558f8</id>
<content type='text'>
dash echo mangles backslash escapes, and soju authenticates against its
in-memory copy so sojudb changes need a restart to take effect.
</content>
</entry>
<entry>
<title>rpi-base: role-owned netplan replaces wpa_supplicant; auto-upgrades gain -updates and 04:00 reboot</title>
<updated>2026-08-01T14:53:44Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-08-01T14:53:44Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=a457c0932b697fd32febe79c473c7e06dccd355a'/>
<id>urn:sha1:a457c0932b697fd32febe79c473c7e06dccd355a</id>
<content type='text'>
Networking: one 40-net.yaml (eth0 always, wifi via rpi_base_wifi_*) with
cloud-init's 50-cloud-init.yaml deleted after first boot. cloud-init still
renders it on an instance_id bump, and it sorts later, so editing the
card's network-config and bumping meta-data is the headless rescue path.
ssh_deletekeys off so a rescue bump keeps the host keys.

Auto-updates: periodic switches and local policy merged into one
52unattended-upgrades-local; 50unattended-upgrades stays a distro
conffile and debconf's 20auto-upgrades can no longer disable anything.
</content>
</entry>
<entry>
<title>scrypted: pin the docker api version for watchtower</title>
<updated>2026-07-31T19:47:57Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-31T19:47:57Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=553be3539fcc475ca91ffed17208b730eef7c331'/>
<id>urn:sha1:553be3539fcc475ca91ffed17208b730eef7c331</id>
<content type='text'>
watchtower (last released 2023) defaults to docker api 1.25, which
docker.io 29 dropped (minimum 1.44), leaving it in a restart loop.
</content>
</entry>
<entry>
<title>rpi-base: drop the duplicate cloud-init logrotate config</title>
<updated>2026-07-31T19:47:57Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-31T19:47:57Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=3ad4754b313d21d8969405e89f9b9aeb77d799ac'/>
<id>urn:sha1:3ad4754b313d21d8969405e89f9b9aeb77d799ac</id>
<content type='text'>
26.04 ships the same snippet from both cloud-init and cloud-init-base;
the duplicate log entry made every logrotate run exit 1 fleet-wide.
Keep the base one; dpkg remembers conffile deletions across upgrades.
</content>
</entry>
<entry>
<title>pleroma/otp: proxy to 127.0.0.1 instead of localhost</title>
<updated>2026-07-31T19:31:51Z</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-31T19:31:51Z</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/ansible-roles/commit/?id=24f089781c548ad6e4c9e5b3c364e37b8a8a59bb'/>
<id>urn:sha1:24f089781c548ad6e4c9e5b3c364e37b8a8a59bb</id>
<content type='text'>
nginx expands localhost to [::1] too, but the endpoint binds only
127.0.0.1, so every request burned a refused ipv6 connect first and
flooded the error log.
</content>
</entry>
</feed>
