Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Remote Execution

Run agentful agents on remote servers through a secure HTTP API using agentful serve.

Default port: 3000 (configurable with --port flag)

Quick Start

# Start server with Tailscale (recommended) - default port 3000
agentful serve
 
# Start server with HMAC authentication on custom port
agentful serve --auth=hmac --secret=$SECRET --https --cert=cert.pem --key=key.pem --port=8080
 
# Start server for local SSH tunnel access
agentful serve --auth=none

Authentication Modes

Tailscale (Recommended)

Zero-configuration networking with WireGuard encryption. Perfect for free VPS deployments.

Benefits:
  • Network-level security (no application auth needed)
  • Works behind NAT/firewalls automatically
  • Free tier supports 100 devices
  • Zero-trust architecture
Setup:
# Install Tailscale on server
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
 
# Start agentful server
agentful serve
 
# Trigger from any device on your tailnet
curl http://server-name:3000/trigger \
  -H "Content-Type: application/json" \
  -d '{"agent":"backend","task":"Review API changes"}'

HMAC Authentication

Signature-based authentication with replay protection. Use for public endpoints and CI/CD integration.

Features:
  • HMAC-SHA256 signatures
  • 5-minute replay protection window
  • Rate limiting (60 req/min per IP)
  • Requires HTTPS in production
Setup:
# Generate strong secret
export SECRET=$(openssl rand -hex 32)
 
# Start server with HTTPS
agentful serve \
  --auth=hmac \
  --secret=$SECRET \
  --https \
  --cert=/etc/letsencrypt/live/example.com/fullchain.pem \
  --key=/etc/letsencrypt/live/example.com/privkey.pem
Client Usage:
import crypto from 'crypto';
 
const secret = process.env.AGENTFUL_SECRET;
const body = JSON.stringify({
  agent: 'backend',
  task: 'Implement user authentication'
});
 
// Generate timestamp
const timestamp = Date.now().toString();
 
// Generate HMAC signature
const signature = crypto
  .createHmac('sha256', secret)
  .update(timestamp + body)
  .digest('hex');
 
// Make request
const response = await fetch('https://example.com:3000/trigger', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Agentful-Signature': signature,
    'X-Agentful-Timestamp': timestamp,
  },
  body,
});

SSH Tunnel

Localhost-only access for traditional SSH tunnel setups.

Setup:
# On server: start server bound to localhost only
agentful serve --auth=none
 
# On client: create SSH tunnel
ssh -L 3000:localhost:3000 user@server
 
# Trigger from local machine
curl http://localhost:3000/trigger \
  -H "Content-Type: application/json" \
  -d '{"agent":"reviewer","task":"Review code quality"}'

API Endpoints

POST /trigger

Execute an agent with a task.

Request:
{
  "agent": "backend",
  "task": "Implement user authentication",
  "timeout": 600000,
  "env": {
    "NODE_ENV": "production"
  }
}
Response:
{
  "executionId": "550e8400-e29b-41d4-a716-446655440000",
  "state": "pending",
  "agent": "backend",
  "task": "Implement user authentication"
}

GET /status/

Check execution status and retrieve output.

Response:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "agent": "backend",
  "task": "Implement user authentication",
  "state": "completed",
  "startTime": 1737536400000,
  "endTime": 1737536723000,
  "duration": 323000,
  "output": "✅ Implemented JWT authentication...",
  "exitCode": 0,
  "error": null,
  "metadata": {
    "name": "backend",
    "description": "Backend development specialist"
  }
}

State values: pending, running, completed, failed

GET /agents

List all available agents.

Response:
{
  "agents": [
    "backend",
    "frontend",
    "tester",
    "reviewer",
    "fixer"
  ]
}

GET /executions

List recent executions with optional filtering.

Query parameters:
  • agent - Filter by agent name
  • state - Filter by state
  • limit - Max results (default: 100)
Response:
{
  "executions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "agent": "backend",
      "task": "Implement user authentication",
      "state": "completed",
      "startTime": 1737536400000,
      "endTime": 1737536723000,
      "duration": 323000,
      "exitCode": 0
    }
  ],
  "count": 1
}

GET /health

Health check endpoint (no authentication required).

Response:
{
  "status": "healthy",
  "uptime": 3600,
  "version": "1.0.0"
}

Running in Background

Using Process Managers (Recommended)

For production deployments, use a process manager like systemd or PM2 instead of the --daemon flag.

systemd (Linux)

# Create systemd service file
sudo tee /etc/systemd/system/agentful.service > /dev/null <<EOF
[Unit]
Description=agentful Remote Execution Server
After=network.target
 
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/project
ExecStart=/usr/bin/npx @itz4blitz/agentful serve --auth=tailscale
Restart=on-failure
RestartSec=10
StandardOutput=append:/var/log/agentful/server.log
StandardError=append:/var/log/agentful/server.err.log
 
[Install]
WantedBy=multi-user.target
EOF
 
# Create log directory
sudo mkdir -p /var/log/agentful
sudo chown ubuntu:ubuntu /var/log/agentful
 
