neotoma 0.22.0 → 0.22.2

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.
Files changed (118) hide show
  1. package/dist/actions.d.ts +13 -0
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +236 -93
  4. package/dist/actions.js.map +1 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +11 -0
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/config.d.ts +13 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +90 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/docs/api/rest_api.md +81 -0
  13. package/dist/docs/developer/getting_started.md +45 -0
  14. package/dist/docs/developer/mcp/proxy.md +48 -28
  15. package/dist/docs/developer/npm_scripts.md +10 -4
  16. package/dist/docs/infrastructure/client_instance_deployment.md +50 -2
  17. package/dist/docs/operations/configuration.md +44 -1
  18. package/dist/docs/security/advisories/2026-08-26-jwt-bearer-unverified-fallback-auth-bypass.md +59 -0
  19. package/dist/docs/security/advisories/README.md +1 -0
  20. package/dist/docs/security/practices.md +5 -1
  21. package/dist/docs/subsystems/agent_session_architecture.md +69 -0
  22. package/dist/docs/subsystems/usage_digest.md +127 -0
  23. package/dist/docs/testing/automated_test_catalog.md +49 -26
  24. package/dist/docs/testing/testing_standard.md +1 -1
  25. package/dist/middleware/aauth_admission.js +1 -1
  26. package/dist/middleware/aauth_admission.js.map +1 -1
  27. package/dist/middleware/db_abort_context.d.ts +24 -0
  28. package/dist/middleware/db_abort_context.d.ts.map +1 -0
  29. package/dist/middleware/db_abort_context.js +41 -0
  30. package/dist/middleware/db_abort_context.js.map +1 -0
  31. package/dist/proxy/mcp_stdio_proxy.d.ts +134 -6
  32. package/dist/proxy/mcp_stdio_proxy.d.ts.map +1 -1
  33. package/dist/proxy/mcp_stdio_proxy.js +373 -17
  34. package/dist/proxy/mcp_stdio_proxy.js.map +1 -1
  35. package/dist/repositories/sqlite/local_db_adapter.d.ts.map +1 -1
  36. package/dist/repositories/sqlite/local_db_adapter.js +20 -0
  37. package/dist/repositories/sqlite/local_db_adapter.js.map +1 -1
  38. package/dist/repositories/sqlite/sqlite_client.d.ts.map +1 -1
  39. package/dist/repositories/sqlite/sqlite_client.js +10 -0
  40. package/dist/repositories/sqlite/sqlite_client.js.map +1 -1
  41. package/dist/repositories/worker/worker_file_database.d.ts +135 -1
  42. package/dist/repositories/worker/worker_file_database.d.ts.map +1 -1
  43. package/dist/repositories/worker/worker_file_database.js +726 -27
  44. package/dist/repositories/worker/worker_file_database.js.map +1 -1
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +182 -77
  47. package/dist/server.js.map +1 -1
  48. package/dist/services/attachment_resolution.d.ts +149 -0
  49. package/dist/services/attachment_resolution.d.ts.map +1 -0
  50. package/dist/services/attachment_resolution.js +206 -0
  51. package/dist/services/attachment_resolution.js.map +1 -0
  52. package/dist/services/attachment_resolution_sqlite.d.ts +57 -0
  53. package/dist/services/attachment_resolution_sqlite.d.ts.map +1 -0
  54. package/dist/services/attachment_resolution_sqlite.js +75 -0
  55. package/dist/services/attachment_resolution_sqlite.js.map +1 -0
  56. package/dist/services/entity_queries.d.ts.map +1 -1
  57. package/dist/services/entity_queries.js +85 -23
  58. package/dist/services/entity_queries.js.map +1 -1
  59. package/dist/services/entity_snapshot_at_time.d.ts.map +1 -1
  60. package/dist/services/entity_snapshot_at_time.js +23 -3
  61. package/dist/services/entity_snapshot_at_time.js.map +1 -1
  62. package/dist/services/file_input_diagnostics.d.ts +89 -0
  63. package/dist/services/file_input_diagnostics.d.ts.map +1 -0
  64. package/dist/services/file_input_diagnostics.js +174 -0
  65. package/dist/services/file_input_diagnostics.js.map +1 -0
  66. package/dist/services/interpretation.d.ts.map +1 -1
  67. package/dist/services/interpretation.js +22 -8
  68. package/dist/services/interpretation.js.map +1 -1
  69. package/dist/services/mcp_auth.d.ts +19 -3
  70. package/dist/services/mcp_auth.d.ts.map +1 -1
  71. package/dist/services/mcp_auth.js +23 -33
  72. package/dist/services/mcp_auth.js.map +1 -1
  73. package/dist/services/mcp_oauth.d.ts +14 -1
  74. package/dist/services/mcp_oauth.d.ts.map +1 -1
  75. package/dist/services/mcp_oauth.js +86 -1
  76. package/dist/services/mcp_oauth.js.map +1 -1
  77. package/dist/services/raw_storage.d.ts.map +1 -1
  78. package/dist/services/raw_storage.js +14 -2
  79. package/dist/services/raw_storage.js.map +1 -1
  80. package/dist/services/readiness.d.ts +85 -0
  81. package/dist/services/readiness.d.ts.map +1 -0
  82. package/dist/services/readiness.js +93 -0
  83. package/dist/services/readiness.js.map +1 -0
  84. package/dist/services/route_shadowing.d.ts +67 -0
  85. package/dist/services/route_shadowing.d.ts.map +1 -0
  86. package/dist/services/route_shadowing.js +126 -0
  87. package/dist/services/route_shadowing.js.map +1 -0
  88. package/dist/services/schema_definitions.d.ts.map +1 -1
  89. package/dist/services/schema_definitions.js +171 -0
  90. package/dist/services/schema_definitions.js.map +1 -1
  91. package/dist/services/schema_lag_repair.d.ts.map +1 -1
  92. package/dist/services/schema_lag_repair.js +32 -12
  93. package/dist/services/schema_lag_repair.js.map +1 -1
  94. package/dist/services/schema_registry.d.ts.map +1 -1
  95. package/dist/services/schema_registry.js +12 -5
  96. package/dist/services/schema_registry.js.map +1 -1
  97. package/dist/services/snapshot_computation.d.ts.map +1 -1
  98. package/dist/services/snapshot_computation.js +12 -8
  99. package/dist/services/snapshot_computation.js.map +1 -1
  100. package/dist/services/subscriptions/sse_hub.d.ts.map +1 -1
  101. package/dist/services/subscriptions/sse_hub.js +19 -1
  102. package/dist/services/subscriptions/sse_hub.js.map +1 -1
  103. package/dist/shared/action_handlers/entity_handlers.d.ts.map +1 -1
  104. package/dist/shared/action_handlers/entity_handlers.js +110 -26
  105. package/dist/shared/action_handlers/entity_handlers.js.map +1 -1
  106. package/dist/shared/action_schemas.d.ts +6 -6
  107. package/dist/shared/contract_mappings.d.ts.map +1 -1
  108. package/dist/shared/contract_mappings.js +8 -1
  109. package/dist/shared/contract_mappings.js.map +1 -1
  110. package/dist/shared/openapi_types.d.ts +108 -3
  111. package/dist/shared/openapi_types.d.ts.map +1 -1
  112. package/dist/tool_definitions.d.ts.map +1 -1
  113. package/dist/tool_definitions.js +18 -13
  114. package/dist/tool_definitions.js.map +1 -1
  115. package/openapi.yaml +83 -3
  116. package/package.json +2 -1
  117. package/skills/status/SKILL.md +0 -1
  118. package/skills/where/SKILL.md +315 -0
