Skip to content

Standalone server

@graphorin/server is the optional standalone runtime. Same library packages, different lifetime — promote your assistant to a daemon with a network API the moment it has to outlive a single Node.js process.

Library mode is the default

You only need the standalone server when:

  • your assistant has to survive process restart, OR
  • you expose it over the network (browser, Slack bot, mobile app), OR
  • you want durable triggers (cron, interval, idle, event).

For a CLI script or a desktop app, embed the library packages directly. See Architecture § Two ways to ship.

Capabilities

CapabilityLibrary modeStandalone server
Agent runs
Memory + sessions
Workflows
Tools / Skills / MCP
Durable HITL across process restart✓ (with the right checkpoint store)
Triggers (cron / interval / idle / event)manual scheduling✓ daemon
REST + WebSocket + SSE
Server-token authentication
Prometheus metrics endpoint
Health checks
Replay endpointmanual via SQLite

REST surface

Built on hono (MIT) and @hono/node-server (MIT). The default basePath is /v1. Every authenticated endpoint requires a bearer token signed with HMAC-SHA256 against the deployment-wide pepper. The unauthenticated /v1/health route is exempt.

MethodPathPurpose
GET/v1/healthLiveness + readiness summary (probes for storage, audit log, secrets, triggers, encryption).
GET/v1/health/secretsAuthenticated drilldown of the active SecretsStore.
GET/v1/metricsPrometheus exposition (path configurable; auth optional).
GET/v1/agentsList registered agents.
GET/v1/agents/:idDescribe a single agent.
POST/v1/agents/:id/runRun an agent synchronously and return the final output.
POST/v1/agents/:id/streamStart a streaming run; returns runId plus subscription metadata for the SSE / WebSocket channel.
GET/v1/runs/:runId/stateRead the current RunState.
POST/v1/runs/:runId/abortAbort a run.
POST/v1/runs/:runId/resumeResume a paused run with a directive (Phase 14a stub today).
POST/v1/runs/:runId/replayReplay a recorded run from the audit / cassette artefacts.
GET/v1/sessionsList sessions.
POST/v1/sessionsCreate a session.
GET/v1/sessions/:idRead a session.
DELETE/v1/sessions/:idDelete a session.
GET/v1/sessions/:id/messagesList messages.
GET/v1/sessions/:id/handoffsList handoffs.
POST/v1/sessions/:id/exportStream a JSONL export.
POST/v1/sessions/:id/replayReplay a recorded session.
POST/v1/memory/searchHybrid search across the memory tiers.
POST/v1/memory/factsPersist a fact.
DELETE/v1/memory/facts/:idSoft-delete a fact.
POST/v1/memory/blocksDefine / update a working block.
DELETE/v1/memory/blocks/:labelDetach a working block.
GET/v1/skillsList loaded skills.
GET/v1/skills/:nameDescribe a skill.
POST/v1/skills/installInstall a skill from a configured source.
GET/v1/mcp/serversList configured MCP servers.
POST/v1/mcp/serversRegister a new MCP server connection.
DELETE/v1/mcp/servers/:idDisconnect a server.
GET/v1/auditTailable audit log.
POST/v1/audit/verifyWalk + verify the SHA-256 hash chain.
POST/v1/audit/exportExport the audit log.
GET/v1/tokensList server tokens.
POST/v1/tokensIssue a token.
DELETE/v1/tokens/:idRevoke a token.
GET/v1/triggersList configured triggers.
GET/v1/workflowsList configured workflows.
POST/v1/auth/session/ws-ticketMint a single-use WebSocket session ticket.

WebSocket protocol

@graphorin/protocol ships the graphorin.protocol.v1 contract — a typed message envelope for live event streaming over WebSocket. Built on @hono/node-ws (MIT).

ts
import { GraphorinClient } from '@graphorin/client';

const client = new GraphorinClient({
  baseURL: 'wss://assistant.example.com',
  token: process.env.SERVER_TOKEN,
});

for await (const event of client.runAgent('planner', { prompt: 'Plan a hike.' })) {
  if (event.type === 'text.delta') process.stdout.write(event.delta);
}

The browser-friendly client is published as @graphorin/client and depends only on @graphorin/protocol. SSE is the documented fallback for environments that cannot upgrade to WebSocket.

Triggers

@graphorin/triggers is the durable scheduling layer. Four trigger kinds:

KindSpec
cronStandard 5-field cron expression.
intervalFixed interval in milliseconds.
idleFires after N ms of agent inactivity.
eventListens on a named event channel.

Declare a trigger using the four typed factories:

ts
import { cron, interval, idle, event, createScheduler } from '@graphorin/triggers';

const morningSummary = cron(
  'morning-summary',
  '0 8 * * *',
  async () => {
    await agent.run('Send the morning summary.');
  },
);

const scheduler = createScheduler({ store: sqlite.triggers });
await scheduler.register(morningSummary);
await scheduler.start();

The triggers daemon (mounted by @graphorin/server) owns the schedule, persists the next-firing time across restarts, fires the registered callback, and audits every fire decision.

Idempotency

All POST endpoints accept an Idempotency-Key header. Repeated submissions within the configured TTL return the original response (or the in-flight result for a still-running call).

Disconnect policy

Long-running streams (agent runs, workflows) survive client disconnects through the configurable disconnect.policy:

PolicyBehaviour on client disconnect
'continue' (default)Run continues; client reconnects via GET /v1/agents/:id/runs/:runId/follow.
'pause'Run is paused; resumed when the client reconnects with the same runId.
'abort'Run aborts with client-disconnected.

Health checks

GET /v1/health returns:

json
{
  "status": "ok",
  "version": "0.1.0",
  "checks": {
    "storage": "ok",
    "audit-log": "ok",
    "secrets": "ok",
    "triggers": "ok",
    "providers": { "openai": "ok", "ollama": "unreachable" }
  }
}

Provider checks are passive — the server never opens an outbound connection it doesn't already have.

Prometheus metrics

GET /metrics exposes the counters from Observability in Prometheus exposition format, plus the standard process / Node.js metrics.

Configuration

toml
# graphorin.config.toml
[server]
host = "127.0.0.1"
port = 8787

[storage]
path = "./assistant.db"
encryption-at-rest = "keyring:graphorin_db_key?service=graphorin"

[secrets]
backend = "keychain"

[triggers]
enabled = true

[observability]
exporter = "otlp"
otlp-url = "https://otel.example.com/v1/traces"

The CLI command graphorin start --config graphorin.config.toml boots the server.

Process model

Recommended deployment patterns:

  • systemd: ship the unit template the project provides under examples/systemd/.
  • Docker: ship the image template under examples/docker/.
  • Kubernetes: ship the manifests under examples/k8s/.

All three templates run Graphorin as a non-root user with the audit log on its own mountpoint and the secrets store unreadable by the application's main filesystem path.

Next steps

  • CLIgraphorin start, graphorin doctor, graphorin token.
  • Deployment — production checklists.
  • Security — server-token authentication, audit log.
  • Observability — what gets traced.

Graphorin · v0.1.0 · MIT License · © 2026 Oleksiy Stepurenko