mia
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| GITEA_URL | No | Optional Gitea URL | |
| MIA_REPOS | No | Local checkouts for `repo_script` / runbook `local` steps | |
| BESZEL_URL | No | Optional Beszel URL | |
| GITEA_TOKEN | No | Optional Gitea token | |
| BESZEL_TOKEN | No | Optional Beszel token | |
| MIA_READONLY | No | `true` = no applies. Makes every write tool refuse to apply. | |
| MIA_INVENTORY | No | Path of fleet.yaml | ~/.config/mia/fleet.yaml |
| MIA_SSH_REUSE | No | ssh behaviour | |
| MIA_STATE_DIR | No | Journal, snapshots, oversized outputs | ~/.local/state/mia |
| MIA_GIT_AUTHOR | No | Local checkouts for `repo_script` / runbook `local` steps | |
| MIA_PROBE_FROM | No | `ru_probe` vantage host | |
| MIA_CONCURRENCY | No | ssh behaviour | |
| MIA_SSH_OPTIONS | No | ssh behaviour | |
| JELLYFIN_API_KEY | No | Optional Jellyfin API key used by the home media integrations | |
| MIA_PROBE_IFACES | No | `ru_probe` ISP interfaces (e.g. `wan0=ISP-A,wan1=ISP-B`) | |
| MIA_PROBE_CONTROL | No | `ru_probe` datacenter control host | |
| MIA_REMNAWAVE_MCP | No | Command that starts mcp-remnawave for panel steps of runbooks | |
| TAILSCALE_API_KEY | No | Optional Tailscale API key | |
| REMNAWAVE_BASE_URL | No | Remnawave panel base URL, read-only use | |
| MIA_EDGE_SCRIPT_URL | No | URL of the public install script used by `node_bootstrap`, pinned to a reviewed commit | |
| REMNAWAVE_API_TOKEN | No | Remnawave panel API token, read-only use | |
| CLOUDFLARE_API_TOKEN | No | Optional Cloudflare API token (DNS records) | |
| MIA_TUNING_SCRIPT_URL | No | URL of the public install script used by `tuning_apply`, pinned to a reviewed commit |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| fleet_hostsA | The inventory (fleet.yaml): hosts with address, roles, tags, groups, services. No host is contacted. Start here to learn the names used by every other tool. |
| fleet_discoverA | Cross-check the inventory with the tailnet (local |
| fleet_statusA | One-screen health of the fleet: every host probed over ssh in parallel — uptime, load, memory, disk, pending updates/reboot, remnanode container, certificate days, Tailscale, panel state and users online — plus the codes of open issues (details: fleet_audit). With target all it also lists every tailnet device that is not in fleet.yaml: Linux machines are probed with the default ssh user, others shown with their online state. |
| fleet_auditC | Audit hosts against the fleet baseline (as the nodes run now: vps-edge-run.sh bootstrap + ufw-blacklist.sh ipsets, MGMT-PORTS chain, beszel-agent, security updates automatic with manual reboots) and general health: firewall (ufw off, management ports public, other public ports listed), Tailscale, sshd hardening, fail2ban jails, node_exporter, beszel-agent, MGMT-PORTS, ipset blacklists, auto-updates, tuning (bbr/fq, conntrack), remnanode container/compose/SECRET_KEY, certificate expiry, geo updater, disks, memory, temperatures, pending security updates, reboot, failed units, NTP, inventory services, panel node state. Findings have stable codes (policy.skip in fleet.yaml mutes them). |
| host_inspectB | Everything about one host in one call: system, pending updates, systemd units, listening sockets, firewall, Tailscale, docker, remnanode (compose, certificate, geo files), inventory services, tuning — plus audit findings and the matching panel node. |
| host_logsA | Tail logs on a host. source: a service name (container stdout / systemd journal of the unit — e.g. remnanode, fail2ban, tailscaled), a log file name (remnanode.log = xray logs in /var/log/remnanode, auth, syslog, apt, install-*), or explicit unit:, container:, file:, journal (whole journal). |
| host_portsB | What is reachable on a host and from where: every listening socket classified as local / tailnet-only / public-allowed / blocked by ufw / PUBLIC because ufw is off, with the process name. Plus the public UFW allows. |
| host_firewallB | Firewall detail of a host: |
| host_dockerC | Docker on a host: containers (state, image, restarts), compose projects, disk usage of images/volumes/build cache. |
| host_file_readA | Read a file on a host (as root when sudo is configured) or list a directory. Secrets (SECRET_KEY, private keys, tokens, passwords) are masked. |
| host_connectionsA | Established TCP connections per host on a local port (default 443), total and by remote address family — to watch traffic move from an old ingress to a new one, or drain before removing a node. |
| host_net_checkA | Network checks run FROM hosts (in parallel): tcp connect, tls (TLS1.3 + h2 — what a REALITY dest needs), http timings, dns, ping, trace. Use to pick/verify a REALITY dest, check a node reaches the panel, or see if a destination is filtered from a location. |
| host_services_discoverA | What runs on a host that the inventory does not know yet: docker compose projects (with their files), standalone containers, non-stock systemd services, and known apps with an API (Jellyfin, Tdarr) — returned as ready |
| tuning_statusB | Kernel/network tuning state from the owner's vps-network-tuning-script ( |
| ru_probeA | Is an address reachable from Russia (or any censored network): a TLS handshake from a vantage host through EVERY local ISP interface (MIA_PROBE_FROM, MIA_PROBE_IFACES) with the address forced (--resolve, not DNS), plus a control from a datacenter host (MIA_PROBE_CONTROL). Verdict per target: ok | partial (one ISP) | blocked-ru (both ISPs fail, datacenter passes = DPI (TSPU) blocks address:port — roll the DNS back first) | down. Read-only, one handshake per pair; the gate before and after any address switch (ingress, new node). |
| fleet_scheduleA | Everything that runs on a schedule, Cronicle-style: systemd timers and cron jobs on every host — schedule, last run (when, result, duration), next run, failures first. cron does not log results, so for cron only the last start (journal/syslog) is known. Stock OS jobs (apt-daily, fstrim, logrotate …) are hidden unless all=true or they fail. |
| schedule_toggleA | Switch a scheduled job off / on, or run a timer now (the «Расписание» buttons): a systemd timer — disable --now / enable --now (+ reset-failed of its service), run = start its service; a cron job — the exact line in /etc/crontab, /etc/cron.d/* or a user crontab is commented out with «#mia-off » (enable removes the marker; a copy of the file goes to /root/mia-backup first). Reversible. |
| cron_loggingA | Make every cron job write its output to a log and rotate those logs. A line that already appends to a file keeps it and the file joins rotation unless another logrotate config covers it; output to /dev/null or nowhere → >> /var/log/cron/.log 2>&1; a pipe into logger stays in syslog. Rotation: /etc/logrotate.d/cron-jobs (weekly, 8 copies, compress, copytruncate). Every line also runs through /usr/local/sbin/mia-cronrun, which records start, end and exit code so fleet_schedule shows a result for cron jobs (the command itself is unchanged). Stock OS cron files are left alone; copies of touched files go to /root/mia-backup; a logrotate error after the change rolls the rotation file back. Roll out a few hosts at a time and look at fleet_schedule in between. |
| fleet_snapshotA | Record the configuration state of hosts (packages, enabled units, containers/images, ufw rules, listening ports, sshd, users/sudo/authorized keys, crontabs, sysctl, certificates, tracked config files …) to the local state dir, and report what changed since the previous snapshot. Run it on a schedule (e.g. daily cron |
| fleet_changesA | What changed on hosts: diff of recorded snapshots (latest vs previous, or vs the first one after |
| snapshots_listC | Recorded snapshots of a host (newest first), or the hosts that have any. |
| journal_listB | What this server did to hosts: every action with time, host, outcome and planHash (newest first). |
| job_statusA | State of a detached remote job (node_bootstrap): running or finished with exit code, the log tail, a Tailscale login URL if the node waits for authorization. Tries the host over Tailscale (inventory address) first, then the public bootstrap address. Without jobId: lists known jobs. |
| service_actionA | restart / start / stop / update (compose pull + up -d) / recreate a service on hosts — a compose project, systemd unit or container from the inventory (remnanode, certbot, remnawave, tailscaled, fail2ban, node_exporter, docker … or your own). Rolling by default (batch 1 for disruptive actions), verified: the service must be up again. |
| remnanode_updateA | Update remnanode (remnawave/node image) on nodes: compares the running image with Docker Hub, then per node |
| cert_renewA | Renew the remnanode TLS certificate with the docker certbot from vps-edge-run.sh ( |
| geo_updateB | Run the roscomvpn geoip/geosite updater now (update-roscomvpn-geo.service from vps-edge-run.sh) and check it succeeded. |
| blacklist_updateB | Refresh the ipset blacklists now with the fleet script /usr/local/bin/ufw-blacklist.sh (normally run by /etc/cron.d/ufw-blacklist every 2 h) and show the entry counts of the sets afterwards. |
| updates_policyA | Bring hosts to the fleet update policy: security updates install automatically (unattended-upgrades, stock security-only origins), NO automatic reboots — reboots stay manual (host_reboot). Writes /etc/apt/apt.conf.d/20auto-upgrades and 99zz-mia-policy, enables apt-daily timers. apt-based hosts only. |
| fail2ban_unbanA | Unban an IP in fail2ban (all jails, or one) on hosts — e.g. after locking yourself out or a false positive. |
| ufw_ruleA | Add or delete a UFW rule on hosts, e.g. open a new public port for an inbound (allow 8443/tcp on the internet interface) or close one. Public exposure of 22/2222/5443/5444/9100 is blocked unless allowDangerous. The rule list before the change is shown in the dry run. |
| apt_upgradeA | Install pending apt updates on hosts (noninteractive, keeps existing config files). Shows what will be upgraded; warns when docker/containerd are in the list (remnanode restarts). Optional reboot afterwards when the kernel asks for it. Rolling, batch 1 by default. |
| host_rebootA | Reboot hosts (shutdown -r +delay) and wait for each to come back over ssh with remnanode running before the next one (rolling, batch 1). The dry run shows users online per node (panel) and whether a reboot is pending anyway. |
| host_execA | Run an arbitrary shell command on hosts (bash, through sudo when configured). For anything the dedicated tools do not cover. Output tail per host; the journal keeps the command. Parallel by default (batch 10). |
| tuning_applyA | Apply or roll back the kernel/network tuning of vps-network-tuning-script (initial.sh apply|rollback): BBR+fq, conntrack, NOFILE, swap, journald limits. apply creates a backup in /root/edge-tuning-backup-*; rollback restores the latest (or backupDir). |
| disk_cleanupA | Free disk space on hosts: apt cache, journald vacuum to a size, unused docker images and build cache (never volumes), rotated logs. The dry run shows what takes space now. |
| node_bootstrapA | Turn a fresh Ubuntu server into a node with vps-edge-run.sh (github.com/akadorkin/remnanode-install-script): user + sudo, Tailscale (--ssh, exit node), Docker, hardening (ssh, fail2ban, UFW tailscale-only), tuning, node_exporter, geo updater, certbot certificate for the domain, remnanode with the panel's SECRET_KEY. That is only the start of the current baseline: afterwards ops-scripts fleet-node-seal.sh (ipset blocklists, MGMT-PORTS, node_exporter, beszel-agent, log-upload) — the node_add runbook (scenario_start) runs the whole sequence; fleet_audit shows what is missing. Runs detached on the server over its public IP (the firewall reset cuts public ssh); poll with job_status. The server reboots 3 min after success. Needs the domain's A record pointing at the server. |
| jellyfin_statusA | Jellyfin on a host: version and pending restart, libraries (type, folders — with their host paths — and scan state), who is watching what right now (direct play vs transcode), item counts, scheduled tasks that failed last time or are running. |
| tdarr_statusB | Tdarr on a host: server version/uptime, nodes (paused, workers), libraries (folder, cache, output, watching, processing), global pause, overall statistics. |
| jellyfin_library_add_pathA | Add a folder to an existing Jellyfin library. The folder may be given as a host path (translated through the Jellyfin container mounts) or as the path inside the container; it must exist on both sides. Optionally scans the library afterwards. |
| jellyfin_library_createC | Create a Jellyfin library with folders. Library options (metadata language, savers, fetchers …) can be copied from an existing library of your choice; otherwise Jellyfin defaults apply. |
| jellyfin_scanC | Scan Jellyfin for new/changed files: one library (metadata/images for new items only) or all libraries. |
| jellyfin_task_runC | Start a Jellyfin scheduled task by name or key (e.g. "Scan Media Library", "Extract Chapter Images", "Clean Cache", RefreshLibrary). |
| tdarr_library_addA | Add folders to Tdarr: one new library per folder, cloned from an existing library (same flow/plugin stack, transcode/health-check settings, cache and output unless given), with counters reset. Folders may be host paths (translated through the Tdarr container mounts). The dry run shows every library object that will be inserted. Optionally starts a "find new" scan of each. |
| tdarr_scanA | Scan a Tdarr library: scanFindNew (only new files, default) or scanFresh (re-scan everything). |
| tdarr_pauseA | Pause or resume all Tdarr nodes (global setting), e.g. while the server is busy streaming. |
| repo_scriptA | Run one of your own fleet scripts from a local repo checkout (MIA_REPOS, e.g. ops=~/src/ops-scripts) on hosts: deploy/migrate-blacklist.sh, lock-mgmt-ports-apply.sh, install-beszel.sh … The script (plus files listed in |
| host_copyA | Copy files or directories from one host to others through this machine (certificates, /opt/remnanode/docker-compose.yml with the node key, /etc/log-upload/secrets.env, haproxy.cfg …): what the owner did by hand when moving an ingress or cloning a node. Contents are never shown: the plan lists size and sha256 on the source and on each target. Existing target paths are archived to /root/mia-backup-.tgz first. For one env-style file, |
| tailnet_devicesA | Every device of the tailnet (Tailscale API with TAILSCALE_API_KEY, else this machine's |
| tailnet_device_removeA | Remove a machine from the tailnet (Tailscale API, needs TAILSCALE_API_KEY): the last step of taking a server out of service. The dry run shows the device, when it was last seen and whether it is still in fleet.yaml; an online device or one still in the inventory is refused unless force. |
| tailnet_joinA | Join a server to the tailnet over its public address (hosts..bootstrap in fleet.yaml): for a machine that is already set up but logged out of Tailscale or never joined. The key is a fresh one-time key from the Tailscale API (TAILSCALE_API_KEY), made only on apply and passed through the environment — never in the plan or journal; without the API it falls back to TAILSCALE_AUTH_KEY. Runs |
| dns_recordsB | DNS records in Cloudflare (CLOUDFLARE_API_TOKEN): by name, or every record that points at an IP across all zones — e.g. everything still on an old ingress address. |
| dns_changeA | Change A records in Cloudflare with a dry run: set (name → IP, created or updated; one A record per name), move (every A record on one IP to another — an ingress switch), remove (records by name, or every A record on an IP). The plan shows each record before and after; after apply every record is read back. |
| gitea_runnersB | Gitea Actions runners (GITEA_URL + GITEA_TOKEN): name, online/offline, busy, labels — to spot stale registrations and runners that announce another node's label. |
| gitea_runner_removeA | Delete a runner registration in Gitea (a node taken out of service). Dry run shows the runner; a busy runner is refused. |
| gitea_runner_registerA | Register the Gitea Actions runner on a node with a runner bootstrap script from your own repo checkout (MIA_REPOS; default ops:deploy/bootstrap-node-runner.sh ): a fresh registration token comes from the Gitea API at run time and reaches the script through an environment variable, never the plan or journal. Verified afterwards: act_runner active and the label is exactly node-:host (copied configs announced other nodes' labels before). |
| beszel_systemsC | Systems (cards) in the Beszel hub: name, host, status — to find duplicates a new agent created and cards of removed nodes. |
| beszel_system_editA | Rename or delete a Beszel card, found by exact name or host (tailnet name / IP). Delete only after the agent on that machine is stopped, or it recreates the card. |
| host_file_editA | Edit one of the fleet's registry files on a host the way it is done by hand: clone the entry of a template node for a new node (Prometheus overrides, log-collector inventory, reboot-reminder lists, IP lists) or remove the entries of a removed node (e.g. node_servers.keep.txt). Works on plain lines (an IP inside a one-line list is edited in place), JSON and YAML (comments kept). The dry run shows the diff; the file is backed up (.bak-mia-), written in place (owner and mode kept), optionally a unit is reloaded. |
| fleet_host_editA | Edit a host in fleet.yaml (this machine's inventory, not the server): add, change fields, move between groups, switch off/on (disabled), remove. The dry run shows the diff of fleet.yaml (layout and comments kept) and refuses a result mia could not load; apply writes it with a backup in the state directory and refuses if the file changed since the plan. |
| scenario_listB | Runbooks for whole operations (add a node and bring it to the fleet standard, switch the ingress, remove a node, change xray rules): their parameters, and the runs started so far. With run: the step log of that run. |
| scenario_startA | Start a runbook. Read-only checks run on their own; the first step that changes something comes back with its exact plan (dry run) and waits. Show that plan to the owner and continue with scenario_step only after the owner said yes to that step. |
| scenario_stepA | Answer the step a run is waiting on: apply (run the plan shown — only with the owner's explicit yes for this step), done (a manual step the owner has finished), skip, or retry (re-check / re-plan). Returns what happened and the next step that needs an answer. Runs are saved: resume any time with decision retry. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| morning_check | Daily look at the fleet: health, audit, what changed overnight |
| node_down | Incident: a node is down / disconnected in the panel / users complain about a location |
| rolling_update | Update remnanode on all nodes one by one, then OS updates |
| onboard_node | Bring a fresh VPS into the fleet as a remnanode |
| security_review | Security posture of the fleet: exposure, access, drift |
| weekly_maintenance | Weekly: certificates, geo/blacklist freshness, disks, updates, drift |
| home_check | Home servers: containers, temperatures, disks, Jellyfin and Tdarr state |
| media_add_folder | Add a folder with new media to Jellyfin and Tdarr on a home server |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 63 tools
Tools are organized by clear domain prefixes (fleet_, host_, jellyfin_, tdarr_, dns_, gitea_, tailnet_, scenario_) and each names a distinct resource+action. A few boundaries blur where a fleet-wide tool overlaps host-level ones (fleet_status vs fleet_audit vs host_inspect; fleet_changes vs fleet_snapshot vs snapshots_list; host_firewall vs host_ports), but descriptions clarify the split.
Uniform snake_case with predictable domain prefixes throughout. Minor deviations exist in verb placement (gitea_runner_remove/gitea_runner_register vs gitea_runners; jellyfin_library_add_path vs jellyfin_library_create), but the convention remains readable and largely consistent.
63 tools is far beyond the 25+ heavy threshold, and the surface spans many unrelated subsystems (fleet ops, Jellyfin, Tdarr, DNS, Gitea, Tailscale, runbooks). Even with broad legitimate scope, the sheer count risks overwhelming an agent and makes selection costly.
Near-exhaustive lifecycle coverage: reads, writes, edits, dry-run/plan modalitities, backups, verification, and orchestration runbooks. CRUD plus ops (reboot, service actions, cert renewal, disk cleanup, snapshots, journal) is present across every domain, with no obvious dead ends.