@graphorin/server 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +6 -6
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +9 -8
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/ws/dispatcher.d.ts.map +1 -1
- package/dist/ws/upgrade.d.ts.map +1 -1
- package/dist/ws/upgrade.js +2 -1
- package/dist/ws/upgrade.js.map +1 -1
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @graphorin/server
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`826ee59`](https://github.com/o-stepper/graphorin/commit/826ee5933ad38693b0dd2f20a110abfecba7d23d), [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
|
|
10
|
+
- @graphorin/observability@0.6.1
|
|
11
|
+
- @graphorin/core@0.6.1
|
|
12
|
+
- @graphorin/protocol@0.6.1
|
|
13
|
+
- @graphorin/security@0.6.1
|
|
14
|
+
- @graphorin/store-sqlite@0.6.1
|
|
15
|
+
- @graphorin/triggers@0.6.1
|
|
16
|
+
|
|
3
17
|
## 0.6.0
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
|
|
8
|
-
- **Version:** v0.6.
|
|
8
|
+
- **Version:** v0.6.1
|
|
9
9
|
- **License:** [MIT](./LICENSE) (© 2026 Oleksiy Stepurenko)
|
|
10
10
|
- **Repository:** <https://github.com/o-stepper/graphorin/tree/main/packages/server>
|
|
11
11
|
- **Issues:** <https://github.com/o-stepper/graphorin/issues>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
| **SSE fallback** | `GET /v1/sessions/:id/events` for non-WebSocket-friendly clients (corporate proxies, restricted networks). Same sanitization, same `Last-Event-ID` resume semantics. |
|
|
22
22
|
| **Lifecycle hooks** | `beforeStart` → pre-bind validation → storage migrations → bind → triggers / consolidator daemons start → `onReady`; `SIGTERM` → `beforeShutdown` → daemons stop → drain → exit. |
|
|
23
23
|
| **Pre-bind validation** | Resolves every `*Ref` field via `@graphorin/security` resolvers before binding the listener; missing pepper / unresolvable `SecretRef` / missing encryption peer fail fast with typed errors. |
|
|
24
|
-
| **REST endpoints** | Agents (`run`, `stream`, `state`, `abort`, `resume`), workflows (`execute`, `resume`, `state`, `checkpoints
|
|
25
|
-
| **Auth / scope** | `Authorization: Bearer
|
|
26
|
-
| **Idempotency** | SQLite-backed `idempotency_records` + LRU read cache. Replays cached responses with `Idempotency-Replayed: true`; mismatched payloads return `409
|
|
24
|
+
| **REST endpoints** | Agents (`run`, `stream`, `state`, `abort`, `resume`), workflows (`execute`, `resume`, `state`, `checkpoints`; `fork` answers an honest `501` until thread forking lands on the REST surface), sessions, memory, skills, MCP, tokens, audit (`list`, `export`, `verify`), the `POST /v1/session/ws-ticket` browser ticket route, triggers (`list`, `get`, `fire`, `disable`, `prune`), replay (`runs/:runId/replay`, `sessions/:id/replay`), `/health`, `/health/secrets`, `/metrics`. Every side-effecting endpoint honours an `Idempotency-Key` per IETF draft-07. |
|
|
25
|
+
| **Auth / scope** | `Authorization: Bearer gph_<env>_v1_<...>` (HMAC-SHA256 + pepper) wired through `TokenVerifier` from `@graphorin/security`. Per-route scope grammar `<resource>:<action>[:<id>]`. WebSocket upgrade honours both bearer and the single-use ticket flow. |
|
|
26
|
+
| **Idempotency** | SQLite-backed `idempotency_records` + LRU read cache. Replays cached responses with `Idempotency-Replayed: true`; mismatched payloads return `409`, and a concurrent duplicate of an in-flight key gets `409 idempotency-in-flight` with a `Retry-After` hint. |
|
|
27
27
|
| **CORS / CSRF / rate limit** | Deny-by-default CORS; double-submit CSRF for browser flows; sliding-window per-IP rate limit. |
|
|
28
28
|
| **Audit middleware** | Forwards every authenticated request through `appendAudit` (`@graphorin/security/audit`) with attribution, route, status, and duration. `POST /v1/audit/verify` walks the SHA-256 hash chain and surfaces the integrity status. |
|
|
29
29
|
| **Triggers daemon** | Wraps `@graphorin/triggers` `Scheduler`; `beforeStart` loads every persisted `trigger_state` row + applies the per-trigger `catchupPolicy`; `beforeShutdown` stops the scheduler. Cron / interval / idle / event triggers survive process restarts. |
|
|
@@ -109,8 +109,8 @@ The most-frequently-touched options:
|
|
|
109
109
|
|
|
110
110
|
## Status
|
|
111
111
|
|
|
112
|
-
`@graphorin/server` is part of the Graphorin framework's `v0.6.
|
|
112
|
+
`@graphorin/server` is part of the Graphorin framework's `v0.6.1` pre-release. Once published, the package follows the lockstep release cadence shared by every `@graphorin/*` package on the `0.x` line.
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
116
|
-
**Project Graphorin** · v0.6.
|
|
116
|
+
**Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","names":[],"sources":["../src/app.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmQiB,iBAxJD,uBAAA,CAAA,CAwJgB,EAAA,IAAA;;;;;;AAMf,UANA,eAAA,CAMA;EAQQ,SAAA,OAAA,EAAA,MAAA;EAMH,SAAA,MAAA,EAlBH,gBAkBG;EAMD,SAAA,GAAA,EAvBL,IAuBK,CAAA;IAMI,SAAA,EA7BS,eA6BT;EAKL,CAAA,CAAA;EACT,SAAA,MAAA,EAlCQ,aAkCR;EACqC,SAAA,SAAA,EAlC1B,gBAkC0B;EAAO,SAAA,IAAA,EAjCtC,eAiCsC;EAW3C,SAAA,WAAA,EAAmB;IAOd,SAAA,IAAA,EAAA,MAAmB;IAEhB,SAAA,IAAA,EAAA,MAAA;EAMS,CAAA,GAAA,SAAA;EAEV;;;;;;EASC,SAAA,YAAA,EA9DK,YA8DL,GAAA,SAAA;EACH;;;;;EAuBe,SAAA,SAAA,EAhFV,aAgFU,GAAA,SAAA;EAA6B;;;;;EAYnB,SAAA,QAAA,EAtFrB,cAsFqB,GAAA,SAAA;
|
|
1
|
+
{"version":3,"file":"app.d.ts","names":[],"sources":["../src/app.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmQiB,iBAxJD,uBAAA,CAAA,CAwJgB,EAAA,IAAA;;;;;;AAMf,UANA,eAAA,CAMA;EAQQ,SAAA,OAAA,EAAA,MAAA;EAMH,SAAA,MAAA,EAlBH,gBAkBG;EAMD,SAAA,GAAA,EAvBL,IAuBK,CAAA;IAMI,SAAA,EA7BS,eA6BT;EAKL,CAAA,CAAA;EACT,SAAA,MAAA,EAlCQ,aAkCR;EACqC,SAAA,SAAA,EAlC1B,gBAkC0B;EAAO,SAAA,IAAA,EAjCtC,eAiCsC;EAW3C,SAAA,WAAA,EAAmB;IAOd,SAAA,IAAA,EAAA,MAAmB;IAEhB,SAAA,IAAA,EAAA,MAAA;EAMS,CAAA,GAAA,SAAA;EAEV;;;;;;EASC,SAAA,YAAA,EA9DK,YA8DL,GAAA,SAAA;EACH;;;;;EAuBe,SAAA,SAAA,EAhFV,aAgFU,GAAA,SAAA;EAA6B;;;;;EAYnB,SAAA,QAAA,EAtFrB,cAsFqB,GAAA,SAAA;EAsBpB;;;;;yBAtGG;;;;;oBAKL;WACT;;;;;;MACqC;;;;;;;;;;KAWpC,mBAAA;mBACW;;sBAAc,oBAAA,CACmB;;;;;UAKvC,mBAAA;;oBAEG;;;;;;6BAMS;;mBAEV;;kBAED;;oBAEE;uBACG;;sBAED;oBACF;oBACA;iBACH;mBACE;;;;;;0BAMO;;;;;;sBAMJ;;;;;oBAKF;;;;;gCAKY,qBAAqB,QAAQ;;;;;;4BAMjC;;mBAET;;;;mCAIgB;;;;;;;;;;;;;;iBAsBb,YAAA,WAAsB,sBAA2B,QAAQ"}
|
package/dist/app.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { bridgeCommentaryToAudit, createLateBoundCommentarySink } from "./commentary/audit-bridge.js";
|
|
2
3
|
import "./commentary/index.js";
|
|
3
4
|
import { LifecycleDoubleStartError, LifecycleNotStartedError, ShutdownTimeoutError } from "./errors/index.js";
|
|
@@ -48,6 +49,9 @@ import { Hono } from "hono";
|
|
|
48
49
|
|
|
49
50
|
//#region src/app.ts
|
|
50
51
|
/**
|
|
52
|
+
* @stable
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
51
55
|
* Pre-built audit-db binding shipped from `@graphorin/store-sqlite`.
|
|
52
56
|
* Registered exactly once per process so {@link openAuditDb} can find
|
|
53
57
|
* a default binding without forcing operators to wire it manually.
|
|
@@ -139,10 +143,7 @@ function rowToEntry(row) {
|
|
|
139
143
|
hash: row.hash
|
|
140
144
|
};
|
|
141
145
|
}
|
|
142
|
-
|
|
143
|
-
* @stable
|
|
144
|
-
*/
|
|
145
|
-
const VERSION = "0.6.0";
|
|
146
|
+
const VERSION = version;
|
|
146
147
|
/**
|
|
147
148
|
* Build a fully-wired Graphorin server. The returned handle is
|
|
148
149
|
* inert until `start()` is awaited.
|
|
@@ -153,7 +154,7 @@ async function createServer(options = {}) {
|
|
|
153
154
|
const config = options.validatedConfig ?? parseServerConfig(options.config ?? {});
|
|
154
155
|
const now = options.now ?? Date.now;
|
|
155
156
|
const startedAt = now();
|
|
156
|
-
const version = options.version ?? VERSION;
|
|
157
|
+
const version$1 = options.version ?? VERSION;
|
|
157
158
|
let storeEncryption;
|
|
158
159
|
if (options.store === void 0 && config.storage.encryption.enabled) {
|
|
159
160
|
if (config.storage.encryption.passphraseRef === void 0) throw new Error("[graphorin/server] storage.encryption.enabled is true but no passphraseRef is configured.");
|
|
@@ -174,7 +175,7 @@ async function createServer(options = {}) {
|
|
|
174
175
|
} } : {}
|
|
175
176
|
});
|
|
176
177
|
const metricRegistry = options.metricRegistry ?? createServerMetricRegistry();
|
|
177
|
-
metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version });
|
|
178
|
+
metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version: version$1 });
|
|
178
179
|
const runs = options.runs ?? new RunStateTracker({
|
|
179
180
|
now,
|
|
180
181
|
onTerminal: (info) => {
|
|
@@ -239,7 +240,7 @@ async function createServer(options = {}) {
|
|
|
239
240
|
};
|
|
240
241
|
}
|
|
241
242
|
return {
|
|
242
|
-
version,
|
|
243
|
+
version: version$1,
|
|
243
244
|
config,
|
|
244
245
|
app,
|
|
245
246
|
agents,
|
|
@@ -298,7 +299,7 @@ async function createServer(options = {}) {
|
|
|
298
299
|
});
|
|
299
300
|
}
|
|
300
301
|
mountRoutes(app, config, {
|
|
301
|
-
version,
|
|
302
|
+
version: version$1,
|
|
302
303
|
startedAt,
|
|
303
304
|
now,
|
|
304
305
|
agents,
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","names":["config: ServerConfigSpec","storeEncryption:\n | { enabled: true; cipher?: never; passphraseResolver: () => Promise<string> }\n | undefined","serverInstance: ServerType | undefined","listening: { readonly host: string; readonly port: number } | undefined","stopRunPruning: (() => void) | undefined","auditDb: AuditDb | undefined","preBind: Awaited<ReturnType<typeof runPreBind>> | undefined","verifier: TokenVerifier | undefined","pepperHandle: import('@graphorin/security').SecretValue | undefined","triggersDaemon: TriggersDaemon | undefined","consolidatorDaemon: ConsolidatorDaemon | undefined","dispatcher: WsDispatcher | undefined","tickets: WsTicketStore | undefined","wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined","commentaryAuditSink: LateBoundCommentarySink | undefined","out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n }"],"sources":["../src/app.ts"],"sourcesContent":["/**\n * `createServer({...})` - the single programmatic entry point.\n *\n * The factory returns a {@link GraphorinServer} handle the operator\n * controls via `start()` / `stop()`. The same handle is consumed by\n * the `graphorin start` CLI binary in `@graphorin/cli`.\n *\n * Phase 14a covers the full HTTP surface, the lifecycle plumbing,\n * the Hono app composition, and the auth / scope / idempotency /\n * audit middleware stack. Phase 14b/c attach the WebSocket layer +\n * triggers daemon + observability endpoints to the same handle.\n *\n * @packageDocumentation\n */\n\nimport type { AddressInfo } from 'node:net';\nimport process from 'node:process';\nimport { negotiateSubprotocol, SUBPROTOCOL_NAME } from '@graphorin/protocol';\nimport { applyProcessHardening, generatePepper, TokenVerifier } from '@graphorin/security';\nimport { type AuditDb, openAuditDb, registerAuditDbBinding } from '@graphorin/security/audit';\nimport {\n createSqliteStore,\n type GraphorinSqliteStore,\n loadCipherDriver,\n readWalSize,\n} from '@graphorin/store-sqlite';\nimport { type ServerType, serve } from '@hono/node-server';\nimport { createNodeWebSocket } from '@hono/node-ws';\nimport { Hono } from 'hono';\nimport {\n bridgeCommentaryToAudit,\n createLateBoundCommentarySink,\n type LateBoundCommentarySink,\n} from './commentary/index.js';\nimport { parseServerConfig, type ServerConfigInput, type ServerConfigSpec } from './config.js';\nimport type { ConsolidatorDaemon, ConsolidatorLike } from './consolidator/daemon.js';\nimport { createConsolidatorDaemon } from './consolidator/daemon.js';\nimport {\n ConfigInvalidError,\n LifecycleDoubleStartError,\n LifecycleNotStartedError,\n ShutdownTimeoutError,\n} from './errors/index.js';\nimport {\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type HealthCheckOptions,\n} from './health/index.js';\nimport type { ServerVariables } from './internal/context.js';\nimport { type LifecycleHooks, type OnErrorContext, runPreBind } from './lifecycle/index.js';\nimport { createServerMetricRegistry, SERVER_METRIC_NAMES } from './metrics/catalog.js';\nimport type { MetricRegistry } from './metrics/registry.js';\nimport {\n createAnonymousAuthMiddleware,\n createAuditMiddleware,\n createAuthMiddleware,\n createCorsMiddleware,\n createCsrfMiddleware,\n createIdempotencyMiddleware,\n createRateLimitMiddleware,\n createRequestStateMiddleware,\n} from './middleware/index.js';\nimport { AgentRegistry, WorkflowRegistry } from './registry/index.js';\nimport { createReplayRoutes, type ReplayApi } from './replay/index.js';\nimport {\n type AuditApi,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type McpApi,\n type MemoryApi,\n type SessionApi,\n type SkillsApi,\n} from './routes/index.js';\nimport { RunStateTracker, scheduleRunPruning } from './runtime/run-state.js';\nimport { createSseRoutes } from './sse/index.js';\nimport type { TriggersDaemon } from './triggers/daemon.js';\nimport { createTriggersDaemon } from './triggers/daemon.js';\nimport { createTriggersRoutes } from './triggers/routes.js';\nimport {\n createWsDispatcher,\n createWsTicketStore,\n type WsDispatcher,\n type WsTicketStore,\n} from './ws/index.js';\nimport { createWsUpgradeEvents } from './ws/upgrade.js';\n\n/**\n * Pre-built audit-db binding shipped from `@graphorin/store-sqlite`.\n * Registered exactly once per process so {@link openAuditDb} can find\n * a default binding without forcing operators to wire it manually.\n *\n * Exported as {@link ensureStoreAuditBinding} so the CLI (Phase 15\n * `graphorin audit verify | prune | export`) can reach into the same\n * binding without booting the HTTP listener.\n *\n * @stable\n */\nlet storeAuditBindingRegistered = false;\nexport function ensureStoreAuditBinding(): void {\n if (storeAuditBindingRegistered) return;\n registerAuditDbBinding(\n {\n id: 'better-sqlite3-multiple-ciphers',\n description: 'Default audit-db binding shipped by @graphorin/store-sqlite.',\n open: async (opts) => {\n const driver = await loadCipherDriver();\n const passphrase = await opts.passphrase.use((value) => value);\n const Db = driver as unknown as new (\n path: string,\n ) => {\n pragma(s: string): unknown;\n prepare(sql: string): {\n run(...args: unknown[]): unknown;\n get(...args: unknown[]): unknown;\n all(...args: unknown[]): unknown;\n iterate(...args: unknown[]): IterableIterator<unknown>;\n };\n exec(sql: string): unknown;\n close(): void;\n open: boolean;\n };\n const db = new Db(opts.path);\n db.pragma(`key = '${passphrase.replace(/'/g, \"''\")}'`);\n db.pragma('journal_mode = WAL');\n db.pragma('synchronous = NORMAL');\n db.pragma('busy_timeout = 5000');\n db.pragma('foreign_keys = ON');\n db.exec(\n `CREATE TABLE IF NOT EXISTS audit_log (\n seq INTEGER PRIMARY KEY,\n ts INTEGER NOT NULL,\n actor_json TEXT NOT NULL,\n action TEXT NOT NULL,\n target TEXT NOT NULL,\n decision TEXT NOT NULL,\n context_json TEXT,\n metadata_json TEXT,\n prev_hash TEXT NOT NULL,\n hash TEXT NOT NULL UNIQUE\n ) WITHOUT ROWID;`,\n );\n return {\n binding: 'better-sqlite3-multiple-ciphers',\n path: opts.path,\n async insert(entry) {\n db.prepare(\n `INSERT INTO audit_log (\n seq, ts, actor_json, action, target, decision,\n context_json, metadata_json, prev_hash, hash\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n entry.seq,\n entry.ts,\n JSON.stringify(entry.actor),\n entry.action,\n entry.target,\n entry.decision,\n entry.context !== undefined ? JSON.stringify(entry.context) : null,\n entry.metadata !== undefined ? JSON.stringify(entry.metadata) : null,\n entry.prevHash,\n entry.hash,\n );\n return entry;\n },\n async latest() {\n const row = db.prepare('SELECT * FROM audit_log ORDER BY seq DESC LIMIT 1').get() as\n | undefined\n | {\n seq: number;\n ts: number;\n actor_json: string;\n action: string;\n target: string;\n decision: string;\n context_json: string | null;\n metadata_json: string | null;\n prev_hash: string;\n hash: string;\n };\n if (row === undefined) return undefined;\n return rowToEntry(row);\n },\n async *iterate(bounds) {\n const lo = bounds?.fromSeq ?? 1;\n const hi = bounds?.toSeq ?? Number.MAX_SAFE_INTEGER;\n const iter = db\n .prepare('SELECT * FROM audit_log WHERE seq BETWEEN ? AND ? ORDER BY seq ASC')\n .iterate(lo, hi);\n for (const row of iter) yield rowToEntry(row as Parameters<typeof rowToEntry>[0]);\n },\n async count() {\n const row = db.prepare('SELECT COUNT(*) AS n FROM audit_log').get() as { n: number };\n return row.n;\n },\n async deleteUpTo(threshold) {\n const before = db\n .prepare('SELECT COUNT(*) AS n FROM audit_log WHERE seq <= ?')\n .get(threshold) as { n: number };\n db.prepare('DELETE FROM audit_log WHERE seq <= ?').run(threshold);\n return before.n;\n },\n async replaceEntry(entry) {\n db.prepare(`UPDATE audit_log SET prev_hash = ?, hash = ? WHERE seq = ?`).run(\n entry.prevHash,\n entry.hash,\n entry.seq,\n );\n },\n async close() {\n if (db.open) db.close();\n },\n };\n },\n },\n { setAsDefault: true },\n );\n storeAuditBindingRegistered = true;\n}\n\nfunction rowToEntry(row: {\n seq: number;\n ts: number;\n actor_json: string;\n action: string;\n target: string;\n decision: string;\n context_json: string | null;\n metadata_json: string | null;\n prev_hash: string;\n hash: string;\n}) {\n return {\n seq: row.seq,\n ts: row.ts,\n actor: JSON.parse(row.actor_json),\n action: row.action,\n target: row.target,\n decision: row.decision as 'success' | 'denied' | 'error' | 'not-found',\n ...(row.context_json !== null ? { context: JSON.parse(row.context_json) } : {}),\n ...(row.metadata_json !== null ? { metadata: JSON.parse(row.metadata_json) } : {}),\n prevHash: row.prev_hash,\n hash: row.hash,\n };\n}\n\n/**\n * Public surface returned by {@link createServer}.\n *\n * @stable\n */\nexport interface GraphorinServer {\n readonly version: string;\n readonly config: ServerConfigSpec;\n readonly app: Hono<{ Variables: ServerVariables }>;\n readonly agents: AgentRegistry;\n readonly workflows: WorkflowRegistry;\n readonly runs: RunStateTracker;\n readonly listeningOn: { readonly host: string; readonly port: number } | undefined;\n /**\n * Optional WebSocket dispatcher exposed when `server.ws.enabled =\n * true`. Route handlers + the agent / workflow runtimes call\n * `dispatcher.emit(subject, event)` to fan out events to subscribed\n * clients. Returns `undefined` when WS is disabled.\n */\n readonly wsDispatcher: WsDispatcher | undefined;\n /**\n * Optional WebSocket ticket store exposed when `server.ws.enabled\n * = true`. Surfaced primarily for tests; the\n * `POST /v1/session/ws-ticket` route uses it transparently.\n */\n readonly wsTickets: WsTicketStore | undefined;\n /**\n * Optional triggers daemon - populated when the operator wired a\n * scheduler (or an in-process trigger surface) at construction\n * time. Phase 14c integration.\n */\n readonly triggers: TriggersDaemon | undefined;\n /**\n * Optional consolidator daemon - populated when the operator\n * supplied a `Consolidator` instance via `createServer({\n * consolidator })`. Phase 14c integration.\n */\n readonly consolidator: ConsolidatorDaemon | undefined;\n /**\n * Phase 14c Prometheus registry. Always present; sample updates\n * are observable via `metrics.snapshot()`.\n */\n readonly metrics: MetricRegistry;\n start(): Promise<{ readonly host: string; readonly port: number }>;\n stop(options?: { readonly force?: boolean }): Promise<void>;\n}\n\n/**\n * Discriminated union accepted by `CreateServerOptions.triggers`. A\n * caller may either supply a fully-built daemon (e.g. constructed\n * around a custom `Scheduler`) or just the underlying scheduler - the\n * server wraps it with {@link createTriggersDaemon} automatically.\n *\n * @stable\n */\nexport type TriggersDaemonInput =\n | { readonly daemon: TriggersDaemon }\n | { readonly scheduler: import('@graphorin/triggers').Scheduler };\n\n/**\n * @stable\n */\nexport interface CreateServerOptions {\n /** Loaded `graphorin.config.ts` payload - see `defineConfig({...})`. */\n readonly config?: ServerConfigInput;\n /**\n * Optional pre-validated config. When supplied, `config` is ignored\n * and the schema validation step is skipped. Useful for tests + the\n * `graphorin migrate` CLI command which bypasses the listener.\n */\n readonly validatedConfig?: ServerConfigSpec;\n /** Pre-built SQLite store. Tests inject an in-memory store. */\n readonly store?: GraphorinSqliteStore;\n /** Optional pre-built tracker. Tests inject deterministic timing. */\n readonly runs?: RunStateTracker;\n /** Optional pre-built registries. */\n readonly agents?: AgentRegistry;\n readonly workflows?: WorkflowRegistry;\n /** Optional in-process domain adapters wired into REST routes. */\n readonly sessions?: SessionApi;\n readonly memory?: MemoryApi;\n readonly skills?: SkillsApi;\n readonly mcp?: McpApi;\n readonly audit?: AuditApi;\n /**\n * Optional consolidator surface (`@graphorin/memory`). Phase 14c\n * starts/stops the runtime alongside the server lifecycle and\n * surfaces its status through `/v1/health`.\n */\n readonly consolidator?: ConsolidatorLike;\n /**\n * Optional triggers daemon - pass an existing one (e.g. built\n * from `createScheduler`) or a triggers configuration the server\n * should wrap with the daemon adapter.\n */\n readonly triggers?: TriggersDaemonInput;\n /**\n * Optional replay API consumed by the scope-enforced replay\n * endpoints. Phase 14c.\n */\n readonly replay?: ReplayApi;\n /**\n * Optional probes that augment `/v1/health`. Provided by consumer\n * code (e.g. `embedder` provides `embedderLoaded`).\n */\n readonly healthProbes?: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n /**\n * Optional Prometheus metric registry override. When omitted, the\n * server constructs the canonical registry from\n * {@link createServerMetricRegistry}.\n */\n readonly metricRegistry?: MetricRegistry;\n /** Lifecycle hook overrides. */\n readonly hooks?: LifecycleHooks;\n /** Wall-clock provider for tests. */\n readonly now?: () => number;\n /** Override the cipher peer probe. Tests inject a stub. */\n readonly probeCipherPeer?: () => Promise<void>;\n /** Override the package version reported on `/v1/health`. */\n readonly version?: string;\n /** Skip `applyProcessHardening` (tests). */\n readonly skipHardening?: boolean;\n /** Skip starting the actual listener (tests). */\n readonly skipListen?: boolean;\n}\n\n/**\n * @stable\n */\nexport const VERSION = '0.6.0';\n\n/**\n * Build a fully-wired Graphorin server. The returned handle is\n * inert until `start()` is awaited.\n *\n * @stable\n */\nexport async function createServer(options: CreateServerOptions = {}): Promise<GraphorinServer> {\n const config: ServerConfigSpec =\n options.validatedConfig ?? parseServerConfig(options.config ?? {});\n const now = options.now ?? Date.now;\n const startedAt = now();\n const version = options.version ?? VERSION;\n\n // IP-1: when encryption is configured, resolve the passphrase ref\n // BEFORE constructing the store and thread the encryption config\n // through - `graphorin init --encrypted` produced a config nothing\n // honoured, and a database encrypted via `storage encrypt` could not\n // be opened by the server at all.\n let storeEncryption:\n | { enabled: true; cipher?: never; passphraseResolver: () => Promise<string> }\n | undefined;\n if (options.store === undefined && config.storage.encryption.enabled) {\n if (config.storage.encryption.passphraseRef === undefined) {\n throw new Error(\n '[graphorin/server] storage.encryption.enabled is true but no passphraseRef is configured.',\n );\n }\n const { resolveSecret } = await import('@graphorin/security/secrets');\n const passphrase = await resolveSecret(config.storage.encryption.passphraseRef);\n storeEncryption = {\n enabled: true,\n passphraseResolver: async () => passphrase.use((v) => v),\n } as never;\n }\n const store =\n options.store ??\n (await createSqliteStore({\n path: config.storage.path,\n mode: config.storage.mode,\n ...(storeEncryption !== undefined\n ? {\n encryption: {\n enabled: true,\n ...(config.storage.encryption.cipher !== undefined\n ? { cipher: config.storage.encryption.cipher as never }\n : {}),\n passphraseResolver: storeEncryption.passphraseResolver,\n },\n }\n : {}),\n }));\n\n const metricRegistry = options.metricRegistry ?? createServerMetricRegistry();\n metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version });\n\n // IP-15: agent / workflow run completions move graphorin_agent_runs_total +\n // graphorin_agent_run_duration_seconds. The tracker stays metric-agnostic -\n // it fires a terminal callback the server turns into samples.\n const runs =\n options.runs ??\n new RunStateTracker({\n now,\n onTerminal: (info) => {\n metricRegistry.inc(SERVER_METRIC_NAMES.agentRunsTotal, { status: info.status });\n if (info.durationMs !== undefined) {\n metricRegistry.observe(SERVER_METRIC_NAMES.agentRunDuration, info.durationMs / 1000);\n }\n },\n });\n const agents = options.agents ?? new AgentRegistry();\n const workflows = options.workflows ?? new WorkflowRegistry();\n\n const app = new Hono<{ Variables: ServerVariables }>();\n\n // Global middleware in the documented order. Per-route scope +\n // idempotency layers are mounted by the route factories so the\n // composition stays declarative + auditable.\n app.use(\n '*',\n createRequestStateMiddleware({\n now,\n ...(config.server.trustProxy ? { trustProxy: true } : {}),\n }),\n );\n app.use('*', createCorsMiddleware(config.server.cors));\n app.use('*', createCsrfMiddleware(config.server.csrf));\n app.use('*', createRateLimitMiddleware(config.server.rateLimit, { now }));\n\n let serverInstance: ServerType | undefined;\n let listening: { readonly host: string; readonly port: number } | undefined;\n let started = false;\n let stopped = false;\n // IP-16: stops the periodic terminal-run prune sweep on shutdown.\n let stopRunPruning: (() => void) | undefined;\n let auditDb: AuditDb | undefined;\n let preBind: Awaited<ReturnType<typeof runPreBind>> | undefined;\n let verifier: TokenVerifier | undefined;\n let pepperHandle: import('@graphorin/security').SecretValue | undefined;\n\n let triggersDaemon: TriggersDaemon | undefined;\n if (options.triggers !== undefined) {\n if ('daemon' in options.triggers) {\n triggersDaemon = options.triggers.daemon;\n } else {\n triggersDaemon = createTriggersDaemon({ scheduler: options.triggers.scheduler });\n }\n }\n let consolidatorDaemon: ConsolidatorDaemon | undefined;\n if (options.consolidator !== undefined) {\n consolidatorDaemon = createConsolidatorDaemon({ consolidator: options.consolidator });\n }\n\n let dispatcher: WsDispatcher | undefined;\n let tickets: WsTicketStore | undefined;\n let wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined;\n // IP-21: the dispatcher is built here, before the audit DB opens in start().\n // Hand it a late-bound commentary sink now and install the audit-writing\n // target once the DB exists - otherwise the sanitizer's documented decisions\n // (wrapped/stripped frames with before/after digests) are dropped.\n let commentaryAuditSink: LateBoundCommentarySink | undefined;\n if (config.server.ws.enabled) {\n commentaryAuditSink = createLateBoundCommentarySink();\n dispatcher = createWsDispatcher({\n commentary: {\n policy: config.server.ws.commentarySanitization.policy,\n applyToEvents: config.server.ws.commentarySanitization.applyToEvents,\n sink: commentaryAuditSink,\n },\n replayBuffer: {\n maxEvents: config.server.stream.replayBuffer.maxEvents,\n ttlMs: config.server.stream.replayBuffer.ttlSeconds * 1000,\n },\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now,\n });\n tickets = createWsTicketStore({\n ttlMs: config.server.ws.ticketTtlMs,\n now,\n });\n wsAdapter = createNodeWebSocket({ app });\n // The WS server inside @hono/node-ws is created without any\n // subprotocol-negotiation policy; without one, the `ws` library\n // never echoes back `Sec-WebSocket-Protocol` and clients that\n // advertised a subprotocol close the connection immediately.\n // Mutate the options to install a Graphorin-aware policy that\n // selects `graphorin.protocol.v1` when the client offered it.\n const wssOptions = (\n wsAdapter.wss as unknown as {\n options: {\n handleProtocols?: (\n protocols: Set<string>,\n request: import('node:http').IncomingMessage,\n ) => string | false;\n };\n }\n ).options;\n wssOptions.handleProtocols = (protocols) => {\n const negotiated = negotiateSubprotocol(Array.from(protocols));\n if (negotiated !== null) return negotiated;\n // Browser ticket flow: the `WebSocket` constructor cannot set an\n // `Authorization` header, so the browser client offers two\n // subprotocol tokens - the canonical `graphorin.protocol.v1`\n // name plus a `ticket.<value>` token (see the wire contract in\n // `@graphorin/protocol`'s `subprotocol.ts`:\n // `SUBPROTOCOL_NAME` / `TICKET_SUBPROTOCOL_PREFIX` /\n // `parseTicketSubprotocol`). The server MUST echo back exactly\n // the canonical name (never the `ticket.*` token) so the\n // handshake's `Sec-WebSocket-Protocol` response stays valid; the\n // ticket value is consumed separately by `createWsUpgradeEvents`\n // via `parseTicketSubprotocol` and exchanged through the\n // single-use `WsTicketStore`.\n for (const candidate of protocols) {\n if (candidate === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;\n }\n return false;\n };\n }\n\n const handle: GraphorinServer = {\n version,\n config,\n app,\n agents,\n workflows,\n runs,\n metrics: metricRegistry,\n get wsDispatcher() {\n return dispatcher;\n },\n get wsTickets() {\n return tickets;\n },\n get triggers() {\n return triggersDaemon;\n },\n get consolidator() {\n return consolidatorDaemon;\n },\n get listeningOn() {\n return listening;\n },\n async start(): Promise<{ readonly host: string; readonly port: number }> {\n if (started) throw new LifecycleDoubleStartError();\n started = true;\n try {\n if (options.hooks?.beforeStart !== undefined) {\n await options.hooks.beforeStart({ config });\n }\n if (options.skipHardening !== true && config.hardening.applyOnStart) {\n applyProcessHardening({\n refuseRoot: config.hardening.refuseRoot,\n umask: config.hardening.umask,\n });\n }\n preBind = await runPreBind({\n config,\n store,\n ...(options.probeCipherPeer !== undefined\n ? { probeCipherPeer: options.probeCipherPeer }\n : {}),\n });\n\n if (\n config.audit.enabled &&\n preBind.auditPassphrase !== undefined &&\n preBind.auditPath !== undefined\n ) {\n ensureStoreAuditBinding();\n auditDb = await openAuditDb({\n path: preBind.auditPath,\n passphrase: preBind.auditPassphrase,\n ...(config.audit.cipher !== undefined ? { cipher: config.audit.cipher } : {}),\n });\n // IP-21: now that the audit chain is open, route the WS dispatcher's\n // commentary-sanitizer decisions into it.\n commentaryAuditSink?.bind(bridgeCommentaryToAudit(auditDb));\n }\n\n if (config.auth.kind === 'token') {\n const pepper = preBind.pepper ?? (await fallbackPepper(options.skipHardening === true));\n pepperHandle = pepper;\n verifier = new TokenVerifier({\n tokenStore: store.authTokens,\n pepper,\n acceptPrefix: config.auth.tokenPrefix,\n acceptEnvironments: config.auth.tokenEnvironments,\n ...(config.auth.perIpFailureThreshold !== undefined\n ? { perIpFailureThreshold: config.auth.perIpFailureThreshold }\n : {}),\n ...(config.auth.perIpLockoutMs !== undefined\n ? { perIpLockoutMs: config.auth.perIpLockoutMs }\n : {}),\n now,\n });\n }\n\n mountRoutes(app, config, {\n version,\n startedAt,\n now,\n agents,\n workflows,\n runs,\n store,\n metricRegistry,\n ...(options.sessions !== undefined ? { sessions: options.sessions } : {}),\n ...(options.memory !== undefined ? { memory: options.memory } : {}),\n ...(options.skills !== undefined ? { skills: options.skills } : {}),\n ...(options.mcp !== undefined ? { mcp: options.mcp } : {}),\n ...(options.audit !== undefined ? { audit: options.audit } : {}),\n ...(options.replay !== undefined ? { replay: options.replay } : {}),\n ...(options.healthProbes !== undefined ? { healthProbes: options.healthProbes } : {}),\n ...(verifier !== undefined ? { verifier } : {}),\n ...(auditDb !== undefined ? { auditDb } : {}),\n ...(pepperHandle !== undefined ? { pepper: pepperHandle } : {}),\n ...(dispatcher !== undefined ? { wsDispatcher: dispatcher } : {}),\n ...(tickets !== undefined ? { wsTickets: tickets } : {}),\n ...(wsAdapter !== undefined ? { wsAdapter } : {}),\n ...(triggersDaemon !== undefined ? { triggersDaemon } : {}),\n ...(consolidatorDaemon !== undefined ? { consolidatorDaemon } : {}),\n });\n\n // Start the consolidator first so it is ready to handle fired\n // triggers, bridge its cron / idle triggers onto the scheduler\n // (MCON-4 - without this nothing pipes triggers into the\n // consolidator and background consolidation never runs), then start\n // the scheduler last so it only fires fully-wired triggers.\n if (consolidatorDaemon !== undefined) {\n await consolidatorDaemon.start();\n if (\n triggersDaemon !== undefined &&\n consolidatorDaemon.consolidator.registerWithScheduler !== undefined\n ) {\n await consolidatorDaemon.consolidator.registerWithScheduler(triggersDaemon.scheduler);\n }\n }\n if (triggersDaemon !== undefined) {\n await triggersDaemon.start();\n }\n\n // Sample a couple of gauges immediately so the very first\n // `/v1/metrics` scrape after start carries non-zero data.\n try {\n const wal = readWalSize(store.connection);\n metricRegistry.set(SERVER_METRIC_NAMES.storageWalSize, wal);\n } catch {\n // Best-effort.\n }\n metricRegistry.set(\n SERVER_METRIC_NAMES.serverUptime,\n Math.max(0, Math.floor((now() - startedAt) / 1000)),\n );\n metricRegistry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());\n metricRegistry.set(SERVER_METRIC_NAMES.replayBufferEvents, 0);\n\n // IP-16: terminal run records (each holding an AbortController) would\n // otherwise accumulate forever; sweep them on a periodic timer.\n stopRunPruning = scheduleRunPruning(runs, now);\n\n if (options.skipListen !== true) {\n serverInstance = serve({\n fetch: app.fetch.bind(app),\n hostname: config.server.host,\n port: config.server.port,\n });\n if (wsAdapter !== undefined && serverInstance !== undefined) {\n wsAdapter.injectWebSocket(serverInstance);\n }\n await new Promise<void>((resolve) => {\n const server = serverInstance as unknown as {\n once(event: 'listening', cb: () => void): void;\n };\n if ('once' in server && typeof server.once === 'function') {\n server.once('listening', () => resolve());\n } else {\n // serve(...) returned a Node http.Server already bound.\n setImmediate(() => resolve());\n }\n });\n const address = (\n serverInstance as unknown as { address(): AddressInfo | string | null }\n ).address();\n if (address !== null && typeof address === 'object') {\n listening = { host: address.address, port: address.port };\n } else {\n listening = { host: config.server.host, port: config.server.port };\n }\n } else {\n listening = { host: config.server.host, port: config.server.port };\n }\n\n if (options.hooks?.onReady !== undefined) {\n await options.hooks.onReady({ config, listeningOn: listening });\n }\n return listening;\n } catch (err) {\n started = false;\n await emitError(options.hooks, { error: err, phase: 'beforeStart' });\n throw err;\n }\n },\n async stop({ force }: { readonly force?: boolean } = {}): Promise<void> {\n if (!started) throw new LifecycleNotStartedError();\n if (stopped) return;\n stopped = true;\n // IP-16: halt the prune sweep before draining.\n stopRunPruning?.();\n stopRunPruning = undefined;\n const drainTimeoutMs = force === true ? 0 : config.server.shutdown.drainTimeoutMs;\n try {\n if (options.hooks?.beforeShutdown !== undefined) {\n await options.hooks.beforeShutdown({\n config,\n inflight: runs.inflightCount(),\n drainTimeoutMs,\n });\n }\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n\n if (triggersDaemon !== undefined) {\n try {\n await triggersDaemon.stop();\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n }\n if (consolidatorDaemon !== undefined) {\n try {\n await consolidatorDaemon.stop();\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n }\n\n // Pending reservations (e.g. awaited WS subscriptions for the\n // streaming endpoints) hold no work in progress; abort them\n // immediately so the drain only waits for live runs.\n runs.abortPending('server-shutdown');\n const drained = await drainInFlight(runs, drainTimeoutMs, now);\n if (!drained && drainTimeoutMs > 0) {\n // Force-abort everything that didn't drain in time.\n runs.abortAll(new ShutdownTimeoutError(drainTimeoutMs, runs.runningCount()));\n }\n\n if (dispatcher !== undefined) {\n try {\n dispatcher.shutdown();\n } catch {\n // Best-effort during stop().\n }\n }\n\n if (serverInstance !== undefined) {\n await new Promise<void>((resolve) => {\n (serverInstance as unknown as { close(cb: () => void): void }).close(() => resolve());\n });\n serverInstance = undefined;\n }\n if (auditDb !== undefined) {\n await auditDb.close();\n auditDb = undefined;\n }\n await store.close();\n listening = undefined;\n verifier = undefined;\n started = false;\n preBind = undefined;\n dispatcher = undefined;\n tickets = undefined;\n wsAdapter = undefined;\n },\n };\n return handle;\n}\n\ninterface MountRoutesContext {\n readonly version: string;\n readonly startedAt: number;\n readonly now: () => number;\n readonly agents: AgentRegistry;\n readonly workflows: WorkflowRegistry;\n readonly runs: RunStateTracker;\n readonly store: GraphorinSqliteStore;\n readonly metricRegistry: MetricRegistry;\n readonly sessions?: SessionApi;\n readonly memory?: MemoryApi;\n readonly skills?: SkillsApi;\n readonly mcp?: McpApi;\n readonly audit?: AuditApi;\n readonly replay?: ReplayApi;\n readonly healthProbes?: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n readonly verifier?: TokenVerifier;\n readonly auditDb?: AuditDb;\n readonly pepper?: import('@graphorin/security').SecretValue;\n readonly wsDispatcher?: WsDispatcher;\n readonly wsTickets?: WsTicketStore;\n readonly wsAdapter?: ReturnType<typeof createNodeWebSocket>;\n readonly triggersDaemon?: TriggersDaemon;\n readonly consolidatorDaemon?: ConsolidatorDaemon;\n}\n\n/** IP-23: is `host` a loopback interface (so an open /metrics is not exposed)? */\nfunction isLoopbackHost(host: string): boolean {\n const h = host.trim().toLowerCase();\n return h === '127.0.0.1' || h === '::1' || h === '[::1]' || h === 'localhost';\n}\n\nfunction mountRoutes(\n app: Hono<{ Variables: ServerVariables }>,\n config: ServerConfigSpec,\n ctx: MountRoutesContext,\n): void {\n const base = config.server.basePath;\n const probes =\n ctx.healthProbes ??\n (() =>\n buildDefaultHealthProbes(\n ctx.store,\n ctx.triggersDaemon,\n ctx.consolidatorDaemon,\n ctx.wsDispatcher,\n config,\n ));\n const health = createExtendedHealthRoutes({\n version: ctx.version,\n startedAt: ctx.startedAt,\n now: ctx.now,\n probes,\n });\n app.route(`${base}/health`, health);\n\n // IP-13: `auth.kind='none'` disables authentication on every route. It is the\n // documented trusted-loopback / single-operator mode, but binding a\n // non-loopback host with auth off exposes full admin access (including the WS\n // stream) to anyone who can reach it - warn loudly rather than silently.\n if (config.auth.kind === 'none' && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: auth.kind='none' disables authentication on every endpoint, ` +\n `but the server binds the non-loopback host '${config.server.host}'. Anyone who can reach ` +\n `it has full admin access - use auth.kind='token' for non-loopback deployments or bind a ` +\n `loopback host.`,\n );\n }\n\n if (config.metrics.enabled) {\n // IP-23: an unauthenticated /metrics endpoint leaks operational intel\n // (trigger ids in labels, consolidator budgets). It is fine on a loopback\n // host, but binding a non-loopback host with auth off silently exposes it.\n if (!config.metrics.requireAuth && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: /metrics is unauthenticated (metrics.requireAuth=false) and ` +\n `the server binds the non-loopback host '${config.server.host}'. The exposition leaks ` +\n `operational detail to anyone who can reach it - set metrics.requireAuth=true or bind a ` +\n `loopback host.`,\n );\n }\n const metricsRoute = createMetricsRoutes({\n registry: ctx.metricRegistry,\n requireAuth: config.metrics.requireAuth,\n refresh: () =>\n refreshLiveMetrics({\n registry: ctx.metricRegistry,\n store: ctx.store,\n runs: ctx.runs,\n startedAt: ctx.startedAt,\n now: ctx.now,\n ...(ctx.triggersDaemon !== undefined ? { triggersDaemon: ctx.triggersDaemon } : {}),\n ...(ctx.consolidatorDaemon !== undefined\n ? { consolidatorDaemon: ctx.consolidatorDaemon }\n : {}),\n ...(ctx.wsDispatcher !== undefined ? { wsDispatcher: ctx.wsDispatcher } : {}),\n }),\n });\n app.route(`${base}${config.metrics.path}`, metricsRoute);\n }\n\n // Authenticated subtree begins here. The health endpoint above is\n // intentionally outside the auth boundary so liveness probes work\n // before the token verifier is wired. The WebSocket upgrade path is\n // also exempt: the upgrade handler in `ws/upgrade.ts` performs its\n // own bearer + ticket validation inline (the HTTP auth middleware\n // would otherwise short-circuit the upgrade with a 401 response\n // before the handler can negotiate the subprotocol).\n const wsUpgradePath = config.server.ws.enabled ? `${base}${config.server.ws.path}` : undefined;\n const metricsPath =\n config.metrics.enabled && !config.metrics.requireAuth\n ? `${base}${config.metrics.path}`\n : undefined;\n function shouldSkipAuth(path: string): boolean {\n if (path === `${base}/health` || path === `${base}/health/`) return true;\n if (wsUpgradePath !== undefined) {\n if (path === wsUpgradePath || path === `${wsUpgradePath}/`) return true;\n }\n if (metricsPath !== undefined) {\n if (path === metricsPath || path === `${metricsPath}/`) return true;\n }\n return false;\n }\n // IP-13: in the no-auth loopback mode (`auth.kind='none'`) there is no\n // verifier, but the authenticated subtree must still be reachable - install\n // an anonymous middleware that stamps a fully-authorized principal so the\n // domain routes serve instead of every one of them returning 401.\n const anonymousAuth = ctx.verifier === undefined && config.auth.kind === 'none';\n if (ctx.verifier !== undefined || anonymousAuth) {\n const authMw =\n ctx.verifier !== undefined\n ? createAuthMiddleware({ verifier: ctx.verifier })\n : createAnonymousAuthMiddleware();\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return authMw(c, next);\n });\n if (ctx.auditDb !== undefined) {\n const auditMw = createAuditMiddleware({ auditDb: ctx.auditDb, now: ctx.now });\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return auditMw(c, next);\n });\n }\n }\n\n // Idempotency middleware - applied to side-effecting endpoints. We\n // mount it once on the authenticated subtree so handlers don't have\n // to duplicate the configuration per-route.\n if (config.server.idempotency.enabled) {\n const idempotencyMw = createIdempotencyMiddleware({\n store: ctx.store.idempotency,\n config: config.server.idempotency,\n now: ctx.now,\n // IP-6: token minting returns a raw secret - never cache it.\n excludeResponseCachePaths: [`${base}/tokens`],\n // IP-15: publish the live cache hit ratio gauge.\n metricRegistry: ctx.metricRegistry,\n });\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return idempotencyMw(c, next);\n });\n }\n\n // Mounted AFTER the auth middleware so the scope check has a\n // verified token to inspect; the unauthenticated `/v1/health` GET\n // continues to serve the rollup.\n app.route(`${base}/health/secrets`, createSecretsHealthRoutes());\n\n app.route(\n `${base}/agents`,\n createAgentRoutes({\n agents: ctx.agents,\n runs: ctx.runs,\n // IP-2: the streaming dispatcher reaches the route layer.\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n app.route(`${base}/runs`, createRunRoutes({ agents: ctx.agents, runs: ctx.runs }));\n app.route(\n `${base}/workflows`,\n createWorkflowRoutes({\n workflows: ctx.workflows,\n runs: ctx.runs,\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n if (ctx.sessions !== undefined) {\n app.route(`${base}/sessions`, createSessionRoutes({ sessions: ctx.sessions }));\n }\n if (ctx.memory !== undefined) {\n app.route(`${base}/memory`, createMemoryRoutes({ memory: ctx.memory }));\n }\n if (ctx.skills !== undefined) {\n app.route(`${base}/skills`, createSkillsRoutes({ skills: ctx.skills }));\n }\n if (ctx.mcp !== undefined) {\n app.route(`${base}/mcp`, createMcpRoutes({ mcp: ctx.mcp }));\n }\n if (ctx.audit !== undefined) {\n app.route(`${base}/audit`, createAuditRoutes({ audit: ctx.audit }));\n }\n if (ctx.triggersDaemon !== undefined) {\n app.route(`${base}/triggers`, createTriggersRoutes({ daemon: ctx.triggersDaemon }));\n }\n if (ctx.replay !== undefined) {\n app.route(\n `${base}`,\n createReplayRoutes({\n replay: ctx.replay,\n ...(ctx.auditDb !== undefined ? { auditDb: ctx.auditDb } : {}),\n now: ctx.now,\n }),\n );\n }\n if (config.auth.kind === 'token' && ctx.pepper !== undefined) {\n app.route(\n `${base}/tokens`,\n createTokensRoutes({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n defaultEnv: 'live',\n allowedEnvs: config.auth.tokenEnvironments,\n }),\n );\n }\n if (ctx.wsTickets !== undefined) {\n app.route(`${base}`, createAuthRoutes({ tickets: ctx.wsTickets }));\n }\n // IP-13: mount the WS upgrade when a verifier is wired OR auth is disabled\n // (`auth.kind='none'`). The old condition required a verifier, so\n // `ws.enabled: true` under no-auth was silently ignored - the route never\n // mounted and clients got a bare 404 with no explanation.\n if (\n ctx.wsDispatcher !== undefined &&\n ctx.wsTickets !== undefined &&\n ctx.wsAdapter !== undefined &&\n (ctx.verifier !== undefined || anonymousAuth) &&\n config.server.ws.enabled\n ) {\n const dispatcher = ctx.wsDispatcher;\n const tickets = ctx.wsTickets;\n const verifier = ctx.verifier;\n const runs = ctx.runs;\n app.get(\n `${base}${config.server.ws.path}`,\n ctx.wsAdapter.upgradeWebSocket((c) =>\n createWsUpgradeEvents(c, {\n dispatcher,\n tickets,\n ...(verifier !== undefined ? { verifier } : {}),\n anonymous: anonymousAuth,\n runs,\n now: ctx.now,\n }),\n ),\n );\n }\n if (ctx.wsDispatcher !== undefined && config.server.sse.enabled) {\n app.route(\n `${base}${config.server.sse.path}`,\n createSseRoutes({\n dispatcher: ctx.wsDispatcher,\n keepAliveMs: config.server.sse.keepAliveMs,\n // IP-9: bound the per-connection delivery queue.\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now: ctx.now,\n }),\n );\n }\n}\n\nasync function drainInFlight(\n runs: RunStateTracker,\n drainTimeoutMs: number,\n now: () => number,\n): Promise<boolean> {\n if (runs.runningCount() === 0) return true;\n if (drainTimeoutMs <= 0) return runs.runningCount() === 0;\n const deadline = now() + drainTimeoutMs;\n while (now() < deadline) {\n if (runs.runningCount() === 0) return true;\n await new Promise((resolve) => setTimeout(resolve, Math.min(50, deadline - now())));\n }\n return runs.runningCount() === 0;\n}\n\nasync function emitError(hooks: LifecycleHooks | undefined, ctx: OnErrorContext): Promise<void> {\n if (hooks?.onError === undefined) return;\n try {\n await hooks.onError(ctx);\n } catch {\n // onError must never throw further; swallow.\n }\n}\n\ninterface RefreshLiveMetricsOptions {\n readonly registry: MetricRegistry;\n readonly store: GraphorinSqliteStore;\n readonly runs: RunStateTracker;\n readonly startedAt: number;\n readonly now: () => number;\n readonly triggersDaemon?: TriggersDaemon;\n readonly consolidatorDaemon?: ConsolidatorDaemon;\n readonly wsDispatcher?: WsDispatcher;\n}\n\nasync function refreshLiveMetrics(options: RefreshLiveMetricsOptions): Promise<void> {\n const { registry, store, runs, startedAt, now } = options;\n\n try {\n const wal = readWalSize(store.connection);\n registry.set(SERVER_METRIC_NAMES.storageWalSize, wal);\n } catch {\n // Best-effort.\n }\n\n registry.set(\n SERVER_METRIC_NAMES.serverUptime,\n Math.max(0, Math.floor((now() - startedAt) / 1000)),\n );\n registry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());\n\n if (options.wsDispatcher !== undefined) {\n const sizes = options.wsDispatcher.size();\n registry.set(SERVER_METRIC_NAMES.replayBufferEvents, sizes.subscriptions);\n }\n\n if (options.triggersDaemon !== undefined) {\n const metrics = options.triggersDaemon.metrics();\n for (const [triggerId, counts] of metrics.fires) {\n const sanitized = sanitizeMetricLabelValue(triggerId);\n const successCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {\n trigger_id: sanitized,\n status: 'success',\n });\n const errorCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {\n trigger_id: sanitized,\n status: 'error',\n });\n const successDelta = counts.success - successCurrent;\n const errorDelta = counts.error - errorCurrent;\n if (successDelta > 0) {\n registry.inc(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n { trigger_id: sanitized, status: 'success' },\n successDelta,\n );\n }\n if (errorDelta > 0) {\n registry.inc(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n { trigger_id: sanitized, status: 'error' },\n errorDelta,\n );\n }\n }\n }\n\n if (options.consolidatorDaemon !== undefined) {\n try {\n const status = await options.consolidatorDaemon.status();\n registry.set(SERVER_METRIC_NAMES.consolidatorQueueDepth, status.queueDepth, {\n phase: 'aggregate',\n });\n registry.set(SERVER_METRIC_NAMES.consolidatorDlqSize, status.dlqSize);\n registry.set(SERVER_METRIC_NAMES.consolidatorBudgetRemainingUsd, status.budget.costRemaining);\n registry.set(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingTokens,\n status.budget.tokensRemaining,\n );\n } catch {\n // Best-effort.\n }\n }\n}\n\n/**\n * Convert an arbitrary user-supplied identifier (trigger id) into a\n * Prometheus-safe label value. Replaces every character outside the\n * `[A-Za-z0-9_:]` range with `_`. This guarantees the cardinality\n * never explodes on UTF-8 sequences while keeping the value\n * recognizable.\n */\nfunction sanitizeMetricLabelValue(value: string): string {\n return value.replace(/[^A-Za-z0-9_:.-]/g, '_').slice(0, 200);\n}\n\nfunction readCounter(\n registry: MetricRegistry,\n name: string,\n labels: Record<string, string>,\n): number {\n const snap = registry.snapshot().counters[name] ?? [];\n for (const entry of snap) {\n if (matchesLabels(entry.labels, labels)) return entry.value;\n }\n return 0;\n}\n\nfunction matchesLabels(\n a: Record<string, string | number | boolean>,\n b: Record<string, string>,\n): boolean {\n for (const k of Object.keys(b)) {\n if (String(a[k]) !== b[k]) return false;\n }\n for (const k of Object.keys(a)) {\n if (!(k in b)) return false;\n }\n return true;\n}\n\nfunction buildDefaultHealthProbes(\n store: GraphorinSqliteStore,\n triggersDaemon: TriggersDaemon | undefined,\n consolidatorDaemon: ConsolidatorDaemon | undefined,\n dispatcher: WsDispatcher | undefined,\n config: ServerConfigSpec,\n): HealthCheckOptions {\n const out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n } = {\n store,\n walWarnThresholdBytes: config.health.walWarnThresholdBytes,\n encryptionEnabled: config.storage.encryption.enabled,\n // IP-1: when this process built the encrypted store itself, the\n // keyed open at boot proved the cipher peer - report the fact.\n ...(config.storage.encryption.enabled ? { cipherPeerInstalled: true } : {}),\n };\n if (triggersDaemon !== undefined) out.triggers = triggersDaemon;\n if (consolidatorDaemon !== undefined) out.consolidator = consolidatorDaemon;\n if (dispatcher !== undefined) {\n const sizes = dispatcher.size();\n // The dispatcher only exposes (subscribers, subscriptions); the\n // per-subject replay-buffer occupancy is owned by each\n // subscription, not the dispatcher itself. Surface the active\n // subscription count as a usable proxy + the raw subscriber count\n // so operators can correlate.\n out.replayBuffer = {\n eventsBuffered: sizes.subscriptions,\n subscribers: sizes.subscribers,\n subscriptions: sizes.subscriptions,\n };\n }\n return Object.freeze(out as HealthCheckOptions);\n}\n\nasync function fallbackPepper(skipHardening: boolean) {\n // Test-only path: when `skipHardening` is set we mint an ephemeral\n // pepper so the verifier is constructible without a real keyring.\n if (!skipHardening) {\n throw new Error(\n '[graphorin/server] missing resolved pepper after pre-bind; auth.pepperRef is required when auth.kind = token.',\n );\n }\n return generatePepper();\n}\n\nvoid process;\nvoid ConfigInvalidError;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app.js","names":["VERSION: string","pkg.version","config: ServerConfigSpec","version","storeEncryption:\n | { enabled: true; cipher?: never; passphraseResolver: () => Promise<string> }\n | undefined","serverInstance: ServerType | undefined","listening: { readonly host: string; readonly port: number } | undefined","stopRunPruning: (() => void) | undefined","auditDb: AuditDb | undefined","preBind: Awaited<ReturnType<typeof runPreBind>> | undefined","verifier: TokenVerifier | undefined","pepperHandle: import('@graphorin/security').SecretValue | undefined","triggersDaemon: TriggersDaemon | undefined","consolidatorDaemon: ConsolidatorDaemon | undefined","dispatcher: WsDispatcher | undefined","tickets: WsTicketStore | undefined","wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined","commentaryAuditSink: LateBoundCommentarySink | undefined","out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n }"],"sources":["../src/app.ts"],"sourcesContent":["/**\n * `createServer({...})` - the single programmatic entry point.\n *\n * The factory returns a {@link GraphorinServer} handle the operator\n * controls via `start()` / `stop()`. The same handle is consumed by\n * the `graphorin start` CLI binary in `@graphorin/cli`.\n *\n * Phase 14a covers the full HTTP surface, the lifecycle plumbing,\n * the Hono app composition, and the auth / scope / idempotency /\n * audit middleware stack. Phase 14b/c attach the WebSocket layer +\n * triggers daemon + observability endpoints to the same handle.\n *\n * @packageDocumentation\n */\n\nimport type { AddressInfo } from 'node:net';\nimport process from 'node:process';\nimport { negotiateSubprotocol, SUBPROTOCOL_NAME } from '@graphorin/protocol';\nimport { applyProcessHardening, generatePepper, TokenVerifier } from '@graphorin/security';\nimport { type AuditDb, openAuditDb, registerAuditDbBinding } from '@graphorin/security/audit';\nimport {\n createSqliteStore,\n type GraphorinSqliteStore,\n loadCipherDriver,\n readWalSize,\n} from '@graphorin/store-sqlite';\nimport { type ServerType, serve } from '@hono/node-server';\nimport { createNodeWebSocket } from '@hono/node-ws';\nimport { Hono } from 'hono';\nimport {\n bridgeCommentaryToAudit,\n createLateBoundCommentarySink,\n type LateBoundCommentarySink,\n} from './commentary/index.js';\nimport { parseServerConfig, type ServerConfigInput, type ServerConfigSpec } from './config.js';\nimport type { ConsolidatorDaemon, ConsolidatorLike } from './consolidator/daemon.js';\nimport { createConsolidatorDaemon } from './consolidator/daemon.js';\nimport {\n ConfigInvalidError,\n LifecycleDoubleStartError,\n LifecycleNotStartedError,\n ShutdownTimeoutError,\n} from './errors/index.js';\nimport {\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type HealthCheckOptions,\n} from './health/index.js';\nimport type { ServerVariables } from './internal/context.js';\nimport { type LifecycleHooks, type OnErrorContext, runPreBind } from './lifecycle/index.js';\nimport { createServerMetricRegistry, SERVER_METRIC_NAMES } from './metrics/catalog.js';\nimport type { MetricRegistry } from './metrics/registry.js';\nimport {\n createAnonymousAuthMiddleware,\n createAuditMiddleware,\n createAuthMiddleware,\n createCorsMiddleware,\n createCsrfMiddleware,\n createIdempotencyMiddleware,\n createRateLimitMiddleware,\n createRequestStateMiddleware,\n} from './middleware/index.js';\nimport { AgentRegistry, WorkflowRegistry } from './registry/index.js';\nimport { createReplayRoutes, type ReplayApi } from './replay/index.js';\nimport {\n type AuditApi,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type McpApi,\n type MemoryApi,\n type SessionApi,\n type SkillsApi,\n} from './routes/index.js';\nimport { RunStateTracker, scheduleRunPruning } from './runtime/run-state.js';\nimport { createSseRoutes } from './sse/index.js';\nimport type { TriggersDaemon } from './triggers/daemon.js';\nimport { createTriggersDaemon } from './triggers/daemon.js';\nimport { createTriggersRoutes } from './triggers/routes.js';\nimport {\n createWsDispatcher,\n createWsTicketStore,\n type WsDispatcher,\n type WsTicketStore,\n} from './ws/index.js';\nimport { createWsUpgradeEvents } from './ws/upgrade.js';\n\n/**\n * Pre-built audit-db binding shipped from `@graphorin/store-sqlite`.\n * Registered exactly once per process so {@link openAuditDb} can find\n * a default binding without forcing operators to wire it manually.\n *\n * Exported as {@link ensureStoreAuditBinding} so the CLI (Phase 15\n * `graphorin audit verify | prune | export`) can reach into the same\n * binding without booting the HTTP listener.\n *\n * @stable\n */\nlet storeAuditBindingRegistered = false;\nexport function ensureStoreAuditBinding(): void {\n if (storeAuditBindingRegistered) return;\n registerAuditDbBinding(\n {\n id: 'better-sqlite3-multiple-ciphers',\n description: 'Default audit-db binding shipped by @graphorin/store-sqlite.',\n open: async (opts) => {\n const driver = await loadCipherDriver();\n const passphrase = await opts.passphrase.use((value) => value);\n const Db = driver as unknown as new (\n path: string,\n ) => {\n pragma(s: string): unknown;\n prepare(sql: string): {\n run(...args: unknown[]): unknown;\n get(...args: unknown[]): unknown;\n all(...args: unknown[]): unknown;\n iterate(...args: unknown[]): IterableIterator<unknown>;\n };\n exec(sql: string): unknown;\n close(): void;\n open: boolean;\n };\n const db = new Db(opts.path);\n db.pragma(`key = '${passphrase.replace(/'/g, \"''\")}'`);\n db.pragma('journal_mode = WAL');\n db.pragma('synchronous = NORMAL');\n db.pragma('busy_timeout = 5000');\n db.pragma('foreign_keys = ON');\n db.exec(\n `CREATE TABLE IF NOT EXISTS audit_log (\n seq INTEGER PRIMARY KEY,\n ts INTEGER NOT NULL,\n actor_json TEXT NOT NULL,\n action TEXT NOT NULL,\n target TEXT NOT NULL,\n decision TEXT NOT NULL,\n context_json TEXT,\n metadata_json TEXT,\n prev_hash TEXT NOT NULL,\n hash TEXT NOT NULL UNIQUE\n ) WITHOUT ROWID;`,\n );\n return {\n binding: 'better-sqlite3-multiple-ciphers',\n path: opts.path,\n async insert(entry) {\n db.prepare(\n `INSERT INTO audit_log (\n seq, ts, actor_json, action, target, decision,\n context_json, metadata_json, prev_hash, hash\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n entry.seq,\n entry.ts,\n JSON.stringify(entry.actor),\n entry.action,\n entry.target,\n entry.decision,\n entry.context !== undefined ? JSON.stringify(entry.context) : null,\n entry.metadata !== undefined ? JSON.stringify(entry.metadata) : null,\n entry.prevHash,\n entry.hash,\n );\n return entry;\n },\n async latest() {\n const row = db.prepare('SELECT * FROM audit_log ORDER BY seq DESC LIMIT 1').get() as\n | undefined\n | {\n seq: number;\n ts: number;\n actor_json: string;\n action: string;\n target: string;\n decision: string;\n context_json: string | null;\n metadata_json: string | null;\n prev_hash: string;\n hash: string;\n };\n if (row === undefined) return undefined;\n return rowToEntry(row);\n },\n async *iterate(bounds) {\n const lo = bounds?.fromSeq ?? 1;\n const hi = bounds?.toSeq ?? Number.MAX_SAFE_INTEGER;\n const iter = db\n .prepare('SELECT * FROM audit_log WHERE seq BETWEEN ? AND ? ORDER BY seq ASC')\n .iterate(lo, hi);\n for (const row of iter) yield rowToEntry(row as Parameters<typeof rowToEntry>[0]);\n },\n async count() {\n const row = db.prepare('SELECT COUNT(*) AS n FROM audit_log').get() as { n: number };\n return row.n;\n },\n async deleteUpTo(threshold) {\n const before = db\n .prepare('SELECT COUNT(*) AS n FROM audit_log WHERE seq <= ?')\n .get(threshold) as { n: number };\n db.prepare('DELETE FROM audit_log WHERE seq <= ?').run(threshold);\n return before.n;\n },\n async replaceEntry(entry) {\n db.prepare(`UPDATE audit_log SET prev_hash = ?, hash = ? WHERE seq = ?`).run(\n entry.prevHash,\n entry.hash,\n entry.seq,\n );\n },\n async close() {\n if (db.open) db.close();\n },\n };\n },\n },\n { setAsDefault: true },\n );\n storeAuditBindingRegistered = true;\n}\n\nfunction rowToEntry(row: {\n seq: number;\n ts: number;\n actor_json: string;\n action: string;\n target: string;\n decision: string;\n context_json: string | null;\n metadata_json: string | null;\n prev_hash: string;\n hash: string;\n}) {\n return {\n seq: row.seq,\n ts: row.ts,\n actor: JSON.parse(row.actor_json),\n action: row.action,\n target: row.target,\n decision: row.decision as 'success' | 'denied' | 'error' | 'not-found',\n ...(row.context_json !== null ? { context: JSON.parse(row.context_json) } : {}),\n ...(row.metadata_json !== null ? { metadata: JSON.parse(row.metadata_json) } : {}),\n prevHash: row.prev_hash,\n hash: row.hash,\n };\n}\n\n/**\n * Public surface returned by {@link createServer}.\n *\n * @stable\n */\nexport interface GraphorinServer {\n readonly version: string;\n readonly config: ServerConfigSpec;\n readonly app: Hono<{ Variables: ServerVariables }>;\n readonly agents: AgentRegistry;\n readonly workflows: WorkflowRegistry;\n readonly runs: RunStateTracker;\n readonly listeningOn: { readonly host: string; readonly port: number } | undefined;\n /**\n * Optional WebSocket dispatcher exposed when `server.ws.enabled =\n * true`. Route handlers + the agent / workflow runtimes call\n * `dispatcher.emit(subject, event)` to fan out events to subscribed\n * clients. Returns `undefined` when WS is disabled.\n */\n readonly wsDispatcher: WsDispatcher | undefined;\n /**\n * Optional WebSocket ticket store exposed when `server.ws.enabled\n * = true`. Surfaced primarily for tests; the\n * `POST /v1/session/ws-ticket` route uses it transparently.\n */\n readonly wsTickets: WsTicketStore | undefined;\n /**\n * Optional triggers daemon - populated when the operator wired a\n * scheduler (or an in-process trigger surface) at construction\n * time. Phase 14c integration.\n */\n readonly triggers: TriggersDaemon | undefined;\n /**\n * Optional consolidator daemon - populated when the operator\n * supplied a `Consolidator` instance via `createServer({\n * consolidator })`. Phase 14c integration.\n */\n readonly consolidator: ConsolidatorDaemon | undefined;\n /**\n * Phase 14c Prometheus registry. Always present; sample updates\n * are observable via `metrics.snapshot()`.\n */\n readonly metrics: MetricRegistry;\n start(): Promise<{ readonly host: string; readonly port: number }>;\n stop(options?: { readonly force?: boolean }): Promise<void>;\n}\n\n/**\n * Discriminated union accepted by `CreateServerOptions.triggers`. A\n * caller may either supply a fully-built daemon (e.g. constructed\n * around a custom `Scheduler`) or just the underlying scheduler - the\n * server wraps it with {@link createTriggersDaemon} automatically.\n *\n * @stable\n */\nexport type TriggersDaemonInput =\n | { readonly daemon: TriggersDaemon }\n | { readonly scheduler: import('@graphorin/triggers').Scheduler };\n\n/**\n * @stable\n */\nexport interface CreateServerOptions {\n /** Loaded `graphorin.config.ts` payload - see `defineConfig({...})`. */\n readonly config?: ServerConfigInput;\n /**\n * Optional pre-validated config. When supplied, `config` is ignored\n * and the schema validation step is skipped. Useful for tests + the\n * `graphorin migrate` CLI command which bypasses the listener.\n */\n readonly validatedConfig?: ServerConfigSpec;\n /** Pre-built SQLite store. Tests inject an in-memory store. */\n readonly store?: GraphorinSqliteStore;\n /** Optional pre-built tracker. Tests inject deterministic timing. */\n readonly runs?: RunStateTracker;\n /** Optional pre-built registries. */\n readonly agents?: AgentRegistry;\n readonly workflows?: WorkflowRegistry;\n /** Optional in-process domain adapters wired into REST routes. */\n readonly sessions?: SessionApi;\n readonly memory?: MemoryApi;\n readonly skills?: SkillsApi;\n readonly mcp?: McpApi;\n readonly audit?: AuditApi;\n /**\n * Optional consolidator surface (`@graphorin/memory`). Phase 14c\n * starts/stops the runtime alongside the server lifecycle and\n * surfaces its status through `/v1/health`.\n */\n readonly consolidator?: ConsolidatorLike;\n /**\n * Optional triggers daemon - pass an existing one (e.g. built\n * from `createScheduler`) or a triggers configuration the server\n * should wrap with the daemon adapter.\n */\n readonly triggers?: TriggersDaemonInput;\n /**\n * Optional replay API consumed by the scope-enforced replay\n * endpoints. Phase 14c.\n */\n readonly replay?: ReplayApi;\n /**\n * Optional probes that augment `/v1/health`. Provided by consumer\n * code (e.g. `embedder` provides `embedderLoaded`).\n */\n readonly healthProbes?: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n /**\n * Optional Prometheus metric registry override. When omitted, the\n * server constructs the canonical registry from\n * {@link createServerMetricRegistry}.\n */\n readonly metricRegistry?: MetricRegistry;\n /** Lifecycle hook overrides. */\n readonly hooks?: LifecycleHooks;\n /** Wall-clock provider for tests. */\n readonly now?: () => number;\n /** Override the cipher peer probe. Tests inject a stub. */\n readonly probeCipherPeer?: () => Promise<void>;\n /** Override the package version reported on `/v1/health`. */\n readonly version?: string;\n /** Skip `applyProcessHardening` (tests). */\n readonly skipHardening?: boolean;\n /** Skip starting the actual listener (tests). */\n readonly skipListen?: boolean;\n}\n\n/**\n * @stable\n */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\n/**\n * Build a fully-wired Graphorin server. The returned handle is\n * inert until `start()` is awaited.\n *\n * @stable\n */\nexport async function createServer(options: CreateServerOptions = {}): Promise<GraphorinServer> {\n const config: ServerConfigSpec =\n options.validatedConfig ?? parseServerConfig(options.config ?? {});\n const now = options.now ?? Date.now;\n const startedAt = now();\n const version = options.version ?? VERSION;\n\n // IP-1: when encryption is configured, resolve the passphrase ref\n // BEFORE constructing the store and thread the encryption config\n // through - `graphorin init --encrypted` produced a config nothing\n // honoured, and a database encrypted via `storage encrypt` could not\n // be opened by the server at all.\n let storeEncryption:\n | { enabled: true; cipher?: never; passphraseResolver: () => Promise<string> }\n | undefined;\n if (options.store === undefined && config.storage.encryption.enabled) {\n if (config.storage.encryption.passphraseRef === undefined) {\n throw new Error(\n '[graphorin/server] storage.encryption.enabled is true but no passphraseRef is configured.',\n );\n }\n const { resolveSecret } = await import('@graphorin/security/secrets');\n const passphrase = await resolveSecret(config.storage.encryption.passphraseRef);\n storeEncryption = {\n enabled: true,\n passphraseResolver: async () => passphrase.use((v) => v),\n } as never;\n }\n const store =\n options.store ??\n (await createSqliteStore({\n path: config.storage.path,\n mode: config.storage.mode,\n ...(storeEncryption !== undefined\n ? {\n encryption: {\n enabled: true,\n ...(config.storage.encryption.cipher !== undefined\n ? { cipher: config.storage.encryption.cipher as never }\n : {}),\n passphraseResolver: storeEncryption.passphraseResolver,\n },\n }\n : {}),\n }));\n\n const metricRegistry = options.metricRegistry ?? createServerMetricRegistry();\n metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version });\n\n // IP-15: agent / workflow run completions move graphorin_agent_runs_total +\n // graphorin_agent_run_duration_seconds. The tracker stays metric-agnostic -\n // it fires a terminal callback the server turns into samples.\n const runs =\n options.runs ??\n new RunStateTracker({\n now,\n onTerminal: (info) => {\n metricRegistry.inc(SERVER_METRIC_NAMES.agentRunsTotal, { status: info.status });\n if (info.durationMs !== undefined) {\n metricRegistry.observe(SERVER_METRIC_NAMES.agentRunDuration, info.durationMs / 1000);\n }\n },\n });\n const agents = options.agents ?? new AgentRegistry();\n const workflows = options.workflows ?? new WorkflowRegistry();\n\n const app = new Hono<{ Variables: ServerVariables }>();\n\n // Global middleware in the documented order. Per-route scope +\n // idempotency layers are mounted by the route factories so the\n // composition stays declarative + auditable.\n app.use(\n '*',\n createRequestStateMiddleware({\n now,\n ...(config.server.trustProxy ? { trustProxy: true } : {}),\n }),\n );\n app.use('*', createCorsMiddleware(config.server.cors));\n app.use('*', createCsrfMiddleware(config.server.csrf));\n app.use('*', createRateLimitMiddleware(config.server.rateLimit, { now }));\n\n let serverInstance: ServerType | undefined;\n let listening: { readonly host: string; readonly port: number } | undefined;\n let started = false;\n let stopped = false;\n // IP-16: stops the periodic terminal-run prune sweep on shutdown.\n let stopRunPruning: (() => void) | undefined;\n let auditDb: AuditDb | undefined;\n let preBind: Awaited<ReturnType<typeof runPreBind>> | undefined;\n let verifier: TokenVerifier | undefined;\n let pepperHandle: import('@graphorin/security').SecretValue | undefined;\n\n let triggersDaemon: TriggersDaemon | undefined;\n if (options.triggers !== undefined) {\n if ('daemon' in options.triggers) {\n triggersDaemon = options.triggers.daemon;\n } else {\n triggersDaemon = createTriggersDaemon({ scheduler: options.triggers.scheduler });\n }\n }\n let consolidatorDaemon: ConsolidatorDaemon | undefined;\n if (options.consolidator !== undefined) {\n consolidatorDaemon = createConsolidatorDaemon({ consolidator: options.consolidator });\n }\n\n let dispatcher: WsDispatcher | undefined;\n let tickets: WsTicketStore | undefined;\n let wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined;\n // IP-21: the dispatcher is built here, before the audit DB opens in start().\n // Hand it a late-bound commentary sink now and install the audit-writing\n // target once the DB exists - otherwise the sanitizer's documented decisions\n // (wrapped/stripped frames with before/after digests) are dropped.\n let commentaryAuditSink: LateBoundCommentarySink | undefined;\n if (config.server.ws.enabled) {\n commentaryAuditSink = createLateBoundCommentarySink();\n dispatcher = createWsDispatcher({\n commentary: {\n policy: config.server.ws.commentarySanitization.policy,\n applyToEvents: config.server.ws.commentarySanitization.applyToEvents,\n sink: commentaryAuditSink,\n },\n replayBuffer: {\n maxEvents: config.server.stream.replayBuffer.maxEvents,\n ttlMs: config.server.stream.replayBuffer.ttlSeconds * 1000,\n },\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now,\n });\n tickets = createWsTicketStore({\n ttlMs: config.server.ws.ticketTtlMs,\n now,\n });\n wsAdapter = createNodeWebSocket({ app });\n // The WS server inside @hono/node-ws is created without any\n // subprotocol-negotiation policy; without one, the `ws` library\n // never echoes back `Sec-WebSocket-Protocol` and clients that\n // advertised a subprotocol close the connection immediately.\n // Mutate the options to install a Graphorin-aware policy that\n // selects `graphorin.protocol.v1` when the client offered it.\n const wssOptions = (\n wsAdapter.wss as unknown as {\n options: {\n handleProtocols?: (\n protocols: Set<string>,\n request: import('node:http').IncomingMessage,\n ) => string | false;\n };\n }\n ).options;\n wssOptions.handleProtocols = (protocols) => {\n const negotiated = negotiateSubprotocol(Array.from(protocols));\n if (negotiated !== null) return negotiated;\n // Browser ticket flow: the `WebSocket` constructor cannot set an\n // `Authorization` header, so the browser client offers two\n // subprotocol tokens - the canonical `graphorin.protocol.v1`\n // name plus a `ticket.<value>` token (see the wire contract in\n // `@graphorin/protocol`'s `subprotocol.ts`:\n // `SUBPROTOCOL_NAME` / `TICKET_SUBPROTOCOL_PREFIX` /\n // `parseTicketSubprotocol`). The server MUST echo back exactly\n // the canonical name (never the `ticket.*` token) so the\n // handshake's `Sec-WebSocket-Protocol` response stays valid; the\n // ticket value is consumed separately by `createWsUpgradeEvents`\n // via `parseTicketSubprotocol` and exchanged through the\n // single-use `WsTicketStore`.\n for (const candidate of protocols) {\n if (candidate === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;\n }\n return false;\n };\n }\n\n const handle: GraphorinServer = {\n version,\n config,\n app,\n agents,\n workflows,\n runs,\n metrics: metricRegistry,\n get wsDispatcher() {\n return dispatcher;\n },\n get wsTickets() {\n return tickets;\n },\n get triggers() {\n return triggersDaemon;\n },\n get consolidator() {\n return consolidatorDaemon;\n },\n get listeningOn() {\n return listening;\n },\n async start(): Promise<{ readonly host: string; readonly port: number }> {\n if (started) throw new LifecycleDoubleStartError();\n started = true;\n try {\n if (options.hooks?.beforeStart !== undefined) {\n await options.hooks.beforeStart({ config });\n }\n if (options.skipHardening !== true && config.hardening.applyOnStart) {\n applyProcessHardening({\n refuseRoot: config.hardening.refuseRoot,\n umask: config.hardening.umask,\n });\n }\n preBind = await runPreBind({\n config,\n store,\n ...(options.probeCipherPeer !== undefined\n ? { probeCipherPeer: options.probeCipherPeer }\n : {}),\n });\n\n if (\n config.audit.enabled &&\n preBind.auditPassphrase !== undefined &&\n preBind.auditPath !== undefined\n ) {\n ensureStoreAuditBinding();\n auditDb = await openAuditDb({\n path: preBind.auditPath,\n passphrase: preBind.auditPassphrase,\n ...(config.audit.cipher !== undefined ? { cipher: config.audit.cipher } : {}),\n });\n // IP-21: now that the audit chain is open, route the WS dispatcher's\n // commentary-sanitizer decisions into it.\n commentaryAuditSink?.bind(bridgeCommentaryToAudit(auditDb));\n }\n\n if (config.auth.kind === 'token') {\n const pepper = preBind.pepper ?? (await fallbackPepper(options.skipHardening === true));\n pepperHandle = pepper;\n verifier = new TokenVerifier({\n tokenStore: store.authTokens,\n pepper,\n acceptPrefix: config.auth.tokenPrefix,\n acceptEnvironments: config.auth.tokenEnvironments,\n ...(config.auth.perIpFailureThreshold !== undefined\n ? { perIpFailureThreshold: config.auth.perIpFailureThreshold }\n : {}),\n ...(config.auth.perIpLockoutMs !== undefined\n ? { perIpLockoutMs: config.auth.perIpLockoutMs }\n : {}),\n now,\n });\n }\n\n mountRoutes(app, config, {\n version,\n startedAt,\n now,\n agents,\n workflows,\n runs,\n store,\n metricRegistry,\n ...(options.sessions !== undefined ? { sessions: options.sessions } : {}),\n ...(options.memory !== undefined ? { memory: options.memory } : {}),\n ...(options.skills !== undefined ? { skills: options.skills } : {}),\n ...(options.mcp !== undefined ? { mcp: options.mcp } : {}),\n ...(options.audit !== undefined ? { audit: options.audit } : {}),\n ...(options.replay !== undefined ? { replay: options.replay } : {}),\n ...(options.healthProbes !== undefined ? { healthProbes: options.healthProbes } : {}),\n ...(verifier !== undefined ? { verifier } : {}),\n ...(auditDb !== undefined ? { auditDb } : {}),\n ...(pepperHandle !== undefined ? { pepper: pepperHandle } : {}),\n ...(dispatcher !== undefined ? { wsDispatcher: dispatcher } : {}),\n ...(tickets !== undefined ? { wsTickets: tickets } : {}),\n ...(wsAdapter !== undefined ? { wsAdapter } : {}),\n ...(triggersDaemon !== undefined ? { triggersDaemon } : {}),\n ...(consolidatorDaemon !== undefined ? { consolidatorDaemon } : {}),\n });\n\n // Start the consolidator first so it is ready to handle fired\n // triggers, bridge its cron / idle triggers onto the scheduler\n // (MCON-4 - without this nothing pipes triggers into the\n // consolidator and background consolidation never runs), then start\n // the scheduler last so it only fires fully-wired triggers.\n if (consolidatorDaemon !== undefined) {\n await consolidatorDaemon.start();\n if (\n triggersDaemon !== undefined &&\n consolidatorDaemon.consolidator.registerWithScheduler !== undefined\n ) {\n await consolidatorDaemon.consolidator.registerWithScheduler(triggersDaemon.scheduler);\n }\n }\n if (triggersDaemon !== undefined) {\n await triggersDaemon.start();\n }\n\n // Sample a couple of gauges immediately so the very first\n // `/v1/metrics` scrape after start carries non-zero data.\n try {\n const wal = readWalSize(store.connection);\n metricRegistry.set(SERVER_METRIC_NAMES.storageWalSize, wal);\n } catch {\n // Best-effort.\n }\n metricRegistry.set(\n SERVER_METRIC_NAMES.serverUptime,\n Math.max(0, Math.floor((now() - startedAt) / 1000)),\n );\n metricRegistry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());\n metricRegistry.set(SERVER_METRIC_NAMES.replayBufferEvents, 0);\n\n // IP-16: terminal run records (each holding an AbortController) would\n // otherwise accumulate forever; sweep them on a periodic timer.\n stopRunPruning = scheduleRunPruning(runs, now);\n\n if (options.skipListen !== true) {\n serverInstance = serve({\n fetch: app.fetch.bind(app),\n hostname: config.server.host,\n port: config.server.port,\n });\n if (wsAdapter !== undefined && serverInstance !== undefined) {\n wsAdapter.injectWebSocket(serverInstance);\n }\n await new Promise<void>((resolve) => {\n const server = serverInstance as unknown as {\n once(event: 'listening', cb: () => void): void;\n };\n if ('once' in server && typeof server.once === 'function') {\n server.once('listening', () => resolve());\n } else {\n // serve(...) returned a Node http.Server already bound.\n setImmediate(() => resolve());\n }\n });\n const address = (\n serverInstance as unknown as { address(): AddressInfo | string | null }\n ).address();\n if (address !== null && typeof address === 'object') {\n listening = { host: address.address, port: address.port };\n } else {\n listening = { host: config.server.host, port: config.server.port };\n }\n } else {\n listening = { host: config.server.host, port: config.server.port };\n }\n\n if (options.hooks?.onReady !== undefined) {\n await options.hooks.onReady({ config, listeningOn: listening });\n }\n return listening;\n } catch (err) {\n started = false;\n await emitError(options.hooks, { error: err, phase: 'beforeStart' });\n throw err;\n }\n },\n async stop({ force }: { readonly force?: boolean } = {}): Promise<void> {\n if (!started) throw new LifecycleNotStartedError();\n if (stopped) return;\n stopped = true;\n // IP-16: halt the prune sweep before draining.\n stopRunPruning?.();\n stopRunPruning = undefined;\n const drainTimeoutMs = force === true ? 0 : config.server.shutdown.drainTimeoutMs;\n try {\n if (options.hooks?.beforeShutdown !== undefined) {\n await options.hooks.beforeShutdown({\n config,\n inflight: runs.inflightCount(),\n drainTimeoutMs,\n });\n }\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n\n if (triggersDaemon !== undefined) {\n try {\n await triggersDaemon.stop();\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n }\n if (consolidatorDaemon !== undefined) {\n try {\n await consolidatorDaemon.stop();\n } catch (err) {\n await emitError(options.hooks, { error: err, phase: 'beforeShutdown' });\n }\n }\n\n // Pending reservations (e.g. awaited WS subscriptions for the\n // streaming endpoints) hold no work in progress; abort them\n // immediately so the drain only waits for live runs.\n runs.abortPending('server-shutdown');\n const drained = await drainInFlight(runs, drainTimeoutMs, now);\n if (!drained && drainTimeoutMs > 0) {\n // Force-abort everything that didn't drain in time.\n runs.abortAll(new ShutdownTimeoutError(drainTimeoutMs, runs.runningCount()));\n }\n\n if (dispatcher !== undefined) {\n try {\n dispatcher.shutdown();\n } catch {\n // Best-effort during stop().\n }\n }\n\n if (serverInstance !== undefined) {\n await new Promise<void>((resolve) => {\n (serverInstance as unknown as { close(cb: () => void): void }).close(() => resolve());\n });\n serverInstance = undefined;\n }\n if (auditDb !== undefined) {\n await auditDb.close();\n auditDb = undefined;\n }\n await store.close();\n listening = undefined;\n verifier = undefined;\n started = false;\n preBind = undefined;\n dispatcher = undefined;\n tickets = undefined;\n wsAdapter = undefined;\n },\n };\n return handle;\n}\n\ninterface MountRoutesContext {\n readonly version: string;\n readonly startedAt: number;\n readonly now: () => number;\n readonly agents: AgentRegistry;\n readonly workflows: WorkflowRegistry;\n readonly runs: RunStateTracker;\n readonly store: GraphorinSqliteStore;\n readonly metricRegistry: MetricRegistry;\n readonly sessions?: SessionApi;\n readonly memory?: MemoryApi;\n readonly skills?: SkillsApi;\n readonly mcp?: McpApi;\n readonly audit?: AuditApi;\n readonly replay?: ReplayApi;\n readonly healthProbes?: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n readonly verifier?: TokenVerifier;\n readonly auditDb?: AuditDb;\n readonly pepper?: import('@graphorin/security').SecretValue;\n readonly wsDispatcher?: WsDispatcher;\n readonly wsTickets?: WsTicketStore;\n readonly wsAdapter?: ReturnType<typeof createNodeWebSocket>;\n readonly triggersDaemon?: TriggersDaemon;\n readonly consolidatorDaemon?: ConsolidatorDaemon;\n}\n\n/** IP-23: is `host` a loopback interface (so an open /metrics is not exposed)? */\nfunction isLoopbackHost(host: string): boolean {\n const h = host.trim().toLowerCase();\n return h === '127.0.0.1' || h === '::1' || h === '[::1]' || h === 'localhost';\n}\n\nfunction mountRoutes(\n app: Hono<{ Variables: ServerVariables }>,\n config: ServerConfigSpec,\n ctx: MountRoutesContext,\n): void {\n const base = config.server.basePath;\n const probes =\n ctx.healthProbes ??\n (() =>\n buildDefaultHealthProbes(\n ctx.store,\n ctx.triggersDaemon,\n ctx.consolidatorDaemon,\n ctx.wsDispatcher,\n config,\n ));\n const health = createExtendedHealthRoutes({\n version: ctx.version,\n startedAt: ctx.startedAt,\n now: ctx.now,\n probes,\n });\n app.route(`${base}/health`, health);\n\n // IP-13: `auth.kind='none'` disables authentication on every route. It is the\n // documented trusted-loopback / single-operator mode, but binding a\n // non-loopback host with auth off exposes full admin access (including the WS\n // stream) to anyone who can reach it - warn loudly rather than silently.\n if (config.auth.kind === 'none' && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: auth.kind='none' disables authentication on every endpoint, ` +\n `but the server binds the non-loopback host '${config.server.host}'. Anyone who can reach ` +\n `it has full admin access - use auth.kind='token' for non-loopback deployments or bind a ` +\n `loopback host.`,\n );\n }\n\n if (config.metrics.enabled) {\n // IP-23: an unauthenticated /metrics endpoint leaks operational intel\n // (trigger ids in labels, consolidator budgets). It is fine on a loopback\n // host, but binding a non-loopback host with auth off silently exposes it.\n if (!config.metrics.requireAuth && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: /metrics is unauthenticated (metrics.requireAuth=false) and ` +\n `the server binds the non-loopback host '${config.server.host}'. The exposition leaks ` +\n `operational detail to anyone who can reach it - set metrics.requireAuth=true or bind a ` +\n `loopback host.`,\n );\n }\n const metricsRoute = createMetricsRoutes({\n registry: ctx.metricRegistry,\n requireAuth: config.metrics.requireAuth,\n refresh: () =>\n refreshLiveMetrics({\n registry: ctx.metricRegistry,\n store: ctx.store,\n runs: ctx.runs,\n startedAt: ctx.startedAt,\n now: ctx.now,\n ...(ctx.triggersDaemon !== undefined ? { triggersDaemon: ctx.triggersDaemon } : {}),\n ...(ctx.consolidatorDaemon !== undefined\n ? { consolidatorDaemon: ctx.consolidatorDaemon }\n : {}),\n ...(ctx.wsDispatcher !== undefined ? { wsDispatcher: ctx.wsDispatcher } : {}),\n }),\n });\n app.route(`${base}${config.metrics.path}`, metricsRoute);\n }\n\n // Authenticated subtree begins here. The health endpoint above is\n // intentionally outside the auth boundary so liveness probes work\n // before the token verifier is wired. The WebSocket upgrade path is\n // also exempt: the upgrade handler in `ws/upgrade.ts` performs its\n // own bearer + ticket validation inline (the HTTP auth middleware\n // would otherwise short-circuit the upgrade with a 401 response\n // before the handler can negotiate the subprotocol).\n const wsUpgradePath = config.server.ws.enabled ? `${base}${config.server.ws.path}` : undefined;\n const metricsPath =\n config.metrics.enabled && !config.metrics.requireAuth\n ? `${base}${config.metrics.path}`\n : undefined;\n function shouldSkipAuth(path: string): boolean {\n if (path === `${base}/health` || path === `${base}/health/`) return true;\n if (wsUpgradePath !== undefined) {\n if (path === wsUpgradePath || path === `${wsUpgradePath}/`) return true;\n }\n if (metricsPath !== undefined) {\n if (path === metricsPath || path === `${metricsPath}/`) return true;\n }\n return false;\n }\n // IP-13: in the no-auth loopback mode (`auth.kind='none'`) there is no\n // verifier, but the authenticated subtree must still be reachable - install\n // an anonymous middleware that stamps a fully-authorized principal so the\n // domain routes serve instead of every one of them returning 401.\n const anonymousAuth = ctx.verifier === undefined && config.auth.kind === 'none';\n if (ctx.verifier !== undefined || anonymousAuth) {\n const authMw =\n ctx.verifier !== undefined\n ? createAuthMiddleware({ verifier: ctx.verifier })\n : createAnonymousAuthMiddleware();\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return authMw(c, next);\n });\n if (ctx.auditDb !== undefined) {\n const auditMw = createAuditMiddleware({ auditDb: ctx.auditDb, now: ctx.now });\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return auditMw(c, next);\n });\n }\n }\n\n // Idempotency middleware - applied to side-effecting endpoints. We\n // mount it once on the authenticated subtree so handlers don't have\n // to duplicate the configuration per-route.\n if (config.server.idempotency.enabled) {\n const idempotencyMw = createIdempotencyMiddleware({\n store: ctx.store.idempotency,\n config: config.server.idempotency,\n now: ctx.now,\n // IP-6: token minting returns a raw secret - never cache it.\n excludeResponseCachePaths: [`${base}/tokens`],\n // IP-15: publish the live cache hit ratio gauge.\n metricRegistry: ctx.metricRegistry,\n });\n app.use(`${base}/*`, async (c, next) => {\n if (shouldSkipAuth(c.req.path)) {\n await next();\n return;\n }\n return idempotencyMw(c, next);\n });\n }\n\n // Mounted AFTER the auth middleware so the scope check has a\n // verified token to inspect; the unauthenticated `/v1/health` GET\n // continues to serve the rollup.\n app.route(`${base}/health/secrets`, createSecretsHealthRoutes());\n\n app.route(\n `${base}/agents`,\n createAgentRoutes({\n agents: ctx.agents,\n runs: ctx.runs,\n // IP-2: the streaming dispatcher reaches the route layer.\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n app.route(`${base}/runs`, createRunRoutes({ agents: ctx.agents, runs: ctx.runs }));\n app.route(\n `${base}/workflows`,\n createWorkflowRoutes({\n workflows: ctx.workflows,\n runs: ctx.runs,\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n if (ctx.sessions !== undefined) {\n app.route(`${base}/sessions`, createSessionRoutes({ sessions: ctx.sessions }));\n }\n if (ctx.memory !== undefined) {\n app.route(`${base}/memory`, createMemoryRoutes({ memory: ctx.memory }));\n }\n if (ctx.skills !== undefined) {\n app.route(`${base}/skills`, createSkillsRoutes({ skills: ctx.skills }));\n }\n if (ctx.mcp !== undefined) {\n app.route(`${base}/mcp`, createMcpRoutes({ mcp: ctx.mcp }));\n }\n if (ctx.audit !== undefined) {\n app.route(`${base}/audit`, createAuditRoutes({ audit: ctx.audit }));\n }\n if (ctx.triggersDaemon !== undefined) {\n app.route(`${base}/triggers`, createTriggersRoutes({ daemon: ctx.triggersDaemon }));\n }\n if (ctx.replay !== undefined) {\n app.route(\n `${base}`,\n createReplayRoutes({\n replay: ctx.replay,\n ...(ctx.auditDb !== undefined ? { auditDb: ctx.auditDb } : {}),\n now: ctx.now,\n }),\n );\n }\n if (config.auth.kind === 'token' && ctx.pepper !== undefined) {\n app.route(\n `${base}/tokens`,\n createTokensRoutes({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n defaultEnv: 'live',\n allowedEnvs: config.auth.tokenEnvironments,\n }),\n );\n }\n if (ctx.wsTickets !== undefined) {\n app.route(`${base}`, createAuthRoutes({ tickets: ctx.wsTickets }));\n }\n // IP-13: mount the WS upgrade when a verifier is wired OR auth is disabled\n // (`auth.kind='none'`). The old condition required a verifier, so\n // `ws.enabled: true` under no-auth was silently ignored - the route never\n // mounted and clients got a bare 404 with no explanation.\n if (\n ctx.wsDispatcher !== undefined &&\n ctx.wsTickets !== undefined &&\n ctx.wsAdapter !== undefined &&\n (ctx.verifier !== undefined || anonymousAuth) &&\n config.server.ws.enabled\n ) {\n const dispatcher = ctx.wsDispatcher;\n const tickets = ctx.wsTickets;\n const verifier = ctx.verifier;\n const runs = ctx.runs;\n app.get(\n `${base}${config.server.ws.path}`,\n ctx.wsAdapter.upgradeWebSocket((c) =>\n createWsUpgradeEvents(c, {\n dispatcher,\n tickets,\n ...(verifier !== undefined ? { verifier } : {}),\n anonymous: anonymousAuth,\n runs,\n now: ctx.now,\n }),\n ),\n );\n }\n if (ctx.wsDispatcher !== undefined && config.server.sse.enabled) {\n app.route(\n `${base}${config.server.sse.path}`,\n createSseRoutes({\n dispatcher: ctx.wsDispatcher,\n keepAliveMs: config.server.sse.keepAliveMs,\n // IP-9: bound the per-connection delivery queue.\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now: ctx.now,\n }),\n );\n }\n}\n\nasync function drainInFlight(\n runs: RunStateTracker,\n drainTimeoutMs: number,\n now: () => number,\n): Promise<boolean> {\n if (runs.runningCount() === 0) return true;\n if (drainTimeoutMs <= 0) return runs.runningCount() === 0;\n const deadline = now() + drainTimeoutMs;\n while (now() < deadline) {\n if (runs.runningCount() === 0) return true;\n await new Promise((resolve) => setTimeout(resolve, Math.min(50, deadline - now())));\n }\n return runs.runningCount() === 0;\n}\n\nasync function emitError(hooks: LifecycleHooks | undefined, ctx: OnErrorContext): Promise<void> {\n if (hooks?.onError === undefined) return;\n try {\n await hooks.onError(ctx);\n } catch {\n // onError must never throw further; swallow.\n }\n}\n\ninterface RefreshLiveMetricsOptions {\n readonly registry: MetricRegistry;\n readonly store: GraphorinSqliteStore;\n readonly runs: RunStateTracker;\n readonly startedAt: number;\n readonly now: () => number;\n readonly triggersDaemon?: TriggersDaemon;\n readonly consolidatorDaemon?: ConsolidatorDaemon;\n readonly wsDispatcher?: WsDispatcher;\n}\n\nasync function refreshLiveMetrics(options: RefreshLiveMetricsOptions): Promise<void> {\n const { registry, store, runs, startedAt, now } = options;\n\n try {\n const wal = readWalSize(store.connection);\n registry.set(SERVER_METRIC_NAMES.storageWalSize, wal);\n } catch {\n // Best-effort.\n }\n\n registry.set(\n SERVER_METRIC_NAMES.serverUptime,\n Math.max(0, Math.floor((now() - startedAt) / 1000)),\n );\n registry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());\n\n if (options.wsDispatcher !== undefined) {\n const sizes = options.wsDispatcher.size();\n registry.set(SERVER_METRIC_NAMES.replayBufferEvents, sizes.subscriptions);\n }\n\n if (options.triggersDaemon !== undefined) {\n const metrics = options.triggersDaemon.metrics();\n for (const [triggerId, counts] of metrics.fires) {\n const sanitized = sanitizeMetricLabelValue(triggerId);\n const successCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {\n trigger_id: sanitized,\n status: 'success',\n });\n const errorCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {\n trigger_id: sanitized,\n status: 'error',\n });\n const successDelta = counts.success - successCurrent;\n const errorDelta = counts.error - errorCurrent;\n if (successDelta > 0) {\n registry.inc(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n { trigger_id: sanitized, status: 'success' },\n successDelta,\n );\n }\n if (errorDelta > 0) {\n registry.inc(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n { trigger_id: sanitized, status: 'error' },\n errorDelta,\n );\n }\n }\n }\n\n if (options.consolidatorDaemon !== undefined) {\n try {\n const status = await options.consolidatorDaemon.status();\n registry.set(SERVER_METRIC_NAMES.consolidatorQueueDepth, status.queueDepth, {\n phase: 'aggregate',\n });\n registry.set(SERVER_METRIC_NAMES.consolidatorDlqSize, status.dlqSize);\n registry.set(SERVER_METRIC_NAMES.consolidatorBudgetRemainingUsd, status.budget.costRemaining);\n registry.set(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingTokens,\n status.budget.tokensRemaining,\n );\n } catch {\n // Best-effort.\n }\n }\n}\n\n/**\n * Convert an arbitrary user-supplied identifier (trigger id) into a\n * Prometheus-safe label value. Replaces every character outside the\n * `[A-Za-z0-9_:]` range with `_`. This guarantees the cardinality\n * never explodes on UTF-8 sequences while keeping the value\n * recognizable.\n */\nfunction sanitizeMetricLabelValue(value: string): string {\n return value.replace(/[^A-Za-z0-9_:.-]/g, '_').slice(0, 200);\n}\n\nfunction readCounter(\n registry: MetricRegistry,\n name: string,\n labels: Record<string, string>,\n): number {\n const snap = registry.snapshot().counters[name] ?? [];\n for (const entry of snap) {\n if (matchesLabels(entry.labels, labels)) return entry.value;\n }\n return 0;\n}\n\nfunction matchesLabels(\n a: Record<string, string | number | boolean>,\n b: Record<string, string>,\n): boolean {\n for (const k of Object.keys(b)) {\n if (String(a[k]) !== b[k]) return false;\n }\n for (const k of Object.keys(a)) {\n if (!(k in b)) return false;\n }\n return true;\n}\n\nfunction buildDefaultHealthProbes(\n store: GraphorinSqliteStore,\n triggersDaemon: TriggersDaemon | undefined,\n consolidatorDaemon: ConsolidatorDaemon | undefined,\n dispatcher: WsDispatcher | undefined,\n config: ServerConfigSpec,\n): HealthCheckOptions {\n const out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n } = {\n store,\n walWarnThresholdBytes: config.health.walWarnThresholdBytes,\n encryptionEnabled: config.storage.encryption.enabled,\n // IP-1: when this process built the encrypted store itself, the\n // keyed open at boot proved the cipher peer - report the fact.\n ...(config.storage.encryption.enabled ? { cipherPeerInstalled: true } : {}),\n };\n if (triggersDaemon !== undefined) out.triggers = triggersDaemon;\n if (consolidatorDaemon !== undefined) out.consolidator = consolidatorDaemon;\n if (dispatcher !== undefined) {\n const sizes = dispatcher.size();\n // The dispatcher only exposes (subscribers, subscriptions); the\n // per-subject replay-buffer occupancy is owned by each\n // subscription, not the dispatcher itself. Surface the active\n // subscription count as a usable proxy + the raw subscriber count\n // so operators can correlate.\n out.replayBuffer = {\n eventsBuffered: sizes.subscriptions,\n subscribers: sizes.subscribers,\n subscriptions: sizes.subscriptions,\n };\n }\n return Object.freeze(out as HealthCheckOptions);\n}\n\nasync function fallbackPepper(skipHardening: boolean) {\n // Test-only path: when `skipHardening` is set we mint an ephemeral\n // pepper so the verifier is constructible without a real keyring.\n if (!skipHardening) {\n throw new Error(\n '[graphorin/server] missing resolved pepper after pre-bind; auth.pepperRef is required when auth.kind = token.',\n );\n }\n return generatePepper();\n}\n\nvoid process;\nvoid ConfigInvalidError;\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -81,8 +81,7 @@ import "./triggers/index.js";
|
|
|
81
81
|
*
|
|
82
82
|
* @packageDocumentation
|
|
83
83
|
*/
|
|
84
|
-
|
|
85
|
-
declare const VERSION = "0.6.0";
|
|
84
|
+
declare const VERSION: string;
|
|
86
85
|
//#endregion
|
|
87
86
|
export { AgentNotFoundError, type AgentRegistration, AgentRegistry, type AgentRoutesDeps, type AgentSummary, type AuditApi, type AuditErrorSink, type AuditMiddlewareOptions, type AuditRoutesDeps, type AuthMiddlewareOptions, type AuthRoutesDeps, type BareEventFrame, type BaseHealthCheck, type BeforeShutdownContext, type BeforeStartContext, ConfigInvalidError, type ConsolidatorCheck, type ConsolidatorDaemon, type ConsolidatorLike, type ConsolidatorStatusLike, type CreateConsolidatorDaemonOptions, type CreateServerOptions, type CreateTriggersDaemonOptions, DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS, type DeliveryCommentaryConfig, type DeliveryCommentaryDecision, type DeliveryCommentaryPattern, type DeliveryCommentaryPolicy, type DeliveryCommentaryPolicyConfig, type DeliveryCommentaryReason, type DeliveryCommentarySanitizer, type DeliveryCommentarySink, type DeliveryCommentaryTransport, type EmbedderCheck, type EncryptionCheck, type GraphorinServer, GraphorinServerError, GraphorinServerErrorCode, HTTP_REQUEST_AUDIT_ACTION, type HealthCheck, type HealthCheckOptions, type HealthChecks, type HealthRollup, type HealthRouteOptions, type HealthRoutesDeps, type HealthStatus, type HealthSummary, IdempotencyConflictError, IdempotencyKeyRequiredError, type IdempotencyMiddlewareOptions, type IdempotencyRequireKeyMode, type LabelSet, LifecycleDoubleStartError, type LifecycleHooks, LifecycleNotStartedError, type McpApi, type McpRoutesDeps, type MemoryApi, type MemoryRoutesDeps, type MetricKind, MetricRegistry, type MetricsRoutesOptions, MigrationFailedError, type OnErrorContext, type OnReadyContext, type ParsedSubject, type PreBindResult, PrebindEncryptionPeerMissingError, PrebindEncryptionRequiredError, PrebindPepperMissingError, PrebindSecretUnresolvableError, type ReplayApi, type ReplayBuffer, type ReplayBufferCheck, type ReplayBufferOptions, type ReplayBufferProbe, type ReplayBufferSlice, type ReplayMode, type ReplayResponse, type ReplayRoutesDeps, type RequestStateMiddlewareOptions, type RequestToken, RouteHandlerMissingError, type RunDescriptor, type RunHandle, type RunPreBindOptions, type RunStateSnapshot, RunStateTracker, type RunStatus, SERVER_METRIC_NAMES, type ScopeRequirement, type SecretRefString, type SecretsCheck, type SecretsSource, type ServerAgentLike, type ServerConfigInput, ServerConfigSchema, type ServerConfigSpec, type ServerRequestState, type ServerVariables, type ServerWorkflowLike, type SessionApi, type SessionRoutesDeps, ShutdownTimeoutError, type SkillsApi, type SkillsRoutesDeps, type SseRoutesDeps, type StorageCheck, type SubscribeResult, type TokensRoutesDeps, type TriggersCheck, type TriggersDaemon, type TriggersDaemonInput, type TriggersDaemonMetrics, type TriggersDaemonStatus, type TriggersRoutesDeps, VERSION, WorkflowNotFoundError, type WorkflowRegistration, WorkflowRegistry, type WorkflowRoutesDeps, type WorkflowSummary, type WsDispatcher, type WsDispatcherOptions, type WsDispatcherWarning, type WsSubscriberHandle, type WsSubscriptionSnapshot, type WsTicket, type WsTicketConsumeResult, type WsTicketStore, type WsTicketStoreOptions, type WsUpgradeOptions, collectHealth, createAgentRoutes, createAuditMiddleware, createAuditRoutes, createAuthMiddleware, createAuthRoutes, createConsolidatorDaemon, createCorsMiddleware, createCsrfMiddleware, createDeliveryCommentarySanitizer, createExtendedHealthRoutes, createHealthRoutes, createIdempotencyMiddleware, createMcpRoutes, createMemoryRoutes, createMetricsRoutes, createRateLimitMiddleware, createReplayBuffer, createReplayRoutes, createRequestStateMiddleware, createRunRoutes, createScopeMiddleware, createSecretsHealthRoutes, createServer, createServerMetricRegistry, createSessionRoutes, createSkillsRoutes, createSseRoutes, createTokensRoutes, createTriggersDaemon, createTriggersRoutes, createWorkflowRoutes, createWsDispatcher, createWsTicketStore, createWsUpgradeEvents, defaultCatchupPolicy, defineConfig, ensureStoreAuditBinding, parseServerConfig, requiredScopeFor, rollup, runPreBind, tryParseSubject };
|
|
88
87
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgCa"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS } from "./commentary/built-in-patterns.js";
|
|
2
3
|
import { createDeliveryCommentarySanitizer } from "./commentary/sanitizer.js";
|
|
3
4
|
import "./commentary/index.js";
|
|
@@ -78,8 +79,8 @@ import "./triggers/index.js";
|
|
|
78
79
|
*
|
|
79
80
|
* @packageDocumentation
|
|
80
81
|
*/
|
|
81
|
-
/** Canonical version constant
|
|
82
|
-
const VERSION =
|
|
82
|
+
/** Canonical version constant, derived from `package.json` at build time. */
|
|
83
|
+
const VERSION = version;
|
|
83
84
|
|
|
84
85
|
//#endregion
|
|
85
86
|
export { AgentNotFoundError, AgentRegistry, ConfigInvalidError, DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS, GraphorinServerError, HTTP_REQUEST_AUDIT_ACTION, IdempotencyConflictError, IdempotencyKeyRequiredError, LifecycleDoubleStartError, LifecycleNotStartedError, MetricRegistry, MigrationFailedError, PrebindEncryptionPeerMissingError, PrebindEncryptionRequiredError, PrebindPepperMissingError, PrebindSecretUnresolvableError, RouteHandlerMissingError, RunStateTracker, SERVER_METRIC_NAMES, ServerConfigSchema, ShutdownTimeoutError, VERSION, WorkflowNotFoundError, WorkflowRegistry, collectHealth, createAgentRoutes, createAuditMiddleware, createAuditRoutes, createAuthMiddleware, createAuthRoutes, createConsolidatorDaemon, createCorsMiddleware, createCsrfMiddleware, createDeliveryCommentarySanitizer, createExtendedHealthRoutes, createHealthRoutes, createIdempotencyMiddleware, createMcpRoutes, createMemoryRoutes, createMetricsRoutes, createRateLimitMiddleware, createReplayBuffer, createReplayRoutes, createRequestStateMiddleware, createRunRoutes, createScopeMiddleware, createSecretsHealthRoutes, createServer, createServerMetricRegistry, createSessionRoutes, createSkillsRoutes, createSseRoutes, createTokensRoutes, createTriggersDaemon, createTriggersRoutes, createWorkflowRoutes, createWsDispatcher, createWsTicketStore, createWsUpgradeEvents, defaultCatchupPolicy, defineConfig, ensureStoreAuditBinding, parseServerConfig, requiredScopeFor, rollup, runPreBind, tryParseSubject };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/server` - standalone server runtime for the Graphorin\n * framework.\n *\n * Phase 14a entry point. Ships:\n *\n * - {@link createServer} - the single programmatic entry that wires\n * the Hono app, the auth + scope + idempotency + audit middleware\n * stack, REST endpoints (agents / workflows / sessions / memory /\n * skills / mcp / tokens / audit / health), the lifecycle hooks\n * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the\n * pre-bind secrets-resolution + storage-migration runner, and the\n * graceful SIGTERM drain.\n * - {@link defineConfig} + {@link parseServerConfig} - typed\n * configuration loader with a Zod-validated schema; missing fields\n * fall back to documented production-ready defaults.\n * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write\n * registries the route handlers consult to look up user-defined\n * agents / workflows.\n * - {@link RunStateTracker} - in-memory bookkeeping for in-flight\n * runs (run id, status, AbortController). Phase 14b/c promote the\n * tracker to a SQLite-backed durable variant.\n * - The full middleware factory suite - re-exported from\n * `@graphorin/server/middleware` so operators can compose a custom\n * Hono app on top of the same primitives.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant
|
|
1
|
+
{"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/server` - standalone server runtime for the Graphorin\n * framework.\n *\n * Phase 14a entry point. Ships:\n *\n * - {@link createServer} - the single programmatic entry that wires\n * the Hono app, the auth + scope + idempotency + audit middleware\n * stack, REST endpoints (agents / workflows / sessions / memory /\n * skills / mcp / tokens / audit / health), the lifecycle hooks\n * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the\n * pre-bind secrets-resolution + storage-migration runner, and the\n * graceful SIGTERM drain.\n * - {@link defineConfig} + {@link parseServerConfig} - typed\n * configuration loader with a Zod-validated schema; missing fields\n * fall back to documented production-ready defaults.\n * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write\n * registries the route handlers consult to look up user-defined\n * agents / workflows.\n * - {@link RunStateTracker} - in-memory bookkeeping for in-flight\n * runs (run id, status, AbortController). Phase 14b/c promote the\n * tracker to a SQLite-backed durable variant.\n * - The full middleware factory suite - re-exported from\n * `@graphorin/server/middleware` so operators can compose a custom\n * Hono app on top of the same primitives.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport {\n type CreateServerOptions,\n createServer,\n ensureStoreAuditBinding,\n type GraphorinServer,\n type TriggersDaemonInput,\n} from './app.js';\nexport {\n createDeliveryCommentarySanitizer,\n DEFAULT_APPLY_TO_EVENTS,\n DEFAULT_DELIVERY_COMMENTARY_PATTERNS,\n type DeliveryCommentaryConfig,\n type DeliveryCommentaryDecision,\n type DeliveryCommentaryPattern,\n type DeliveryCommentaryPolicy,\n type DeliveryCommentaryReason,\n type DeliveryCommentarySanitizer,\n type DeliveryCommentarySink,\n type DeliveryCommentaryTransport,\n} from './commentary/index.js';\nexport {\n type DeliveryCommentaryPolicyConfig,\n defineConfig,\n type IdempotencyRequireKeyMode,\n parseServerConfig,\n type SecretRefString,\n type SecretsSource,\n type ServerConfigInput,\n ServerConfigSchema,\n type ServerConfigSpec,\n} from './config.js';\nexport {\n type ConsolidatorDaemon,\n type ConsolidatorLike,\n type ConsolidatorStatusLike,\n type CreateConsolidatorDaemonOptions,\n createConsolidatorDaemon,\n} from './consolidator/index.js';\nexport * from './errors/index.js';\nexport {\n type BaseHealthCheck,\n type ConsolidatorCheck,\n collectHealth,\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type EmbedderCheck,\n type EncryptionCheck,\n type HealthCheck,\n type HealthCheckOptions,\n type HealthChecks,\n type HealthRollup,\n type HealthRouteOptions,\n type HealthStatus,\n type HealthSummary,\n type MetricsRoutesOptions,\n type ReplayBufferCheck,\n type ReplayBufferProbe,\n rollup,\n type SecretsCheck,\n type StorageCheck,\n type TriggersCheck,\n} from './health/index.js';\nexport type { RequestToken, ServerRequestState, ServerVariables } from './internal/context.js';\nexport {\n type BeforeShutdownContext,\n type BeforeStartContext,\n type LifecycleHooks,\n type OnErrorContext,\n type OnReadyContext,\n type PreBindResult,\n type RunPreBindOptions,\n runPreBind,\n} from './lifecycle/index.js';\nexport {\n createServerMetricRegistry,\n type LabelSet,\n type MetricKind,\n MetricRegistry,\n SERVER_METRIC_NAMES,\n} from './metrics/index.js';\nexport {\n type AuditErrorSink,\n type AuditMiddlewareOptions,\n type AuthMiddlewareOptions,\n createAuditMiddleware,\n createAuthMiddleware,\n createCorsMiddleware,\n createCsrfMiddleware,\n createIdempotencyMiddleware,\n createRateLimitMiddleware,\n createRequestStateMiddleware,\n createScopeMiddleware,\n HTTP_REQUEST_AUDIT_ACTION,\n type IdempotencyMiddlewareOptions,\n type RequestStateMiddlewareOptions,\n type ScopeRequirement,\n} from './middleware/index.js';\nexport {\n type AgentRegistration,\n AgentRegistry,\n type AgentSummary,\n type ServerAgentLike,\n type ServerWorkflowLike,\n type WorkflowRegistration,\n WorkflowRegistry,\n type WorkflowSummary,\n} from './registry/index.js';\nexport {\n createReplayRoutes,\n type ReplayApi,\n type ReplayMode,\n type ReplayResponse,\n type ReplayRoutesDeps,\n} from './replay/index.js';\nexport {\n type AgentRoutesDeps,\n type AuditApi,\n type AuditRoutesDeps,\n type AuthRoutesDeps,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createHealthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type HealthRoutesDeps,\n type McpApi,\n type McpRoutesDeps,\n type MemoryApi,\n type MemoryRoutesDeps,\n type SessionApi,\n type SessionRoutesDeps,\n type SkillsApi,\n type SkillsRoutesDeps,\n type TokensRoutesDeps,\n type WorkflowRoutesDeps,\n} from './routes/index.js';\nexport {\n type RunDescriptor,\n type RunHandle,\n type RunStateSnapshot,\n RunStateTracker,\n type RunStatus,\n} from './runtime/run-state.js';\nexport { createSseRoutes, type SseRoutesDeps } from './sse/index.js';\nexport {\n type CreateTriggersDaemonOptions,\n createTriggersDaemon,\n createTriggersRoutes,\n defaultCatchupPolicy,\n type TriggersDaemon,\n type TriggersDaemonMetrics,\n type TriggersDaemonStatus,\n type TriggersRoutesDeps,\n} from './triggers/index.js';\nexport {\n type BareEventFrame,\n createReplayBuffer,\n createWsDispatcher,\n createWsTicketStore,\n createWsUpgradeEvents,\n type ParsedSubject,\n type ReplayBuffer,\n type ReplayBufferOptions,\n type ReplayBufferSlice,\n requiredScopeFor,\n type SubscribeResult,\n tryParseSubject,\n type WsDispatcher,\n type WsDispatcherOptions,\n type WsDispatcherWarning,\n type WsSubscriberHandle,\n type WsSubscriptionSnapshot,\n type WsTicket,\n type WsTicketConsumeResult,\n type WsTicketStore,\n type WsTicketStoreOptions,\n type WsUpgradeOptions,\n} from './ws/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAaA,UAAkBC"}
|
package/dist/package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/server\",\n \"version\": \"0.6.1\",\n \"description\": \"Standalone server runtime for the Graphorin framework: createServer({...}) factory, Hono HTTP app, REST endpoints (agents / workflows / sessions / memory / skills / mcp / tokens / audit / health / health/secrets / metrics / triggers / runs/:runId/replay / sessions/:id/replay / audit/verify), per-request authentication + scope middleware (HMAC-SHA256 + pepper, server pepper required), brute-force lockout, IETF draft-07 Idempotency-Key middleware with SQLite backing + LRU read cache, deny-by-default CORS, double-submit CSRF for browser flows, sliding-window rate limit, audit middleware that streams every authenticated request through the tamper-evident hash chain, programmatic AgentRegistry / WorkflowRegistry, lifecycle hooks (beforeStart / onReady / beforeShutdown), pre-bind secrets resolution + storage migration runner that fails fast on missing pepper / unresolvable SecretRef / missing encryption peer, graceful SIGTERM drain with state preservation, durable triggers daemon (cron / interval / idle / event survive process restart per the catch-up policy contract), consolidator daemon lifecycle integration with hard stop timeout, per-subsystem health rollup (storage / embedder / secrets / encryption / consolidator / triggers / replay-buffer), Prometheus text exposition `/v1/metrics` with the canonical graphorin_* metric inventory, scope-enforced replay endpoints (sanitized by default, raw requires `traces:read:raw` admin scope, every invocation appended to the audit chain), and `POST /v1/audit/verify` chain integrity check. Created and maintained by Oleksiy Stepurenko.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/server\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/server\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"server\",\n \"http\",\n \"rest\",\n \"hono\",\n \"idempotency\",\n \"auth\",\n \"scopes\",\n \"rate-limit\",\n \"cors\",\n \"csrf\",\n \"audit\",\n \"lifecycle\",\n \"graceful-shutdown\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./config\": {\n \"types\": \"./dist/config.d.ts\",\n \"import\": \"./dist/config.js\"\n },\n \"./middleware\": {\n \"types\": \"./dist/middleware/index.d.ts\",\n \"import\": \"./dist/middleware/index.js\"\n },\n \"./errors\": {\n \"types\": \"./dist/errors/index.d.ts\",\n \"import\": \"./dist/errors/index.js\"\n },\n \"./registry\": {\n \"types\": \"./dist/registry/index.d.ts\",\n \"import\": \"./dist/registry/index.js\"\n },\n \"./ws\": {\n \"types\": \"./dist/ws/index.d.ts\",\n \"import\": \"./dist/ws/index.js\"\n },\n \"./sse\": {\n \"types\": \"./dist/sse/index.d.ts\",\n \"import\": \"./dist/sse/index.js\"\n },\n \"./commentary\": {\n \"types\": \"./dist/commentary/index.d.ts\",\n \"import\": \"./dist/commentary/index.js\"\n },\n \"./triggers\": {\n \"types\": \"./dist/triggers/index.d.ts\",\n \"import\": \"./dist/triggers/index.js\"\n },\n \"./consolidator\": {\n \"types\": \"./dist/consolidator/index.d.ts\",\n \"import\": \"./dist/consolidator/index.js\"\n },\n \"./health\": {\n \"types\": \"./dist/health/index.d.ts\",\n \"import\": \"./dist/health/index.js\"\n },\n \"./metrics\": {\n \"types\": \"./dist/metrics/index.d.ts\",\n \"import\": \"./dist/metrics/index.js\"\n },\n \"./replay\": {\n \"types\": \"./dist/replay/index.d.ts\",\n \"import\": \"./dist/replay/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\",\n \"@graphorin/observability\": \"workspace:*\",\n \"@graphorin/protocol\": \"workspace:*\",\n \"@graphorin/security\": \"workspace:*\",\n \"@graphorin/store-sqlite\": \"workspace:*\",\n \"@graphorin/triggers\": \"workspace:*\",\n \"@hono/node-server\": \"^1.19.0\",\n \"@hono/node-ws\": \"^1.3.0\",\n \"hono\": \"^4.12.25\",\n \"zod\": \"^3.25.0\"\n },\n \"peerDependencies\": {\n \"@graphorin/agent\": \"workspace:>=0.5.0 <1.0.0\",\n \"@graphorin/memory\": \"workspace:>=0.5.0 <1.0.0\",\n \"@graphorin/sessions\": \"workspace:>=0.5.0 <1.0.0\",\n \"@graphorin/workflow\": \"workspace:>=0.5.0 <1.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"@graphorin/agent\": {\n \"optional\": true\n },\n \"@graphorin/memory\": {\n \"optional\": true\n },\n \"@graphorin/sessions\": {\n \"optional\": true\n },\n \"@graphorin/workflow\": {\n \"optional\": true\n }\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {\n \"@graphorin/agent\": \"workspace:*\",\n \"@graphorin/memory\": \"workspace:*\",\n \"@graphorin/sessions\": \"workspace:*\",\n \"@graphorin/workflow\": \"workspace:*\",\n \"@types/better-sqlite3\": \"^7.6.13\",\n \"@types/ws\": \"^8.18.1\",\n \"better-sqlite3\": \"^12.9.0\",\n \"ws\": \"^8.20.1\"\n }\n}\n"],"mappings":";cAEa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","names":[],"sources":["../../src/ws/dispatcher.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA0EA;;;;;AA0BiB,UAhEA,mBAAA,CAgEsB;EAe3B,SAAA,UAAe,CAAA,EA9EH,wBAiFK;EAkBZ,SAAA,YAAY,CAAA,EAlGH,mBAkGG;EACP;;;;EAyBS,SAAA,uBAAA,CAAA,EAAA,MAAA;EAInB,SAAA,GAAA,CAAA,EAAA,GAAA,GAAA,MAAA;EAI6C;;;;AAazD;EA6BgB,SAAA,MAAA,CAAA,EAAA,CAAA,KAAkB,EAlKN,mBAkKgB,EAAA,GAAA,
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","names":[],"sources":["../../src/ws/dispatcher.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA0EA;;;;;AA0BiB,UAhEA,mBAAA,CAgEsB;EAe3B,SAAA,UAAe,CAAA,EA9EH,wBAiFK;EAkBZ,SAAA,YAAY,CAAA,EAlGH,mBAkGG;EACP;;;;EAyBS,SAAA,uBAAA,CAAA,EAAA,MAAA;EAInB,SAAA,GAAA,CAAA,EAAA,GAAA,GAAA,MAAA;EAI6C;;;;AAazD;EA6BgB,SAAA,MAAA,CAAA,EAAA,CAAA,KAAkB,EAlKN,mBAkKgB,EAAA,GAAA,IAAA;;;;;;;KA1JhC,mBAAA;;;mBAIW;;;;;;;;;;;;;;;;UAYN,kBAAA;;;0BAGS,cAAc;;cAE1B;;;;;;;;;;;;;;;;;;;;UAqBG,sBAAA;;;wBAGO;;;;;;;;;;;KAYZ,eAAA;;yBAGiB;;;;;;;;;;;;UAkBZ,YAAA;sBACK;yBACG;;;;;6BAKI;;;;;;;;;;;;;;MAYvB;;;;;;;+BAOyB;;gDAInB;;2CAI+B,cAAc;gDACT;;;;;;;;;;;;;;UAY/B,cAAA;;;;;;;;;;iBA6BD,kBAAA,WAA4B,sBAA2B"}
|
package/dist/ws/upgrade.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.d.ts","names":[],"sources":["../../src/ws/upgrade.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","names":[],"sources":["../../src/ws/upgrade.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AA2DA;;;AAQsB,UARL,gBAAA,CAQK;EAQJ,SAAA,UAAA,EAfK,YAeL;EAAe,SAAA,OAAA,EAdb,aAca;EA2BX;;;;;EAGnB,SAAA,QAAA,CAAA,EAtCmB,aAsCnB;EAAO;;;;;;;kBA9BQ;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BI,qBAAA,IACjB;aAAqB;aACf,mBACR,QAAQ"}
|
package/dist/ws/upgrade.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "../package.js";
|
|
1
2
|
import { ClientMessageSchema, RPC_ERROR_CODES, SUBPROTOCOL_NAME, closeCodeFor, isCancelledNotification, isInitializeRequest, isPingRequest, isRunCancelRequest, isSubscribeRequest, isUnsubscribeRequest, negotiateSubprotocol, parseTicketSubprotocol } from "@graphorin/protocol";
|
|
2
3
|
import { parseScope, scopeMatches } from "@graphorin/security/auth";
|
|
3
4
|
|
|
@@ -100,7 +101,7 @@ async function createWsUpgradeEvents(c, options) {
|
|
|
100
101
|
sendRpcSuccess(ws, message.id, {
|
|
101
102
|
serverInfo: {
|
|
102
103
|
name: "graphorin-server",
|
|
103
|
-
version
|
|
104
|
+
version
|
|
104
105
|
},
|
|
105
106
|
capabilities: {
|
|
106
107
|
subscriptions: true,
|
package/dist/ws/upgrade.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.js","names":["unregister: (() => void) | undefined","payload: unknown","frame: ServerMessage","INVOKE_SCOPE: ParsedScope","ANONYMOUS_WS_SCOPES: ReadonlyArray<ParsedScope>"],"sources":["../../src/ws/upgrade.ts"],"sourcesContent":["/**\n * Hono WebSocket upgrade handler. Wires the `@hono/node-ws` adapter\n * to the dispatcher: validates the subprotocol, runs auth (bearer\n * via `requireAuth` middleware OR ticket via the in-memory store),\n * and bridges the per-connection `WSContext` callbacks to the\n * dispatcher's RPC handler.\n *\n * ## Subprotocol + browser ticket flow\n *\n * The negotiated subprotocol is always {@link SUBPROTOCOL_NAME}\n * (`graphorin.protocol.v1`) - the canonical wire contract lives in\n * `@graphorin/protocol`'s `subprotocol.ts`. Two auth paths exist:\n *\n * - **Bearer (non-browser):** the client sends `Authorization:\n * Bearer <token>` and a single `Sec-WebSocket-Protocol:\n * graphorin.protocol.v1` token.\n * - **Ticket (browser):** the `WebSocket` constructor cannot set\n * headers, so the browser client offers two subprotocol tokens -\n * `graphorin.protocol.v1` and `ticket.<value>`. The server echoes\n * back only the canonical name (in `app.ts`'s `handleProtocols`),\n * and {@link resolveUpgradeAuth} extracts the ticket via\n * `parseTicketSubprotocol` and redeems it against the single-use\n * {@link WsTicketStore}. Tickets are short-lived and one-shot to\n * bound replay.\n *\n * @packageDocumentation\n */\n\nimport {\n ClientMessageSchema,\n closeCodeFor,\n isCancelledNotification,\n isInitializeRequest,\n isPingRequest,\n isRunCancelRequest,\n isSubscribeRequest,\n isUnsubscribeRequest,\n negotiateSubprotocol,\n parseTicketSubprotocol,\n RPC_ERROR_CODES,\n type ServerMessage,\n SUBPROTOCOL_NAME,\n} from '@graphorin/protocol';\nimport type { ParsedScope, TokenVerifier } from '@graphorin/security/auth';\nimport { parseScope, scopeMatches } from '@graphorin/security/auth';\nimport type { Context } from 'hono';\nimport type { WSContext, WSEvents } from 'hono/ws';\n\nimport type { ServerVariables } from '../internal/context.js';\nimport type { RunStateTracker } from '../runtime/run-state.js';\nimport type { WsDispatcher } from './dispatcher.js';\nimport type { WsTicket, WsTicketStore } from './ticket.js';\n\n/**\n * Public configuration accepted by {@link createWsUpgradeEvents}.\n *\n * @stable\n */\nexport interface WsUpgradeOptions {\n readonly dispatcher: WsDispatcher;\n readonly tickets: WsTicketStore;\n /**\n * Token verifier for bearer / ticket upgrades. Optional only in the\n * IP-13 no-auth loopback mode (`anonymous: true`), where there is no\n * verifier to construct and every upgrade is accepted.\n */\n readonly verifier?: TokenVerifier;\n /**\n * IP-13: authentication is disabled server-wide (`auth.kind='none'`).\n * Accept the upgrade unconditionally with a full (`admin:*`) scope grant\n * instead of silently refusing to mount the WS route. Trusted-loopback\n * / single-operator mode only.\n */\n readonly anonymous?: boolean;\n readonly runs?: RunStateTracker;\n readonly now?: () => number;\n /**\n * Subprotocol the server advertises. Defaults to\n * {@link SUBPROTOCOL_NAME}; tests can override to exercise the\n * mismatch path.\n */\n readonly serverSubprotocol?: string;\n readonly newSubscriptionId?: () => string;\n readonly newSubscriberId?: () => string;\n}\n\n/**\n * Build the `WSEvents` callback bag the Hono helper consumes. The\n * function takes the request `Context` so the upgrade can read the\n * `Authorization` header / `Sec-WebSocket-Protocol` ticket directly.\n *\n * Production wiring on `@hono/node-ws`:\n *\n * ```ts\n * const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app });\n * app.get('/v1/ws', upgradeWebSocket((c) => createWsUpgradeEvents(c, deps)));\n * injectWebSocket(serve(...));\n * ```\n *\n * @stable\n */\nexport async function createWsUpgradeEvents(\n c: Context<{ Variables: ServerVariables }>,\n options: WsUpgradeOptions,\n): Promise<WSEvents> {\n const negotiated = negotiateSubprotocol(c.req.header('sec-websocket-protocol') ?? '');\n if (negotiated === null) {\n return rejectImmediately(closeCodeFor('protocol.violation'), 'protocol.violation');\n }\n const auth = await resolveUpgradeAuth(c, options);\n if (auth.kind === 'denied') {\n return rejectImmediately(closeCodeFor(auth.reason), auth.reason);\n }\n\n const subscriberId = options.newSubscriberId?.() ?? defaultSubscriberId();\n const newSubscriptionId =\n options.newSubscriptionId ?? (() => defaultSubscriptionId(subscriberId));\n\n let unregister: (() => void) | undefined;\n let initialized = false;\n\n return {\n onOpen: (_event, ws) => {\n const handle = {\n id: subscriberId,\n tokenId: auth.tokenId,\n grantedScopes: auth.grantedScopes,\n send: (frame: ServerMessage) => {\n ws.send(JSON.stringify(frame));\n },\n close: (code: number, reason: string) => {\n ws.close(code, reason);\n },\n // IP-9: surface the socket's real backlog so the dispatcher's\n // backpressure threshold measures something - the synchronous\n // outstanding-events counter never accumulates.\n bufferedAmount: () => {\n const raw = (ws as { raw?: { bufferedAmount?: number } }).raw;\n return raw?.bufferedAmount ?? 0;\n },\n };\n const reg = options.dispatcher.registerSubscriber(handle);\n unregister = reg.unregister;\n },\n onMessage: (event, ws) => {\n const raw = typeof event.data === 'string' ? event.data : '';\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n sendRpcError(ws, undefined, RPC_ERROR_CODES.PARSE_ERROR, 'Frame is not valid JSON.');\n ws.close(closeCodeFor('protocol.violation'), 'protocol.violation');\n return;\n }\n const parsed = ClientMessageSchema.safeParse(payload);\n if (!parsed.success) {\n sendRpcError(\n ws,\n (payload as { id?: string | number } | null)?.id,\n RPC_ERROR_CODES.INVALID_REQUEST,\n 'Frame failed schema validation.',\n );\n ws.close(closeCodeFor('protocol.violation'), 'protocol.violation');\n return;\n }\n const message = parsed.data;\n if (isCancelledNotification(message)) {\n // IP-8: only an initialized connection holding `agents:invoke` may\n // cancel a run. A notification carries no id, so an unauthorised one\n // is silently ignored (there is no error channel) rather than aborting\n // an arbitrary run.\n if (initialized && grantsInvokeScope(auth.grantedScopes)) {\n options.runs?.abort(message.params.requestId, 'mcp-cancel');\n }\n return;\n }\n if (!initialized && !isInitializeRequest(message) && !isPingRequest(message)) {\n // IP-21: a frame before `initialize` is a protocol-sequencing error,\n // not an auth failure - the connection is already authenticated.\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.PROTOCOL_VIOLATION,\n 'Send `initialize` before any other RPC.',\n );\n return;\n }\n if (isInitializeRequest(message)) {\n initialized = true;\n sendRpcSuccess(ws, message.id, {\n serverInfo: {\n name: 'graphorin-server',\n version: '0.6.0',\n },\n capabilities: {\n subscriptions: true,\n replayBuffer: true,\n sseFallback: true,\n },\n });\n return;\n }\n if (isPingRequest(message)) {\n const nonce = message.params?.nonce;\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'pong',\n ...(nonce !== undefined ? { nonce } : {}),\n } satisfies ServerMessage),\n );\n sendRpcSuccess(ws, message.id, { ok: true });\n return;\n }\n if (isSubscribeRequest(message)) {\n const subscriptionId = newSubscriptionId();\n const result = options.dispatcher.subscribe({\n subscriberId,\n subject: message.params.subject,\n subscriptionId,\n ...(message.params.sinceEventId !== undefined\n ? { sinceEventId: message.params.sinceEventId }\n : {}),\n });\n if (!result.ok) {\n sendRpcError(\n ws,\n message.id,\n mapSubscribeErrorCode(result.reason),\n mapSubscribeErrorMessage(result.reason, message.params.subject),\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n subscriptionId,\n subject: message.params.subject,\n snapshotEventId: result.snapshotEventId,\n replayedCount: result.replayedCount,\n });\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'subscribed',\n subscriptionId,\n subject: message.params.subject,\n ...(result.snapshotEventId !== undefined\n ? { snapshotEventId: result.snapshotEventId }\n : {}),\n } satisfies ServerMessage),\n );\n return;\n }\n if (isUnsubscribeRequest(message)) {\n const removed = options.dispatcher.unsubscribe(message.params.subscriptionId);\n if (!removed) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n `Subscription '${message.params.subscriptionId}' not active.`,\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n subscriptionId: message.params.subscriptionId,\n unsubscribed: true,\n });\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'unsubscribed',\n subscriptionId: message.params.subscriptionId,\n } satisfies ServerMessage),\n );\n return;\n }\n if (isRunCancelRequest(message)) {\n // IP-8: mirror the REST `POST /runs/:runId/abort` scope gate so a\n // bearer token cannot abort an arbitrary run without `agents:invoke`.\n if (!grantsInvokeScope(auth.grantedScopes)) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.SCOPE_DENIED,\n \"Token lacks required scope 'agents:invoke'.\",\n );\n return;\n }\n const runId = message.params.runId;\n const aborted = options.runs?.abort(runId, message.params.reason ?? 'rpc-cancel') === true;\n if (!aborted) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.RUN_NOT_FOUND,\n `Run '${runId}' is not active.`,\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n runId,\n cancelled: true,\n partialStateAvailable: true,\n });\n return;\n }\n },\n onClose: () => {\n try {\n unregister?.();\n } finally {\n unregister = undefined;\n }\n },\n onError: () => {\n try {\n unregister?.();\n } finally {\n unregister = undefined;\n }\n },\n };\n}\n\nfunction sendRpcSuccess(ws: WSContext, id: string | number, result: unknown): void {\n const frame: ServerMessage = { v: '1', jsonrpc: '2.0', id, result };\n ws.send(JSON.stringify(frame));\n}\n\nfunction sendRpcError(\n ws: WSContext,\n id: string | number | undefined,\n code: number,\n message: string,\n): void {\n const frame: ServerMessage = {\n v: '1',\n jsonrpc: '2.0',\n id: id ?? 0,\n error: { code, message },\n };\n ws.send(JSON.stringify(frame));\n}\n\nfunction rejectImmediately(code: number, reason: string): WSEvents {\n return {\n onOpen: (_event, ws) => {\n ws.close(code, reason);\n },\n };\n}\n\ninterface ResolvedUpgradeAuthOk {\n readonly kind: 'ok';\n readonly tokenId: string;\n readonly grantedScopes: ReadonlyArray<ParsedScope>;\n}\n\ninterface ResolvedUpgradeAuthDenied {\n readonly kind: 'denied';\n readonly reason: 'auth.required' | 'auth.invalid' | 'auth.scope_denied';\n}\n\ntype ResolvedUpgradeAuth = ResolvedUpgradeAuthOk | ResolvedUpgradeAuthDenied;\n\nasync function resolveUpgradeAuth(\n c: Context<{ Variables: ServerVariables }>,\n options: WsUpgradeOptions,\n): Promise<ResolvedUpgradeAuth> {\n // Phase 14a's HTTP middleware may have already verified the\n // bearer token (`requireAuth` runs before the upgrade route on\n // the authenticated subtree); if so, reuse the result.\n const state = c.var.state;\n if (state?.auth?.kind === 'token') {\n return {\n kind: 'ok',\n tokenId: state.auth.token.tokenId,\n grantedScopes: state.auth.grantedScopes,\n };\n }\n // IP-13: no-auth loopback mode - accept the upgrade with a full scope grant\n // instead of silently failing to mount. Checked before ticket/bearer so the\n // verifier may legitimately be absent.\n if (options.anonymous === true) {\n return { kind: 'ok', tokenId: 'anonymous', grantedScopes: ANONYMOUS_WS_SCOPES };\n }\n const ticketValue = parseTicketSubprotocol(c.req.header('sec-websocket-protocol') ?? '');\n if (ticketValue !== undefined) {\n const consumed = options.tickets.consume(ticketValue);\n if (!consumed.ok) {\n return { kind: 'denied', reason: 'auth.invalid' };\n }\n return acceptTicket(consumed.ticket);\n }\n const header = c.req.header('authorization') ?? c.req.header('Authorization');\n if (header?.toLowerCase().startsWith('bearer ') === true) {\n if (options.verifier === undefined) return { kind: 'denied', reason: 'auth.required' };\n const token = header.slice(7).trim();\n // P-05: pass the client IP like the HTTP auth middleware does, so\n // the verifier's per-IP failure threshold / lockout engages for\n // upgrade attempts too - without it `GET /v1/ws` was a\n // lockout-free brute-force surface.\n const ip = c.get('state')?.clientIp;\n const verified = await options.verifier.verify(token, ip !== undefined ? { ip } : {});\n if (!verified.ok) return { kind: 'denied', reason: 'auth.invalid' };\n return {\n kind: 'ok',\n tokenId: verified.token.tokenId,\n grantedScopes: verified.token.scopes,\n };\n }\n return { kind: 'denied', reason: 'auth.required' };\n}\n\n/**\n * IP-8: the `agents:invoke` scope required to cancel a run over the WS\n * transport - the same requirement the REST `POST /runs/:runId/abort` route\n * enforces. `scopeMatches` honours wildcards (`agents:*`, `admin:*`).\n */\nconst INVOKE_SCOPE: ParsedScope = parseScope('agents:invoke');\n\n/**\n * IP-13: full scope grant handed to a no-auth (`auth.kind='none'`) upgrade.\n * `admin:*` matches every required scope, including the {@link INVOKE_SCOPE}\n * gate on run cancellation.\n */\nconst ANONYMOUS_WS_SCOPES: ReadonlyArray<ParsedScope> = [parseScope('admin:*')];\n\nfunction grantsInvokeScope(granted: ReadonlyArray<ParsedScope>): boolean {\n for (const scope of granted) {\n if (scopeMatches(scope, INVOKE_SCOPE)) return true;\n }\n return false;\n}\n\nfunction acceptTicket(ticket: WsTicket): ResolvedUpgradeAuth {\n return {\n kind: 'ok',\n tokenId: ticket.tokenId,\n grantedScopes: ticket.scopes,\n };\n}\n\nfunction mapSubscribeErrorCode(\n reason: 'subject-malformed' | 'subject-unknown' | 'subject-wildcard' | 'scope-denied',\n): number {\n switch (reason) {\n case 'scope-denied':\n return RPC_ERROR_CODES.SCOPE_DENIED;\n case 'subject-wildcard':\n return RPC_ERROR_CODES.INVALID_PARAMS;\n case 'subject-unknown':\n return RPC_ERROR_CODES.METHOD_NOT_FOUND;\n case 'subject-malformed':\n return RPC_ERROR_CODES.INVALID_PARAMS;\n }\n}\n\nfunction mapSubscribeErrorMessage(\n reason: 'subject-malformed' | 'subject-unknown' | 'subject-wildcard' | 'scope-denied',\n subject: string,\n): string {\n switch (reason) {\n case 'scope-denied':\n return `Token lacks required scope for subject '${subject}'.`;\n case 'subject-wildcard':\n return `Wildcard subjects are not supported in this version. Subject: '${subject}'.`;\n case 'subject-unknown':\n return `Unknown subject grammar: '${subject}'.`;\n case 'subject-malformed':\n return `Subject '${subject}' is malformed.`;\n }\n}\n\nfunction defaultSubscriberId(): string {\n return `sub-conn-${randomToken(8)}`;\n}\n\nfunction defaultSubscriptionId(subscriberId: string): string {\n return `${subscriberId}-${randomToken(6)}`;\n}\n\nfunction randomToken(length: number): string {\n const bytes = new Uint8Array(length);\n if (\n typeof globalThis.crypto !== 'undefined' &&\n typeof globalThis.crypto.getRandomValues === 'function'\n ) {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Buffer.from(bytes).toString('hex');\n}\n\nvoid SUBPROTOCOL_NAME;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"upgrade.js","names":["unregister: (() => void) | undefined","payload: unknown","pkg.version","frame: ServerMessage","INVOKE_SCOPE: ParsedScope","ANONYMOUS_WS_SCOPES: ReadonlyArray<ParsedScope>"],"sources":["../../src/ws/upgrade.ts"],"sourcesContent":["import pkg from '../../package.json' with { type: 'json' };\n/**\n * Hono WebSocket upgrade handler. Wires the `@hono/node-ws` adapter\n * to the dispatcher: validates the subprotocol, runs auth (bearer\n * via `requireAuth` middleware OR ticket via the in-memory store),\n * and bridges the per-connection `WSContext` callbacks to the\n * dispatcher's RPC handler.\n *\n * ## Subprotocol + browser ticket flow\n *\n * The negotiated subprotocol is always {@link SUBPROTOCOL_NAME}\n * (`graphorin.protocol.v1`) - the canonical wire contract lives in\n * `@graphorin/protocol`'s `subprotocol.ts`. Two auth paths exist:\n *\n * - **Bearer (non-browser):** the client sends `Authorization:\n * Bearer <token>` and a single `Sec-WebSocket-Protocol:\n * graphorin.protocol.v1` token.\n * - **Ticket (browser):** the `WebSocket` constructor cannot set\n * headers, so the browser client offers two subprotocol tokens -\n * `graphorin.protocol.v1` and `ticket.<value>`. The server echoes\n * back only the canonical name (in `app.ts`'s `handleProtocols`),\n * and {@link resolveUpgradeAuth} extracts the ticket via\n * `parseTicketSubprotocol` and redeems it against the single-use\n * {@link WsTicketStore}. Tickets are short-lived and one-shot to\n * bound replay.\n *\n * @packageDocumentation\n */\n\nimport {\n ClientMessageSchema,\n closeCodeFor,\n isCancelledNotification,\n isInitializeRequest,\n isPingRequest,\n isRunCancelRequest,\n isSubscribeRequest,\n isUnsubscribeRequest,\n negotiateSubprotocol,\n parseTicketSubprotocol,\n RPC_ERROR_CODES,\n type ServerMessage,\n SUBPROTOCOL_NAME,\n} from '@graphorin/protocol';\nimport type { ParsedScope, TokenVerifier } from '@graphorin/security/auth';\nimport { parseScope, scopeMatches } from '@graphorin/security/auth';\nimport type { Context } from 'hono';\nimport type { WSContext, WSEvents } from 'hono/ws';\n\nimport type { ServerVariables } from '../internal/context.js';\nimport type { RunStateTracker } from '../runtime/run-state.js';\nimport type { WsDispatcher } from './dispatcher.js';\nimport type { WsTicket, WsTicketStore } from './ticket.js';\n\n/**\n * Public configuration accepted by {@link createWsUpgradeEvents}.\n *\n * @stable\n */\nexport interface WsUpgradeOptions {\n readonly dispatcher: WsDispatcher;\n readonly tickets: WsTicketStore;\n /**\n * Token verifier for bearer / ticket upgrades. Optional only in the\n * IP-13 no-auth loopback mode (`anonymous: true`), where there is no\n * verifier to construct and every upgrade is accepted.\n */\n readonly verifier?: TokenVerifier;\n /**\n * IP-13: authentication is disabled server-wide (`auth.kind='none'`).\n * Accept the upgrade unconditionally with a full (`admin:*`) scope grant\n * instead of silently refusing to mount the WS route. Trusted-loopback\n * / single-operator mode only.\n */\n readonly anonymous?: boolean;\n readonly runs?: RunStateTracker;\n readonly now?: () => number;\n /**\n * Subprotocol the server advertises. Defaults to\n * {@link SUBPROTOCOL_NAME}; tests can override to exercise the\n * mismatch path.\n */\n readonly serverSubprotocol?: string;\n readonly newSubscriptionId?: () => string;\n readonly newSubscriberId?: () => string;\n}\n\n/**\n * Build the `WSEvents` callback bag the Hono helper consumes. The\n * function takes the request `Context` so the upgrade can read the\n * `Authorization` header / `Sec-WebSocket-Protocol` ticket directly.\n *\n * Production wiring on `@hono/node-ws`:\n *\n * ```ts\n * const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app });\n * app.get('/v1/ws', upgradeWebSocket((c) => createWsUpgradeEvents(c, deps)));\n * injectWebSocket(serve(...));\n * ```\n *\n * @stable\n */\nexport async function createWsUpgradeEvents(\n c: Context<{ Variables: ServerVariables }>,\n options: WsUpgradeOptions,\n): Promise<WSEvents> {\n const negotiated = negotiateSubprotocol(c.req.header('sec-websocket-protocol') ?? '');\n if (negotiated === null) {\n return rejectImmediately(closeCodeFor('protocol.violation'), 'protocol.violation');\n }\n const auth = await resolveUpgradeAuth(c, options);\n if (auth.kind === 'denied') {\n return rejectImmediately(closeCodeFor(auth.reason), auth.reason);\n }\n\n const subscriberId = options.newSubscriberId?.() ?? defaultSubscriberId();\n const newSubscriptionId =\n options.newSubscriptionId ?? (() => defaultSubscriptionId(subscriberId));\n\n let unregister: (() => void) | undefined;\n let initialized = false;\n\n return {\n onOpen: (_event, ws) => {\n const handle = {\n id: subscriberId,\n tokenId: auth.tokenId,\n grantedScopes: auth.grantedScopes,\n send: (frame: ServerMessage) => {\n ws.send(JSON.stringify(frame));\n },\n close: (code: number, reason: string) => {\n ws.close(code, reason);\n },\n // IP-9: surface the socket's real backlog so the dispatcher's\n // backpressure threshold measures something - the synchronous\n // outstanding-events counter never accumulates.\n bufferedAmount: () => {\n const raw = (ws as { raw?: { bufferedAmount?: number } }).raw;\n return raw?.bufferedAmount ?? 0;\n },\n };\n const reg = options.dispatcher.registerSubscriber(handle);\n unregister = reg.unregister;\n },\n onMessage: (event, ws) => {\n const raw = typeof event.data === 'string' ? event.data : '';\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n sendRpcError(ws, undefined, RPC_ERROR_CODES.PARSE_ERROR, 'Frame is not valid JSON.');\n ws.close(closeCodeFor('protocol.violation'), 'protocol.violation');\n return;\n }\n const parsed = ClientMessageSchema.safeParse(payload);\n if (!parsed.success) {\n sendRpcError(\n ws,\n (payload as { id?: string | number } | null)?.id,\n RPC_ERROR_CODES.INVALID_REQUEST,\n 'Frame failed schema validation.',\n );\n ws.close(closeCodeFor('protocol.violation'), 'protocol.violation');\n return;\n }\n const message = parsed.data;\n if (isCancelledNotification(message)) {\n // IP-8: only an initialized connection holding `agents:invoke` may\n // cancel a run. A notification carries no id, so an unauthorised one\n // is silently ignored (there is no error channel) rather than aborting\n // an arbitrary run.\n if (initialized && grantsInvokeScope(auth.grantedScopes)) {\n options.runs?.abort(message.params.requestId, 'mcp-cancel');\n }\n return;\n }\n if (!initialized && !isInitializeRequest(message) && !isPingRequest(message)) {\n // IP-21: a frame before `initialize` is a protocol-sequencing error,\n // not an auth failure - the connection is already authenticated.\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.PROTOCOL_VIOLATION,\n 'Send `initialize` before any other RPC.',\n );\n return;\n }\n if (isInitializeRequest(message)) {\n initialized = true;\n sendRpcSuccess(ws, message.id, {\n serverInfo: {\n name: 'graphorin-server',\n version: pkg.version,\n },\n capabilities: {\n subscriptions: true,\n replayBuffer: true,\n sseFallback: true,\n },\n });\n return;\n }\n if (isPingRequest(message)) {\n const nonce = message.params?.nonce;\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'pong',\n ...(nonce !== undefined ? { nonce } : {}),\n } satisfies ServerMessage),\n );\n sendRpcSuccess(ws, message.id, { ok: true });\n return;\n }\n if (isSubscribeRequest(message)) {\n const subscriptionId = newSubscriptionId();\n const result = options.dispatcher.subscribe({\n subscriberId,\n subject: message.params.subject,\n subscriptionId,\n ...(message.params.sinceEventId !== undefined\n ? { sinceEventId: message.params.sinceEventId }\n : {}),\n });\n if (!result.ok) {\n sendRpcError(\n ws,\n message.id,\n mapSubscribeErrorCode(result.reason),\n mapSubscribeErrorMessage(result.reason, message.params.subject),\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n subscriptionId,\n subject: message.params.subject,\n snapshotEventId: result.snapshotEventId,\n replayedCount: result.replayedCount,\n });\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'subscribed',\n subscriptionId,\n subject: message.params.subject,\n ...(result.snapshotEventId !== undefined\n ? { snapshotEventId: result.snapshotEventId }\n : {}),\n } satisfies ServerMessage),\n );\n return;\n }\n if (isUnsubscribeRequest(message)) {\n const removed = options.dispatcher.unsubscribe(message.params.subscriptionId);\n if (!removed) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND,\n `Subscription '${message.params.subscriptionId}' not active.`,\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n subscriptionId: message.params.subscriptionId,\n unsubscribed: true,\n });\n ws.send(\n JSON.stringify({\n v: '1',\n kind: 'unsubscribed',\n subscriptionId: message.params.subscriptionId,\n } satisfies ServerMessage),\n );\n return;\n }\n if (isRunCancelRequest(message)) {\n // IP-8: mirror the REST `POST /runs/:runId/abort` scope gate so a\n // bearer token cannot abort an arbitrary run without `agents:invoke`.\n if (!grantsInvokeScope(auth.grantedScopes)) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.SCOPE_DENIED,\n \"Token lacks required scope 'agents:invoke'.\",\n );\n return;\n }\n const runId = message.params.runId;\n const aborted = options.runs?.abort(runId, message.params.reason ?? 'rpc-cancel') === true;\n if (!aborted) {\n sendRpcError(\n ws,\n message.id,\n RPC_ERROR_CODES.RUN_NOT_FOUND,\n `Run '${runId}' is not active.`,\n );\n return;\n }\n sendRpcSuccess(ws, message.id, {\n runId,\n cancelled: true,\n partialStateAvailable: true,\n });\n return;\n }\n },\n onClose: () => {\n try {\n unregister?.();\n } finally {\n unregister = undefined;\n }\n },\n onError: () => {\n try {\n unregister?.();\n } finally {\n unregister = undefined;\n }\n },\n };\n}\n\nfunction sendRpcSuccess(ws: WSContext, id: string | number, result: unknown): void {\n const frame: ServerMessage = { v: '1', jsonrpc: '2.0', id, result };\n ws.send(JSON.stringify(frame));\n}\n\nfunction sendRpcError(\n ws: WSContext,\n id: string | number | undefined,\n code: number,\n message: string,\n): void {\n const frame: ServerMessage = {\n v: '1',\n jsonrpc: '2.0',\n id: id ?? 0,\n error: { code, message },\n };\n ws.send(JSON.stringify(frame));\n}\n\nfunction rejectImmediately(code: number, reason: string): WSEvents {\n return {\n onOpen: (_event, ws) => {\n ws.close(code, reason);\n },\n };\n}\n\ninterface ResolvedUpgradeAuthOk {\n readonly kind: 'ok';\n readonly tokenId: string;\n readonly grantedScopes: ReadonlyArray<ParsedScope>;\n}\n\ninterface ResolvedUpgradeAuthDenied {\n readonly kind: 'denied';\n readonly reason: 'auth.required' | 'auth.invalid' | 'auth.scope_denied';\n}\n\ntype ResolvedUpgradeAuth = ResolvedUpgradeAuthOk | ResolvedUpgradeAuthDenied;\n\nasync function resolveUpgradeAuth(\n c: Context<{ Variables: ServerVariables }>,\n options: WsUpgradeOptions,\n): Promise<ResolvedUpgradeAuth> {\n // Phase 14a's HTTP middleware may have already verified the\n // bearer token (`requireAuth` runs before the upgrade route on\n // the authenticated subtree); if so, reuse the result.\n const state = c.var.state;\n if (state?.auth?.kind === 'token') {\n return {\n kind: 'ok',\n tokenId: state.auth.token.tokenId,\n grantedScopes: state.auth.grantedScopes,\n };\n }\n // IP-13: no-auth loopback mode - accept the upgrade with a full scope grant\n // instead of silently failing to mount. Checked before ticket/bearer so the\n // verifier may legitimately be absent.\n if (options.anonymous === true) {\n return { kind: 'ok', tokenId: 'anonymous', grantedScopes: ANONYMOUS_WS_SCOPES };\n }\n const ticketValue = parseTicketSubprotocol(c.req.header('sec-websocket-protocol') ?? '');\n if (ticketValue !== undefined) {\n const consumed = options.tickets.consume(ticketValue);\n if (!consumed.ok) {\n return { kind: 'denied', reason: 'auth.invalid' };\n }\n return acceptTicket(consumed.ticket);\n }\n const header = c.req.header('authorization') ?? c.req.header('Authorization');\n if (header?.toLowerCase().startsWith('bearer ') === true) {\n if (options.verifier === undefined) return { kind: 'denied', reason: 'auth.required' };\n const token = header.slice(7).trim();\n // P-05: pass the client IP like the HTTP auth middleware does, so\n // the verifier's per-IP failure threshold / lockout engages for\n // upgrade attempts too - without it `GET /v1/ws` was a\n // lockout-free brute-force surface.\n const ip = c.get('state')?.clientIp;\n const verified = await options.verifier.verify(token, ip !== undefined ? { ip } : {});\n if (!verified.ok) return { kind: 'denied', reason: 'auth.invalid' };\n return {\n kind: 'ok',\n tokenId: verified.token.tokenId,\n grantedScopes: verified.token.scopes,\n };\n }\n return { kind: 'denied', reason: 'auth.required' };\n}\n\n/**\n * IP-8: the `agents:invoke` scope required to cancel a run over the WS\n * transport - the same requirement the REST `POST /runs/:runId/abort` route\n * enforces. `scopeMatches` honours wildcards (`agents:*`, `admin:*`).\n */\nconst INVOKE_SCOPE: ParsedScope = parseScope('agents:invoke');\n\n/**\n * IP-13: full scope grant handed to a no-auth (`auth.kind='none'`) upgrade.\n * `admin:*` matches every required scope, including the {@link INVOKE_SCOPE}\n * gate on run cancellation.\n */\nconst ANONYMOUS_WS_SCOPES: ReadonlyArray<ParsedScope> = [parseScope('admin:*')];\n\nfunction grantsInvokeScope(granted: ReadonlyArray<ParsedScope>): boolean {\n for (const scope of granted) {\n if (scopeMatches(scope, INVOKE_SCOPE)) return true;\n }\n return false;\n}\n\nfunction acceptTicket(ticket: WsTicket): ResolvedUpgradeAuth {\n return {\n kind: 'ok',\n tokenId: ticket.tokenId,\n grantedScopes: ticket.scopes,\n };\n}\n\nfunction mapSubscribeErrorCode(\n reason: 'subject-malformed' | 'subject-unknown' | 'subject-wildcard' | 'scope-denied',\n): number {\n switch (reason) {\n case 'scope-denied':\n return RPC_ERROR_CODES.SCOPE_DENIED;\n case 'subject-wildcard':\n return RPC_ERROR_CODES.INVALID_PARAMS;\n case 'subject-unknown':\n return RPC_ERROR_CODES.METHOD_NOT_FOUND;\n case 'subject-malformed':\n return RPC_ERROR_CODES.INVALID_PARAMS;\n }\n}\n\nfunction mapSubscribeErrorMessage(\n reason: 'subject-malformed' | 'subject-unknown' | 'subject-wildcard' | 'scope-denied',\n subject: string,\n): string {\n switch (reason) {\n case 'scope-denied':\n return `Token lacks required scope for subject '${subject}'.`;\n case 'subject-wildcard':\n return `Wildcard subjects are not supported in this version. Subject: '${subject}'.`;\n case 'subject-unknown':\n return `Unknown subject grammar: '${subject}'.`;\n case 'subject-malformed':\n return `Subject '${subject}' is malformed.`;\n }\n}\n\nfunction defaultSubscriberId(): string {\n return `sub-conn-${randomToken(8)}`;\n}\n\nfunction defaultSubscriptionId(subscriberId: string): string {\n return `${subscriberId}-${randomToken(6)}`;\n}\n\nfunction randomToken(length: number): string {\n const bytes = new Uint8Array(length);\n if (\n typeof globalThis.crypto !== 'undefined' &&\n typeof globalThis.crypto.getRandomValues === 'function'\n ) {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Buffer.from(bytes).toString('hex');\n}\n\nvoid SUBPROTOCOL_NAME;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsGA,eAAsB,sBACpB,GACA,SACmB;AAEnB,KADmB,qBAAqB,EAAE,IAAI,OAAO,yBAAyB,IAAI,GAAG,KAClE,KACjB,QAAO,kBAAkB,aAAa,qBAAqB,EAAE,qBAAqB;CAEpF,MAAM,OAAO,MAAM,mBAAmB,GAAG,QAAQ;AACjD,KAAI,KAAK,SAAS,SAChB,QAAO,kBAAkB,aAAa,KAAK,OAAO,EAAE,KAAK,OAAO;CAGlE,MAAM,eAAe,QAAQ,mBAAmB,IAAI,qBAAqB;CACzE,MAAM,oBACJ,QAAQ,4BAA4B,sBAAsB,aAAa;CAEzE,IAAIA;CACJ,IAAI,cAAc;AAElB,QAAO;EACL,SAAS,QAAQ,OAAO;GACtB,MAAM,SAAS;IACb,IAAI;IACJ,SAAS,KAAK;IACd,eAAe,KAAK;IACpB,OAAO,UAAyB;AAC9B,QAAG,KAAK,KAAK,UAAU,MAAM,CAAC;;IAEhC,QAAQ,MAAc,WAAmB;AACvC,QAAG,MAAM,MAAM,OAAO;;IAKxB,sBAAsB;AAEpB,YADa,GAA6C,KAC9C,kBAAkB;;IAEjC;AAED,gBADY,QAAQ,WAAW,mBAAmB,OAAO,CACxC;;EAEnB,YAAY,OAAO,OAAO;GACxB,MAAM,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;GAC1D,IAAIC;AACJ,OAAI;AACF,cAAU,KAAK,MAAM,IAAI;WACnB;AACN,iBAAa,IAAI,QAAW,gBAAgB,aAAa,2BAA2B;AACpF,OAAG,MAAM,aAAa,qBAAqB,EAAE,qBAAqB;AAClE;;GAEF,MAAM,SAAS,oBAAoB,UAAU,QAAQ;AACrD,OAAI,CAAC,OAAO,SAAS;AACnB,iBACE,IACC,SAA6C,IAC9C,gBAAgB,iBAChB,kCACD;AACD,OAAG,MAAM,aAAa,qBAAqB,EAAE,qBAAqB;AAClE;;GAEF,MAAM,UAAU,OAAO;AACvB,OAAI,wBAAwB,QAAQ,EAAE;AAKpC,QAAI,eAAe,kBAAkB,KAAK,cAAc,CACtD,SAAQ,MAAM,MAAM,QAAQ,OAAO,WAAW,aAAa;AAE7D;;AAEF,OAAI,CAAC,eAAe,CAAC,oBAAoB,QAAQ,IAAI,CAAC,cAAc,QAAQ,EAAE;AAG5E,iBACE,IACA,QAAQ,IACR,gBAAgB,oBAChB,0CACD;AACD;;AAEF,OAAI,oBAAoB,QAAQ,EAAE;AAChC,kBAAc;AACd,mBAAe,IAAI,QAAQ,IAAI;KAC7B,YAAY;MACV,MAAM;MACGC;MACV;KACD,cAAc;MACZ,eAAe;MACf,cAAc;MACd,aAAa;MACd;KACF,CAAC;AACF;;AAEF,OAAI,cAAc,QAAQ,EAAE;IAC1B,MAAM,QAAQ,QAAQ,QAAQ;AAC9B,OAAG,KACD,KAAK,UAAU;KACb,GAAG;KACH,MAAM;KACN,GAAI,UAAU,SAAY,EAAE,OAAO,GAAG,EAAE;KACzC,CAAyB,CAC3B;AACD,mBAAe,IAAI,QAAQ,IAAI,EAAE,IAAI,MAAM,CAAC;AAC5C;;AAEF,OAAI,mBAAmB,QAAQ,EAAE;IAC/B,MAAM,iBAAiB,mBAAmB;IAC1C,MAAM,SAAS,QAAQ,WAAW,UAAU;KAC1C;KACA,SAAS,QAAQ,OAAO;KACxB;KACA,GAAI,QAAQ,OAAO,iBAAiB,SAChC,EAAE,cAAc,QAAQ,OAAO,cAAc,GAC7C,EAAE;KACP,CAAC;AACF,QAAI,CAAC,OAAO,IAAI;AACd,kBACE,IACA,QAAQ,IACR,sBAAsB,OAAO,OAAO,EACpC,yBAAyB,OAAO,QAAQ,QAAQ,OAAO,QAAQ,CAChE;AACD;;AAEF,mBAAe,IAAI,QAAQ,IAAI;KAC7B;KACA,SAAS,QAAQ,OAAO;KACxB,iBAAiB,OAAO;KACxB,eAAe,OAAO;KACvB,CAAC;AACF,OAAG,KACD,KAAK,UAAU;KACb,GAAG;KACH,MAAM;KACN;KACA,SAAS,QAAQ,OAAO;KACxB,GAAI,OAAO,oBAAoB,SAC3B,EAAE,iBAAiB,OAAO,iBAAiB,GAC3C,EAAE;KACP,CAAyB,CAC3B;AACD;;AAEF,OAAI,qBAAqB,QAAQ,EAAE;AAEjC,QAAI,CADY,QAAQ,WAAW,YAAY,QAAQ,OAAO,eAAe,EAC/D;AACZ,kBACE,IACA,QAAQ,IACR,gBAAgB,wBAChB,iBAAiB,QAAQ,OAAO,eAAe,eAChD;AACD;;AAEF,mBAAe,IAAI,QAAQ,IAAI;KAC7B,gBAAgB,QAAQ,OAAO;KAC/B,cAAc;KACf,CAAC;AACF,OAAG,KACD,KAAK,UAAU;KACb,GAAG;KACH,MAAM;KACN,gBAAgB,QAAQ,OAAO;KAChC,CAAyB,CAC3B;AACD;;AAEF,OAAI,mBAAmB,QAAQ,EAAE;AAG/B,QAAI,CAAC,kBAAkB,KAAK,cAAc,EAAE;AAC1C,kBACE,IACA,QAAQ,IACR,gBAAgB,cAChB,8CACD;AACD;;IAEF,MAAM,QAAQ,QAAQ,OAAO;AAE7B,QAAI,EADY,QAAQ,MAAM,MAAM,OAAO,QAAQ,OAAO,UAAU,aAAa,KAAK,OACxE;AACZ,kBACE,IACA,QAAQ,IACR,gBAAgB,eAChB,QAAQ,MAAM,kBACf;AACD;;AAEF,mBAAe,IAAI,QAAQ,IAAI;KAC7B;KACA,WAAW;KACX,uBAAuB;KACxB,CAAC;AACF;;;EAGJ,eAAe;AACb,OAAI;AACF,kBAAc;aACN;AACR,iBAAa;;;EAGjB,eAAe;AACb,OAAI;AACF,kBAAc;aACN;AACR,iBAAa;;;EAGlB;;AAGH,SAAS,eAAe,IAAe,IAAqB,QAAuB;CACjF,MAAMC,QAAuB;EAAE,GAAG;EAAK,SAAS;EAAO;EAAI;EAAQ;AACnE,IAAG,KAAK,KAAK,UAAU,MAAM,CAAC;;AAGhC,SAAS,aACP,IACA,IACA,MACA,SACM;CACN,MAAMA,QAAuB;EAC3B,GAAG;EACH,SAAS;EACT,IAAI,MAAM;EACV,OAAO;GAAE;GAAM;GAAS;EACzB;AACD,IAAG,KAAK,KAAK,UAAU,MAAM,CAAC;;AAGhC,SAAS,kBAAkB,MAAc,QAA0B;AACjE,QAAO,EACL,SAAS,QAAQ,OAAO;AACtB,KAAG,MAAM,MAAM,OAAO;IAEzB;;AAgBH,eAAe,mBACb,GACA,SAC8B;CAI9B,MAAM,QAAQ,EAAE,IAAI;AACpB,KAAI,OAAO,MAAM,SAAS,QACxB,QAAO;EACL,MAAM;EACN,SAAS,MAAM,KAAK,MAAM;EAC1B,eAAe,MAAM,KAAK;EAC3B;AAKH,KAAI,QAAQ,cAAc,KACxB,QAAO;EAAE,MAAM;EAAM,SAAS;EAAa,eAAe;EAAqB;CAEjF,MAAM,cAAc,uBAAuB,EAAE,IAAI,OAAO,yBAAyB,IAAI,GAAG;AACxF,KAAI,gBAAgB,QAAW;EAC7B,MAAM,WAAW,QAAQ,QAAQ,QAAQ,YAAY;AACrD,MAAI,CAAC,SAAS,GACZ,QAAO;GAAE,MAAM;GAAU,QAAQ;GAAgB;AAEnD,SAAO,aAAa,SAAS,OAAO;;CAEtC,MAAM,SAAS,EAAE,IAAI,OAAO,gBAAgB,IAAI,EAAE,IAAI,OAAO,gBAAgB;AAC7E,KAAI,QAAQ,aAAa,CAAC,WAAW,UAAU,KAAK,MAAM;AACxD,MAAI,QAAQ,aAAa,OAAW,QAAO;GAAE,MAAM;GAAU,QAAQ;GAAiB;EACtF,MAAM,QAAQ,OAAO,MAAM,EAAE,CAAC,MAAM;EAKpC,MAAM,KAAK,EAAE,IAAI,QAAQ,EAAE;EAC3B,MAAM,WAAW,MAAM,QAAQ,SAAS,OAAO,OAAO,OAAO,SAAY,EAAE,IAAI,GAAG,EAAE,CAAC;AACrF,MAAI,CAAC,SAAS,GAAI,QAAO;GAAE,MAAM;GAAU,QAAQ;GAAgB;AACnE,SAAO;GACL,MAAM;GACN,SAAS,SAAS,MAAM;GACxB,eAAe,SAAS,MAAM;GAC/B;;AAEH,QAAO;EAAE,MAAM;EAAU,QAAQ;EAAiB;;;;;;;AAQpD,MAAMC,eAA4B,WAAW,gBAAgB;;;;;;AAO7D,MAAMC,sBAAkD,CAAC,WAAW,UAAU,CAAC;AAE/E,SAAS,kBAAkB,SAA8C;AACvE,MAAK,MAAM,SAAS,QAClB,KAAI,aAAa,OAAO,aAAa,CAAE,QAAO;AAEhD,QAAO;;AAGT,SAAS,aAAa,QAAuC;AAC3D,QAAO;EACL,MAAM;EACN,SAAS,OAAO;EAChB,eAAe,OAAO;EACvB;;AAGH,SAAS,sBACP,QACQ;AACR,SAAQ,QAAR;EACE,KAAK,eACH,QAAO,gBAAgB;EACzB,KAAK,mBACH,QAAO,gBAAgB;EACzB,KAAK,kBACH,QAAO,gBAAgB;EACzB,KAAK,oBACH,QAAO,gBAAgB;;;AAI7B,SAAS,yBACP,QACA,SACQ;AACR,SAAQ,QAAR;EACE,KAAK,eACH,QAAO,2CAA2C,QAAQ;EAC5D,KAAK,mBACH,QAAO,kEAAkE,QAAQ;EACnF,KAAK,kBACH,QAAO,6BAA6B,QAAQ;EAC9C,KAAK,oBACH,QAAO,YAAY,QAAQ;;;AAIjC,SAAS,sBAA8B;AACrC,QAAO,YAAY,YAAY,EAAE;;AAGnC,SAAS,sBAAsB,cAA8B;AAC3D,QAAO,GAAG,aAAa,GAAG,YAAY,EAAE;;AAG1C,SAAS,YAAY,QAAwB;CAC3C,MAAM,QAAQ,IAAI,WAAW,OAAO;AACpC,KACE,OAAO,WAAW,WAAW,eAC7B,OAAO,WAAW,OAAO,oBAAoB,WAE7C,YAAW,OAAO,gBAAgB,MAAM;KAExC,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,EAC/B,OAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI;AAG9C,QAAO,OAAO,KAAK,MAAM,CAAC,SAAS,MAAM"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphorin/server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Standalone server runtime for the Graphorin framework: createServer({...}) factory, Hono HTTP app, REST endpoints (agents / workflows / sessions / memory / skills / mcp / tokens / audit / health / health/secrets / metrics / triggers / runs/:runId/replay / sessions/:id/replay / audit/verify), per-request authentication + scope middleware (HMAC-SHA256 + pepper, server pepper required), brute-force lockout, IETF draft-07 Idempotency-Key middleware with SQLite backing + LRU read cache, deny-by-default CORS, double-submit CSRF for browser flows, sliding-window rate limit, audit middleware that streams every authenticated request through the tamper-evident hash chain, programmatic AgentRegistry / WorkflowRegistry, lifecycle hooks (beforeStart / onReady / beforeShutdown), pre-bind secrets resolution + storage migration runner that fails fast on missing pepper / unresolvable SecretRef / missing encryption peer, graceful SIGTERM drain with state preservation, durable triggers daemon (cron / interval / idle / event survive process restart per the catch-up policy contract), consolidator daemon lifecycle integration with hard stop timeout, per-subsystem health rollup (storage / embedder / secrets / encryption / consolidator / triggers / replay-buffer), Prometheus text exposition `/v1/metrics` with the canonical graphorin_* metric inventory, scope-enforced replay endpoints (sanitized by default, raw requires `traces:read:raw` admin scope, every invocation appended to the audit chain), and `POST /v1/audit/verify` chain integrity check. Created and maintained by Oleksiy Stepurenko.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Oleksiy Stepurenko",
|
|
@@ -105,12 +105,12 @@
|
|
|
105
105
|
"@hono/node-ws": "^1.3.0",
|
|
106
106
|
"hono": "^4.12.25",
|
|
107
107
|
"zod": "^3.25.0",
|
|
108
|
-
"@graphorin/core": "0.6.
|
|
109
|
-
"@graphorin/observability": "0.6.
|
|
110
|
-
"@graphorin/protocol": "0.6.
|
|
111
|
-
"@graphorin/security": "0.6.
|
|
112
|
-
"@graphorin/store-sqlite": "0.6.
|
|
113
|
-
"@graphorin/triggers": "0.6.
|
|
108
|
+
"@graphorin/core": "0.6.1",
|
|
109
|
+
"@graphorin/observability": "0.6.1",
|
|
110
|
+
"@graphorin/protocol": "0.6.1",
|
|
111
|
+
"@graphorin/security": "0.6.1",
|
|
112
|
+
"@graphorin/store-sqlite": "0.6.1",
|
|
113
|
+
"@graphorin/triggers": "0.6.1"
|
|
114
114
|
},
|
|
115
115
|
"peerDependencies": {
|
|
116
116
|
"@graphorin/agent": ">=0.5.0 <1.0.0",
|
|
@@ -137,14 +137,14 @@
|
|
|
137
137
|
"provenance": true
|
|
138
138
|
},
|
|
139
139
|
"devDependencies": {
|
|
140
|
-
"@graphorin/agent": ">=0.6.0",
|
|
141
|
-
"@graphorin/memory": ">=0.6.0",
|
|
142
|
-
"@graphorin/sessions": ">=0.6.0",
|
|
143
|
-
"@graphorin/workflow": ">=0.6.0",
|
|
144
140
|
"@types/better-sqlite3": "^7.6.13",
|
|
145
141
|
"@types/ws": "^8.18.1",
|
|
146
142
|
"better-sqlite3": "^12.9.0",
|
|
147
|
-
"ws": "^8.20.1"
|
|
143
|
+
"ws": "^8.20.1",
|
|
144
|
+
"@graphorin/agent": "0.6.1",
|
|
145
|
+
"@graphorin/memory": "0.6.1",
|
|
146
|
+
"@graphorin/sessions": "0.6.1",
|
|
147
|
+
"@graphorin/workflow": "0.6.1"
|
|
148
148
|
},
|
|
149
149
|
"scripts": {
|
|
150
150
|
"build": "tsdown",
|