mirror of
https://github.com/fscotto/infra.git
synced 2026-10-03 13:29:58 +00:00
Merge main and reconcile Atlas checklist
This commit is contained in:
21
AGENTS.md
21
AGENTS.md
@@ -70,6 +70,8 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
||||
- Gitea cutover network configuration before activation:
|
||||
`ansible-playbook ansible/site.yml --limit prometheus --tags gitea_cutover,prometheus_backup --check --diff -e server_gitea_on_atlas=true`
|
||||
and `ansible-playbook ansible/site.yml --limit atlas --tags gitea --check --diff`
|
||||
- Atlas daily Navidrome music copy:
|
||||
`ansible-playbook ansible/site.yml --limit atlas --tags music_sync --check --diff`
|
||||
- Atlas network/share hardening:
|
||||
`ansible-playbook ansible/site.yml --limit atlas --tags hardening,sharing --check --diff`
|
||||
- Atlas ZFS snapshot retention and scrub timers:
|
||||
@@ -177,7 +179,9 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
- `profile_backend_phase1` temporarily runs rootless Navidrome and Syncthing on Atlas until Uranus replaces
|
||||
them. It binds only to Atlas' LAN IP, never `wg0`; Navidrome and the Syncthing GUI admit only Aegis as
|
||||
the source-NAT gateway, while native Syncthing ports admit the configured LAN. It initializes fresh
|
||||
state only and never migrates or deletes source application data.
|
||||
state only and never migrates or deletes source application data. The enabled rootless
|
||||
`atlas-music-sync.timer` copies `/zpool/archive/Music` to `/zpool/media/music` daily at 00:45
|
||||
Europe/Rome without deleting destination files; it requires both datasets to be mounted.
|
||||
- `wireguard_overlay` manages `wg0` between Prometheus (`10.0.0.1`) and Aegis (`10.0.0.2`). It persists private
|
||||
keys only on their respective hosts, exchanges only derived public keys through Ansible, and verifies a real peer
|
||||
handshake. Prometheus opens `51820/udp`; Aegis is the LAN gateway. Its persistent IPv4 forwarding, narrowly scoped
|
||||
@@ -266,13 +270,24 @@ successfully. The first monthly scrub remains a runtime check.
|
||||
files are deployed; live read-only SSH, shell denial, and write denial were verified. On 2026-09-30
|
||||
a manual export, Atlas pull, checksum verification, and temporary restore passed; both SQLite
|
||||
databases passed integrity checks and a restored Git repository passed `git fsck`. Both daily
|
||||
timers are enabled for 02:00/03:00 Europe/Rome; their first scheduled results remain unverified.
|
||||
timers are enabled for 02:00/03:00 Europe/Rome. On 2026-10-01 their first scheduled export and
|
||||
pull succeeded: Atlas verified the payload checksum and published `20261001T000001Z` as `latest`.
|
||||
- [x] Decide whether a common SMB/NFS namespace is required: no. `Archive` (SMB) and `photobook` (NFS)
|
||||
remain intentionally distinct; `docs/atlas-sharing-decision.md` records the decision. No ACL or export
|
||||
change is authorized by this decision.
|
||||
|
||||
### Priority 3 - Service expansion
|
||||
- [ ] After data protection and recovery are validated, populate `/zpool/media/music` and validate Navidrome.
|
||||
- [x] Populate `/zpool/media/music` and validate Navidrome. On 2026-09-30, 21,158 files
|
||||
(93,937,810,350 regular-file bytes) were copied from `/zpool/archive/Music` using a temporary
|
||||
ZFS snapshot; a checksum-based rsync dry run found no differences or extra files. Navidrome saw
|
||||
all files through its read-only mount, completed a scan, indexed 18,168 tracks, and responded
|
||||
over HTTP. Some imported playlists still reference obsolete Windows paths. The source was left
|
||||
intact and the temporary snapshot was removed.
|
||||
- [x] Schedule a daily, non-deleting copy from `Archive/Music` to the separate Navidrome music
|
||||
dataset. The rootless `atlas-music-sync.timer` is enabled for 00:45 Europe/Rome; a manual
|
||||
idempotent service run succeeded on 2026-10-01. The first scheduled run triggered at
|
||||
00:45 CEST on 2026-10-02 and exited successfully (`Result=success`, status 0); the next
|
||||
run is scheduled for 2026-10-03 00:45 CEST.
|
||||
- [x] Design the staged Prometheus-to-Atlas Gitea migration in `docs/atlas-gitea-migration.md`.
|
||||
The approved topology keeps NPM on Prometheus and moves HTTPS and public SSH (TCP/2222) together;
|
||||
Gitea runs as an `admin`-owned rootless user Quadlet on Atlas with an internal `gitea` user.
|
||||
|
||||
@@ -311,7 +311,12 @@ l'interfaccia amministrativa resta su `127.0.0.1:81`, raggiungibile via tunnel S
|
||||
Atlas ospita temporaneamente Navidrome e Syncthing rootless fino alla sostituzione con Uranus. I
|
||||
servizi sono inizializzati **ex novo**, senza migrare lo stato precedente, rispettivamente sotto
|
||||
`/zpool/services/data/navidrome` e `/zpool/services/data/syncthing`; la musica in
|
||||
`/zpool/media/music` viene popolata separatamente. Sono vincolati all'indirizzo LAN di Atlas
|
||||
`/zpool/media/music` è stata popolata separatamente da `/zpool/archive/Music` il 2026-09-30;
|
||||
Navidrome ha completato la scansione. Il timer rootless `atlas-music-sync.timer` copia i file nuovi
|
||||
o modificati ogni giorno alle 00:45 Europe/Rome, senza eliminare quelli presenti solo nella
|
||||
destinazione; entrambi i dataset ZFS devono essere montati. La prima esecuzione schedulata è
|
||||
riuscita il 2026-10-02. Alcune playlist originali contengono
|
||||
ancora vecchi percorsi Windows. I servizi sono vincolati all'indirizzo LAN di Atlas
|
||||
(`192.168.178.55`), mai a WireGuard. `wireguard_overlay` collega invece Prometheus (`10.0.0.1`)
|
||||
e Aegis (`10.0.0.2`): le chiavi private restano sui rispettivi host e Ansible scambia solo le pubbliche.
|
||||
Prometheus apre `51820/udp`; Aegis inoltra soltanto il traffico overlay→LAN dichiarato e applica
|
||||
@@ -740,6 +745,7 @@ yamllint ansible/path/to/file.yml
|
||||
podman-compose -f /opt/docker/server/docker-compose.yml config
|
||||
ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff
|
||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
|
||||
ansible-playbook ansible/site.yml --limit atlas --tags music_sync --check --diff
|
||||
```
|
||||
|
||||
## Tag supportati dal playbook
|
||||
|
||||
@@ -296,7 +296,11 @@ Atlas temporarily hosts rootless Navidrome and Syncthing until Uranus replaces t
|
||||
Atlas' LAN address (`192.168.178.55`); WireGuard remains exclusively between Prometheus (`10.0.0.1`)
|
||||
and Aegis (`10.0.0.2`). Their state is initialized ex novo in `/zpool/services/data/navidrome` and
|
||||
`/zpool/services/data/syncthing`; no source application state is migrated. The music library at
|
||||
`/zpool/media/music` is populated separately.
|
||||
`/zpool/media/music` was populated separately from `/zpool/archive/Music` on 2026-09-30;
|
||||
Navidrome completed its library scan. The rootless `atlas-music-sync.timer` copies new and changed
|
||||
files daily at 00:45 Europe/Rome, without deleting destination-only files. Both ZFS datasets must
|
||||
be mounted. Its first scheduled run succeeded on 2026-10-02. Some source playlists still contain
|
||||
obsolete Windows paths.
|
||||
|
||||
The Gitea move from Prometheus to Atlas is tracked in
|
||||
[`docs/atlas-gitea-migration.md`](docs/atlas-gitea-migration.md). The final consistent copy runs in
|
||||
@@ -731,6 +735,7 @@ ansible-lint ansible/roles/<role>
|
||||
yamllint ansible/path/to/file.yml
|
||||
podman-compose -f /opt/docker/server/docker-compose.yml config
|
||||
ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff
|
||||
ansible-playbook ansible/site.yml --limit atlas --tags music_sync --check --diff
|
||||
```
|
||||
|
||||
## Tags
|
||||
|
||||
@@ -151,6 +151,7 @@ backend_phase1_bind_address: "{{ ansible_host }}"
|
||||
backend_phase1_firewalld_zone: "{{ atlas_firewalld_zone }}"
|
||||
backend_phase1_npm_source_ip: "{{ atlas_aegis_ip }}"
|
||||
backend_phase1_syncthing_native_subnet: "{{ atlas_lan_subnet }}"
|
||||
backend_phase1_music_sync_enabled: true
|
||||
|
||||
rocky_manage_openzfs_repo: true
|
||||
rocky_manage_syncthing_binary: false
|
||||
|
||||
@@ -30,3 +30,7 @@ backend_phase1_timezone: Europe/Rome
|
||||
backend_phase1_services:
|
||||
- atlas-navidrome.service
|
||||
- atlas-syncthing.service
|
||||
backend_phase1_music_sync_enabled: false
|
||||
backend_phase1_music_source_dir: "{{ backend_phase1_archive_dir }}/Music"
|
||||
backend_phase1_music_sync_calendar: "*-*-* 00:45:00 Europe/Rome"
|
||||
backend_phase1_user_systemd_dir: "{{ backend_phase1_user_home }}/.config/systemd/user"
|
||||
|
||||
@@ -18,21 +18,28 @@
|
||||
- backend_phase1_app_data_root.startswith('/')
|
||||
- backend_phase1_navidrome_data_dir.startswith(backend_phase1_app_data_root + '/')
|
||||
- backend_phase1_syncthing_root.startswith(backend_phase1_app_data_root + '/')
|
||||
- >-
|
||||
not (backend_phase1_music_sync_enabled | bool) or
|
||||
(backend_phase1_music_source_dir.startswith(backend_phase1_archive_dir + '/')
|
||||
and backend_phase1_music_sync_calendar | length > 0)
|
||||
fail_msg: >-
|
||||
Disable the rootful media-stack gate and provide the Atlas LAN bind
|
||||
address, firewall sources, and absolute ZFS-backed paths before
|
||||
enabling phase one. This role does not manage Prometheus or migrate
|
||||
application data.
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Read the rootless service account
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ backend_phase1_username }}"
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Record rootless service account IDs
|
||||
ansible.builtin.set_fact:
|
||||
backend_phase1_uid: "{{ ansible_facts['getent_passwd'][backend_phase1_username][1] }}"
|
||||
backend_phase1_gid: "{{ ansible_facts['getent_passwd'][backend_phase1_username][2] }}"
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Read system service state before starting rootless Syncthing
|
||||
ansible.builtin.service_facts:
|
||||
@@ -65,6 +72,7 @@
|
||||
loop_control:
|
||||
label: "{{ item.dataset }}"
|
||||
register: backend_phase1_zfs_facts
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Require mounted datasets at the declared paths
|
||||
ansible.builtin.assert:
|
||||
@@ -79,6 +87,23 @@
|
||||
loop: "{{ backend_phase1_zfs_facts.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.dataset }}"
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Inspect the music copy source
|
||||
ansible.builtin.stat:
|
||||
path: "{{ backend_phase1_music_source_dir }}"
|
||||
register: backend_phase1_music_source_stat
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Require an existing music source directory
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- backend_phase1_music_source_stat.stat.isdir | default(false)
|
||||
fail_msg: >-
|
||||
{{ backend_phase1_music_source_dir }} must exist before enabling the daily music copy.
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Enable lingering for the rootless service account
|
||||
ansible.builtin.command:
|
||||
@@ -87,12 +112,14 @@
|
||||
- enable-linger
|
||||
- "{{ backend_phase1_username }}"
|
||||
creates: "/var/lib/systemd/linger/{{ backend_phase1_username }}"
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Start the rootless user systemd manager
|
||||
ansible.builtin.systemd:
|
||||
name: "user@{{ backend_phase1_uid }}.service"
|
||||
state: started
|
||||
when: not ansible_check_mode
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Create rootless Quadlet and application directories
|
||||
ansible.builtin.file:
|
||||
@@ -113,6 +140,43 @@
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
|
||||
- name: Install rsync for the daily music copy
|
||||
ansible.builtin.dnf:
|
||||
name: rsync
|
||||
state: present
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Create the rootless user systemd directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ backend_phase1_user_systemd_dir }}"
|
||||
state: directory
|
||||
owner: "{{ backend_phase1_username }}"
|
||||
group: "{{ backend_phase1_user_group }}"
|
||||
mode: "0700"
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Install the daily music copy service
|
||||
ansible.builtin.template:
|
||||
src: atlas-music-sync.service.j2
|
||||
dest: "{{ backend_phase1_user_systemd_dir }}/atlas-music-sync.service"
|
||||
owner: "{{ backend_phase1_username }}"
|
||||
group: "{{ backend_phase1_user_group }}"
|
||||
mode: "0644"
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Install the daily music copy timer
|
||||
ansible.builtin.template:
|
||||
src: atlas-music-sync.timer.j2
|
||||
dest: "{{ backend_phase1_user_systemd_dir }}/atlas-music-sync.timer"
|
||||
owner: "{{ backend_phase1_username }}"
|
||||
group: "{{ backend_phase1_user_group }}"
|
||||
mode: "0644"
|
||||
when: backend_phase1_music_sync_enabled | bool
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Render the rootless Navidrome Quadlet
|
||||
ansible.builtin.template:
|
||||
src: atlas-navidrome.container.j2
|
||||
@@ -140,6 +204,7 @@
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ backend_phase1_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ backend_phase1_uid }}/bus"
|
||||
when: not ansible_check_mode
|
||||
tags: [music_sync]
|
||||
|
||||
- name: Permit NPM access to phase-one web interfaces through Aegis
|
||||
ansible.posix.firewalld:
|
||||
@@ -186,3 +251,19 @@
|
||||
when:
|
||||
- backend_phase1_start_services | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
- name: Enable the daily music copy timer
|
||||
become_user: "{{ backend_phase1_username }}"
|
||||
ansible.builtin.systemd:
|
||||
name: atlas-music-sync.timer
|
||||
scope: user
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ backend_phase1_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ backend_phase1_uid }}/bus"
|
||||
when:
|
||||
- backend_phase1_music_sync_enabled | bool
|
||||
- not ansible_check_mode
|
||||
tags: [music_sync]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Copy Atlas Archive music to the Navidrome library
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStartPre=/usr/bin/mountpoint -q {{ backend_phase1_archive_dir }}
|
||||
ExecStartPre=/usr/bin/mountpoint -q {{ backend_phase1_music_dir }}
|
||||
ExecStartPre=/usr/bin/test -d {{ backend_phase1_music_source_dir }}
|
||||
ExecStart=/usr/bin/rsync -aH --no-perms --no-owner --no-group --delay-updates --stats -- {{ backend_phase1_music_source_dir }}/ {{ backend_phase1_music_dir }}/
|
||||
@@ -0,0 +1,11 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Schedule the daily Atlas Navidrome music copy
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ backend_phase1_music_sync_calendar }}
|
||||
Persistent=true
|
||||
Unit=atlas-music-sync.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user