agentsclimarketplace

Resonate server deployment

Skill resonatehq/resonate-skills/resonate-server-deployment

Agent skills for building with Resonate — durable execution for long-running, crash-safe workflows.

Install
npx -y skills add resonatehq/resonate-skills --skill resonate-server-deployment

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Deploy and configure the Resonate server on Linux systems with systemd. Covers installation, public URL configuration, JWT authentication, and troubleshooting.

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

12.9 KB, as published. Nobody here has run it

Resonate Server Deployment

Overview

Deploy the Resonate server on Linux systems using systemd for process management. This skill covers installation, configuration of public URLs, JWT authentication setup, and common troubleshooting scenarios.

Deploying on GCP instead? See resonate-server-deployment-cloud-run for the Cloud Run + Cloud SQL variant.

Prerequisites

  • Linux system with systemd
  • Root/sudo access
  • curl installed
  • (Optional) openssl for generating JWT keys
  • (Optional) jwt-cli for generating tokens

Quick Start

Basic Deployment (No Auth)

# Download and run
curl -L -o deploy-resonate.sh https://example.com/deploy-resonate.sh
chmod +x deploy-resonate.sh
sudo ./deploy-resonate.sh

Deployment with Public URL and JWT Auth

# Generate RSA key pair first
openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem

# Deploy with configuration (setting the public key enables JWT auth)
sudo RESONATE_SERVER__URL=https://resonate.example.com \
     RESONATE_AUTH__PUBLICKEY=/path/to/public_key.pem \
     ./deploy-resonate.sh

Configuration Options

