Skip to content
Back to skills

Slv Hermes

CSecurity

Deploy a self-hosted Pyth Hermes price feed API stack (NATS + Beacon + Hermes) on a VPS. Hermes serves Pyth Network's REST/WS price feeds (`/v2/updates/price/latest`, `/v2/price_feeds`, `/ws`) by listening to Pythnet for accumulator messages and to the Wormhole network (via Beacon, a highly-available spy) for VAAs. The recipe builds all three components from source, drops systemd units, and validates `/v2/price_feeds` returns 200.

  • 89 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
devopsrustgobashnodegitapi

Works with

  • cli
  • api

Security analysis

C63/100
  • criticalAccesses sensitive system or user directories
  • criticalReads or references SSH private keys
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add ValidatorsDAO/slv --skill slv-hermes --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Slv Hermes?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Slv Hermes
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/validatorsdao-slv-hermes/badge)](https://www.skillsdirectory.com/skills/validatorsdao-slv-hermes)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: slv-hermes
description: Deploy a self-hosted Pyth Hermes price feed API stack (NATS + Beacon + Hermes) on a VPS. Hermes serves Pyth Network's REST/WS price feeds (`/v2/updates/price/latest`, `/v2/price_feeds`, `/ws`) by listening to Pythnet for accumulator messages and to the Wormhole network (via Beacon, a highly-available spy) for VAAs. The recipe builds all three components from source, drops systemd units, and validates `/v2/price_feeds` returns 200.
---

# SLV Hermes Skill

Ansible recipe + skill docs for deploying a self-hosted Pyth Hermes API
(REST + WebSocket price feeds) on a single VPS.

## What is Hermes?

> Hermes is a web service designed to monitor both Pythnet and the
> Wormhole Network for the next generation of Pyth price updates.
> It supersedes the Pyth Price Service, offering these updates through
> a user-friendly web API.
> — [hermes server README](https://github.com/pyth-network/pyth-crosschain/blob/main/apps/hermes/server/README.md)

Public endpoint is `https://hermes.pyth.network` (10 req/10s/IP rate
limit).  Self-hosting removes the rate limit, lets you pin a region, and
— combined with `slv-pythnet` — gives you full control over the data path.

## Stack components

```
┌──────────────────────────────────────────────────────────────┐
│ VPS (single host)                                            │
│                                                              │
│  nats-server :4222 ◄──┐                                      │
│  (JetStream)          │ writes VAAs                          │
│                       │                                      │
│                  ┌────┴──────────┐         REST/WS           │
│                  │ beacon         │◄────────────────────┐   │
│                  │  spy gRPC :7072│                     │   │
│                  └────┬───────────┘                     │   │
│                       │                                 │   │
│                       │ reads accumulator               │   │
│                       │                            ┌────┴───┐│
│                       └────► Pythnet HTTP/WS ────► │ hermes ││ ◄── clients
│                              (public or self-hosted) │ :7575 ││
│                                                    └────────┘│
└──────────────────────────────────────────────────────────────┘
                       ▲
                       │ Wormhole P2P gossip
                       ▼
                Wormhole guardian network
                (3 bootstrap peers, UDP :8999)
```

| Component | Purpose | Port |
|---|---|---|
| `nats-server` (JetStream) | VAA dedup/stream broker for Beacon | `:4222` |
| `pyth-network/beacon` | HA rewrite of Wormhole `spy`; writes VAAs to NATS, exposes spy gRPC | `:7072` (loopback), `:8999/udp` (P2P) |
| `pyth-network/pyth-crosschain` (apps/hermes/server) | Rust REST/WS API | `:7575` |

## VPS sizing

Hermes is light — the whole stack idles under 1 core and ~1 GB resident.
The constraint is network: Wormhole P2P + Pythnet WS combined run at a
few Mbps each way.

| Spec | Recommended | Minimum |
|---|---|---|
| vCPU | 4 | 2 |
| RAM | 8 GB | 4 GB |
| Disk | 80 GB SSD | 40 GB |
| Network | 1 Gbps (effective 100 Mbps suffices) | 100 Mbps |
| OS | Ubuntu 22.04 / 24.04 | — |

## Directory Structure

```
ansible/
  mainnet-hermes/  — orchestrator + lifecycle playbooks
  cmn/             — shared common playbooks (install_hermes_stack.yml lives here)
jinja/
  mainnet-hermes/  — systemd unit templates (nats/beacon/hermes)
```

## CLI Command ↔ Playbook Mapping

The `slv hermes` (alias `slv h`) CLI commands map to these playbooks.

| CLI Command | Playbook | Description |
|---|---|---|
| `slv h deploy` | `mainnet-hermes/init.yml` | Full Hermes stack deployment |
| `slv h start` | `mainnet-hermes/start_node.yml` | Start NATS → Beacon → Hermes |
| `slv h stop` | `mainnet-hermes/stop_node.yml` | Stop in reverse order |
| `slv h restart` | `mainnet-hermes/restart_node.yml` | Restart all three |
| `slv h update` | `mainnet-hermes/update_hermes.yml` | Rebuild from `hermes_repo_ref` and restart |

## Key Variables (extra_vars)

All optional — defaults work for a public-Pythnet bootstrap.

| Variable | Default | Description |
|---|---|---|
| `hermes_repo` | `https://github.com/pyth-network/pyth-crosschain` | Upstream repo |
| `hermes_repo_ref` | `main` | Branch / tag / SHA |
| `beacon_repo` | `https://github.com/pyth-network/beacon` | Upstream repo |
| `beacon_repo_ref` | `main` | Branch / tag / SHA |
| `nats_version` | `v2.10.22` | nats-server release tag |
| `hermes_rust_toolchain` | `1.82.0` | Rust toolchain for hermes build |
| `hermes_pythnet_http_addr` | `https://pythnet.rpcpool.com` | Pythnet HTTP RPC URL |
| `hermes_pythnet_ws_addr` | `wss://pythnet.rpcpool.com` | Pythnet WS RPC URL |
| `hermes_wormhole_env` | `mainnet` | `mainnet` or `testnet` |
| `hermes_rpc_listen_addr` | `0.0.0.0:7575` | REST/WS bind |
| `hermes_metrics_listen_addr` | `127.0.0.1:7576` | Prometheus metrics bind |
| `beacon_listen_port` | `8999` | UDP/QUIC port for Wormhole P2P |
| `beacon_spy_grpc_addr` | `127.0.0.1:7072` | spy gRPC bind (loopback recommended) |
| `nats_port` | `4222` | NATS client port |

## Usage

### 1. Write inventory

Copy `examples/inventory.yml` to `~/.slv/inventory.mainnet.hermes.yml` and
fill in your VPS:

```yaml
all:
  hosts:
    hermes-1:
      ansible_host: "<vps-ip>"
      ansible_user: "solv"
      ansible_ssh_private_key_file: "~/.ssh/id_rsa"
  vars:
    # Bootstrap with public Pythnet first, swap to self-hosted later.
    hermes_pythnet_http_addr: "https://pythnet.rpcpool.com"
    hermes_pythnet_ws_addr: "wss://pythnet.rpcpool.com"
```

### 2. Deploy

```bash
slv hermes deploy
# or directly:
ansible-playbook -i ~/.slv/inventory.mainnet.hermes.yml \
  ~/.slv/mainnet-hermes/init.yml
```

### 3. Verify

```bash
SOL_USD=ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d
curl -s "http://<vps-ip>:7575/v2/updates/price/latest?ids[]=${SOL_USD}" \
  | jq '.parsed[0] | {price: .price.price, expo: .price.expo, time: .price.publish_time}'
```

### 4. (Optional) Switch to self-hosted Pythnet

After deploying a Pythnet RPC node via the `slv-pythnet` skill, edit the
inventory:

```yaml
    hermes_pythnet_http_addr: "http://<pythnet-rpc-ip>:8899"
    hermes_pythnet_ws_addr:   "ws://<pythnet-rpc-ip>:8900"
```

Then redeploy or restart:

```bash
slv hermes restart
```

## API surface

The standard Hermes API spec applies — anything the public
`hermes.pyth.network` endpoint serves works against the self-hosted
instance.  Common endpoints:

| Endpoint | Purpose |
|---|---|
| `GET /v2/price_feeds` | List all feed IDs + metadata (~3000 entries) |
| `GET /v2/updates/price/latest?ids[]=<id>` | Latest parsed price + binary VAA for given feed ID(s) |
| `GET /v2/updates/price/<publish_time>?ids[]=<id>` | Historical update at a unix timestamp |
| `GET /ws` (WebSocket) | Subscribe to price updates |

Reference: <https://hermes.pyth.network/docs/>

## Known gotchas

- **UDP port `:8999` must be reachable from the public internet.**
  Wormhole P2P uses QUIC over UDP and the gossip mesh expects this peer
  to be externally addressable.  If your VPS provider has a firewall by
  default, add an allow rule for UDP/8999 inbound.
- **Beacon auto-generates `/home/solv/beacon-node.key`** on first start.
  This is a libp2p identity, not a wallet — you can delete it to rotate.
- The receive-buffer warning `failed to sufficiently increase receive
  buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB)` is benign
  but indicates UDP throughput is suboptimal; bump
  `net.core.rmem_max=2097152` if you want it gone.

Files in this skill

  • SKILL.md8 KB
  • examples/inventory.yml2.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.