Skip to content

Capstone: The Operations Console

The Operations Console is a Hermes-based server monitor that runs in Docker on a $5 VPS, alerts you on Telegram when something breaks, accepts commands to fix things, and has a desktop plugin showing live system status. Every piece of this capstone draws directly from the five days of the course. You'll build it by composing what you've already learned, not by starting from scratch.

Owl mascot

What you'll learn

  • A cron job runs system health checks every 30 minutes and alerts on Telegram when thresholds are breached
  • A custom skill documents the health check procedure, the agent wrote it after the first successful run
  • A Desktop plugin shows live gateway status, active session, current model, and last health check result
  • Everything runs in a Docker container with resource limits, network isolation, and non-root user

Architecture

The Operations Console utilizes a multi-layered structure connecting scheduled jobs, client gateways, and isolated sandboxes. The following diagram maps the data flow and orchestration paths between these components:

What you're building

  1. A health check skill , a SKILL.md that documents the full procedure: check disk usage (df -h), memory (free -h), running services (systemctl is-active for each service), and recent errors (journalctl --since "30 minutes ago" --priority=err). The agent creates this skill after you walk it through the procedure once.

  2. A cron job , fires every 30 minutes, runs the health check skill, compares results against thresholds (disk > 80%, memory > 90%, any service not active), and delivers alerts to your Telegram home channel.

  3. Telegram command interface , from your phone, you can send:

    • /status , run the health check on demand
    • /restart nginx , restart a service
    • /logs nginx 50 , get the last 50 lines of a service's logs
    • /disk , show disk usage summary
  4. A Desktop plugin , a pane that shows gateway connection status, active model, last health check timestamp, and a "Run Health Check" button that shows results inline.

  5. Docker deployment , the entire setup runs in a Docker container with memory: 512m, network: bridge (needs outbound for Telegram API), user: 1000:1000, and a mounted volume for persistent logs.

Prerequisites

Complete before starting:

Build it

Phase 1: Create the health check skill

Start a Hermes session and walk it through a full health check:

I want you to document a server health check procedure as a reusable skill.
Here's what we need to check every time:

1. Disk usage on all mounted filesystems (df -h). Flag anything above 80%.
2. Memory usage (free -h). Flag anything above 90%.
3. Running services: nginx, postgresql, docker, sshd.
   For each, run 'systemctl is-active <service>'.
   Flag any service that isn't active.
4. Recent errors: 'journalctl --since "30 minutes ago" --priority=err --no-pager'.
   Summarize any errors found.

After each check, produce a summary: what's healthy, what's not, and
recommended actions for anything that's not healthy.

Now save this as a skill called "server-health-check".

Hermes will run through the checks, verify the procedure works, and call skill_manage to write ~/.hermes/skills/server-health-check/SKILL.md. Verify it was created:

bash
hermes skills list | grep server-health

Phase 2: Create the cron job

To automate the monitoring checks, register a cron job with the agent that triggers the health check skill every 30 minutes and forwards alerts to Telegram:

bash
hermes cron create "every 30m" \
  --name "Server Health Check" \
  --prompt "Run the server-health-check skill. If any threshold is breached \
(disk > 80%, memory > 90%, any service not active), send an alert with the \
specific issue and recommended action. If everything is healthy, write a \
one-line summary to /var/log/hermes/health.log." \
  --deliver telegram

Verify that the cron registration is correct and active by manually triggering the job immediately:

bash
hermes cron run <job-id>

Check your Telegram, you should receive the health check result.

Phase 3: Configure Telegram commands

Hermes in the gateway naturally responds to messages. The commands are just prompts:

  • "Run the server-health-check skill and report results" (mapped to /status by convention)
  • "Restart the nginx service and confirm it's active" (mapped to /restart nginx)
  • "Show the last 50 lines of nginx logs" (mapped to /logs nginx 50)
  • "Show disk usage summary from df -h" (mapped to /disk)

For structured command handling, write a skill that maps these patterns:

markdown
# telegram-ops-commands

## When to Use
When the user sends a message matching one of the command patterns below.

## Procedure
- `/status` or "status": Run the server-health-check skill and report results.
- `/restart <service>` or "restart <service>": Run systemctl restart <service>,
  then systemctl is-active <service> to confirm. Report the result.
