Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

06 – API reference [SPEC]

REST endpoints, database schema, request/response schemas, GolemEvent types, and error codes

Reader orientation: This document is the API reference for Bardo Compute, the managed VM hosting service for Golems (mortal autonomous DeFi agents). It belongs to the Compute layer of Bardo (the Rust runtime for these agents). The key concept before diving in: the API is a REST service backed by Turso (libSQL) for persistence, accepting x402 (EIP-3009 USDC micropayment) headers for provisioning and extension, and emitting typed GolemEvent variants for all state transitions. Terms like Golem, Grimoire, Styx, and PolicyCage are defined inline on first use; a full glossary lives in 00-overview.md § Terminology.


Database schema (Turso / libSQL)

machines table

PRAGMA foreign_keys = ON;

CREATE TABLE machines (
  id            TEXT PRIMARY KEY,           -- UUID v4
  user_id       TEXT NOT NULL,              -- Privy user ID
  machine_id    TEXT,                       -- Fly machine ID (null during provisioning)
  machine_name  TEXT NOT NULL UNIQUE,       -- golem-{nanoid(12)}
  private_ip    TEXT,                       -- Fly 6PN IPv6 (null during provisioning)
  status        TEXT NOT NULL DEFAULT 'provisioning',
                                            -- provisioning|booting|ready|draining|crashed|destroyed
  vm_size       TEXT NOT NULL DEFAULT 'small',
                                            -- micro|small|medium|large
  strategy_type TEXT,                       -- yield-optimizer, dca, keeper, etc.
  tool_profile  TEXT NOT NULL DEFAULT 'full',
  custody_mode  TEXT NOT NULL DEFAULT 'delegation',
                                            -- delegation|embedded|local_key
  ttl_seconds   INTEGER NOT NULL,           -- Total TTL granted (cumulative)
  expires_at    INTEGER NOT NULL,           -- Epoch seconds (authoritative TTL)
  created_at    INTEGER NOT NULL DEFAULT (unixepoch()),
  destroyed_at  INTEGER,
  destroy_reason TEXT,                      -- ttl_expired|user_destroyed|admin_destroyed|
                                            --   provision_failed|boot_timeout|thanatopsis|orphaned
  region        TEXT NOT NULL,
  wallet_address TEXT,                      -- Custody-mode wallet address (Base)
  erc8004_id    TEXT,                       -- ERC-8004 on-chain identity (Base)
  styx_connected INTEGER NOT NULL DEFAULT 0, -- 0 or 1
  cache_version INTEGER NOT NULL DEFAULT 0  -- Incremented on status change
);

CREATE INDEX idx_machines_user_id ON machines(user_id);
CREATE INDEX idx_machines_status ON machines(status);
CREATE INDEX idx_machines_expires_at ON machines(expires_at);
CREATE UNIQUE INDEX idx_machines_machine_name ON machines(machine_name);

billing_events table

CREATE TABLE billing_events (
  id                 TEXT PRIMARY KEY,
  user_id            TEXT NOT NULL,
  machine_id         TEXT NOT NULL REFERENCES machines(id),
  type               TEXT NOT NULL,             -- provision|extension|refund|failed_provision|self_extension
  amount_micro_usdc  INTEGER NOT NULL,
  tx_hash            TEXT,
  from_address       TEXT NOT NULL,
  to_address         TEXT NOT NULL,
  payer_type         TEXT NOT NULL,             -- owner|self|external_user
  nonce              TEXT NOT NULL UNIQUE,
  ttl_seconds_granted INTEGER NOT NULL,
  vm_size            TEXT NOT NULL,
  created_at         INTEGER NOT NULL DEFAULT (unixepoch()),
  metadata           TEXT
);

CREATE INDEX idx_billing_user_id ON billing_events(user_id);
CREATE INDEX idx_billing_machine_id ON billing_events(machine_id);
CREATE INDEX idx_billing_type ON billing_events(type);
CREATE INDEX idx_billing_created_at ON billing_events(created_at);

machine_events table

