Skip to content

Operate MyOS

This section serves two readers at once, by depth:

  • The human superadmin reads the framing — what a change does, the trade-offs, and what to tell the agent. Stay at the top level of each topic.
  • The AI agent sysadmin (or a hands-on operator) opens the Commands blocks for the exact, deterministic steps.

That's deliberate: the superadmin directs; the agent executes. The decisions live here once; the commands are one click deeper, not duplicated. For a machine-readable map of this site, see /draft/llms.txt. For the authoritative deep reference, see Reference and the runbooks.

Authority & guardrails

The agent has broad reach but the human holds the master keys and authorizes anything hard to reverse or outward-facing: pushes to the default branch (they publish), DNS changes, exposing a service to the internet, and anything touching the identity/CA core. Work on a branch; prefer reversible changes. See Direct & Decide.

The fleet & IP map

Every host, its zone, and the ports that matter. Convention: the gateway is ${ip.gateway_suffix} in every zone; servers are static, clients are DHCP. Internal names are split-horizon (resolve only on the LAN or VPN); public names resolve via the edge.

Host Zone Address Role
Firewall (OPNsense) mgmt ${host.opnsense} per-zone gateway, VPN egress, DNS
Hypervisor (Proxmox) mgmt ${host.proxmox} runs the VMs/containers
Control node mgmt ${host.ctrl} runs Ansible; reaches the fleet via a signed cert
Identity (FreeIPA) servers ${host.freeipa} directory, Kerberos, CA, DNS
Fileserver (ZFS/NFS) servers ${host.fileserver} home + media datasets
Registry (zot) servers ${host.registry_ip} internal images + pull-through cache
SSO (Authelia) servers ${host.sso} identity provider / forward-auth
Observability servers ${host.obs} Grafana + metrics + logs
Notifications (ntfy) servers ${host.ntfy} alert + push bus
Origin Caddy dmz ${host.origin_caddy} TLS terminator + reverse proxy
Git (Forgejo) dmz ${host.forgejo} git + CI
Hidden DNS primary dmz ${host.bind_primary} authoritative source, AXFR to edges
Jump host dmz ${host.jump} SSH pivot to the fleet

Network zones: management ${ip.mgmt}, servers ${ip.servers}, DMZ ${ip.dmz}, workstations ${ip.workstations}, IoT ${ip.iot}, guest ${ip.guest}, road-warrior VPN ${ip.wg_rw}, all inside ${ip.space}.

The authoritative source for this table is the host/IP allocation in Design & Decisions plus the provisioning manifest — keep them in sync.

The Ansible workflow

The fleet is provisioned by a clone-and-cloud-init script and configured by Ansible (built-in modules only). Almost every change is: edit code → run the relevant playbook → verify → commit.

Commands — apply config to a host

Run from the control node, as the unprivileged automation user. Limit to one host while testing:

ansible-playbook playbooks/site.yml -l <host>

A coordinated, one-at-a-time OS update across the fleet:

ansible-playbook playbooks/update.yml      # serial: 1

Day-2 tasks

Add or remove a VM

Decision: new service → does it need its own kernel/appliance (a VM) or is it a light single app (a container)? Either way it keeps its own IP, zone, and firewall — consolidation onto shared hosts is out, because it would collapse the trust model.

Commands — add a host
  1. Add a row to the provisioning manifest (provision/vms.conf): name, ID, cores, RAM, disk, zone VLAN, IP, gateway.
  2. Clone it: provision/lab-provision.sh.
  3. Add it to ansible/inventory.ini under a group that's part of all_linux:children.
  4. ansible-playbook playbooks/site.yml -l <host> — the baseline + monitoring agent apply automatically; the host appears in dashboards with no edits.

Add a web service

Decision: first pick its exposure tier (below). Then run it as a container (quadlet) or point at an existing backend, add a vhost to the right Caddy plane, and add DNS.

Commands — expose an app
  1. Run the app (the backends role; set backend_app in the host's vars), or use an existing backend.
  2. Add a vhost to the right plane: origin_caddy (public, Tier 0/1), or the internal proxy planes for private user apps / admin UIs (Tier 2).
  3. DNS: a public name is added on the hidden primary (${host.bind_primary}) and AXFRs to the edges; an internal-only name is a split-horizon override on the firewall's resolver.
  4. To gate it with SSO, set forward_auth on the vhost and add an Authelia access-control rule.

Exposure tiers (the key decision per service)

Tier Meaning Reachable from Trust anchor
0 Public, no auth internet (via edge) n/a (intentionally open)
1 Public + SSO internet (via edge) identity (Authelia)
2 Private LAN / VPN only the network

Default to Tier 2. Always-on VPN on roaming devices makes private nearly free. Promote to Tier 1 only when a service must work without the VPN or is shared with non-VPN people. High-value targets (smart-home, admin UIs) stay Tier 2 regardless.

Egress control

Decision: everything egresses via the VPN by default. Only when a VPN exit IP gets blocked by some destination do you send that destination out the real WAN — exposing your real IP for it alone. The bypass list is empty by default; treat adding to it as a deliberate privacy trade-off.

Secrets

Decision: automation secrets live in the Ansible vault; human break-glass logins live in KeePass. Never paste secrets into chat or commits. Some admin passwords live in both.

Commands — read a secret
scripts/secrets.sh ls          # list KeePass entries + vault vars
scripts/secrets.sh kp <entry>  # reveal a login
ansible-vault view group_vars/all/vault.yml

Updates, backups, certificates, DNS, observability

Decisions, briefly:

  • Updates: OS security patches are automatic per-host; the coordinated cross-fleet update is one box at a time. App versions (SSO, identity major versions) are upgraded deliberately.
  • Backups: daily hypervisor snapshots cover the VMs; app-consistent database dumps ride along; the fileserver pool is protected by ZFS snapshots + replication to a second box.
  • Certificates & SSH-CA: host/service certs come from the identity CA; the SSH-CA is hardware (YubiKey) backed and split — one CA grants access, another grants only host identity.
  • DNS: the hidden primary (${host.bind_primary}) is the source of truth; edits re-sign and AXFR to the edge secondaries automatically. Internal-only names are split-horizon on the resolver.
  • Observability: agents push to the observability host; a new host gets the agent automatically and shows up in dashboards. Any failed service/container pushes an alert to ntfy.

Each of these has a full runbook under Reference › Runbooks.

Direct & Decide (superadmin oversight)

The parts a superadmin owns rather than delegates:

  • Credential custody (four tiers): automation's machine credential, the AI agent's own short-lived hardware-signed certificate, human web logins (KeePass + YubiKey), and WebAuthn. The agent never borrows automation's credential, and its standing certificate cannot root its own workstation or touch the identity core — elevation for those is just-in-time and operator-signed.
  • The kill switch: the agent's access is revocable at any time — certificate expiry, a revocation list, or a live kill script. Its access is deliberate, granted, and auditable, never permanent.
  • Outward-facing approvals: publishing (a push to the default branch deploys public docs), DNS, and any new internet exposure are human-authorized.

The how-to behind these lives in the SSH-CA, control-node, and agent-operating-model runbooks under Reference.