Files
homelab/ansible/playbooks/provision-agent-host.yml
T
JARVISandClaude Opus 5.5 15aeb8e222 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>
2026-10-22 15:41:54 -05:00

67 lines
2.9 KiB
YAML

---
# Example playbook. The role takes multica_pat as a VALUE — pick ONE of the
# lookups below (or any other) and delete the rest. Changing secret provider
# later is a change to this file only; the role is untouched.
- name: Provision Multica agent hosts
hosts: multica_agent_hosts
become: true
vars:
multica_server_url: "https://multica.wdw-labs.cloud"
multica_app_url: "https://multica.wdw-labs.cloud"
multica_version: "0.6.5"
multica_node_version: "24.22.1"
multica_workspaces_root: "/opt/multica/workspaces"
# No NAS mount under the workspaces root yet, so the mount guard is off.
# When utilidor is mounted, switch both lines together — never set the
# root onto a mount without also setting the guard, or a play that runs
# while the mount is down fills the root filesystem instead of failing.
# multica_workspaces_root: "/mnt/utilidor/multica/workspaces"
# multica_workspaces_root_mount: "/mnt/utilidor"
multica_agent_clis:
- claude
# --- Secret source ----------------------------------------------------
# This homelab keeps Ansible secrets in ansible-vault encrypted files, so
# 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.
#
# HashiCorp Vault (community.hashi_vault):
# multica_pat: "{{ lookup('community.hashi_vault.vault_kv2_get',
# 'multica/agent-host').secret.pat }}"
#
# 1Password (community.general):
# multica_pat: "{{ lookup('community.general.onepassword',
# 'Multica Agent Host PAT', field='credential') }}"
#
# 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