Progress Note — 2026-03-02

Session with Claude (Sonnet 4.6) focused on designing and beginning implementation of a homelab automation system for provisioning Raspberry Pi servers and deploying Docker-based services, driven from the macBook admin workstation with secrets stored in 1Password.


Decisions Made

Vault structure — three vaults with clear separation of concern

VaultPurpose
LabEnvironment-agnostic items (GitHub SSH key, shared secrets)
devLabDev-specific secrets + dev service account token
prodLabProd-specific secrets + prod service account token

Service account tokens are scoped at creation time and cannot be changed. Dev token reads Lab + devLab; prod token reads Lab + prodLab. This enforces env isolation at the 1Password API level — a dev host cannot access prod secrets even if the script is run incorrectly.

1Password item naming conventions

  • Item names use period delimiter: host.rpicm5b, service.n8n
  • Fields intended as env vars are prefixed env.: e.g. env.POSTGRES_PASSWORD
  • Service account token item is named op-service-account in both devLab and prodLab
  • GitHub SSH key lives in Lab vault

Two provisioning scripts, not one

  • provision-host.sh — prepares a bare host (apt, Docker, 1Password CLI, SSH key, git clone)
  • provision-service.sh — deploys a named service to an already-prepared host

Rationale: a host is prepared once; multiple services may be deployed to it over time.

Repo as service catalog

A service is supported if and only if services/<name>/ exists in the repo. Adding support for a new service requires no changes to the provisioning scripts.

Admin username — ops (not tim, not admin)

Generic, non-personal, not on the nose. Consistent across all environments (different usernames per env was considered and rejected — OP token scope already enforces env isolation more reliably).

Secrets stay on the host

The macBook fetches the service account token from the env-specific vault, passes it to the host via OP_SERVICE_ACCOUNT_TOKEN in the SSH command. The host then fetches secrets directly from 1Password and writes .env locally. Secrets never travel over the SSH connection.

install command for file placement

Standard Unix coreutils command — copies a file to a destination and sets permissions/ownership atomically. Used throughout the scripts for placing keys and config files with correct permissions in one step.


Work Completed

  • Created STRATEGY.md documenting vision, vault structure, naming conventions, provisioning flow, and open questions
  • Created provision-host.sh — 7-phase host preparation script
  • Created provision-service.sh — 3-step service deployment script
  • Created common/lib.sh — shared log(), die(), vault_for_env() functions
  • Created common/gen-env.sh — fetches env.* fields from 1Password, emits .env
  • Created services/n8n/ directory (moved docker-compose.yml in)
  • Placed GitHub SSH key on rpicm5b from 1Password via op read pipe + install
  • Verified GitHub SSH authentication from rpicm5b (Hi corneo!)
  • Renamed op-service-account token item in 1Password; rebuilt tokens with correct vault scope after renaming dev vault to Lab

Immediate Next Steps

  • Rebuild rpicm5b image with username ops (currently tim — scripts expect ops)
  • Run provision-host.sh --env dev --host rpicm5b as first real test of the script
  • Run provision-service.sh --env dev --host rpicm5b --service n8n
  • Confirm prod vault name and update lib.sh TODO

Open Questions (carried forward)

See STRATEGY.md for full list. Key ones:

  1. Repo rename — n8n-server-setup → something more general (e.g. homelab)?
  2. Secret merging — host-level and service-level items combined or separate .env?
  3. SSH auth — how does macBook authenticate post-flash (pre-placed key assumed)?