Skip to content

The process behind servePinboard and PinboardDO. Library exports and options: pinboard reference. Wire contract: pinboard registry. Running it: Run a fleet, Operate a fleet.

Routes

MethodPathAuthResponse
GET/healthznone200 {"ok": true}
GET/readyznone200 serving, 503 draining or not serving; empty body
GET/metricspinboard:operator with an unrestricted policy@prometheus-io/client exposition; Node entry with metricsRegistry only, absolute path outside basePath (Metrics)
GET/registry/healthpinboard:operator200 FleetHealth; answers while not serving
GET/registry/workerspinboard:operator200 {"workers": [...]}
GET/registry/overviewpinboard:operator200 fleet overview; ?ns=, ?prefix=, ?tag= filter placements
GET/registry/statspinboard:operator200 {"workers": [...]} per-worker stats collected live
POST/registry/registerpinboard:worker200 {"sessionId", "expiresInMs", "draining", "drainDeadline"?, "duplicates"?}
POST/registry/heartbeatpinboard:worker200 {"expiresInMs", "draining", "drainDeadline"?, "degraded"?, "messages"?}
POST/registry/releasepinboard:worker200 {"released"}
POST/registry/tagpinboard:worker200 {"tagged"}
POST/registry/deregisterpinboard:worker200 {}
POST/registry/wind_downpinboard:operator200 {"queued"}
POST/registry/drainpinboard:operator200 {"drained": true}
POST/registry/undrainpinboard:operator200 {"revoked"}
any, WebSocket upgrades/:ns/:id and /:ns/:id/*the namespaces rule for ns (PassportRules); instance-attested calls bypass passportforwarded to the placed worker
  • A data-plane upgrade whose path is not /:ns/:id answers 404 {"error": "not found"} where an HTTP request answers 404 {"error": "unknown namespace"}.
  • Credentials, bodies, refusals, and headers: pinboard registry.

Environment

Worker settings: PinnedWorker, serve.

Both entries parse the shared PINBOARD_* surface at startup and throw on an invalid value; the parser itself is not exported.

VarDefaultMeaning
PINBOARD_PASSPORTrequiredPassport JSON {providers, namespaces?}: providers feed createPassport (with { env }), namespaces is createPinboard.Options.namespaces; without it the data plane is open (Passport)
REDIS_URLredis://localhost:6379memory: selects MemoryRedis
PINBOARD_LEASE_TTL_MS15000leaseTtlMs; positive ms
PINBOARD_VERIFY_TIMEOUT_MS2500verifyTimeoutMs; positive ms
PINBOARD_SWEEP_INTERVAL_MS5000durability.sweepIntervalMs; positive ms
PINBOARD_KEY_PREFIXpinboard:keyPrefix
PINBOARD_SHARDS1shards; integer >= 1
PINBOARD_HOSTNAME_AWAREoffhostnameAware; 1/true on, 0/false/empty off, anything else throws
PINBOARD_EPOCH0epoch; integer >= 0
PINBOARD_AFFINITY_BALANCE_FACTOR1.25affinity.balanceFactor; number > 1
PINBOARD_TRUSTED_PROXIESunsetaffinity.trustedProxies; * or comma-separated IPs/CIDRs

Node entry only (dist/node/main.js; pnpm --filter @assistant-ui/pinboard start runs it after pnpm --filter @assistant-ui/pinboard build):

VarDefaultMeaning
PORT8080listen port, an integer 0–65535; anything else fails startup
HOSTunset (all interfaces)listen address; empty fails startup
PINBOARD_DURABILITY_POSTGRES_URLunsetconnection string for new PostgresDurabilityStore(new pg.default.Pool({ connectionString: url })) as durability.store; requires the pg package
LOG_LEVELinfopino level (the root logger reads it too)
NODE_ENVunsetdevelopment pretty-prints logs

outlier, forwards, pool, basePath, and the durability constants other than sweepIntervalMs have no environment variable: tune them from code through servePinboard.

An invalid configuration logs invalid configuration and exits 1. main.js calls installPinoLogger(), servePinboard({ ...options, redis, onDisplaced, metricsRegistry: new Registry() }) where onDisplaced exits 1, and installProcessObservability({ workers }). It listens on HOST:PORT. On SIGTERM/SIGINT it calls close() then exits, with a hard exit after 10s.

Dev mode

  • REDIS_URL=memory: (or createPinboard over MemoryRedis) is dev mode: the registry lives in this process, and the process logs a DEV MODE warning at startup.
  • It throws when NODE_ENV, ENVIRONMENT or APP_ENV is production/prod, unless PINBOARD_ALLOW_DEV_IN_PRODUCTION=1. The check reads process.env.NODE_ENV, so bundler defines apply.
  • pnpm --filter @assistant-ui/pinboard dev (bin pinboard-dev) starts it in one command: REDIS_URL=memory:, PORT=8080, HOST=127.0.0.1, and a passport admitting PINBOARD_TOKEN (default dev-token) as worker and operator plus anonymous clients on every namespace. Any of these variables overrides its default; the dev passport applies only with REDIS_URL=memory:. dev-token is public: keep the dev port off shared networks (it listens on 127.0.0.1 unless HOST says otherwise).
  • Workers join with PINBOARD_URL=http://127.0.0.1:8080 PINBOARD_TOKEN=dev-token ADVERTISE_URL=<worker URL>. A fresh boot refuses placements for one lease TTL (503 registry cold start, retry).

Metrics

MetricLabels
pinboard_http_requests_total (counter), pinboard_http_request_duration_seconds (histogram)method, route (/healthz, /readyz, a /registry/* path, /:namespace/:id, or unknown), status_code
pinboard_forwards_totalkind: http, upgrade
pinboard_forward_failures_totaloutcome: aborted, unreachable, failed
pinboard_placement_resolutions_totaloutcome: forward, unknown-ns, no-backends, quarantine, not-serving, no-hostname-workers, binding-quarantine, takeover-pending, env-conflict, nack; takeover denials ride takeover-pending
pinboard_takeovers_totalstrategy: wind_down (outcome: initiated, pending), deny (denied)
pinboard_degraded_heartbeats_totalreason: unreachable, status, incomplete, mismatch
pinboard_register_rejections_totalaccount, pool, reason: policy, unavailable, conflict, invalid
pinboard_internal_errors_totalsource (background loop name)
pinboard_data_plane_connections (gauge)in-flight proxied connections on this node
pinboard_registered_workers, pinboard_active_placements, pinboard_active_hostnames, pinboard_active_envs (gauges)fleet-wide, from one registry.overview() snapshot cached 5s; aggregate with max
pinboard_event_loop_lag_seconds (histogram)mean lag sampled every 2s
pinboard_* Node process defaultscollectDefaultMetrics

Each series counts the scraped node alone. Scraping, aggregation, and the pinboard:operator policy for GET /metrics: Observability.

Worker-side pinned_* collectors: pinned Metrics.