# Specialists

A specialist is a Pi subagent that is the only way to operate one area of the homelab. It has one tool, one skill, and one Vaultwarden note. Every other agent is blocked from the area's CLIs and credentials.

| Agent | Area | What it can do |
|---|---|---|
| `astro.arcane` | Arcane container platform (VM 100) and its GitOps repository | Every `arcane-cli` operation except its own `config`, `auth`, `self-update`; Compose changes of GitOps-managed projects through `gitops show|write|commit|push`, then sync |
| `astro.identity` | Zitadel; Pomerium, nginx, lego on VM 104 | Any Zitadel API call (v2, management, admin, auth) with responses redacted; `dmz status|journal|config-template|render|pomerium-restart|nginx-reload|lego-renew` |
| `astro.network` | UniFi, FreeRADIUS (LXC 108), Tailscale (LXC 105) | Read UniFi sites, devices, clients, networks, firewall policies, WANs; RADIUS and Tailscale checks on Pulsar |

Call one with `/run astro.<name> -- <task>` or through the `subagent` tool (both from the bundled `astro-subagents` extension). Specialists may call each other once (`maxSubagentDepth: 1`). Each tool takes `args`, an array of strings; `["--help"]` lists the subcommands.

## How it is secured

- Credentials and CLIs belong to a dedicated macOS user `specialist` on the host. Agents run as the main user and reach the wrappers only through `sudo -n -u specialist -H /usr/local/bin/specialist-cli`, the single passwordless sudo rule. General sudo asks for a password.
- Credentials come from Vaultwarden (`homelab` / `specialists`, one Secure Note per specialist) through `specialist-cli provision`, into `0600` files. Wrappers never call `bw`.
- Arcane is reached with a Zitadel service user: a fresh token per call, exchanged for a 300 s Arcane token. Other services use API keys, a PAT, or SSH keys that the targets pin to forced commands.
- Every call is logged in `/Users/specialist/.specialists/audit.log`.
- The `specialist-gate` extension registers the tool only inside the matching specialist child and blocks other agents from calling the CLIs or reading `/Users/specialist`. `security-guard` carries the same blocks.

Limits: `network` is read-only on UniFi. `identity` cannot deploy Pomerium route changes; those are edits to `02-pulsar-proxmox/dmz/config.yaml` in the homelab repository, deployed with its `deploy.sh`.

## Enable specialists on a host

Host: Cortex (macOS, Homebrew `node`, `bw`, `jq`, git). Steps 1 to 3 are one-time on the services; 4 to 8 are per host.

1. **Vaultwarden.** Account `specialists@monadeo.com` with **Can view** on collection `specialists` in organization `homelab`. Secure Notes with Hidden fields: `arcane`: `ZITADEL_KEY_JSON`, `GITOPS_DEPLOY_KEY_B64`, `ZITADEL_USER_ID` (plain); `identity`: `ZITADEL_PAT`, `SSH_KEY_DMZ_B64`; `network`: `UNIFI_API_KEY`, `SSH_KEY_PULSAR_B64`. `_B64` fields hold `base64 -i <keyfile> | tr -d '\n'`.
2. **Zitadel** (`https://auth.37pla.net/ui/console`). Service user `spc-arcane` with Access Token Type JWT and a JSON key (Keys, New). Service user `spc-identity` with a Personal Access Token and the Instance Owner role (Default settings, Managers).
3. **Arcane** (Settings, Authentication, Federated Credentials). Issuer `https://auth.37pla.net`, Audience `387825541182390275`, Subject exact = `spc-arcane` user ID, Role Admin, Global, lifetime 300. **UniFi**: API key (Settings, API Keys) and two policies allowing Cortex `10.0.40.37` to TCP 22 on `10.0.20.10` and `10.0.10.37`. **GitHub**: the `spc-arcane-gitops` public key as a write deploy key on `astronaute77/arcane`.
4. **Sudo and user**, as an administrator:
   ```sh
   printf 'cortex ALL=(ALL) ALL\n' | sudo tee /etc/sudoers.d/cortex >/dev/null
   printf 'cortex ALL=(specialist) NOPASSWD: /usr/local/bin/specialist-cli\n' | sudo tee /etc/sudoers.d/specialists >/dev/null
   sudo chmod 0440 /etc/sudoers.d/cortex /etc/sudoers.d/specialists && sudo rm -f /etc/sudoers.d/cortex-nopasswd && sudo visudo -c
   sudo sysadminctl -addUser specialist -fullName "Specialists" -home /Users/specialist -shell /bin/zsh -password -
   sudo dscl . create /Users/specialist IsHidden 1 && sudo createhomedir -c -u specialist && sudo chmod 700 /Users/specialist
   sudo install -m 0755 -o root -g wheel <package>/specialists/entry/specialist-cli.sh /usr/local/bin/specialist-cli
   ```
   Add `cortex ALL=(specialist) NOPASSWD: ALL` as `/etc/sudoers.d/specialists-setup` (mode `0440`) for the duration of steps 5 to 7, then delete it.