CREATE TABLE machine_events (
  id        TEXT PRIMARY KEY,
  machine_id TEXT NOT NULL REFERENCES machines(id),
  user_id   TEXT NOT NULL,
  event     TEXT NOT NULL,
  metadata  TEXT,
  created_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE INDEX idx_events_machine_id ON machine_events(machine_id);
CREATE INDEX idx_events_event ON machine_events(event);
CREATE INDEX idx_events_created_at ON machine_events(created_at);

Supporting tables

-- System locks (leader election)
CREATE TABLE system_locks (
  name       TEXT PRIMARY KEY,
  holder     TEXT,
  acquired_at INTEGER
);

-- Grimoire snapshots
CREATE TABLE grimoire_snapshots (
  id          TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  machine_id  TEXT NOT NULL REFERENCES machines(id),
  machine_name TEXT NOT NULL,
  size_bytes  INTEGER NOT NULL,
  r2_key      TEXT NOT NULL,
  created_at  INTEGER NOT NULL DEFAULT (unixepoch()),
  type        TEXT NOT NULL DEFAULT 'periodic'
);

-- User SSH keys
CREATE TABLE user_keys (
  id          TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  public_key  TEXT NOT NULL,
  fingerprint TEXT NOT NULL UNIQUE,
  label       TEXT,
  added_at    INTEGER NOT NULL DEFAULT (unixepoch())
);

-- Strategy compilation quotas
CREATE TABLE user_quotas (
  user_id                TEXT PRIMARY KEY,
  strategy_compilations  INTEGER NOT NULL DEFAULT 0,
  free_compilations_used INTEGER NOT NULL DEFAULT 0,
  updated_at             INTEGER NOT NULL DEFAULT (unixepoch())
);

-- Admin users
CREATE TABLE admins (
  user_id TEXT PRIMARY KEY,
  role    TEXT NOT NULL DEFAULT 'operator',
  added_at INTEGER NOT NULL DEFAULT (unixepoch())
);

-- Daily revenue aggregates
CREATE TABLE daily_revenue (
  date              TEXT PRIMARY KEY,
  total_micro_usdc  INTEGER NOT NULL DEFAULT 0,
  provision_count   INTEGER NOT NULL DEFAULT 0,
  extension_count   INTEGER NOT NULL DEFAULT 0,
  self_extension_count INTEGER NOT NULL DEFAULT 0,
  refund_count      INTEGER NOT NULL DEFAULT 0,
  refund_micro_usdc INTEGER NOT NULL DEFAULT 0,
  avg_ttl_seconds   INTEGER NOT NULL DEFAULT 0,
  active_machines_eod INTEGER NOT NULL DEFAULT 0,
  unique_users      INTEGER NOT NULL DEFAULT 0
);

-- Archived destroyed machines (90+ days)
CREATE TABLE machines_archive (
  -- Same schema as machines + archived_at
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  machine_id TEXT,
  machine_name TEXT NOT NULL,
  private_ip TEXT,
  status TEXT NOT NULL,
  vm_size TEXT NOT NULL,
  strategy_type TEXT,
  tool_profile TEXT NOT NULL,
  custody_mode TEXT NOT NULL,
  ttl_seconds INTEGER NOT NULL,
  expires_at INTEGER NOT NULL,
  created_at INTEGER NOT NULL,
  destroyed_at INTEGER,
  destroy_reason TEXT,
  region TEXT NOT NULL,
  wallet_address TEXT,
  erc8004_id TEXT,
  archived_at INTEGER NOT NULL DEFAULT (unixepoch())
);

REST API reference

Public endpoints (no auth)

MethodPathDescriptionRate limit
GET/healthAPI health check60/min/IP
GET/v1/pricingCurrent pricing tiers30/min/IP
GET/v1/golemsAgent discovery directory30/min/IP
GET/v1/machines/:name/statusPublic machine status30/min/IP
POST/v1/machines/:name/extendPermissionless extension10/min/IP, 30/hr/machine

GET /health

{
  "status": "healthy",
  "version": "1.0.0",
  "checks": { "db": "ok", "fly_api": "ok" },
  "uptime": 86400
}

GET /v1/pricing

{
  "currency": "USDC",
  "chain": "base",
  "chain_id": 8453,
  "tiers": [
    { "vm_size": "micro", "price_per_hour_micro_usdc": 25000, "min_purchase_hours": 1 },
    { "vm_size": "small", "price_per_hour_micro_usdc": 50000, "min_purchase_hours": 1 },
    { "vm_size": "medium", "price_per_hour_micro_usdc": 100000, "min_purchase_hours": 1 },
    { "vm_size": "large", "price_per_hour_micro_usdc": 200000, "min_purchase_hours": 1 }
  ],
  "max_extension_hours": 720,
  "min_extension_hours": 1,
  "operator_address": "0x..."
}

GET /v1/golems

Query params: capabilities, asset, status, region, limit (default 50, max 200), offset.

GET /v1/machines/:name/status

All time values include server_time for clock skew compensation:

{
  "name": "golem-V1StGXR8_Z5j",
  "status": "ready",
  "vm_size": "small",
  "region": "ams",
  "custody_mode": "delegation",
  "vitality": 0.72,
  "behavioral_phase": "stable",
  "styx_connected": true,
  "ttl": {
    "expires_at": 1710500400,
    "remaining_seconds": 36000,
    "server_time": 1710464400,
    "pricing": { "price_per_hour_micro_usdc": 50000 }
  },
  "performance": { "nav": 1050.25, "uptime_seconds": 604800 }
}

User endpoints (Privy JWT required)

MethodPathDescription
POST/v1/machinesProvision new Golem (returns 202)
GET/v1/machines/mineList user’s machines (paginated)
GET/v1/machines/mine/eventsSSE dashboard events
GET/v1/machines/:nameFull machine details
GET/v1/machines/:name/provision-statusSSE provisioning progress
DELETE/v1/machines/:nameDestroy machine (graceful Thanatopsis)
POST/v1/keysAdd SSH public key
GET/v1/keysList user’s SSH keys
DELETE/v1/keys/:fingerprintRemove SSH key
GET/v1/billing/mineBilling history
POST/v1/ssh/ticketGet SSH ticket

POST /v1/machines (202 Accepted)

{
  "id": "uuid",
  "machine_name": "golem-V1StGXR8_Z5j",
  "status": "provisioning",
  "url": "https://golem-V1StGXR8_Z5j.bardo.money",
  "vm_size": "small",
  "region": "ams",
  "custody_mode": "delegation",
  "ttl_seconds": 36000,
  "expires_at": null,
  "tx_hash": null,
  "wallet_address": null,
  "server_time": 1710464400
}

Internal endpoints (Fly OIDC token)

MethodPathDescription
GET/internal/machines/self/ttlAuthoritative TTL (for cron)
POST/internal/machines/self/crashReport crash
POST/internal/grimoire/syncUpload snapshot
GET/internal/grimoire/:user_id/snapshotDownload snapshot

Admin endpoints (Privy JWT + admin role)

MethodPathDescription
GET/admin/statsSystem metrics
GET/admin/machinesList all machines
GET/admin/machines/:idFull details (any machine)
DELETE/admin/machines/:idForce destroy
POST/admin/machines/:id/expireForce TTL expiry
POST/admin/machines/:id/sweepManual wallet sweep (Embedded mode only)
GET/admin/billing/summaryRevenue summary
POST/admin/refundManual refund
GET/admin/health/detailedExtended health

Admin endpoints use :id (UUID) not :name.

WebSocket SSH endpoint

wss://ssh.bardo.money/ws?machine=golem-V1StGXR8_Z5j&ticket=<ticket>

Machine event types

Events are stored in the machine_events table and pushed via SSE to connected dashboard clients. These are control plane events – distinct from the Golem’s GolemEvent types emitted by the Event Fabric.

EventTriggerMetadata
PaymentVerifiedx402 validated{ trace_id, amount_micro_usdc, nonce }
ProvisioningFly machine started{ trace_id, region, vm_size, custody_mode }
BootingHealth check polling{ trace_id }
ReadyHealth check passed{ trace_id, provision_time_ms, styx_connected }
ExtendedTTL extended{ payer_type, amount_micro_usdc, ttl_added, new_expires_at }
SshConnectedSSH session via ticket{ source_ip, ticket_id }
StrategyUpdatedHot-swap{ strategy_type }
GrimoireSyncedPeriodic sync{ snapshot_size_bytes }
DrainingShutdown initiated{ reason, behavioral_phase }
ThanatopsisStartedDeath protocol begins{ vitality, death_cause }
DestroyedFully destroyed{ destroy_reason, final_ttl_seconds, death_testament_uploaded }
CrashedSupervisor max restarts{ exit_code, restart_count }
WalletSweptControl plane sweep{ usdc_micro_amount, eth_wei, tx_hash }
DelegationExpiredDelegation timeout{ delegation_hash, expiry_cause }
ProvisionFailedProvisioning failed{ trace_id, error, step }
AdminForceDestroyAdmin action{ admin_id, reason }
AdminForceExpireAdmin action{ admin_id, reason }
AdminRefundAdmin refund{ admin_id, amount_micro_usdc, tx_hash }
AdminSweepAdmin sweep{ admin_id, wallet_address, amount_micro_usdc }
Warning30m30 min warning
Warning5m5 min warning

GolemEvent types (from Event Fabric)

The Golem binary emits 50+ typed GolemEvent variants via the Event Fabric. These flow over the Styx WebSocket to surfaces (TUI, web dashboard). They use Rust CamelCase naming with snake_case serde serialization:

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum GolemEvent {
    // Heartbeat
    MarketObservation { timestamp: u64, tick: u64, regime: String, anomalies: Vec<String>, .. },
    HeartbeatTick { timestamp: u64, tick: u64, tier: String, pe: f64, threshold: f64, .. },
    HeartbeatComplete { timestamp: u64, tick: u64, duration_ms: u64, actions_taken: u32, .. },
    TierSelected { timestamp: u64, tick: u64, tier: String, vitality: f64, arousal: f64, .. },

    // Tools
    ToolExecutionStart { timestamp: u64, tool_name: String, risk_tier: String, .. },
    ToolExecutionUpdate { timestamp: u64, tool_name: String, progress: Option<f64>, .. },
    ToolExecutionEnd { timestamp: u64, tool_name: String, result: String, tx_hash: Option<String>, .. },

    // Permits
    PermitCreated { timestamp: u64, permit_id: String, action_kind: String, .. },
    PermitCommitted { timestamp: u64, permit_id: String, tx_hash: Option<String>, .. },
    PermitBlocked { timestamp: u64, permit_id: String, block_reason: String, .. },

    // Inference
    InferenceStart { timestamp: u64, model: String, tier: String, .. },
    InferenceEnd { timestamp: u64, model: String, cost_usd: f64, latency_ms: u64, .. },

    // Dreams
    DreamStart { timestamp: u64, phase: String, .. },
    DreamReplay { timestamp: u64, episode_id: String, .. },
    DreamInsight { timestamp: u64, insight_text: String, confidence: f64, .. },
    DreamEnd { timestamp: u64, insights_generated: u32, entries_pruned: u32, .. },

    // Mortality
    VitalityUpdate { timestamp: u64, economic: f64, epistemic: f64, stochastic: f64, composite: f64, .. },
    PhaseTransition { timestamp: u64, from_phase: String, to_phase: String, vitality: f64, .. },
    ThanatopsisStarted { timestamp: u64, death_cause: String, .. },
    DeathReflection { timestamp: u64, reflection_text: String, .. },

    // Affect
    EmotionUpdate { timestamp: u64, pleasure: f64, arousal: f64, dominance: f64, label: String, .. },
    SomaticMarker { timestamp: u64, situation: String, valence: f64, .. },

    // Memory
    GrimoireWrite { timestamp: u64, entry_type: String, confidence: f64, .. },
    CuratorCycle { timestamp: u64, entries_pruned: u32, entries_compressed: u32, .. },

    // Custody
    SessionKeyRotated { timestamp: u64, old_address: String, new_address: String, .. },
    DelegationWarning { timestamp: u64, remaining_budget_pct: f64, .. },

    // Styx
    StyxConnected { timestamp: u64, latency_ms: u64, .. },
    StyxDisconnected { timestamp: u64, reason: String, .. },
    PheromoneDeposited { timestamp: u64, pheromone_class: String, domain: String, .. },
    BloodstainReceived { timestamp: u64, source_golem: String, warning_count: u32, .. },

    // Trading
    SwapExecuted { timestamp: u64, pair: String, amount_in: f64, amount_out: f64, slippage_bps: u16, gas_usd: f64, .. },
    PositionOpened { timestamp: u64, protocol: String, pool: String, range: String, .. },
    PositionClosed { timestamp: u64, protocol: String, pnl_usd: f64, .. },

    // Economics
    SustainabilityUpdate { timestamp: u64, ratio: f64, daily_revenue: f64, daily_cost: f64, .. },
    TtlExtended { timestamp: u64, added_seconds: u64, new_expires_at: u64, payer_type: String, .. },
}
}

