Turn the user's VPS into their 24/7 agent. Copies their GitHub and Cloudflare access, installs Hermes on Claude Code, to chat with on Telegram or Discord, and puts its dashboard on hermes.their-domain behind a sign-in page: email + one-time code sent by Lumail, several users with roles (admin, member), brute-force locks and saved IPs. `/setup-vps discord` connects Hermes to Discord almost on its own: bot, private channel, access role. Use after connect-vps.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Setup Vps?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/melvynx-setup-vps)
---
name: setup-vps
description: Turn the user's VPS into their 24/7 agent. Copies their GitHub and Cloudflare access, installs Hermes on Claude Code, to chat with on Telegram or Discord, and puts its dashboard on hermes.their-domain behind a sign-in page: email + one-time code sent by Lumail, several users with roles (admin, member), brute-force locks and saved IPs. `/setup-vps discord` connects Hermes to Discord almost on its own: bot, private channel, access role. Use after connect-vps.
argument-hint: "[domain, e.g. example.com | discord] [alias, default hermes]"
user-invocable: true
---
Turn the server the user reached with `ssh hermes` into their own agent that runs 24/7: it gets their GitHub and Cloudflare access, runs Hermes on their Claude subscription, answers them on Telegram or Discord, and shows its dashboard at `https://hermes.<domain>`, where every page the server puts online sits behind one gate: a sign-in page open to every address where each person types their email and the 6-digit code Lumail sends them (no password to leave lying around, one access per person, admin or member), locked after repeated failures, where the user can save their home or office IP to skip it next time. The first argument is the domain (ask if missing and they have several in `cf zones list`), the second the alias (default `hermes`; use it everywhere below). With `discord` as the first argument, only connect Hermes to Discord on a server where it already runs: check `ssh -o BatchMode=yes hermes true`, follow [references/discord.md](references/discord.md) from its step 1, and stop there. Do every step yourself and only ask for what needs a human (signing in, creating a bot, reading a code in their inbox). Talk to the user in their language, short messages, no jargon (never say "token", "tunnel", "reverse proxy" or "systemd" to them; say "access", "the secure link" and "the service"). Write every rule you add (the style block, the `## VPS` blocks) in English, whatever language you talk in.
If you got this skill from a link instead of a local folder, install nothing else from the repo: download a file it links only when a step needs it, with `curl -fsSL https://raw.githubusercontent.com/Melvynx/ai-maxxing-skills/main/skills/setup-vps/<path> -o <scratch>/<file>` (for example `scripts/expose.sh`), and read another skill it names only when you reach it, from `https://raw.githubusercontent.com/Melvynx/ai-maxxing-skills/main/skills/<skill-name>/SKILL.md`.
Never print a token, a key or a password from the Mac or the server, except the one gate password of the fallback in step 12, which the user needs to save. Tokens travel through pipes from one machine to the other, and anything the user must type (a bot token, an API key, their own password) they type in their own Terminal, never in the chat. Run every remote command as `ssh hermes '<command>' < /dev/null`; when the login user is not `root`, prefix `install` and `expose` with `sudo`, never `hermes-setup` (it asks for `sudo` itself). Never open a port on the server's firewall and never publish a page any other way than `expose add`.
1. Check what is ready: `ssh -o BatchMode=yes hermes true` (otherwise run the `connect-vps` skill), `gh auth status` (otherwise `setup-github`), `cf auth whoami` says authenticated (otherwise `setup-cloudflare`), and `jq --version` (otherwise `brew install jq`). If `CLOUDFLARE_ACCOUNT_ID` is empty and `cf accounts list` shows several accounts, ask which one and export it.
2. Put the server scripts in place: `scp scripts/hermes.sh scripts/expose.sh scripts/gate.py scripts/geist.woff2 scripts/discord.sh hermes:/tmp/`, then `ssh hermes 'install -m 755 /tmp/hermes.sh /usr/local/bin/hermes-setup && install -m 755 /tmp/expose.sh /usr/local/bin/expose && install -D -m 755 /tmp/gate.py /usr/local/lib/vps-gate/gate.py && install -m 644 /tmp/geist.woff2 /usr/local/lib/vps-gate/ && install -m 755 /tmp/discord.sh /usr/local/bin/hermes-discord'`. Then the skills that teach the agent its tools: `ssh hermes 'mkdir -p /tmp/vps-skills'` and `scp -r server-skills/server-tools server-skills/github-cli server-skills/ai-maxxing-skills ../cloudflare hermes:/tmp/vps-skills/` (from a link, download each of the four `SKILL.md` into `<scratch>/vps-skills/<name>/` first, the Cloudflare one from `skills/cloudflare/SKILL.md`, then `scp -r <scratch>/vps-skills/* hermes:/tmp/vps-skills/`). `hermes-setup`, `expose` and `hermes-discord` print their commands when run without arguments.
3. Give the server the user's GitHub with [scripts/import-keys.sh](scripts/import-keys.sh) `github hermes`. It installs `gh` there, signs it in with the Mac's access through a pipe, makes `git push` work, and copies their git name and email.
4. Give it Cloudflare with `import-keys.sh cloudflare hermes`. It creates an access named `vps-hermes` that only works from the server's own address (tunnels, DNS, Workers, Pages, R2, KV, D1, firewall, cache) and saves it in `~/.config/cloudflare/env` on the server, loaded by every shell. Rerunning it does nothing while that access works; a new one replaces the old.
5. Install Claude Code and Hermes with `ssh hermes 'hermes-setup install'`, in the background (it takes a few minutes); it skips what is already there and prints both versions.
6. Sign Claude Code in on the server. Tell the user to run `ssh -t hermes claude` in their own Terminal, sign in with their Claude account, open the link it shows, approve, paste the code back, then type `/exit`. Check with `ssh hermes 'hermes-setup claude'`: exit code 5 means not signed in yet; on success it prints `default_model`.
7. Choose the model. The recommended one is that `default_model` (Claude Code's default, `claude-opus-5-5` today) with effort `medium`; if the user asked for another model or effort, use theirs. Run `ssh hermes 'hermes-setup model <model> medium'`; it ends with Hermes answering "OK". On exit code 6, follow the model part of [references/hermes.md](references/hermes.md) (most often the subscription's extra usage). Then copy the Cloudflare access into Hermes with `import-keys.sh hermes-env hermes`.
8. Set up the chat. Ask Telegram or Discord (Telegram is the quickest on a phone, Discord the most hands-off and the best for a team). Telegram: follow its part of [references/hermes.md](references/hermes.md); the user creates the bot and types its token in `hermes gateway setup` in their own Terminal, you run `ssh hermes 'hermes-setup gateway'`. Discord: follow [references/discord.md](references/discord.md) from its step 2; you create the bot in Chrome AI and `hermes-discord` sets up the server, its private channel and who may talk to the agent. The step is done when the bot answers their "hello".
9. Give Hermes the user's working style, the `## Output style` block that `setup-brain` wrote in the Mac's global rules: `awk '/^## Output style[[:space:]]*$/{f=1;print;next} f&&/^## /{f=0} f' ~/.claude/CLAUDE.md | ssh hermes 'hermes-setup style'`. It answers `style ok`, `ssh hermes 'hermes-setup check'` then shows `style=set`, and it puts the block in Hermes's `SOUL.md` and in Claude Code's rules on the server, replacing an older one. When the Mac has no such block, ask questions 2, 3, 4 and 9 of the `setup-brain` interview, build the block with its `references/output-style.md` (next to the `setup-brain` skill, or `https://raw.githubusercontent.com/Melvynx/ai-maxxing-skills/main/skills/brain/setup-brain/references/output-style.md`), add it to `~/.claude/CLAUDE.md` too, then run the same command. Then let Hermes work on its own: `ssh hermes 'hermes-setup autonomy'`. It stops asking "may I run this?" before each action (it can also edit its own instruction files), shows a short line in the chat for each tool it uses, and restarts the chat. Tell the user in one line that Hermes now acts without asking, so it only follows their own instructions, and that `/approvals` in the chat brings the questions back.
10. Teach it its tools: `ssh hermes 'hermes-setup skills <domain> /tmp/vps-skills'`. It installs Cloudflare's `cf` command on the server and puts the `server-tools`, `github-cli`, `cloudflare` and `ai-maxxing-skills` skills in `~/.agents/skills`, the one folder for every skill on the server: Hermes reads it and `~/.claude/skills` links to it, so Claude Code on the server sees the same skills. A skill the user already has under the same name is kept. Then give it the default pack of the package: `ssh hermes 'hermes-setup add base'` (the list in [hermes-pack](hermes-pack): the server's tool skills, `create-dashboard`, `connect-tool`, `create-internal-app`; `hermes-setup catalog` marks them `base`). More come with the `install-skills` skill, or by asking Hermes "install the create-landing skill" (`ai-maxxing-skills` teaches it `hermes-setup catalog` and `add`).
11. Build the gate with `ssh hermes 'expose install'`: it installs Cloudflare's connector, Caddy and the sign-in page ([scripts/gate.py](scripts/gate.py), the `vps-gate` service, in the Hermes look with its Geist font), creates the secure link (named after the server) and starts all three, without touching anything else that already runs on the server. Rerun it after updating `gate.py` or `expose.sh`; it keeps the users, the Lumail key, the login and the saved IPs.
12. Turn on the email sign-in, the default. The owner is the first admin: their email (ask, or take `git config --global user.email`), then `ssh hermes 'expose user add <email> admin'`. Codes leave from Lumail with the user's own domain:
- Lumail must work on the Mac: `npx -y lumail@latest auth test`, otherwise run the `setup-lumail` skill first.
- The domain must be able to send: `npx -y lumail@latest --json domain setup <domain>` lists `data.status` and `data.dnsRecords`. Unless the status is `VERIFIED`, add each record in Cloudflare yourself (`cf dns records create`, never proxied), then run `npx -y lumail@latest --json domain verify <domain>` every 30 seconds until `VERIFIED` (up to 10 minutes; DNS can take a while to spread).
- Give the gate its own key, through a pipe, never printed: `npx -y lumail@latest --json tokens create --name "Sign-in <alias>" --permissions emails --no-reuse | jq -r '.token // .data.token' | ssh hermes 'expose mail "<Alias> <login@<domain>>"'` (`<Alias>` is the alias with a capital letter, the name people see in their inbox). It first sends a test email to the owner and only turns codes on if Lumail accepted it: tell the user to check their inbox (spam too). Exit code 5 prints Lumail's reason (often the domain not verified yet); fix it and rerun.
- Fallback, only if the user has no Lumail account and does not want one: `ssh hermes 'expose login <email>'`. It prints a generated password once: give it to the user with their email, tell them to save both in their password manager right now, and never write it anywhere. To choose it, they run `ssh -t hermes 'expose login <email> --stdin'` in their own Terminal (12 characters or more). Email codes can be turned on later with the commands above.
13. Explain the gate in 4 short lines. Every address reaches the sign-in page: they type their email, get a 6-digit code by email (valid 10 minutes), and the session lasts 7 days. Other people get access with "add <email> to the dashboard" (Hermes runs `expose user add <email> member --invite`, which emails them the links): a `member` opens the pages made for the team, an `admin` opens everything, the Hermes dashboard included; `expose user remove <email>` signs someone out at once. Right after an admin signs in from home or the office, the page offers "Sauvegarder cette IP": that network then skips the sign-in, but only after they tick that it is their own network and not a public Wi-Fi (cafe, hotel, coworking, airport); a 4G network, a VPN or a server address is never offered and refused if forced. 5 wrong codes lock that address for 15 minutes (then 30, 60, up to 24 hours), and 30 failures in 15 minutes lock the sign-in for every address that is not saved; `ssh hermes 'expose unlock'` clears the locks. Never save an IP for them yourself; `expose allow <ip>` is only for an address they name and confirm is their own private network.
14. Put the dashboard online: `ssh hermes 'hermes-setup dashboard'`, then `ssh hermes 'expose add hermes.<domain> 9119 --as-local --admins'` (the Hermes dashboard can run anything on the server, so only admins open it). Exit code 4 means `hermes.<domain>` already points somewhere else: ask before picking another name such as `agent.<domain>`. Then `ssh hermes 'expose status'` must list `site=hermes.<domain> admins-only`, `sign_in=code` (or `sign_in=password` with the fallback) and no line starting with `UNPROTECTED`.
15. Prove it from the Mac, after a minute for the address to spread: `curl -s -o /dev/null -w '%{http_code} %{redirect_url}' https://hermes.<domain>` answers `302` to `/.gate/login`. Then ask the user to open `https://hermes.<domain>` in their browser, type their email, then the code they receive (or the password with the fallback), choose whether to save this IP, and say what they see; the Hermes dashboard means done. `ssh hermes 'expose status'` then shows their saved IP if they chose it.
16. Tell every agent, in the `## VPS` block that `connect-vps` wrote (brain `AGENTS.md`, `~/.claude/rules/vps.md`, `~/.codex/AGENTS.md` if Codex is installed), keeping every existing line: Hermes runs on the server and answers on Telegram or Discord, the dashboard is `https://hermes.<domain>`, GitHub and Cloudflare work from the server, the server's skills live in `~/.agents/skills` (read by Hermes, linked in `~/.claude/skills`), and to put any page online always run `ssh hermes 'expose add <name>.<domain> <port>'`, never a tunnel route, an open port or a public bind. People sign in with their email and a code; `expose user add <email> member|admin --invite` gives someone access, `expose user remove <email>` takes it away, and `--admins` on `expose add` keeps a page for admins. New places are saved by the user from the sign-in page; `expose unlock` clears a lock after too many wrong codes.
End with a short check list, a check or a cross on each line: GitHub on the server (account name), Cloudflare on the server, Claude Code signed in, Hermes answers (model and effort), chat works (Telegram or Discord), Hermes has the user's output style, Hermes acts without asking and shows its tools, tool skills installed (`server-tools`, `github-cli`, `cloudflare`, `ai-maxxing-skills`), default pack installed (`hermes-setup catalog` shows every `base` line `installed` or `own`), gate built (sign-in by email code, test email received, owner admin, saved IPs, brute-force lock on), dashboard online at `https://hermes.<domain>`, no unprotected page, agents told. Then tell the user in 3 lines: send a message to their bot to give the agent a task, open `https://hermes.<domain>` to watch it work, ask "put my site online on the server" when they want a new page behind the same gate, "add <email> to the dashboard" to let someone in, and ask "which skills can you install?" to give it more.