# Start service
sudo systemctl daemon-reload
sudo systemctl enable agentful
sudo systemctl start agentful
 
# Check status
sudo systemctl status agentful
sudo journalctl -u agentful -f

PM2 (Cross-platform)

# Install PM2
npm install -g pm2
 
# Start server
pm2 start "npx @itz4blitz/agentful serve --auth=tailscale" --name agentful
 
# Save process list for auto-restart
pm2 save
pm2 startup
 
# Monitor
pm2 status
pm2 logs agentful
pm2 monit
 
# Stop/restart
pm2 stop agentful
pm2 restart agentful

Manual Background Execution

If you don't have a process manager, use nohup:

# Start server in background
nohup agentful serve --auth=tailscale > .agentful/server.log 2>&1 &
 
# Save PID for later
echo $! > .agentful/server.pid
 
# Check if running
ps aux | grep "$(cat .agentful/server.pid)"
 
# View logs
tail -f .agentful/server.log
 
# Stop server
kill $(cat .agentful/server.pid)
rm .agentful/server.pid

Note: The --daemon flag is currently not supported due to platform-specific process detachment issues. Use one of the methods above instead.

Deployment Examples

Oracle Cloud Free Tier + Tailscale

Oracle offers a generous free tier with 4 ARM cores and 24GB RAM forever.

# 1. Create Oracle Cloud VM
# - ARM Ampere A1 (4 cores, 24GB RAM)
# - Ubuntu 22.04 LTS
 
# 2. Install dependencies
sudo apt update && sudo apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
 
# 3. Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
 
# 4. Install agentful
npm install -g @itz4blitz/agentful
 
# 5. Clone your project
git clone https://github.com/yourname/project.git
cd project
 
# 6. Create systemd service
sudo tee /etc/systemd/system/agentful.service > /dev/null <<EOF
[Unit]
Description=agentful Remote Execution Server
After=network.target
 
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/project
ExecStart=/usr/bin/npx @itz4blitz/agentful serve
Restart=on-failure
RestartSec=10
 
[Install]
WantedBy=multi-user.target
EOF
 
# 7. Start service
sudo systemctl daemon-reload
sudo systemctl enable agentful
sudo systemctl start agentful
 
# 8. Check status
sudo systemctl status agentful
sudo journalctl -u agentful -f

HMAC with Let's Encrypt SSL

# 1. Install Certbot
sudo apt install -y certbot
 
# 2. Get SSL certificate
sudo certbot certonly --standalone -d yourdomain.com
 
# 3. Create systemd service with HMAC
sudo tee /etc/systemd/system/agentful.service > /dev/null <<EOF
[Unit]
Description=agentful Remote Execution Server
After=network.target
 
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/project
Environment="AGENTFUL_SECRET=$(openssl rand -hex 32)"
ExecStart=/usr/bin/npx @itz4blitz/agentful serve \\
  --auth=hmac \\
  --secret=\${AGENTFUL_SECRET} \\
  --https \\
  --cert=/etc/letsencrypt/live/yourdomain.com/fullchain.pem \\
  --key=/etc/letsencrypt/live/yourdomain.com/privkey.pem
Restart=on-failure
RestartSec=10
 
[Install]
WantedBy=multi-user.target
EOF
 
# 4. Start service
sudo systemctl daemon-reload
sudo systemctl enable agentful
sudo systemctl start agentful

Security Considerations

Input Validation

All inputs are validated and sanitized:

  • Agent names: Alphanumeric, hyphens, underscores only
  • Task descriptions: Max 10KB, no shell metacharacters
  • Request bodies: Max 10MB
  • Output storage: Max 1MB per execution
  • Environment variables: Whitelist only (NODE_ENV, DEBUG, LOG_LEVEL)

Rate Limiting

Default limits:

  • 60 requests per minute per IP
  • Returns 429 Too Many Requests with Retry-After header

Replay Protection

HMAC mode uses timestamp-based replay protection:

  • Requests older than 5 minutes are rejected
  • Signature cache limited to 10,000 entries
  • LRU eviction when cache full

Secret Requirements

HMAC secrets must be:

  • Minimum 32 characters (256 bits)
  • Generated with cryptographically secure random (e.g., openssl rand -hex 32)

CORS Configuration

CORS is disabled by default (same-origin only). Enable with:

agentful serve --auth=hmac --cors-origin=https://app.example.com

Monitoring

Health Checks

# Check server health
curl http://localhost:3000/health
 
# Response
{
  "status": "healthy",
  "uptime": 3600,
  "version": "1.0.0"
}

Logs

Server logs include:

  • Request authentication results
  • Execution start/completion
  • Error details
  • Rate limit violations
# View logs with systemd
sudo journalctl -u agentful -f
 
# View logs in PM2
pm2 logs agentful

Metrics

Track key metrics:

  • Execution count and duration
  • Success/failure rates
  • Rate limit hits
  • Memory usage

Troubleshooting

"Invalid agent name" error

Agent names must match /^[a-zA-Z0-9_-]+$/. Check that:

  • Agent file exists in .claude/agents/
  • Name contains only alphanumeric, hyphens, underscores

"HMAC signature verification failed"