Each variant carries timestamp (Unix ms), golem_id, sequence (monotonic), and tick (heartbeat number). See prd2/01-golem/13-runtime-extensions.md for the full GolemEvent catalog with field definitions.


GolemState (client-side representation)

#![allow(unused)]
fn main() {
/// Client-side state for rendering Golem status in TUI or web dashboard.
/// Assembled from health endpoint + GolemEvent stream.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GolemState {
    // Identity
    pub machine_name: String,
    pub user_id: String,
    pub vm_size: VmTier,
    pub region: String,
    pub custody_mode: String,

    // TTL (with clock skew compensation)
    pub expires_at: u64,
    pub clock_offset: i64,       // server_time - local_time (seconds)

    // Strategy
    pub strategy_type: String,

    // Performance
    pub nav: f64,
    pub pnl_percent: f64,
    pub sharpe: f64,
    pub max_drawdown: f64,
    pub uptime_seconds: u64,

    // Vitality
    pub vitality: f64,
    pub behavioral_phase: String,
    pub sustainability_ratio: Option<f64>,

    // Status
    pub status: String,          // "booting", "ready", "draining", "crashed"
    pub styx_connected: bool,
    pub tool_count: u32,
    pub heartbeat_count: u64,
}
}

Error codes

CodeHTTP statusMeaning
PAYMENT_INVALID402x402 header missing or malformed
PAYMENT_INSUFFICIENT402Below minimum for VM size
PAYMENT_BALANCE_LOW402On-chain USDC insufficient
PAYMENT_NONCE_USED409EIP-3009 nonce consumed
PAYMENT_EXPIRED402validBefore has passed
MACHINE_NOT_FOUND404Name or ID not found
MACHINE_NOT_READY409Not in ready state
MACHINE_LIMIT_REACHED403At 5 active machines
EXTENSION_TOO_LARGE400>720h single extension
EXTENSION_TOO_SMALL400Below 1-hour minimum
EXTENSION_CONFLICT409CAS conflict (retry)
VALID_BEFORE_TOO_TIGHT400<300s window
REGION_FULL503Per-region cap reached
GLOBAL_CAP_REACHED503Global cap reached
UNAUTHORIZED401Missing/invalid JWT
FORBIDDEN403Valid JWT but not owner/admin
RATE_LIMITED429Rate limit exceeded
PROVISION_FAILED500Fly or health check failed
GOLEM_NOT_FOUND404Proxy: no matching machine
SSH_TICKET_INVALID401Ticket expired or consumed
ERC8004_REGISTRATION_FAILED500On-chain identity registration failed
STYX_CONNECTION_FAILED503Styx WebSocket handshake failed (non-fatal)

Environment variables

bardo-control

VariableDescription
DATABASE_URLTurso connection string
DATABASE_AUTH_TOKENTurso auth token
FLY_API_TOKENFly.io API token
FLY_ORG_SLUGFly org for OIDC issuer URL
BARDO_OPERATOR_ADDRESSUSDC recipient (Base)
PRIVY_APP_IDPrivy app ID
PRIVY_APP_SECRETPrivy API secret
BASE_RPC_URLBase chain RPC
STEP_CA_URLSSH CA URL
STEP_CA_FINGERPRINTCA root certificate fingerprint
BARDO_ADMIN_SECRETBreak-glass admin auth
REDIS_URLOptional – rate limiting only

bardo-machines (Golem VMs)

All config injected via golem.toml at machine creation. See 02-provisioning.md.


Retention policy

  • Active machines: No retention limit
  • Destroyed machines: Archived after 90 days to machines_archive
  • Billing events: Kept permanently (financial records)
  • Machine events: Kept with associated machine
  • Grimoire snapshots: Latest 5 per machine (R2). Full history in Styx Archive layer.
  • Daily revenue: Kept permanently