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
| Method | Path | Auth | Response |
|---|---|---|---|
GET | /healthz | none | 200 {"ok": true} |
GET | /readyz | none | 200 serving, 503 draining or not serving; empty body |
GET | /metrics | pinboard:operator with an unrestricted policy | @prometheus-io/client exposition; Node entry with metricsRegistry only, absolute path outside basePath (Metrics) |
GET | /registry/health | pinboard:operator | 200 FleetHealth; answers while not serving |
GET | /registry/workers | pinboard:operator | 200 {"workers": [...]} |
GET | /registry/overview | pinboard:operator | 200 fleet overview; ?ns=, ?prefix=, ?tag= filter placements |
GET | /registry/stats | pinboard:operator | 200 {"workers": [...]} per-worker stats collected live |
POST | /registry/register | pinboard:worker | 200 {"sessionId", "expiresInMs", "draining", "drainDeadline"?, "duplicates"?} |
POST | /registry/heartbeat | pinboard:worker | 200 {"expiresInMs", "draining", "drainDeadline"?, "degraded"?, "messages"?} |
POST | /registry/release | pinboard:worker | 200 {"released"} |
POST | /registry/tag | pinboard:worker | 200 {"tagged"} |
POST | /registry/deregister | pinboard:worker | 200 {} |
POST | /registry/wind_down | pinboard:operator | 200 {"queued"} |
POST | /registry/drain | pinboard:operator | 200 {"drained": true} |
POST | /registry/undrain | pinboard:operator | 200 {"revoked"} |
| any, WebSocket upgrades | /:ns/:id and /:ns/:id/* | the namespaces rule for ns (PassportRules); instance-attested calls bypass passport | forwarded to the placed worker |
- A data-plane upgrade whose path is not
/:ns/:idanswers404 {"error": "not found"}where an HTTP request answers404 {"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.
| Var | Default | Meaning |
|---|---|---|
PINBOARD_PASSPORT | required | Passport JSON {providers, namespaces?}: providers feed createPassport (with { env }), namespaces is createPinboard.Options.namespaces; without it the data plane is open (Passport) |
REDIS_URL | redis://localhost:6379 | memory: selects MemoryRedis |
PINBOARD_LEASE_TTL_MS | 15000 | leaseTtlMs; positive ms |
PINBOARD_VERIFY_TIMEOUT_MS | 2500 | verifyTimeoutMs; positive ms |
PINBOARD_SWEEP_INTERVAL_MS | 5000 | durability.sweepIntervalMs; positive ms |
PINBOARD_KEY_PREFIX | pinboard: | keyPrefix |
PINBOARD_SHARDS | 1 | shards; integer >= 1 |
PINBOARD_HOSTNAME_AWARE | off | hostnameAware; 1/true on, 0/false/empty off, anything else throws |
PINBOARD_EPOCH | 0 | epoch; integer >= 0 |
PINBOARD_AFFINITY_BALANCE_FACTOR | 1.25 | affinity.balanceFactor; number > 1 |
PINBOARD_TRUSTED_PROXIES | unset | affinity.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):
| Var | Default | Meaning |
|---|---|---|
PORT | 8080 | listen port, an integer 0–65535; anything else fails startup |
HOST | unset (all interfaces) | listen address; empty fails startup |
PINBOARD_DURABILITY_POSTGRES_URL | unset | connection string for new PostgresDurabilityStore(new pg.default.Pool({ connectionString: url })) as durability.store; requires the pg package |
LOG_LEVEL | info | pino level (the root logger reads it too) |
NODE_ENV | unset | development 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:(orcreatePinboardoverMemoryRedis) is dev mode: the registry lives in this process, and the process logs aDEV MODEwarning at startup.- It throws when
NODE_ENV,ENVIRONMENTorAPP_ENVisproduction/prod, unlessPINBOARD_ALLOW_DEV_IN_PRODUCTION=1. The check readsprocess.env.NODE_ENV, so bundler defines apply. pnpm --filter @assistant-ui/pinboard dev(binpinboard-dev) starts it in one command:REDIS_URL=memory:,PORT=8080,HOST=127.0.0.1, and a passport admittingPINBOARD_TOKEN(defaultdev-token) as worker and operator plus anonymous clients on every namespace. Any of these variables overrides its default; the dev passport applies only withREDIS_URL=memory:.dev-tokenis public: keep the dev port off shared networks (it listens on 127.0.0.1 unlessHOSTsays 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
| Metric | Labels |
|---|---|
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_total | kind: http, upgrade |
pinboard_forward_failures_total | outcome: aborted, unreachable, failed |
pinboard_placement_resolutions_total | outcome: 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_total | strategy: wind_down (outcome: initiated, pending), deny (denied) |
pinboard_degraded_heartbeats_total | reason: unreachable, status, incomplete, mismatch |
pinboard_register_rejections_total | account, pool, reason: policy, unavailable, conflict, invalid |
pinboard_internal_errors_total | source (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 defaults | collectDefaultMetrics |
Each series counts the scraped node alone. Scraping, aggregation, and the pinboard:operator policy for GET /metrics: Observability.
Worker-side pinned_* collectors: pinned Metrics.