spaceship-earth: per-host Multica PAT, root LV growth, headless Obsidian (WDWL-22)
- provision-agent-host.yml: multica_pat now comes from vault_multica_pats[inventory_hostname_short], one daemon-<host> token per host. A pre_task asserts the key exists (never prints the value) so a missing entry fails by host name instead of as a no_log "censored". - provision-wong-host.yml: spaceship-earth only. Grows the root partition, PV and LV/filesystem with growpart + community.general.lvg/lvol; idempotent (second run changed=0). Then applies obsidian_headless. - roles/obsidian_headless: Obsidian's official headless Sync client (obsidian-headless 0.0.14) as system user obsync under a sandboxed systemd unit with UMask=0077. Vault at /srv/obsidian/vault: everpure/ and wip/ obsync-only, inbox/ granted to multica via access + default ACLs, vault root traverse-only. Verifies the isolation as multica. Sign-in to Obsidian Sync stays manual (see the role README). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04a0937910
commit
15aeb8e222
14 files changed
+634
-4
No files matched your search
@@ -23,10 +23,13 @@
|
||||
|
||||
# --- Secret source ----------------------------------------------------
|
||||
# This homelab keeps Ansible secrets in ansible-vault encrypted files, so
|
||||
# that is the live form. vault_multica_pat is defined in
|
||||
# group_vars/multica_agent_hosts/vault.yml (encrypted); the indirection
|
||||
# keeps the name greppable and the file swappable.
|
||||
multica_pat: "{{ vault_multica_pat }}"
|
||||
# that is the live form. One PAT per host (named daemon-<host>), held in
|
||||
# the global vault (group_vars/all/vault) as a dictionary keyed by short
|
||||
# hostname:
|
||||
# vault_multica_pats: { haunted-mansion: mul_…, spaceship-earth: mul_… }
|
||||
# A host with no entry fails the pre_task below, by name, before anything
|
||||
# runs. The indirection keeps the name greppable and the file swappable.
|
||||
multica_pat: "{{ vault_multica_pats[inventory_hostname_short] }}"
|
||||
#
|
||||
# Alternatives, if the backend ever consolidates. Each is a one-line
|
||||
# change here; the role is untouched by any of them.
|
||||
@@ -42,5 +45,22 @@
|
||||
# Environment variable on the control node:
|
||||
# multica_pat: "{{ lookup('env', 'MULTICA_PAT') }}"
|
||||
|
||||
# The role's own PAT assert is no_log, so a missing dictionary entry would
|
||||
# surface there only as "censored". This checks key presence, never the
|
||||
# value, so its failure can name the host.
|
||||
pre_tasks:
|
||||
- name: Require a per-host Multica PAT in the vault
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vault_multica_pats is defined
|
||||
- vault_multica_pats is mapping
|
||||
- inventory_hostname_short in vault_multica_pats
|
||||
fail_msg: >-
|
||||
No Multica PAT for {{ inventory_hostname_short }}: add
|
||||
vault_multica_pats['{{ inventory_hostname_short }}'] to
|
||||
group_vars/all/vault.
|
||||
quiet: true
|
||||
tags: [always, multica_pat_check]
|
||||
|
||||
roles:
|
||||
- role: multica_agent_host
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# provision-wong-host.yml — spaceship-earth (WDWL-22)
|
||||
#
|
||||
# spaceship-earth runs exactly ONE agent: wong, the personal vault's librarian
|
||||
# and its only writer. No other agent may be pinned to its runtime. The
|
||||
# isolation is enforced by the OS, not by agent instructions: Obsidian Sync
|
||||
# runs as `obsync`, and Wong's daemon user (multica) can reach inbox/ only.
|
||||
#
|
||||
# Run order on a fresh host:
|
||||
# 1. ansible-playbook playbooks/provision-agent-host.yml --limit spaceship-earth
|
||||
# (multica daemon user, Node.js, multica CLI, Claude Code)
|
||||
# 2. ansible-playbook playbooks/provision-wong-host.yml
|
||||
# (root LV growth, headless Obsidian, vault folder isolation)
|
||||
#
|
||||
# Step 2 needs Node.js and the multica user from step 1, and asserts both.
|
||||
# The Obsidian account sign-in is manual; see roles/obsidian_headless/README.md.
|
||||
# =============================================================================
|
||||
|
||||
- name: Grow the root filesystem to fill the disk
|
||||
hosts: spaceship-earth
|
||||
become: true
|
||||
tags: [wong_disk]
|
||||
|
||||
vars:
|
||||
root_vg: ubuntu-vg
|
||||
root_lv: ubuntu-lv
|
||||
|
||||
tasks:
|
||||
- name: Disk | Find the PV backing the root VG
|
||||
ansible.builtin.set_fact:
|
||||
_root_pv: >-
|
||||
{{ ansible_facts['lvm']['pvs'] | dict2items
|
||||
| selectattr('value.vg', 'equalto', root_vg)
|
||||
| map(attribute='key') | first }}
|
||||
|
||||
- name: Disk | Split the PV into disk and partition number
|
||||
ansible.builtin.set_fact:
|
||||
_root_disk: "{{ _root_pv | regex_replace('p?[0-9]+$', '') }}"
|
||||
_root_partno: "{{ _root_pv | regex_search('[0-9]+$') }}"
|
||||
|
||||
- name: Disk | Install growpart
|
||||
ansible.builtin.package:
|
||||
name: cloud-guest-utils
|
||||
state: present
|
||||
|
||||
# growpart exits 1 with "NOCHANGE" when the partition already fills the disk.
|
||||
- name: Disk | Grow the partition
|
||||
ansible.builtin.command:
|
||||
argv: [growpart, "{{ _root_disk }}", "{{ _root_partno }}"]
|
||||
register: _growpart
|
||||
changed_when: _growpart.rc == 0 and 'CHANGED' in _growpart.stdout
|
||||
failed_when: _growpart.rc != 0 and 'NOCHANGE' not in _growpart.stdout
|
||||
|
||||
- name: Disk | Grow the PV and VG
|
||||
community.general.lvg:
|
||||
vg: "{{ root_vg }}"
|
||||
pvs: "{{ _root_pv }}"
|
||||
pvresize: true
|
||||
|
||||
- name: Disk | Extend the root LV and its filesystem into the free space
|
||||
community.general.lvol:
|
||||
vg: "{{ root_vg }}"
|
||||
lv: "{{ root_lv }}"
|
||||
size: +100%FREE
|
||||
resizefs: true
|
||||
|
||||
- name: Headless Obsidian Sync with vault folder isolation
|
||||
hosts: spaceship-earth
|
||||
become: true
|
||||
tags: [obsidian]
|
||||
|
||||
vars:
|
||||
obsidian_headless_vault_path: /srv/obsidian/vault
|
||||
obsidian_headless_device_name: spaceship-earth
|
||||
obsidian_headless_private_folders:
|
||||
- everpure
|
||||
- wip
|
||||
obsidian_headless_shared_folders:
|
||||
- path: inbox
|
||||
user: multica
|
||||
|
||||
roles:
|
||||
- role: obsidian_headless
|
||||
Reference in new issue
Block a user