5. **Bootstrap**, as `specialist` (`sudo -u specialist -H zsh`, `umask 077`): `~/.config/specialists/client.env` with `BW_SERVER=https://vault.37pla.net`, `BW_CLIENTID`, `BW_CLIENTSECRET`; `~/.config/specialists/master-password`. Both `0600`.
6. **Install and configure**, as `specialist`: `sh <package>/specialists/install/install.sh <package> 2.11.1` (`<package>` is the installed pi-astro directory, `~/.pi/agent/npm/node_modules/@astrofoundry/pi-astro`). Write `~/.specialists/config/arcane.json`, `identity.json`, `network.json`:
   ```json
   { "serverUrl": "http://10.0.40.30:3552", "zitadelDomain": "auth.37pla.net", "audience": "387825541182390275",
     "arcaneCli": "/Users/specialist/.specialists/bin/arcane-cli", "arcaneCliConfig": "/Users/specialist/.specialists/config/arcanecli.yml",
     "gitopsRemote": "git@github.com:astronaute77/arcane.git", "gitopsBranch": "main" }
   { "zitadelDomain": "auth.37pla.net", "dmzHost": "10.0.20.10", "dmzUser": "dmz" }
   { "unifiBaseUrl": "https://10.0.10.1/proxy/network/integration", "unifiCertSha256": "<from: specialist-cli network -- unifi fingerprint>", "pulsarHost": "10.0.10.37", "pulsarUser": "root" }
   ```
   Then `sudo -n -u specialist -H /usr/local/bin/specialist-cli provision`; it lists every note and field.
7. **SSH targets.** Install `entry/remote/spc-identity-dmz-entry.sh` as `/usr/local/sbin/spc-identity-dmz-entry` on VM 104 and `entry/remote/spc-network-pulsar-entry.sh` as `/usr/local/sbin/spc-network-pulsar-entry` on Pulsar (`0755`, root). Add each public key to the target's `authorized_keys` (`dmz` on VM 104, root on Pulsar) as:
   `command="/usr/local/sbin/<entry>",no-port-forwarding,no-agent-forwarding,no-pty,no-X11-forwarding ssh-ed25519 ...`
8. **Pi**, as the main user: `~/.pi/agent/specialists.json` (below) and the specialist blocks in `~/.pi/agent/security-guard.json` (`specialist-cli`, `arcane-cli`, `-u specialist` in `operations`; `/Users/specialist/**` in `reads` and `writes`; keep the default rules next to them).
   ```json
   { "entry": "/usr/local/bin/specialist-cli", "user": "specialist",
     "specialists": [
       { "agent": "astro.arcane", "tool": "arcane", "service": "arcane", "timeoutMs": 300000 },
       { "agent": "astro.identity", "tool": "identity", "service": "identity", "timeoutMs": 120000 },
       { "agent": "astro.network", "tool": "network", "service": "network", "timeoutMs": 120000 } ] }
   ```

Check: `sudo -n -u specialist -H /usr/local/bin/specialist-cli arcane --caller test -- projects list` prints JSON; the same for `identity -- dmz status` and `network -- unifi sites`; `/run astro.arcane -- list projects` works in Pi; `bash` with `arcane-cli --help` is blocked in a normal session.

## Rotation and operations

Rotation steps for every field live in the homelab repository, `03-cortex-macmini/ROTATION.md`. After any change in Vaultwarden run `specialist-cli provision`. Host state and the sudo rules are documented in `03-cortex-macmini/README.md`; the forced-command scripts are tracked under `02-pulsar-proxmox/dmz/system/` and `02-pulsar-proxmox/pulsar/system/`.

Planned specialists (not built): `dns`, `edge`, `security` (batch 2); `backup`, `proxmox`, `inference` (batch 3). See `AGENTS.md` for how to add one.