Environment VariableDefaultDescription
RESONATE_VERSIONv0.9.8Server version the install script downloads
RESONATE_SERVER__PORT8001HTTP API port
RESONATE_SERVER__URL(none)Public URL for the server (e.g., https://resonate.example.com)
RESONATE_AUTH__PUBLICKEY(none)Path to JWT public key file; setting it enables JWT auth (there is no separate enable flag)

Server Flags Reference

The Resonate server binary accepts these flags:

resonate serve [flags]

Flags:
  --server-url string           Public URL for the server (included in response headers)
  --server-port int             HTTP API port (default 8001)
  --auth-publickey string       Path to JWT public key for authentication
  --storage-type string         Storage backend: sqlite or postgres (default sqlite)
  --storage-postgres-url string PostgreSQL connection URL

All flags are also settable via RESONATE_-prefixed environment variables with __ for nesting, e.g. RESONATE_SERVER__URL, RESONATE_AUTH__PUBLICKEY, RESONATE_STORAGE__POSTGRES__URL.

Architecture

                    Internet
                       │
                       ▼
              ┌────────────────┐
              │  Nginx/Caddy   │  (SSL termination)
              │  Port 443      │
              └────────────────┘
                       │
                       ▼
              ┌────────────────┐
              │ Resonate Server│  (systemd service)
              │ Port 8001      │
              └────────────────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
       ┌──────────┐      ┌──────────┐
       │ Worker 1 │      │ Worker 2 │
       └──────────┘      └──────────┘

Systemd Service Configuration

Basic Service File

# /etc/systemd/system/resonate.service
[Unit]
Description=Resonate Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/var/lib/resonate
ExecStart=/usr/local/bin/resonate serve
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=resonate
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Service File with Public URL and Auth

# /etc/systemd/system/resonate.service
[Unit]
Description=Resonate Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/var/lib/resonate
ExecStart=/usr/local/bin/resonate serve \
  --server-url https://resonate.example.com \
  --auth-publickey /etc/resonate/public_key.pem
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=resonate
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

JWT Authentication Setup

1. Generate RSA Key Pair

# Generate private key (keep secret!)
openssl genrsa -out private_key.pem 2048

# Extract public key (deploy with server)
openssl rsa -in private_key.pem -pubout -out public_key.pem

2. Install JWT CLI (for generating tokens)

# macOS
brew install mike-engel/jwt-cli/jwt-cli

# Linux (download binary)
curl -L -o jwt https://github.com/mike-engel/jwt-cli/releases/latest/download/jwt-linux
chmod +x jwt
sudo mv jwt /usr/local/bin/

3. Generate Client Tokens

IMPORTANT: An empty payload {} will DENY all access. You must include a prefix claim.

# Unrestricted access (empty prefix = all promises)
jwt encode --secret @private_key.pem -A RS256 '{"prefix":""}'

# Admin access (alternative way to get full access)
jwt encode --secret @private_key.pem -A RS256 '{"role":"admin"}'

# Restricted to prefix (only access promises starting with "my-app")
jwt encode --secret @private_key.pem -A RS256 '{"prefix":"my-app"}'

# Unrestricted with expiration
jwt encode --secret @private_key.pem -A RS256 --exp='+30 days' '{"prefix":""}'

Prefix claim behavior:

PayloadAccess
{}DENIED (no prefix claim = unauthorized)
{"prefix": ""}ALL promises (empty string = unrestricted)
{"prefix": "my-app"}Only promises starting with my-app
{"role": "admin"}ALL promises (admin role)

4. Configure Server

# Copy public key to server
scp public_key.pem root@server:/etc/resonate/

# Update service (public key present → JWT auth on)
sudo RESONATE_AUTH__PUBLICKEY=/etc/resonate/public_key.pem \
     ./deploy-resonate.sh --update-service

5. Configure Clients

Pass the server URL and a JWT token to the client. TypeScript shown — every SDK takes a URL + token the same way; see the per-SDK skill (resonate-basic-ephemeral-world-usage-{typescript,python,rust,go}) for client init in your language. The RESONATE_TOKEN env var and the Authorization: Bearer <jwt> header are identical across all SDKs.

// SDK client
const resonate = new Resonate({
  url: "https://resonate.example.com",
  token: process.env.RESONATE_TOKEN  // JWT token
});

// Direct HTTP calls
const headers = {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${token}`
};

Why API URL Matters

The --server-url flag tells the Resonate server its public address. This is critical when:

  1. Workers poll for tasks: The server returns URLs that workers use for callbacks
  2. Clients connect from different networks: The server advertises its address
  3. Behind a reverse proxy: Server needs to know the external URL, not localhost

Without --server-url:

  • Server returns http://localhost:8001 in responses
  • External workers can't reach callback URLs
  • Polling may fail with connection errors

With --server-url:

  • Server returns https://resonate.example.com in responses
  • Workers and clients use the correct public URL

Directory Structure

/usr/local/bin/
└── resonate              # Server binary

/etc/resonate/
├── public_key.pem        # JWT public key (if auth enabled)
└── resonate.yml          # Optional config file

/var/lib/resonate/
└── resonate.db           # SQLite database (default)

/etc/systemd/system/
└── resonate.service      # Systemd service file

Common Operations

View Logs

# Follow logs
journalctl -u resonate -f

# Last 100 lines
journalctl -u resonate -n 100

# Logs since boot
journalctl -u resonate -b

# Logs from specific time
journalctl -u resonate --since "2024-01-28 10:00:00"

Service Management

# Status
systemctl status resonate

# Start/Stop/Restart
systemctl start resonate
systemctl stop resonate
systemctl restart resonate

# Enable/Disable on boot
systemctl enable resonate
systemctl disable resonate

# Reload service file after changes
systemctl daemon-reload

Check Configuration

# View current service configuration
systemctl cat resonate

# Check what command is running
ps aux | grep resonate

# Test server is responding
curl http://localhost:8001/promises

Troubleshooting

401 Unauthorized Errors

Symptoms:

  • Clients get 401 errors
  • "missing authorization header" in logs
  • Workers can't poll for tasks

Causes & Fixes:

  1. Auth enabled on server but client has no token — add a token to the client (TypeScript shown; every SDK takes a token the same way — see the per-SDK skills)

    // Add token to client
    const resonate = new Resonate({
      url: "https://resonate.example.com",
      token: process.env.RESONATE_TOKEN
    });
    
  2. Token is expired

    # Generate new token (a payload with no prefix/role claim is DENIED — include one)
    jwt encode --secret @private_key.pem -A RS256 --exp='+30 days' '{"prefix":""}'
    
  3. Wrong public key on server

    # Verify key matches
    openssl rsa -in private_key.pem -pubout | diff - public_key.pem
    
  4. Token signed with wrong private key

    # Decode and verify token
    jwt decode $TOKEN
    

Server Not Responding

Symptoms:

  • curl http://localhost:8001 hangs or refuses connection
  • Service shows as active but port not open

Causes & Fixes:

  1. Service crashed on startup

    journalctl -u resonate -n 50
    # Look for error messages
    
  2. Port already in use

    netstat -tlnp | grep 8001
    # Kill conflicting process or change port
    
  3. Firewall blocking port

    # Check UFW (Ubuntu)
    ufw status
    # Allow internal access only (recommended)
    # Don't expose 8001 directly - use reverse proxy
    

Workers Can't Connect

Symptoms:

  • Workers start but never receive tasks
  • "connection refused" errors

Causes & Fixes:

  1. Wrong RESONATE_URL in worker

    # Should be public URL if server is remote
    RESONATE_URL=https://resonate.example.com
    # NOT http://localhost:8001 (unless worker is on same machine)
    
  2. Missing --server-url on server

    # Server doesn't know its public URL
    # Update service with --server-url flag
    
  3. SSL/TLS issues

    # Test with curl
    curl -v https://resonate.example.com/promises
    # Check certificate is valid
    

Database Issues

Symptoms:

  • "database is locked" errors
  • Data not persisting across restarts

Causes & Fixes:

  1. Multiple processes accessing SQLite

    # Only one server should access the DB
    ps aux | grep resonate
    # Kill duplicates
    
  2. Wrong working directory

    # Check where DB is being created
    find / -name "resonate.db" 2>/dev/null
    # Ensure WorkingDirectory is set in service file
    
  3. Disk full

    df -h
    # Clean up or expand disk
    

Production Checklist

  • Server binary installed and versioned
  • Systemd service file created and enabled
  • --server-url set to public URL
  • JWT authentication enabled (if needed)
  • Public key deployed to /etc/resonate/
  • Reverse proxy configured (Nginx/Caddy)
  • SSL certificate installed
  • Firewall configured (only 443 exposed)
  • Logs rotating (journald handles this)
  • Monitoring configured (port 9090 metrics)
  • Backup strategy for database

Reverse Proxy Configuration

Nginx

server {
    listen 443 ssl;
    server_name resonate.example.com;

    ssl_certificate /etc/letsencrypt/live/resonate.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/resonate.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8001;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # For long-polling
        proxy_read_timeout 120s;
        proxy_send_timeout 120s;
    }
}

Caddy

resonate.example.com {
    reverse_proxy localhost:8001
}

Summary

Key deployment steps:

  1. Install binary from GitHub releases
  2. Create systemd service with appropriate flags
  3. Configure --server-url for public accessibility
  4. Enable JWT auth with --auth-publickey if needed
  5. Set up reverse proxy with SSL
  6. Configure clients with correct URL and token

Critical flags:

  • --server-url: Server's public URL (required for external access)
  • --auth-publickey: JWT public key path (required for auth)

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.