Inventory repository: moving lite-dev's secrets¶
How the live inventory moves out of octopodia-abos into its own private repository,
octopodia-abos-inventory, and lite-dev's
secrets out of group_vars/all, where every host would inherit them (OI-124,
known trap 15). The design is
ADR-0042 ยง1; this is phase 4,
step 3. Done once, on octo-ctl, by hand. Afterwards a second host can be added safely.
flowchart LR
A["Copy today's vault<br/>and hosts.yml aside"] --> B["Merge, pull main"]
B --> C["Clone the inventory<br/>beside the code"]
C --> D["hosts.yml in,<br/>abos_tenants added"]
D --> E["vault_split.py"]
E --> F["Check passes"]
F --> G["Playbooks: nothing changes"]
G --> H["Commit, push"]
Where things end up¶
| File in the inventory repository | Holds | Vault ID |
|---|---|---|
hosts.yml |
The hosts, each with the tenants it carries (abos_tenants) and its plain settings, such as console_tenants |
not secret |
group_vars/all/main.yml |
Shared plain settings (admin keys, ports, domains) | not secret |
group_vars/all/vault.yml |
vault_tailscale_auth_key only: the one secret every host shares |
core |
host_vars/lite-dev/vault.yml |
lite-dev's machine secrets: alerts client secret, tunnel token, registry passwords, and the Console's copies of the tenant's link and detail tokens | core |
tenants/lite-dev/vars.yml |
Tenant zero's settings: slug, name, region | not secret |
tenants/lite-dev/vault.yml |
Tenant zero's own secrets: the n8n encryption key, and its own copies of its link and detail tokens (vault_link_token, vault_detail_token) |
lite-dev |
The Console's copies stay under core so a console playbook run needs only core; the tenant keeps
its own (El, 16 October 2026). Every value is encrypted inline, one at a time, so a diff shows which
secret changed without showing it.
Before you start¶
-
octopodia-abos-inventoryexists with its first commit (README,group_vars/all/main.yml,tenants/lite-dev/vars.yml). Done 16 October 2026. - The
lite-devtenant vault password is in Bitwarden. Done 16 October 2026. You also need thecorepassword. -
octo-ctlcan clone from GitHub as you (your SSH key orgh auth login). - No Junior test round running: step 7 runs the n8n playbook.
Steps¶
-
Before merging, copy today's vault and hosts file aside, on
octo-ctl. The merge removesinfra/inventory/group_vars/allfrom the code repository, andhosts.ymlis not in git at all:cd ~/octopodia-abos && git status --short # must print nothing cp infra/inventory/group_vars/all/vault.yml ~/vault-before-split.yml cp infra/inventory/group_vars/all/main.yml ~/main-before-split.yml cp infra/inventory/hosts.yml ~/hosts-before-split.ymlIf
git statusshows the vault ormain.ymlas changed, the copy onocto-ctlis newer than the repository's: that is fine, the copies you just made are the ones used below. Thengit stashso the pull can run. -
Merge to
mainand pull onocto-ctl:cd ~/octopodia-abos && git pull. From now oninfra/ansible.cfglooks for../../octopodia-abos-inventory/hosts.yml, so playbooks fail until step 4 is done. That is expected. -
Clone the inventory beside the code:
-
Put
hosts.ymlin, and name lite-dev's tenant:cp ~/hosts-before-split.yml ~/octopodia-abos-inventory/hosts.yml diff ~/main-before-split.yml ~/octopodia-abos-inventory/group_vars/all/main.ymlIn
hosts.yml, under thelite-devhost, addabos_tenants: [lite-dev]. Ifdiffprinted anything, copy~/main-before-split.ymlovergroup_vars/all/main.yml: theocto-ctlcopy wins. -
Split the vault. It asks for the
corepassword and then thelite-devone, and prints the names it wrote to each file, never a value:python3 ~/octopodia-abos/infra/scripts/vault_split.py --old ~/vault-before-split.yml \ --inventory ~/octopodia-abos-inventory --host lite-dev --tenant lite-devExpect three lines:
group_vars/all/vault.yml (core)with the tailnet key,host_vars/lite-dev/vault.yml (core)with six names, andtenants/lite-dev/vault.yml (lite-dev)with three. If it says No place in the plan for: ..., stop and send me the names; nothing was written. -
Check the layout, and that Ansible sees it the same way:
python3 ~/octopodia-abos/infra/scripts/inventory_check.py ~/octopodia-abos-inventory cd ~/octopodia-abos/infra ansible lite-dev -m debug -a var=vault_abos_alerts_client_secret --vault-id core@prompt ; clear ansible localhost -m debug -a var=vault_abos_alerts_client_secret --vault-id core@prompt ansible localhost -m debug -a var=vault_tailscale_auth_key --vault-id core@prompt ; clearThe check prints
OK: ... a new host would see only vault_tailscale_auth_key. lite-dev shows a value.localhoststands in for a second host: Ansible gives it only whatgroup_vars/allgives every host, so it must sayVARIABLE IS NOT DEFINED!for the alerts secret and show a value for the tailnet key. That is known trap 15's check passing. -
Run the playbooks: nothing changes.
ansible-playbook playbooks/console.yml --limit lite-dev --vault-id core@prompt ansible-playbook playbooks/n8n.yml --limit lite-dev --check --diff --vault-id core@prompt --vault-id lite-dev@promptThe console run passes its guards, including Check n8n did not restart. In the n8n check run, Write the n8n environment file and Write the Compose file must both say
ok, notchanged, and there must be no RUNNING HANDLER [n8n : Recreate n8n]: the encryption key and everything else came out the same as before. (The env file isno_log, so--diffshows nothing for it;okis the proof.) Only if both areok, run it for real, the same command without--check --diff, and confirm n8n's start time is unchanged: -
Commit and push the inventory, as yourself:
-
Tidy up, deleting nothing. Move the old, ignored
hosts.ymlout of the code checkout, so nothing reads it by mistake:mkdir -p ~/_to_delete && mv ~/octopodia-abos/infra/inventory/hosts.yml ~/_to_delete/hosts-old-code-checkout.ymlKeep
~/vault-before-split.yml,~/main-before-split.ymland~/hosts-before-split.ymlwhere they are until phase 4 step 4's runner is working from the new inventory. They are the way back if anything in the new layout turns out wrong. Then move them into~/_to_delete/the same way. The vault copy is still encrypted withcore; the old vault also stays in the code repository's git history.
From now on, hand runs name the vault IDs they need: core for host and Console work, plus the
tenant's for anything that touches that tenant's n8n.
If something goes wrong¶
| Symptom | Cause | Fix |
|---|---|---|
Unable to parse .../octopodia-abos-inventory/hosts.yml or no hosts matched |
The inventory is not cloned beside the code, or hosts.yml is not in it yet |
Steps 3 and 4 |
vault_split.py: already exists: nothing written |
It ran before | Move host_vars/lite-dev/vault.yml and tenants/lite-dev/vault.yml aside and run it again |
Decryption failed |
Wrong password, or the old file was encrypted with another ID | Try again; the old file needs core |
| The check: encrypted as a whole file | A vault.yml made with ansible-vault create or encrypt |
Rebuild it with encrypt_string, value by value (the inventory README) |
Tenant lite-dev is listed ... but tenants/lite-dev/vars.yml or vault.yml is missing |
Step 5 not done, or run against another folder | Step 5 |
vault_n8n_encryption_key is not set from the n8n playbook |
Run without --vault-id lite-dev@prompt |
Add it |
The n8n check run says changed on the env file |
The key in tenants/lite-dev/vault.yml differs from the one n8n started with |
Do not run it for real. Stop and compare with Bitwarden; n8n cannot read its credentials with another key |