Common causes:

  • Clock skew (timestamp >5 minutes old)
  • Wrong secret
  • Signature calculated incorrectly
  • Body modified after signature generated

"Rate limit exceeded"

Server returns 429 Too Many Requests. Wait for Retry-After seconds or implement backoff.

Execution timeouts

Default timeout is 10 minutes. Increase with timeout parameter:

{
  "agent": "backend",
  "task": "Long-running task",
  "timeout": 1800000
}

Client Usage

Once your server is running, use the agentful remote CLI to trigger executions from anywhere.

Configure Remote

# Add a remote server
agentful remote add prod http://my-server:3000
 
# With HMAC authentication
agentful remote add prod https://my-server:3000 --auth=hmac --secret=$SECRET
 
# List configured remotes
agentful remote list

Execute Agents

# Trigger execution
agentful remote exec backend "Fix memory leak" --remote=prod
 
# With live output (polls until complete)
agentful remote exec reviewer "Review PR #123" --remote=prod --follow
 
# Check status
agentful remote status abc123 --remote=prod
 
# List available agents
agentful remote agents --remote=prod
 
# List recent executions
agentful remote executions --remote=prod --state=completed --limit=10

Scheduled Execution (Cron)

Run agents on a schedule using cron or systemd timers.

Cron Examples

# Edit crontab
crontab -e
 
# Run nightly validation at 2 AM
0 2 * * * agentful remote exec reviewer "Run nightly validation" --remote=prod
 
# Security audit every Sunday at 3 AM
0 3 * * 0 agentful remote exec reviewer "Security audit" --remote=prod
 
# Daily backup at midnight
0 0 * * * agentful remote exec backend "Backup database" --remote=prod
 
# Hourly health check
0 * * * * agentful remote health prod || echo "Server down!" | mail -s "Alert" admin@example.com

Systemd Timer

For more control, use systemd timers:

# /etc/systemd/system/agentful-nightly.service
[Unit]
Description=Agentful Nightly Validation
 
[Service]
Type=oneshot
User=ubuntu
ExecStart=/usr/bin/agentful remote exec reviewer "Run nightly validation" --remote=prod --follow
# /etc/systemd/system/agentful-nightly.timer
[Unit]
Description=Run Agentful Nightly Validation
 
[Timer]
OnCalendar=daily
OnCalendar=02:00
Persistent=true
 
[Install]
WantedBy=timers.target
# Enable and start timer
sudo systemctl daemon-reload
sudo systemctl enable agentful-nightly.timer
sudo systemctl start agentful-nightly.timer
 
# Check timer status
sudo systemctl list-timers agentful-nightly.timer

CI/CD Integration

Trigger remote agents from CI/CD pipelines.

GitHub Actions

name: Code Review
on: [pull_request]
 
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger agentful reviewer
        run: |
          npm install -g @itz4blitz/agentful
 
          agentful remote add ci ${{ secrets.AGENTFUL_URL }} \
            --auth=hmac \
            --secret=${{ secrets.AGENTFUL_SECRET }}
 
          agentful remote exec reviewer "Review PR #${{ github.event.pull_request.number }}" \
            --remote=ci \
            --follow

GitLab CI

code_review:
  stage: test
  script:
    - npm install -g @itz4blitz/agentful
    - agentful remote add ci $AGENTFUL_URL --auth=hmac --secret=$AGENTFUL_SECRET
    - agentful remote exec reviewer "Review MR !$CI_MERGE_REQUEST_IID" --remote=ci --follow
  only:
    - merge_requests

curl (No Dependencies)

#!/bin/bash
# trigger-agent.sh
 
SERVER="https://my-server:3000"
SECRET="your-hmac-secret"
AGENT="$1"
TASK="$2"
 
# Generate HMAC signature
TIMESTAMP=$(date +%s000)
BODY='{"agent":"'$AGENT'","task":"'$TASK'"}'
SIGNATURE=$(echo -n "${TIMESTAMP}${BODY}" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
 
# Trigger execution
RESPONSE=$(curl -s -X POST "$SERVER/trigger" \
  -H "Content-Type: application/json" \
  -H "X-Agentful-Signature: $SIGNATURE" \
  -H "X-Agentful-Timestamp: $TIMESTAMP" \
  -d "$BODY")
 
EXEC_ID=$(echo "$RESPONSE" | jq -r '.executionId')
 
echo "Execution started: $EXEC_ID"
 
# Poll for completion
while true; do
  sleep 5
 
  STATUS=$(curl -s "$SERVER/status/$EXEC_ID" \
    -H "X-Agentful-Signature: $(echo -n "${TIMESTAMP}" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)" \
    -H "X-Agentful-Timestamp: $TIMESTAMP")
 
  STATE=$(echo "$STATUS" | jq -r '.state')
 
  if [ "$STATE" = "completed" ]; then
    echo "✓ Execution completed"
    echo "$STATUS" | jq -r '.output'
    exit 0
  elif [ "$STATE" = "failed" ]; then
    echo "✗ Execution failed"
    echo "$STATUS" | jq -r '.error'
    exit 1
  fi
done

Next Steps