- `/logs <service> <n>` or "logs <service> <n>": Run journalctl -u <service>
  -n <n> --no-pager and show the output.
- `/disk` or "disk": Run df -h and report usage percentages.

Install the newly written skill module locally into your agent workspace directory:

bash
hermes skills install ./ops-commands

Phase 4: Build the Desktop plugin

Create ~/.hermes/desktop-plugins/ops-console/plugin.js:

javascript
import { jsx } from 'react/jsx-runtime';
import { useValue, host } from '@hermes/plugin-sdk';
import { StatusDot, Badge, Button, ScrollArea } from '@hermes/plugin-sdk';

export default {
  id: 'ops-console',
  name: 'Operations Console',
  register(ctx) {
    ctx.register({
      id: 'ops-pane',
      area: 'panes',
      data: {
        title: 'Ops Console',
        placement: 'right',
        dock: { pane: 'workspace', pos: 'bottom' },
        height: '220px',
      },
      render: () => {
        const gateway = useValue(host.state.gateway);
        const model = useValue(host.state.model);
        const sessionId = useValue(host.state.activeSessionId);
        const cwd = useValue(host.state.cwd);

        return jsx(ScrollArea, {
          children: jsx('div', {
            style: { padding: '12px', display: 'flex', flexDirection: 'column', gap: '8px' },
            children: [
              jsx('div', {
                style: { display: 'flex', alignItems: 'center', gap: '8px' },
                children: [
                  jsx(StatusDot, { status: gateway ? 'online' : 'offline' }),
                  jsx('span', { style: { fontSize: '13px' }, children: `Gateway: ${gateway ? 'Online' : 'Offline'}` }),
                ],
              }),
              jsx(Badge, { children: `Model: ${model || 'None'}` }),
              jsx('div', { style: { fontSize: '12px', color: 'var(--ui-text-secondary)' }, children: `Session: ${sessionId || 'None'}` }),
              jsx('div', { style: { fontSize: '12px', color: 'var(--ui-text-secondary)' }, children: `CWD: ${cwd || 'Unknown'}` }),
              jsx(Button, {
                size: 'sm',
                onClick: async () => {
                  const result = await host.request('hermes.health', {});
                  host.notify({ kind: 'info', message: `Health check: ${result.status}` });
                },
                children: 'Run Health Check',
              }),
            ],
          }),
        });
      },
    });
  },
};

Phase 5: Deploy in Docker

Construct a Dockerfile to package the application, dependencies, custom skills, and environment variables into a secure container:

dockerfile
FROM python:3.11-slim

RUN apt-get update && apt-get install -y curl systemd && \
    curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

RUN useradd -m -u 1000 deploy
USER deploy
WORKDIR /home/deploy

COPY config.yaml /home/deploy/.hermes/config.yaml
COPY .env /home/deploy/.hermes/.env
COPY skills/ /home/deploy/.hermes/skills/

CMD ["hermes", "gateway", "run"]

Build the container image and launch the process with memory constraints and auto-restart policies configured:

bash
docker build -t hermes-ops .
docker run -d \
  --name hermes-ops \
  --memory=512m \
  --cpus=1.0 \
  -v /srv/hermes/logs:/var/log/hermes \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --restart=always \
  hermes-ops

Confirm it worked

  1. Health check skill: hermes skills list | grep server-health shows the skill
  2. Cron job: hermes cron list shows the job with every 30m schedule. Run it manually with hermes cron run <id> and confirm a Telegram alert arrives
  3. Telegram commands: From Telegram, send /status , the agent should run the health check and reply with results
  4. Desktop plugin: Open the Desktop app, the Ops Console pane should be visible at the bottom of the workspace, showing live gateway status
  5. Docker: docker ps shows the container running. docker logs hermes-ops shows the gateway starting up

Module map, where each piece comes from

Capstone componentCourse lesson
Health check skillWriting Skills by Hand
Cron job with Telegram deliveryCron Jobs
Telegram command interfaceTelegram Setup
Desktop pluginDesktop Plugins
Docker deployment with limitsTerminal Backends
Security (approval mode, secret redaction)Security