package/dist/config.d.ts CHANGED
@@ -24,6 +24,19 @@ export declare const config: {
24
24
  /** When true, MCP clients receive the compact instruction block (same body as runtime fallback) instead of the full fenced block from docs — for dual-host setups that already load workspace Neotoma rules. */
25
25
  mcpCompactInstructions: boolean;
26
26
  oauthClientId: string;
27
+ /**
28
+ * Additional exact callback URLs an operator trusts to receive an authorization
29
+ * code when the authorize request arrives via a tunnel (non-local Host).
30
+ *
31
+ * Comma-separated list of FULL callback URLs, not origins — `https://app.example.com/auth/callback`
32
+ * authorises that path and nothing else on that host. Entries must be https: unless
33
+ * they point at a loopback host. Empty by default: an operator who sets nothing keeps
34
+ * exactly the behaviour they had before this existed.
35
+ *
36
+ * Validated at load, fail-closed on the WHOLE list: one malformed entry rejects
37
+ * every entry and fails startup. See parseTrustedCallbackUrls above for why.
38
+ */
39
+ oauthTrustedCallbackUrls: string[];
27
40
  requireKeyForOauth: boolean;
28
41
  encryption: {
29
42
  enabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgLA,eAAO,MAAM,MAAM;;;;eAIO,QAAQ,GAAG,QAAQ;IAC3C,0FAA0F;;IAE1F,6DAA6D;;;;;;;;;;;;;;;;IA4B7D,gNAAgN;;;;;;;;;;;;;;;;;CAiCjN,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAsBpC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAqQA,eAAO,MAAM,MAAM;;;;eAIO,QAAQ,GAAG,QAAQ;IAC3C,0FAA0F;;IAE1F,6DAA6D;;;;;;;;;;;;;;;;IA4B7D,gNAAgN;;;IAKhN;;;;;;;;;;;OAWG;;;;;;;;;;;;;;;;CAgCJ,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAsBpC"}
package/dist/config.js CHANGED
@@ -164,6 +164,83 @@ const dbBackend = (process.env.NEOTOMA_DB_BACKEND || "sqlite").toLowerCase();
164
164
  if (dbBackend !== "sqlite" && dbBackend !== "libsql") {
165
165
  throw new Error(`Invalid NEOTOMA_DB_BACKEND "${process.env.NEOTOMA_DB_BACKEND}": expected "sqlite" or "libsql"`);
166
166
  }
167
+ const OAUTH_CALLBACK_LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
168
+ /**
169
+ * Why an entry is refused, phrased for an operator reading a startup failure.
170
+ * Returns null when the entry is acceptable.
171
+ */
172
+ function describeTrustedCallbackDefect(entry) {
173
+ let url;
174
+ try {
175
+ url = new URL(entry);
176
+ }
177
+ catch {
178
+ return "not a parseable absolute URL (a full URL with a scheme is required, e.g. https://app.example.com/auth/callback)";
179
+ }
180
+ const protocol = url.protocol.toLowerCase();
181
+ if (protocol !== "https:" && protocol !== "http:") {
182
+ return `scheme "${protocol}" is not supported here (use https:, or http: only for a loopback host)`;
183
+ }
184
+ if (url.username || url.password) {
185
+ return "carries userinfo (user:password@host), which is a URL-spoofing vector and is never load-bearing in a callback";
186
+ }
187
+ const host = url.hostname.toLowerCase();
188
+ if (!host) {
189
+ return "has no host";
190
+ }
191
+ if (protocol === "http:" && !OAUTH_CALLBACK_LOOPBACK_HOSTS.has(host)) {
192
+ return "is plaintext http: to a non-loopback host, which would ship authorization codes in cleartext (use https:)";
193
+ }
194
+ return null;
195
+ }
196
+ /**
197
+ * Parse NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS, FAILING CLOSED on the whole list.
198
+ *
199
+ * If any entry is malformed, the entire list is rejected and startup fails —
200
+ * entries 1 and 3 do not survive a bad entry 2. This is deliberate and is the
201
+ * condition pre-registered on #2384 before implementation.
202
+ *
203
+ * The rejected alternative was skipping a bad entry and honouring the rest. That
204
+ * is friendlier on a typo, but it makes an allowlist enforce something other than
205
+ * what the operator wrote, with no signal that it did: the refusal a bad entry
206
+ * produces is indistinguishable from an exact-match miss. An allowlist is a
207
+ * security control, and a control that silently binds less than its configuration
208
+ * says is the failure mode in docs/foundation/principles.md#1. Refusing to start
209
+ * is loud, immediate, and happens before any authorization request is served,
210
+ * rather than surfacing as an unexplained sign-in failure weeks later.
211
+ *
212
+ * The blast radius of the strict reading is bounded: this throws only when the
213
+ * operator has actually set the variable. Leaving it unset is the default and is
214
+ * unaffected.
215
+ */
216
+ function parseTrustedCallbackUrls(raw) {
217
+ const entries = (raw || "")
218
+ .split(",")
219
+ .map((entry) => entry.trim())
220
+ .filter((entry) => entry.length > 0);
221
+ const defects = entries
222
+ .map((entry, index) => {
223
+ const defect = describeTrustedCallbackDefect(entry);
224
+ // The entry is operator-authored config, not user input, and is already
225
+ // echoed in startup logs; but userinfo is the one part that could carry a
226
+ // credential, so redact it rather than printing it back.
227
+ return defect ? ` entry ${index + 1} ("${redactUrlUserinfo(entry)}"): ${defect}` : null;
228
+ })
229
+ .filter((defect) => defect !== null);
230
+ if (defects.length > 0) {
231
+ throw new Error(`Invalid NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS: ${defects.length} of ${entries.length} ` +
232
+ `entries are not usable as trusted OAuth callback URLs.\n${defects.join("\n")}\n` +
233
+ `The whole list is rejected rather than partially applied, so the allowlist in force ` +
234
+ `always matches what is configured. Fix the entries above, or unset the variable to ` +
235
+ `fall back to the built-in allowlist (localhost, loopback, cursor:/vscode:/app:, this ` +
236
+ `instance's own origin, and the ChatGPT/Claude callbacks).`);
237
+ }
238
+ return entries;
239
+ }
240
+ /** Replace any `user:password@` in a URL-ish string, for safe echoing in errors. */
241
+ function redactUrlUserinfo(value) {
242
+ return value.replace(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/@]*@/, "$1<redacted>@");
243
+ }
167
244
  export const config = {
168
245
  projectRoot,
169
246
  storageBackend,
@@ -196,6 +273,19 @@ export const config = {
196
273
  mcpCompactInstructions: (process.env.NEOTOMA_MCP_COMPACT_INSTRUCTIONS || "").toLowerCase() === "1" ||
197
274
  (process.env.NEOTOMA_MCP_COMPACT_INSTRUCTIONS || "").toLowerCase() === "true",
198
275
  oauthClientId: process.env.NEOTOMA_OAUTH_CLIENT_ID || "",
276
+ /**
277
+ * Additional exact callback URLs an operator trusts to receive an authorization
278
+ * code when the authorize request arrives via a tunnel (non-local Host).
279
+ *
280
+ * Comma-separated list of FULL callback URLs, not origins — `https://app.example.com/auth/callback`
281
+ * authorises that path and nothing else on that host. Entries must be https: unless
282
+ * they point at a loopback host. Empty by default: an operator who sets nothing keeps
283
+ * exactly the behaviour they had before this existed.
284
+ *
285
+ * Validated at load, fail-closed on the WHOLE list: one malformed entry rejects
286
+ * every entry and fails startup. See parseTrustedCallbackUrls above for why.
287
+ */
288
+ oauthTrustedCallbackUrls: parseTrustedCallbackUrls(process.env.NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS),
199
289
  requireKeyForOauth: (process.env.NEOTOMA_REQUIRE_KEY_FOR_OAUTH || "true").toLowerCase() !== "false",
200
290
  // Encryption settings (local backend)
201
291
  encryption: {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -375,6 +375,87 @@ GET /health
375
375
 
376
376
  **No authentication required.**
377
377
 
378
+ ### Usage Statistics
379
+
380
+ Get aggregate usage statistics for the authenticated user. Powers the Neotoma Inspector dashboard summary tiles. Figures are computed on each request from live data — no server-side cache.
381
+
382
+ **Endpoint:** `GET /usage`
383
+
384
+ **Authentication:** Required — Bearer token, AAuth grant, or MCP OAuth token. Guest principals are rejected with `401`.
385
+
386
+ **Query parameters:**
387
+
388
+ | Parameter | Type | Required | Description |
389
+ | --------- | ----------- | -------- | -------------------------------------------------------------------------------------- |
390
+ | `user_id` | UUID string | No | Must match authenticated user if supplied. AAuth-admitted agents cannot override this. |
391
+
392
+ **Request:**
393
+
394
+ ```http
395
+ GET /usage HTTP/1.1
396
+ Authorization: Bearer <NEOTOMA_BEARER_TOKEN>
397
+ ```
398
+
399
+ **curl example:**
400
+
401
+ ```bash
402
+ curl -s \
403
+ -H "Authorization: Bearer $NEOTOMA_BEARER_TOKEN" \
404
+ http://localhost:3080/usage | jq .
405
+ ```
406
+
407
+ **Response:** `200 OK`
408
+
409
+ ```json
410
+ {
411
+ "entities_by_type": {
412
+ "conversation": 214,
413
+ "task": 156,
414
+ "contact": 88
415
+ },
416
+ "total_entities": 500,
417
+ "observations_by_source": {
418
+ "human": 310,
419
+ "llm_summary": 140,
420
+ "sensor": 50
421
+ },
422
+ "total_observations": 500,
423
+ "entities_created_last_7_days": 34,
424
+ "entities_created_last_30_days": 112,
425
+ "entity_types_with_schema": 6,
426
+ "entity_types_total": 10,
427
+ "last_updated": "2026-06-19T09:00:00.000Z"
428
+ }
429
+ ```
430
+
431
+ **Response fields:**
432
+
433
+ | Field | Type | Description |
434
+ | ------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------- |
435
+ | `entities_by_type` | `Record<string, number>` | Active entity counts per type, sorted by count descending. |
436
+ | `total_entities` | `number` | Total active (non-merged) entities. |
437
+ | `observations_by_source` | `Record<string, number>` | Observation counts per `observation_source`; unsourced rows appear as `"unclassified"`. |
438
+ | `total_observations` | `number` | Total observations. |
439
+ | `entities_created_last_7_days` | `number` | Entities created within the last 7 days. |
440
+ | `entities_created_last_30_days` | `number` | Entities created within the last 30 days. |
441
+ | `entity_types_with_schema` | `number` | Distinct entity types present in the data that also have an active registered schema. |
442
+ | `entity_types_total` | `number` | Total distinct entity types present across all active entities. |
443
+ | `last_updated` | `string` (ISO-8601) | Timestamp when these aggregates were computed (reflects request time, not a cache watermark). |
444
+
445
+ **Error responses:**
446
+
447
+ | Status | `error_code` | Cause |
448
+ | ------ | ----------------- | -------------------------------------------------------- |
449
+ | `401` | `AUTH_REQUIRED` | Missing or invalid token; guest principal. |
450
+ | `403` | `FORBIDDEN` | `user_id` query param does not match authenticated user. |
451
+ | `500` | `DB_QUERY_FAILED` | Database query failure. |
452
+
453
+ **OpenAPI:** `operationId: getUsage` — see `openapi.yaml` and `src/shared/openapi_types.ts` (`UsageStats` schema).
454
+
455
+ **CLI equivalent:** `neotoma request --operation getUsage`
456
+
457
+ **See also:** [`docs/subsystems/usage_digest.md`](../subsystems/usage_digest.md#usage-http-api-endpoint) for the full endpoint reference including the distinction between `/usage` HTTP aggregates and `usage_digest` telemetry entities.
458
+
378
459
  ### OpenAPI Specification
379
460
 
380
461
  Get OpenAPI spec for API documentation.
@@ -118,6 +118,51 @@ npm run type-check
118
118
  npm run lint
119
119
  ```
120
120
 
121
+ `npm test` runs the whole Vitest suite, which is wider than the CI baseline lane
122
+ (that lane runs `npm run test:unit`). The extra lanes — `tests/integration`,
123
+ `tests/fixtures` and `inspector/src` — reach build artifacts a plain `npm ci`
124
+ does not produce: the built Inspector SPA at `dist/inspector`, the Inspector's
125
+ own `inspector/node_modules`, and the compiled `packages/cursor-hooks/dist`.
126
+
127
+ ### Local prerequisites and the skip policy
128
+
129
+ On a fresh clone those artifacts are absent, and the suites that need them
130
+ **skip with a named reason** rather than fail. The reason is printed at the top
131
+ of the run:
132
+
133
+ ```
134
+ [neotoma] SKIP inspector-shell suites — missing prerequisite: built inspector assets — npm run build:inspector (or npm run test:setup)
135
+ ```
136
+
137
+ So `npm test` and the pre-commit hook both pass on a fresh clone with no extra
138
+ setup, and any failure you see is attributable to your own change. This is
139
+ deliberate: a failure caused by a missing local build step is indistinguishable
140
+ from a real regression, and that ambiguity is what drove contributors to
141
+ `--no-verify`.
142
+
143
+ To actually **run** the skipped suites — do this before trusting a green run on
144
+ a change that touches the Inspector or the cursor hooks — install and build
145
+ their prerequisites once per checkout:
146
+
147
+ ```bash
148
+ npm run test:setup
149
+ ```
150
+
151
+ Skips are opt-out by construction: the moment the artifact exists the suite runs
152
+ again, with no flag to remember.
153
+
154
+ ### If `dist/` is missing
155
+
156
+ `npm test` builds the server first via `pretest`, so this only bites when you
157
+ invoke Vitest directly. A raw `npx vitest run` on an unbuilt checkout stops
158
+ immediately with:
159
+
160
+ ```
161
+ [neotoma] dist/ is missing. Run `npm test` (pretest builds server) or `npm run build:server` before `npx vitest run`.
162
+ ```
163
+
164
+ rather than emitting hundreds of unrelated failures across `tests/cli/**`.
165
+
121
166
  ## Feature guides
122
167
 
123
168
  - [Plans](plans_guide.md) — store engineering plans as entities, with optional disk mirror.
@@ -8,27 +8,27 @@ Canonical Cursor wiring, screenshots, and transport choice live in **[`mcp_curso
8
8
 
9
9
  Stable **`mcp.json` `command` paths** stay at the repo-root `scripts/` filenames below (no subdirectory indirection). Optional relocation under e.g. `scripts/mcp_launchers/` with thin forwarders was deferred to avoid churn for operators who hardcode absolute paths. Shared behavior lives in **`scripts/lib/neotoma_mcp_source_env.sh`** (`.env.dev` → `.env` → `.env.development`, first file found) and **`scripts/lib/neotoma_mcp_resolve_downstream_url.sh`** (port-file + TCP probe for `MCP_PROXY_DOWNSTREAM_URL`, used by both HTTP proxy shims).
10
10
 
11
- | Script | Transport | Signing / worker | Reload | Default downstream / entry | Env files (via lib) |
12
- |--------|-----------|------------------|--------|------------------------------|---------------------|
13
- | `run_neotoma_mcp_stdio.sh` | stdio → in-process MCP | None (`dist/index.js` or `tsx src/index.ts`) | Manual reconnect after code change | n/a | `.env.dev`, `.env`, `.env.development` |
14
- | `run_neotoma_mcp_stdio_dev_watch.sh` | stdio → `tsx watch src/index.ts` | None | `tsx watch` (stdout risk; not for installed MCP) | n/a | same |
15
- | `run_neotoma_mcp_stdio_prod.sh` | stdio → in-process MCP | None; **`NEOTOMA_ENV=production`** | Manual | n/a | same |
16
- | `run_neotoma_mcp_stdio_prod_watch.sh` | stdio → plain `tsx src/index.ts` | None; prod env | Manual (comment warns: no `watch` on stdio) | n/a | same |
17
- | `run_neotoma_mcp_stdio_dev_shim.sh` | stdio → **`mcp_dev_shim`** | Worker default in-process MCP | Shim restarts worker on file change | n/a | same |
18
- | `run_neotoma_mcp_signed_stdio_dev_shim.sh` | stdio → shim → **HTTP `/mcp`** | **`mcp proxy --aauth`** (AAuth) | Shim + worker reload | `http://127.0.0.1:3080/mcp` unless port file / env | same |
19
- | `run_neotoma_mcp_unsigned_stdio_dev_shim.sh` | stdio → **`mcp proxy`** (no forced AAuth) | `neotoma mcp proxy` | No shim watch (restart MCP to pick up code) | `http://127.0.0.1:3080/mcp` unless port file / env | same |
20
- | `run_neotoma_mcp_unsigned_stdio_proxy.sh` | same as unsigned shim | Deprecated forwarder: **`exec`** unsigned shim | same | same | n/a |
11
+ | Script | Transport | Signing / worker | Reload | Default downstream / entry | Env files (via lib) |
12
+ | -------------------------------------------- | ----------------------------------------- | ---------------------------------------------- | ------------------------------------------------ | -------------------------------------------------- | -------------------------------------- |
13
+ | `run_neotoma_mcp_stdio.sh` | stdio → in-process MCP | None (`dist/index.js` or `tsx src/index.ts`) | Manual reconnect after code change | n/a | `.env.dev`, `.env`, `.env.development` |
14
+ | `run_neotoma_mcp_stdio_dev_watch.sh` | stdio → `tsx watch src/index.ts` | None | `tsx watch` (stdout risk; not for installed MCP) | n/a | same |
15
+ | `run_neotoma_mcp_stdio_prod.sh` | stdio → in-process MCP | None; **`NEOTOMA_ENV=production`** | Manual | n/a | same |
16
+ | `run_neotoma_mcp_stdio_prod_watch.sh` | stdio → plain `tsx src/index.ts` | None; prod env | Manual (comment warns: no `watch` on stdio) | n/a | same |
17
+ | `run_neotoma_mcp_stdio_dev_shim.sh` | stdio → **`mcp_dev_shim`** | Worker default in-process MCP | Shim restarts worker on file change | n/a | same |
18
+ | `run_neotoma_mcp_signed_stdio_dev_shim.sh` | stdio → shim → **HTTP `/mcp`** | **`mcp proxy --aauth`** (AAuth) | Shim + worker reload | `http://127.0.0.1:3080/mcp` unless port file / env | same |
19
+ | `run_neotoma_mcp_unsigned_stdio_dev_shim.sh` | stdio → **`mcp proxy`** (no forced AAuth) | `neotoma mcp proxy` | No shim watch (restart MCP to pick up code) | `http://127.0.0.1:3080/mcp` unless port file / env | same |
20
+ | `run_neotoma_mcp_unsigned_stdio_proxy.sh` | same as unsigned shim | Deprecated forwarder: **`exec`** unsigned shim | same | same | n/a |
21
21
 
22
22
  ## Downstream URL and signing
23
23
 
24
- | Variable | Default / notes |
25
- |----------|-----------------|
26
- | `MCP_PROXY_DOWNSTREAM_URL` | `http://127.0.0.1:3080/mcp` when unset (override per environment). |
27
- | `MCP_PROXY_AAUTH` | When truthy, load `~/.neotoma/aauth/` keys and sign requests (`neotoma mcp proxy --aauth`). |
28
- | `MCP_PROXY_CLIENT_NAME`, `MCP_PROXY_CLIENT_VERSION`, `MCP_PROXY_AGENT_LABEL` | Self-report for `clientInfo` injection. |
29
- | `MCP_PROXY_BEARER_TOKEN`, `MCP_PROXY_CONNECTION_ID` | Optional auth headers. |
30
- | `MCP_PROXY_SESSION_PREFLIGHT`, `MCP_PROXY_SESSION_PREFLIGHT_BASE`, `MCP_PROXY_FAIL_CLOSED` | Trust / preflight behavior. See `src/cli/mcp_proxy.ts` and `src/proxy/mcp_stdio_proxy.ts`. |
31
- | `NEOTOMA_AAUTH_AUTHORITY_OVERRIDE` | Canonical host for signing when downstream uses `127.0.0.1` (often `localhost:<port>`). The signed **stdio shim** script can derive this from `MCP_PROXY_DOWNSTREAM_URL` when unset. |
24
+ | Variable | Default / notes |
25
+ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
26
+ | `MCP_PROXY_DOWNSTREAM_URL` | `http://127.0.0.1:3080/mcp` when unset (override per environment). |
27
+ | `MCP_PROXY_AAUTH` | When truthy, load `~/.neotoma/aauth/` keys and sign requests (`neotoma mcp proxy --aauth`). |
28
+ | `MCP_PROXY_CLIENT_NAME`, `MCP_PROXY_CLIENT_VERSION`, `MCP_PROXY_AGENT_LABEL` | Self-report for `clientInfo` injection. |
29
+ | `MCP_PROXY_BEARER_TOKEN`, `MCP_PROXY_CONNECTION_ID` | Optional auth headers. |
30
+ | `MCP_PROXY_SESSION_PREFLIGHT`, `MCP_PROXY_SESSION_PREFLIGHT_BASE`, `MCP_PROXY_FAIL_CLOSED` | Trust / preflight behavior. See `src/cli/mcp_proxy.ts` and `src/proxy/mcp_stdio_proxy.ts`. |
31
+ | `NEOTOMA_AAUTH_AUTHORITY_OVERRIDE` | Canonical host for signing when downstream uses `127.0.0.1` (often `localhost:<port>`). The signed **stdio shim** script can derive this from `MCP_PROXY_DOWNSTREAM_URL` when unset. |
32
32
 
33
33
  > **Operator security note:** Hosted and operator-managed deployments should set **`MCP_PROXY_FAIL_CLOSED=1`** (or the equivalent `failClosed: true` proxy option) whenever AAuth signing is required. This prevents the proxy from forwarding unsigned downstream requests if signing or session preflight fails.
34
34
 
@@ -57,11 +57,11 @@ When **`NEOTOMA_MCP_USE_LOCAL_PORT_FILE=1`** (or `true`) is set in **`mcp.json`
57
57
 
58
58
  **Verification:** on MCP spawn, stderr should show `[neotoma-mcp-signed-shim]` or `[neotoma-mcp-unsigned-stdio-dev-shim]` for port-file resolution lines, then `[neotoma-mcp-proxy] Starting proxy: downstream=…`.
59
59
 
60
- | Variable | Purpose |
61
- |----------|---------|
62
- | `NEOTOMA_MCP_USE_LOCAL_PORT_FILE` | `1` / `true` → resolve downstream URL from port files + TCP probe. |
60
+ | Variable | Purpose |
61
+ | ------------------------------------- | ------------------------------------------------------------------------------- |
62
+ | `NEOTOMA_MCP_USE_LOCAL_PORT_FILE` | `1` / `true` → resolve downstream URL from port files + TCP probe. |
63
63
  | `NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE` | `dev` or `prod` — which port file(s) to read (preset A sets this per MCP slot). |
64
- | `NEOTOMA_MCP_PORT_PROBE_MS` | TCP probe timeout in ms (default `1200`, max `5000`). |
64
+ | `NEOTOMA_MCP_PORT_PROBE_MS` | TCP probe timeout in ms (default `1200`, max `5000`). |
65
65
 
66
66
  **CLI parity:** When the same env vars are set in the shell (not only in `mcp.json`), the **`neotoma` CLI** `resolveBaseUrl()` path uses the same profile rules and TCP probe after session port env and before `config.json` `base_url`, returning `http://localhost:<port>` so AAuth authority matches the API (for example `neotoma inspector admin unlock`). Profile follows **`NEOTOMA_MCP_LOCAL_HTTP_PORT_PROFILE`** if set, else **`NEOTOMA_ENV`**. Project root: `NEOTOMA_PROJECT_ROOT`, then `project_root` / `repo_root` in `~/.config/neotoma/config.json`, else `cwd`.
67
67
 
@@ -75,15 +75,35 @@ Streamable HTTP MCP keeps each session in **process memory** (`mcpTransports` in
75
75
 
76
76
  As of neotoma#1923, an unknown/expired session returns **404** (not 503), per the [MCP Streamable HTTP session management spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#session-management) — this lets spec-compliant clients auto-reinitialize instead of treating the server as unavailable. Servers predating that fix return 503 for the same condition; `neotoma mcp proxy` recognizes both.
77
77
 
78
- | Symptom | Typical cause | What to do |
79
- |--------|----------------|------------|
80
- | **400** — no session header on a non-`initialize` POST | Proxy never stored an id (failed init, stripped response header) or client skipped `initialize` | Restart the MCP server in the IDE; confirm proxy stderr shows a successful downstream `initialize` and that your reverse proxy **forwards** `mcp-session-id` on responses and requests (custom headers are not hop-by-hop but some templates hide unknown headers). |
81
- | **404** (or, against an older server, **503**) — session header present but unknown on this instance | **Load-balanced replicas** without affinity: `initialize` hit instance A, `tools/call` hit B | Use **sticky sessions** for `POST /mcp` (same as [`/mcp/oauth`](../subsystems/auth.md) guidance), scale MCP to **one** API replica for `/mcp`, or terminate TLS on a single Node process. |
82
- | **404** (or **503**) — same, while using **`neotoma mcp proxy`** | Transient replica drift or API restart after the IDE finished `initialize` | The proxy **replays `initialize` to downstream once** (without emitting a second `initialize` on stdout), captures a fresh `mcp-session-id`, and **retries the failing RPC once**. If the second attempt still fails, fix infra (sticky sessions / single `/mcp` worker) or restart the MCP client. |
83
- | **404** / **503** / **400** after deploy or restart | In-memory map cleared | Restart the MCP client once so `initialize` runs again. |
78
+ | Symptom | Typical cause | What to do |
79
+ | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
80
+ | **400** — no session header on a non-`initialize` POST | Proxy never stored an id (failed init, stripped response header) or client skipped `initialize` | Restart the MCP server in the IDE; confirm proxy stderr shows a successful downstream `initialize` and that your reverse proxy **forwards** `mcp-session-id` on responses and requests (custom headers are not hop-by-hop but some templates hide unknown headers). |
81
+ | **404** (or, against an older server, **503**) — session header present but unknown on this instance | **Load-balanced replicas** without affinity: `initialize` hit instance A, `tools/call` hit B | Use **sticky sessions** for `POST /mcp` (same as [`/mcp/oauth`](../subsystems/auth.md) guidance), scale MCP to **one** API replica for `/mcp`, or terminate TLS on a single Node process. |
82
+ | **404** (or **503**) — same, while using **`neotoma mcp proxy`** | Transient replica drift or API restart after the IDE finished `initialize` | The proxy **replays `initialize` to downstream once** (without emitting a second `initialize` on stdout), captures a fresh `mcp-session-id`, and **retries the failing RPC once**. If the second attempt still fails, fix infra (sticky sessions / single `/mcp` worker) or restart the MCP client. |
83
+ | **404** / **503** / **400** after deploy or restart | In-memory map cleared | Restart the MCP client once so `initialize` runs again. |
84
+ | `handshake_unreachable` — the first `initialize` never reached a server (CONNECT_TIMEOUT, ECONNREFUSED, socket closed) | Backend down, restarting, or the wrong `--downstream-url` | The proxy already retried with backoff (neotoma#2321) and the backend stayed unreachable for the whole window. Check `/health` and the downstream URL, then restart the MCP client once the backend answers. |
85
+ | `handshake_server_error` — the first `initialize` reached the server, which failed internally (5xx, or `-32603` through a 200) | Substrate crash surfacing through the handshake (e.g. neotoma#2316's "DB request aborted by caller") | Also already retried. The server is broken rather than refusing you: check server logs and `/health`, redeploy if needed, then restart the MCP client. |
86
+ | `handshake_auth_rejected` — the first `initialize` was refused for authentication (401) | Invalid/expired bearer, encryption-mode token required, unauthenticated POST, or invalid `X-Connection-Id` | **Not retried, deliberately** — a replay cannot fix a credential. Fix the credential: re-run `neotoma auth mcp-token`, or remove `Authorization` / `X-Connection-Id` from `mcp.json` and reconnect. |
87
+ | Tools appear missing / the agent answers about the graph "from silence" | An exhausted handshake leaves a session with no tools | An exhausted handshake is a **connection failure, not a missing tool** — its error says so explicitly (`NOT a missing tool`). Check proxy stderr for `initialize exhausted` before concluding a tool does not exist. |
84
88
 
85
89
  When debugging, read the proxy line `Downstream error status=… body=…` (stderr): the JSON body includes the real `message` from Neotoma or the MCP SDK.
86
90
 
91
+ ### Handshake retry (neotoma#2321)
92
+
93
+ Session-loss recovery cannot help an `initialize` that never succeeded — there is no session to recover and no cached handshake to replay — so a failed handshake used to be permanent for the life of the client. Every session opened during an outage stayed dark long after the backend recovered, while sessions already established were fine.
94
+
95
+ The proxy now retries the handshake, but only on a failure a replay can fix: a transport failure that never reached a server, or a server that answered with its own internal error. An `initialize` refused for **authentication is never retried**, because retrying would hammer a server that is correctly rejecting a credential and mask the real cause.
96
+
97
+ Retries are bounded twice over: by `NEOTOMA_MCP_PROXY_MAX_ATTEMPTS` (default 4) and by a wall-clock budget, `NEOTOMA_MCP_PROXY_HANDSHAKE_BUDGET_MS` (default 45s). The wall-clock bound matters because the proxy emits nothing until it resolves, so the client sits blocked on its own `initialize` timeout — 60s by default in the MCP SDK — for the whole retry window. Overrunning that would turn a recoverable handshake into a client-side abort.
98
+
99
+ A recovered handshake looks like this on stderr — note the client never sees the intermediate failures, only the successful `initialize`:
100
+
101
+ ```
102
+ [neotoma-mcp-proxy] initialize retry attempt=1/4 (retryable_transport): fetch failed cause=ECONNREFUSED — retrying
103
+ [neotoma-mcp-proxy] initialize retry attempt=2/4 (retryable_server): downstream status=500 — retrying
104
+ [neotoma-mcp-proxy] initialize recovered after 3 attempts
105
+ ```
106
+
87
107
  ## Related
88
108
 
89
109
  - [`mcp_cursor_setup.md`](../mcp_cursor_setup.md) — Cursor `mcp.json` examples, signed shim, port file JSON.
@@ -102,14 +102,20 @@ Keep these aliases for one minor release, update docs and automation to the cano
102
102
 
103
103
  ## Generated files
104
104
 
105
- Some committed files are generated from source and must be kept in sync. Run the generator after making the relevant source change; use `--check` in CI to fail when the file drifts.
105
+ Some committed files are generated from source and must be kept in sync. Run the generator after making the relevant source change. CI policy differs by artifact: a pure tree inventory is re-rendered; contract/registration derivatives still fail on drift.
106
106
 
107
107
  | Script | What it does | When to run | CI usage |
108
108
  |--------|-------------|-------------|----------|
109
- | `generate:test-catalog` | Regenerates `docs/testing/automated_test_catalog.md` from the test tree | After adding, removing, or renaming a test file | `npm run validate:test-catalog` (fails on drift) |
110
- | `validate:test-catalog` | Checks that `docs/testing/automated_test_catalog.md` matches the current test tree | Before every merge | Runs in baseline CI lane |
109
+ | `generate:test-catalog` | Regenerates `docs/testing/automated_test_catalog.md` from the test tree | After adding, removing, or renaming a test file | Baseline lane runs `npm run generate:test-catalog` (render; does not fail on pre-existing staleness) |
110
+ | `validate:test-catalog` | Checks that `docs/testing/automated_test_catalog.md` matches the current test tree | Locally, before opening a PR | Advisory. Not used in CI — the baseline lane runs `generate:test-catalog` instead |
111
111
  | `generate:capability-manifest` | Regenerates `src/shared/capability_manifest.json` by walking git `vX.Y.Z` release tags and recording first/last appearance of each MCP tool in `src/tool_definitions.ts` | After adding or removing an MCP tool, or after cutting a new release tag | `npm run validate:capability-manifest` (fails on drift) |
112
- | `validate:capability-manifest` | Checks that `src/shared/capability_manifest.json` matches what the generator would produce | Before every merge | Runs in CI alongside `validate:test-catalog` |
112
+ | `validate:capability-manifest` | Checks that `src/shared/capability_manifest.json` matches what the generator would produce | Before every merge | Runs in CI alongside `generate:test-catalog` |
113
+
114
+ Sibling artifacts that look similar but are intentionally different:
115
+
116
+ - `openapi.yaml` is an **authored** API contract (not generated). Do not treat staleness of a generated catalog as a reason to auto-rewrite it.
117
+ - `src/shared/openapi_types.ts` is derived from authored `openapi.yaml`. CI regenerates then **fails on `git diff`** — drift means the YAML changed without committing types, which is registration/contract drift, not a pure inventory refresh.
118
+ - `src/shared/capability_manifest.json` stays **fail-on-drift** (`validate:capability-manifest`) because a missed regen is tool/release registration drift, not a filename inventory refresh.
113
119
 
114
120
  ## Validation
115
121
 
@@ -27,8 +27,12 @@ Fly config from the repo; do not reintroduce it.
27
27
  git worktree add ~/repos/neotoma-deploy origin/main
28
28
  cd ~/repos/neotoma-deploy
29
29
 
30
+ # ALWAYS check drift first. This is not optional — see rule 0 below.
31
+ scripts/check_fly_config_drift.sh --app "$CLIENT_FLY_APP" -c fly.operator.toml
32
+
30
33
  flyctl deploy \
31
34
  --app "$CLIENT_FLY_APP" \
35
+ -c fly.operator.toml \
32
36
  --build-arg VITE_NEOTOMA_SANDBOX_UI="" \
33
37
  --build-arg VITE_PUBLIC_BASE_PATH="/" \
34
38
  --no-cache
@@ -36,6 +40,41 @@ flyctl deploy \
36
40
 
37
41
  Everything below explains why each of those pieces is load-bearing.
38
42
 
43
+ ### 0. Check for config drift before every deploy
44
+
45
+ `flyctl deploy` reapplies the committed `[[vm]]` and `[http_service]` blocks
46
+ over the running machine. It does **not** merge, and it prints **no warning
47
+ when it shrinks one**. Whatever the config in your checkout says is what the
48
+ machine becomes.
49
+
50
+ On 2026-09-01 a deploy run from a checkout **139 commits behind main**
51
+ reapplied that checkout's `1gb` / `shared-cpu-1x` guest over an instance
52
+ running `performance` / 2 CPU / 8GB, and dropped its health check. The instance
53
+ went from slow to unreachable for roughly 30 minutes. Throughout, `flyctl
54
+ status` reported `started`, `flyctl machine restart` printed "No health checks
55
+ found", and the `CHECKS` column was empty — nothing anywhere reported a
56
+ problem. It was noticed only because an agent failed to write.
57
+
58
+ Two lessons, both mechanical rather than a matter of care:
59
+
60
+ ```bash
61
+ # 1. Deploy from a checkout that is actually current.
62
+ git -C ~/repos/neotoma-deploy fetch origin && \
63
+ git -C ~/repos/neotoma-deploy status -sb # must show no "behind"
64
+
65
+ # 2. Compare the config against the live machine before applying it.
66
+ scripts/check_fly_config_drift.sh --app "$CLIENT_FLY_APP" -c fly.operator.toml
67
+ ```
68
+
69
+ Exit 0 means the deploy will not resize the machine or drop its checks. Exit 1
70
+ means it would — resolve that before deploying. Exit 2 means the check could
71
+ not run (no `flyctl`, no `jq`, app unreachable); treat that as unknown, never
72
+ as "fine".
73
+
74
+ **A machine scaled up by hand will be reverted by the next deploy.** If an
75
+ instance was resized to address load, that new size has to be written into the
76
+ config it deploys from, or it lasts exactly until the next release.
77
+
39
78
  ## The five load-bearing rules
40
79
 
41
80
  ### 1. Deploy from `main`, not a feature branch
@@ -48,11 +87,20 @@ a repeated source of breakage. Deploy from a clean `origin/main` checkout.
48
87
 
49
88
  ### 2. Pass `--app <name>` explicitly — do NOT rely on `-c fly.toml`
50
89
 
51
- The repo's `fly.toml` declares `app = 'neotoma-sandbox'`. A bare `flyctl deploy`
52
- in the repo therefore targets the **sandbox app**, not the client instance.
90
+ `fly.toml` deliberately declares **no** `app` key (#2013), so a bare `flyctl
91
+ deploy` fails with "the config for your app is missing an app name" rather than
92
+ succeeding against whichever app the file happens to name. It previously said
93
+ `app = 'neotoma-sandbox'`, and a deploy meant for a client instance would
94
+ silently retarget the sandbox and report success.
95
+
53
96
  Always pass `--app "$CLIENT_FLY_APP"` explicitly. Read the app name from the
54
97
  operator binding (below), never hardcode it here.
55
98
 
99
+ Note what `--app` does and does not override: it changes the deploy **target**
100
+ only. The `[[vm]]` and `[http_service]` blocks in whichever config is loaded
101
+ still apply to that target — which is why rule 0's drift check matters even
102
+ when the app name is correct.
103
+
56
104
  ### 3. Never use `fly.sandbox.toml` for a client instance
57
105
 
58
106
  `fly.sandbox.toml` is for the public sandbox only. It sets:
@@ -32,7 +32,8 @@ Resolution order for the data directory and variables: a project-local `.env`, t
32
32
  | `NEOTOMA_DB_BACKEND` | DB driver: `sqlite` (synchronous, zero-config) or `libsql` (concurrent — statements run off the event loop via worker-hosted driver for local files, or @libsql/client for remote sqld/Turso, so slow queries can't freeze the server; recommended for hosted/agent-heavy/shared instances) | `sqlite` |
33
33
  | `NEOTOMA_DB_URL` | libsql connection URL (`file:` for embedded local, `http(s)://`/`libsql://` for remote sqld/Turso) | `file:{NEOTOMA_SQLITE_PATH}` |
34
34
  | `NEOTOMA_DB_AUTH_TOKEN` | Auth token for remote libsql connections | unset |
35
- | `NEOTOMA_DB_READER_WORKERS` | Read-only worker connections for the local `libsql` backend (WAL lets them run concurrently with the writer) | `2` |
35
+ | `NEOTOMA_DB_READER_WORKERS` | Read-only worker connections for the local `libsql` backend (WAL lets them run concurrently with the writer). Reads go to the least-loaded reader, so raising this adds capacity rather than just slots | `2` |
36
+ | `NEOTOMA_DB_STATEMENT_TIMEOUT_MS` | Per-statement budget for reads on that reader pool. On expiry the reader is terminated (the synchronous driver cannot be interrupted mid-statement) and respawns on the next read, so one runaway query cannot hold a pool slot indefinitely. Writes and statements inside a transaction are never timed out. `0` disables | `30000` |
36
37
  | `NEOTOMA_RAW_STORAGE_DIR` | Content-addressed source files | `{dataDir}/sources` |
37
38
  | `NEOTOMA_LOGS_DIR` / `NEOTOMA_EVENT_LOG_PATH` | Log directory and event log file | under `{dataDir}/logs` |
38
39
  | `NEOTOMA_HOST_URL` / `NEOTOMA_PUBLIC_BASE_URL` | Public URL of this instance | auto-discovered or unset |
@@ -61,9 +62,51 @@ See [Running the Server](running_the_server.md) for transports and processes.
61
62
  | --- | --- |
62
63
  | `NEOTOMA_REQUIRE_KEY_FOR_OAUTH` | Require a key for OAuth connections |
63
64
  | `NEOTOMA_OAUTH_CLIENT_ID` | MCP OAuth client id (hosted mode) |
65
+ | `NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS` | Comma-separated **exact** callback URLs additionally trusted to receive an authorization code when the authorize request arrives via a tunnel. Empty by default. See below. |
64
66
  | `NEOTOMA_SANDBOX_MODE` | Opt into the public hosted-sandbox profile |
65
67
  | `NEOTOMA_REFUSE_MODE` | `warn` or `enforce` when a no-auth, non-loopback topology is detected |
66
68
 
69
+ ### Trusted OAuth callback URLs
70
+
71
+ When an authorize request reaches a local-backend instance over a tunnel, the `redirect_uri` must be on an allowlist, so an authorization code is never handed to a third-party site. Built in are the `cursor:`/`vscode:`/`app:` schemes, localhost and loopback, this instance's own origin, and the ChatGPT and Claude callbacks.
72
+
73
+ To let a **self-hosted first-party app** complete sign-in against a hosted instance, set its callback URL:
74
+
75
+ ```bash
76
+ NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS=https://app.example.com/auth/callback
77
+ ```
78
+
79
+ Several entries are comma-separated. Rules worth knowing before you set it:
80
+
81
+ - **Exact full URLs, not origins.** The example above authorises `https://app.example.com/auth/callback` and **not** `https://app.example.com/anything-else`. Configure every callback path you need.
82
+ - **https only**, unless the host is loopback. A plaintext `http://` entry to any other host is rejected rather than honoured.
83
+ - **A bad entry rejects the whole list, and the server will not start.** See below.
84
+
85
+ #### What the server compares
86
+
87
+ A redirect is accepted when the request's callback and a configured entry agree on **scheme, host, port and path** — all four. In detail:
88
+
89
+ | Part | How it is compared |
90
+ | --- | --- |
91
+ | Scheme | Must match. `https` does not authorise its `http` twin. |
92
+ | Host | Case-**insensitive**. `APP.EXAMPLE.COM` matches `app.example.com`. |
93
+ | Port | Must match, but a default port is equivalent to none: `https://app.example.com` = `https://app.example.com:443`. |
94
+ | Path | Case-**sensitive**. `/Auth/Callback` does **not** match `/auth/callback`. A trailing slash is insignificant in either direction. Dot segments resolve first, so `/auth/callback/../admin` is compared as `/auth/admin`. An encoded slash (`%2F`) is not a path separator. |
95
+ | Query and fragment | **Ignored on both sides**, so the usual `?code=...&state=...` callback matches. |
96
+ | Userinfo | Any URL carrying `user:password@` is rejected. |
97
+
98
+ If a redirect is refused, the `400` response names the callback URL the server actually compared (scheme, host and path — query and fragment are stripped) and says how many entries are configured. That is usually enough to tell a near miss — a trailing slash, a port, a case difference — from a typo or an unset variable, without needing server logs.
99
+
100
+ #### A malformed entry fails the whole list, loudly
101
+
102
+ If **any** entry cannot be used as a trusted callback URL, the entire list is rejected and **the server fails to start** with an error naming the offending entry by position and value. Entries 1 and 3 do not quietly survive a bad entry 2.
103
+
104
+ This is deliberate. The friendlier alternative — skip the bad entry, honour the rest — makes the allowlist enforce something other than what you wrote, with no signal that it did: the refusal a dropped entry produces looks exactly like an exact-match miss. A startup failure is noisy, but it happens immediately and before any sign-in is served, rather than surfacing weeks later as an unexplained OAuth failure.
105
+
106
+ Entries are rejected when they are unparseable, use a scheme other than `https:`/`http:`, are plaintext `http:` to a non-loopback host, or carry `user:password@` userinfo. To recover, either fix the entry the error names, or unset the variable entirely to fall back to the built-in allowlist.
107
+
108
+ > The variable is `NEOTOMA_OAUTH_TRUSTED_CALLBACK_URLS`. Early planning notes for this feature called it `NEOTOMA_TRUSTED_OAUTH_CALLBACKS`; that name was never implemented and setting it has no effect.
109
+
67
110
  See [Deployment Modes](deployment.md) and [Agent Access Control](agent_access_control.md).
68
111
 
69
112
  ## Encryption