@remnic/server 9.34.0 → 9.35.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/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1406,6 +1406,8 @@ async function startServer(options) {
|
|
|
1406
1406
|
const portSource = cliServerConfig.port !== void 0 ? "options.port" : envServer.port !== void 0 ? "REMNIC_PORT/ENGRAM_PORT" : "server.port";
|
|
1407
1407
|
const parsedServerConfig = parseServerConfig(serverConfig, { portSource });
|
|
1408
1408
|
const config = parseConfig(remnicConfig);
|
|
1409
|
+
initLogger(void 0, config.debug);
|
|
1410
|
+
log2.debug(`debug logging enabled from config (${resolvedConfigPath.source})`);
|
|
1409
1411
|
const orchestrator = new Orchestrator(config);
|
|
1410
1412
|
await orchestrator.initialize();
|
|
1411
1413
|
const service = new EngramAccessService(orchestrator);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/oauth.ts","../src/server-env.ts"],"sourcesContent":["/**\n * @remnic/server\n *\n * Standalone Remnic memory server.\n *\n * Loads config from `remnic.config.json` (or env vars), creates an Orchestrator,\n * and starts the HTTP access server with MCP endpoint — no OpenClaw required.\n *\n * Usage:\n * npx --package @remnic/server remnic-server\n * npx --package @remnic/server remnic-server --config ./my-remnic.json\n * npx --package @remnic/server remnic-server --port 4320\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseConfig, isOpenaiApiKeyDisabled, resolveRemnicConfigRecord, Orchestrator, EngramAccessService, EngramAccessHttpServer, initLogger, log, getAllValidTokens, getAllValidTokensCached, getAllValidTokenEntriesCached, expandTildePath, type PluginConfig, type RemnicAdminControls, type RemnicAdminDashboardStatus, type RemnicAdminModelOption, type RemnicAdminConfigPatch } from \"@remnic/core\";\nimport { probeBetterSqlite3Driver } from \"@remnic/core/runtime/better-sqlite\";\nimport { applyOAuthEnvOverrides, buildOAuthRequestHandler } from \"./oauth.js\";\nimport { envOverrides, readCompatEnv } from \"./server-env.js\";\nexport { envOverrides };\n\n// ── Config loading ──────────────────────────────────────────────────────────\n\nexport interface ServerConfig {\n remnic: Record<string, unknown>;\n server: {\n host?: string;\n port?: unknown;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n /** Max write requests per rolling window before 429 write_rate_limited (issue #1937). */\n writeRateLimitMaxRequests?: number;\n /** Rolling window for the write rate limit, in ms (issue #1937). */\n writeRateLimitWindowMs?: number;\n adminConsoleEnabled?: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken?: boolean;\n readinessOverride?: boolean;\n /** OAuth authorization-server facade for ChatGPT dev-mode apps (parsed by oauth.ts). */\n oauth?: unknown;\n };\n}\n\nfunction parseServerPort(value: unknown, source: string): number {\n const port = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof port !== \"number\" ||\n !Number.isInteger(port) ||\n port < 1 ||\n port > 65535\n ) {\n throw new Error(`Invalid ${source}: expected an integer port from 1 to 65535`);\n }\n return port;\n}\n\nfunction parseOptionalString(value: unknown, source: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected a string`);\n }\n return value;\n}\n\nfunction parseOptionalNonEmptyString(value: unknown, source: string): string | undefined {\n const parsed = parseOptionalString(value, source);\n if (parsed === undefined) return undefined;\n if (parsed.trim() === \"\") {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return parsed;\n}\n\nfunction parseOptionalPositiveInteger(value: unknown, source: string): number | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof parsed !== \"number\" ||\n !Number.isInteger(parsed) ||\n parsed < 1\n ) {\n throw new Error(`Invalid ${source}: expected a positive integer`);\n }\n return parsed;\n}\n\nfunction parseOptionalBoolean(value: unknown, source: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean`);\n}\n\nexport interface ParsedServerConfig {\n host: string;\n port: number;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n writeRateLimitMaxRequests?: number;\n writeRateLimitWindowMs?: number;\n adminConsoleEnabled: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken: boolean;\n readinessOverride: boolean;\n}\n\nexport function parseServerConfig(\n raw: Partial<ServerConfig[\"server\"]>,\n options?: { portSource?: string },\n): ParsedServerConfig {\n return {\n host: parseOptionalNonEmptyString(raw.host, \"server.host\") ?? \"127.0.0.1\",\n port: raw.port === undefined\n ? 4318\n : parseServerPort(raw.port, options?.portSource ?? \"server.port\"),\n authToken: parseOptionalString(raw.authToken, \"server.authToken\"),\n principal: parseOptionalString(raw.principal, \"server.principal\"),\n maxBodyBytes: parseOptionalPositiveInteger(raw.maxBodyBytes, \"server.maxBodyBytes\"),\n writeRateLimitMaxRequests: parseOptionalPositiveInteger(\n raw.writeRateLimitMaxRequests,\n \"server.writeRateLimitMaxRequests\",\n ),\n writeRateLimitWindowMs: parseOptionalPositiveInteger(\n raw.writeRateLimitWindowMs,\n \"server.writeRateLimitWindowMs\",\n ),\n adminConsoleEnabled: parseOptionalBoolean(raw.adminConsoleEnabled, \"server.adminConsoleEnabled\") ?? false,\n adminConsolePublicDir: parseOptionalString(raw.adminConsolePublicDir, \"server.adminConsolePublicDir\"),\n adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, \"server.adminConsolePrefillToken\") ?? false,\n readinessOverride: parseOptionalBoolean(raw.readinessOverride, \"server.readinessOverride\") ?? false,\n };\n}\n\ninterface ResolvedConfigPath {\n path: string;\n explicit: boolean;\n source: string;\n}\n\nfunction resolveUserPath(value: string): string {\n return path.resolve(expandTildePath(value));\n}\n\nfunction resolveConfigPath(cliPath?: string): ResolvedConfigPath {\n if (cliPath) {\n return { path: resolveUserPath(cliPath), explicit: true, source: \"--config\" };\n }\n\n const envPath = readCompatEnv(\"REMNIC_CONFIG_PATH\", \"ENGRAM_CONFIG_PATH\");\n if (envPath) {\n return { path: resolveUserPath(envPath), explicit: true, source: \"REMNIC_CONFIG_PATH/ENGRAM_CONFIG_PATH\" };\n }\n\n const homeDir = process.env.HOME ?? \"~\";\n const candidates = [\n path.join(process.cwd(), \"remnic.config.json\"),\n path.join(process.cwd(), \"engram.config.json\"),\n path.join(homeDir, \".config\", \"remnic\", \"config.json\"),\n path.join(homeDir, \".config\", \"engram\", \"config.json\"),\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return { path: candidate, explicit: false, source: \"auto-discovery\" };\n }\n }\n\n return { path: path.join(homeDir, \".config\", \"remnic\", \"config.json\"), explicit: false, source: \"auto-discovery\" };\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requirePlainConfigBlock(\n raw: Record<string, unknown>,\n key: \"remnic\" | \"engram\" | \"server\",\n configPath: string,\n): Record<string, unknown> | undefined {\n const value = raw[key];\n if (value === undefined) return undefined;\n if (!isPlainRecord(value)) {\n throw new Error(`Invalid config file ${configPath}: ${key} must be a JSON object`);\n }\n return value;\n}\n\nexport function loadConfigFile(configPath: string): ServerConfig {\n const raw = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(raw)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n requirePlainConfigBlock(raw, \"remnic\", configPath);\n requirePlainConfigBlock(raw, \"engram\", configPath);\n const server = requirePlainConfigBlock(raw, \"server\", configPath);\n return {\n remnic: resolveRemnicConfigRecord(raw),\n server: server ?? {},\n };\n}\n\nfunction loadResolvedConfig(resolved: ResolvedConfigPath): ServerConfig {\n if (!fs.existsSync(resolved.path)) {\n if (resolved.explicit) {\n throw new Error(`Config file from ${resolved.source} not found: ${resolved.path}`);\n }\n return { remnic: {}, server: {} };\n }\n\n const stat = fs.statSync(resolved.path);\n if (!stat.isFile()) {\n if (!resolved.explicit) {\n return { remnic: {}, server: {} };\n }\n throw new Error(`Config file from ${resolved.source} is not a regular file: ${resolved.path}`);\n }\n\n return loadConfigFile(resolved.path);\n}\n\nexport function mergeRemnicConfigForServer(\n fileRemnic: Record<string, unknown>,\n envRemnic: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const effectiveEnvRemnic = { ...(envRemnic ?? {}) };\n if (isOpenaiApiKeyDisabled(fileRemnic.openaiApiKey)) {\n // A local/gateway-only deployment can explicitly disable the direct\n // OpenAI client. Preserve that opt-out even when the process has a\n // global OPENAI_API_KEY for unrelated tools.\n delete effectiveEnvRemnic.openaiApiKey;\n }\n return { ...fileRemnic, ...effectiveEnvRemnic };\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst WRITABLE_BOOLEAN_CONFIG_KEYS = new Set([\n \"citationsAutoDetect\",\n \"citationsEnabled\",\n \"embeddingFallbackEnabled\",\n \"enrichmentAutoOnCreate\",\n \"enrichmentEnabled\",\n \"feedbackEnabled\",\n \"hostEmbeddingProviderEnabled\",\n \"localLlmDisableThinking\",\n \"localLlmEnabled\",\n \"localLlmFallback\",\n \"localLlmFastEnabled\",\n \"memoryExtensionsEnabled\",\n \"namespacesEnabled\",\n \"qmdEnabled\",\n \"queryExpansionEnabled\",\n \"recallPlannerEnabled\",\n \"recallPlannerLlmEnabled\",\n \"recallPlannerTelemetryEnabled\",\n \"rerankEnabled\",\n]);\n\nconst WRITABLE_STRING_CONFIG_KEYS = new Set([\n \"embeddingFallbackModel\",\n \"embeddingFallbackProvider\",\n \"fastGatewayAgentId\",\n \"gatewayAgentId\",\n \"localLlmFastModel\",\n \"localLlmModel\",\n \"localLlmUrl\",\n \"model\",\n \"modelSource\",\n \"openaiBaseUrl\",\n \"qmdEmbedModel\",\n \"qmdGenerateModel\",\n \"qmdRerankModel\",\n \"recallPlannerModel\",\n]);\n\nfunction isWritableConfigKey(key: string): boolean {\n return WRITABLE_BOOLEAN_CONFIG_KEYS.has(key) || WRITABLE_STRING_CONFIG_KEYS.has(key);\n}\n\nfunction hasEnv(name: string): boolean {\n const value = process.env[name];\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction fileExists(candidate: string): boolean {\n try {\n return fs.existsSync(expandTildePath(candidate));\n } catch {\n return false;\n }\n}\n\nfunction canWriteConfigPath(configPath: string): boolean {\n try {\n if (fs.existsSync(configPath)) {\n fs.accessSync(configPath, fs.constants.W_OK);\n return true;\n }\n fs.accessSync(path.dirname(configPath), fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction writeConfigFileAtomically(configPath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const tmpPath = path.join(\n path.dirname(configPath),\n `.${path.basename(configPath)}.tmp-${process.pid}-${Date.now()}`,\n );\n let completed = false;\n try {\n fs.writeFileSync(tmpPath, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n mode: 0o600,\n });\n fs.renameSync(tmpPath, configPath);\n try {\n fs.chmodSync(configPath, 0o600);\n } catch {\n // Best effort for platforms/filesystems that do not support chmod.\n }\n completed = true;\n } finally {\n if (!completed) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best effort cleanup for failed writes.\n }\n }\n }\n}\n\nfunction readJsonRecordIfPresent(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(parsed)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n return parsed;\n}\n\nfunction resolveEditableRemnicBlock(root: Record<string, unknown>): Record<string, unknown> {\n if (isPlainRecord(root.remnic)) return root.remnic;\n if (isPlainRecord(root.engram)) return root.engram;\n return root;\n}\n\nfunction normalizePatchValue(key: string, value: unknown): string | boolean | null {\n if (!isWritableConfigKey(key)) {\n throw new Error(`Unsupported admin config key: ${key}`);\n }\n if (value === null) return null;\n if (WRITABLE_BOOLEAN_CONFIG_KEYS.has(key)) {\n if (typeof value === \"boolean\") return value;\n throw new Error(`Invalid ${key}: expected boolean or null`);\n }\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${key}: expected string or null`);\n }\n const normalized = value.trim();\n if (key === \"modelSource\" && normalized !== \"plugin\" && normalized !== \"gateway\") {\n throw new Error(\"Invalid modelSource: expected plugin or gateway\");\n }\n if (\n key === \"embeddingFallbackProvider\" &&\n normalized !== \"auto\" &&\n normalized !== \"openai\" &&\n normalized !== \"local\"\n ) {\n throw new Error(\"Invalid embeddingFallbackProvider: expected auto, openai, or local\");\n }\n return normalized;\n}\n\nfunction publicConfigValues(config: PluginConfig, serverConfig: ParsedServerConfig): Record<string, string | number | boolean | null> {\n return {\n adminConsoleEnabled: serverConfig.adminConsoleEnabled,\n memoryDir: config.memoryDir,\n model: config.model,\n modelSource: config.modelSource,\n gatewayAgentId: config.gatewayAgentId || null,\n fastGatewayAgentId: config.fastGatewayAgentId || null,\n localLlmEnabled: config.localLlmEnabled,\n localLlmUrl: config.localLlmUrl || null,\n localLlmModel: config.localLlmModel || null,\n localLlmFastEnabled: config.localLlmFastEnabled,\n localLlmFastModel: config.localLlmFastModel || null,\n localLlmFallback: config.localLlmFallback,\n localLlmDisableThinking: config.localLlmDisableThinking,\n qmdEnabled: config.qmdEnabled,\n qmdEmbedModel: config.qmdEmbedModel || null,\n qmdRerankModel: config.qmdRerankModel || null,\n qmdGenerateModel: config.qmdGenerateModel || null,\n embeddingFallbackEnabled: config.embeddingFallbackEnabled,\n embeddingFallbackProvider: config.embeddingFallbackProvider,\n embeddingFallbackModel: config.embeddingFallbackModel || null,\n hostEmbeddingProviderEnabled: config.hostEmbeddingProviderEnabled,\n namespacesEnabled: config.namespacesEnabled,\n recallPlannerEnabled: config.recallPlannerEnabled,\n recallPlannerLlmEnabled: config.recallPlannerLlmEnabled,\n recallPlannerModel: config.recallPlannerModel || null,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n queryExpansionEnabled: config.queryExpansionEnabled,\n rerankEnabled: config.rerankEnabled,\n feedbackEnabled: config.feedbackEnabled,\n memoryExtensionsEnabled: config.memoryExtensionsEnabled,\n enrichmentEnabled: config.enrichmentEnabled,\n enrichmentAutoOnCreate: config.enrichmentAutoOnCreate,\n };\n}\n\nfunction configuredModels(config: PluginConfig): RemnicAdminModelOption[] {\n const models = new Map<string, RemnicAdminModelOption>();\n const add = (id: string | undefined, provider: string, label: string, enabled: boolean, isDefault = false, source = \"config\") => {\n const normalized = id?.trim();\n if (!normalized) return;\n const existing = models.get(`${provider}:${normalized}`);\n models.set(`${provider}:${normalized}`, {\n id: normalized,\n provider,\n label,\n detected: existing?.detected ?? true,\n enabled: existing?.enabled || enabled,\n default: existing?.default || isDefault,\n source,\n });\n };\n\n add(config.model, \"openai\", config.model, !isOpenaiApiKeyDisabled(config.openaiApiKey), config.modelSource === \"plugin\");\n add(config.gatewayAgentId, \"gateway\", config.gatewayAgentId, config.modelSource === \"gateway\", config.modelSource === \"gateway\");\n add(config.fastGatewayAgentId, \"gateway\", config.fastGatewayAgentId, config.modelSource === \"gateway\");\n add(config.localLlmModel, \"local\", config.localLlmModel, config.localLlmEnabled, config.localLlmEnabled);\n add(config.localLlmFastModel, \"local\", `${config.localLlmFastModel} (fast)`, config.localLlmFastEnabled);\n add(config.embeddingFallbackModel, config.embeddingFallbackProvider, `${config.embeddingFallbackModel} (embedding fallback)`, config.embeddingFallbackEnabled);\n add(config.qmdEmbedModel, \"qmd\", `${config.qmdEmbedModel} (embed)`, config.qmdEnabled);\n add(config.qmdRerankModel, \"qmd\", `${config.qmdRerankModel} (rerank)`, config.qmdEnabled);\n add(config.qmdGenerateModel, \"qmd\", `${config.qmdGenerateModel} (generate)`, config.qmdEnabled);\n add(config.recallPlannerModel, \"planner\", `${config.recallPlannerModel} (planner)`, config.recallPlannerLlmEnabled);\n\n return [...models.values()].sort((a, b) => `${a.provider}:${a.id}`.localeCompare(`${b.provider}:${b.id}`));\n}\n\nfunction isLikelyOllamaEndpoint(endpoint: string): boolean {\n return /(?:ollama|11434)/i.test(endpoint);\n}\n\nasync function detectOllamaModels(baseUrl: string | undefined): Promise<RemnicAdminModelOption[]> {\n const configuredEndpoint = baseUrl?.trim();\n const envEndpoint = process.env.OLLAMA_HOST?.trim();\n const endpoint =\n configuredEndpoint && isLikelyOllamaEndpoint(configuredEndpoint)\n ? configuredEndpoint\n : envEndpoint;\n if (!endpoint) return [];\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 900);\n try {\n const url = new URL(\"/api/tags\", endpoint.endsWith(\"/\") ? endpoint : `${endpoint}/`);\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) return [];\n const payload = await response.json() as { models?: Array<{ name?: unknown; model?: unknown }> };\n return (payload.models ?? [])\n .map((model) => typeof model.name === \"string\" ? model.name : typeof model.model === \"string\" ? model.model : \"\")\n .filter((name) => name.length > 0)\n .map((name) => ({\n id: name,\n label: name,\n provider: \"ollama\",\n detected: true,\n enabled: true,\n source: \"ollama\",\n endpoint,\n }));\n } catch {\n return [];\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction dashboardHarnesses(config: PluginConfig) {\n const openclawDetected = hasEnv(\"OPENCLAW_HOME\") || hasEnv(\"OPENCLAW_WORKSPACE\") || fileExists(\"~/.openclaw\");\n const codexDetected = hasEnv(\"CODEX_HOME\") || fileExists(\"~/.codex/auth.json\") || fileExists(\"~/.codex\");\n return [\n {\n id: \"remnic-http\",\n label: \"Remnic HTTP API\",\n detected: true,\n enabled: true,\n source: \"server\",\n detail: \"MCP and REST access server\",\n },\n {\n id: \"openclaw\",\n label: \"OpenClaw\",\n detected: openclawDetected,\n enabled: config.modelSource === \"gateway\" || config.hostEmbeddingProviderEnabled,\n source: openclawDetected ? \"host\" : \"not detected\",\n detail: \"Gateway and host adapters\",\n },\n {\n id: \"codex\",\n label: \"Codex\",\n detected: codexDetected,\n enabled: config.citationsEnabled || config.citationsAutoDetect,\n source: codexDetected ? \"host\" : \"not detected\",\n detail: \"Citation-aware adapter\",\n },\n {\n id: \"qmd\",\n label: \"QMD Search\",\n detected: Boolean(config.qmdPath) || config.qmdEnabled,\n enabled: config.qmdEnabled,\n source: config.qmdPath ? \"qmdPath\" : \"config\",\n detail: config.qmdSearchStrategy,\n },\n ];\n}\n\nfunction dashboardProviders(config: PluginConfig) {\n const localLlmUrl = config.localLlmUrl || \"\";\n const gatewayIds = [config.gatewayAgentId, config.fastGatewayAgentId].filter(Boolean).join(\" \");\n const openaiBaseUrl = process.env.OPENAI_BASE_URL || \"\";\n const sageRouterDetected =\n hasEnv(\"SAGE_ROUTER_URL\") ||\n hasEnv(\"SAGE_ROUTER_HOST\") ||\n /sage[-_ ]?router/i.test(`${gatewayIds} ${openaiBaseUrl}`);\n const ollamaDetected = hasEnv(\"OLLAMA_HOST\") || /(?:ollama|11434)/i.test(localLlmUrl);\n const localDetected = Boolean(localLlmUrl.trim());\n\n return [\n {\n id: \"openai\",\n label: \"OpenAI\",\n detected: !isOpenaiApiKeyDisabled(config.openaiApiKey),\n enabled: config.modelSource === \"plugin\" && !isOpenaiApiKeyDisabled(config.openaiApiKey),\n source: !isOpenaiApiKeyDisabled(config.openaiApiKey) ? \"config/env\" : \"disabled\",\n detail: config.model,\n },\n {\n id: \"sage-router\",\n label: \"Sage Router\",\n detected: sageRouterDetected,\n enabled: config.modelSource === \"gateway\" && sageRouterDetected,\n source: sageRouterDetected ? \"gateway/env\" : \"not detected\",\n detail: config.gatewayAgentId || config.fastGatewayAgentId || openaiBaseUrl || \"OpenAI-compatible provider router\",\n },\n {\n id: \"ollama\",\n label: \"Ollama\",\n detected: ollamaDetected,\n enabled: config.localLlmEnabled && ollamaDetected,\n source: ollamaDetected ? \"localLlmUrl/OLLAMA_HOST\" : \"not detected\",\n detail: \"Local or cloud-compatible Ollama endpoint\",\n },\n {\n id: \"local-openai-compatible\",\n label: \"Local OpenAI-compatible\",\n detected: localDetected && !ollamaDetected,\n enabled: config.localLlmEnabled && localDetected && !ollamaDetected,\n source: localDetected ? \"localLlmUrl\" : \"not detected\",\n detail: localLlmUrl || \"Local provider endpoint\",\n },\n ];\n}\n\nfunction dashboardFeatures(config: PluginConfig) {\n return [\n [\"localLlmEnabled\", \"Local LLM\", config.localLlmEnabled],\n [\"localLlmFastEnabled\", \"Fast Local Tier\", config.localLlmFastEnabled],\n [\"qmdEnabled\", \"QMD Search\", config.qmdEnabled],\n [\"embeddingFallbackEnabled\", \"Embedding Fallback\", config.embeddingFallbackEnabled],\n [\"hostEmbeddingProviderEnabled\", \"Host Embeddings\", config.hostEmbeddingProviderEnabled],\n [\"namespacesEnabled\", \"Namespaces\", config.namespacesEnabled],\n [\"recallPlannerEnabled\", \"Recall Planner\", config.recallPlannerEnabled],\n [\"recallPlannerLlmEnabled\", \"Planner LLM\", config.recallPlannerLlmEnabled],\n [\"citationsEnabled\", \"Citations\", config.citationsEnabled],\n [\"citationsAutoDetect\", \"Citation Auto-detect\", config.citationsAutoDetect],\n [\"queryExpansionEnabled\", \"Query Expansion\", config.queryExpansionEnabled],\n [\"rerankEnabled\", \"Rerank\", config.rerankEnabled],\n [\"feedbackEnabled\", \"Feedback\", config.feedbackEnabled],\n [\"memoryExtensionsEnabled\", \"Memory Extensions\", config.memoryExtensionsEnabled],\n [\"enrichmentEnabled\", \"Entity Enrichment\", config.enrichmentEnabled],\n ].map(([key, label, enabled]) => ({\n key: String(key),\n label: String(label),\n enabled: enabled === true,\n writable: WRITABLE_BOOLEAN_CONFIG_KEYS.has(String(key)),\n restartRequired: true,\n }));\n}\n\nexport function createAdminControls(\n configPath: string,\n config: PluginConfig,\n serverConfig: ParsedServerConfig,\n): RemnicAdminControls {\n let restartRequired = false;\n let displayConfig = config;\n const status = async (): Promise<RemnicAdminDashboardStatus> => {\n const models = configuredModels(displayConfig);\n const ollamaModels = await detectOllamaModels(displayConfig.localLlmUrl);\n const modelKeys = new Set(models.map((model) => `${model.provider}:${model.id}`));\n for (const model of ollamaModels) {\n if (!modelKeys.has(`${model.provider}:${model.id}`)) models.push(model);\n }\n return {\n config: {\n path: configPath,\n exists: fs.existsSync(configPath),\n writable: canWriteConfigPath(configPath),\n restartRequired,\n values: publicConfigValues(displayConfig, serverConfig),\n },\n harnesses: dashboardHarnesses(displayConfig),\n providers: dashboardProviders(displayConfig),\n models,\n features: dashboardFeatures(displayConfig),\n };\n };\n\n return {\n status,\n update: async (patch: RemnicAdminConfigPatch): Promise<RemnicAdminDashboardStatus> => {\n if (!isPlainRecord(patch)) {\n throw new Error(\"Admin config patch must be an object\");\n }\n const normalizedEntries = Object.entries(patch).map(([key, value]) => [key, normalizePatchValue(key, value)] as const);\n if (normalizedEntries.length === 0) return status();\n\n const raw = readJsonRecordIfPresent(configPath);\n const target = resolveEditableRemnicBlock(raw);\n for (const [key, value] of normalizedEntries) {\n if (value === null) {\n delete target[key];\n if (target !== raw) delete raw[key];\n } else {\n target[key] = value;\n }\n }\n\n const nextDisplayConfig = parseConfig(resolveRemnicConfigRecord(raw));\n\n writeConfigFileAtomically(configPath, raw);\n displayConfig = nextDisplayConfig;\n restartRequired = true;\n return status();\n },\n };\n}\n\ninterface PromiseResolvers<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\ntype PromiseConstructorWithResolvers = PromiseConstructor & {\n withResolvers<T>(): PromiseResolvers<T>;\n};\n\n/**\n * Like `setTimeout` wrapped in a Promise, but respects an `AbortSignal`.\n * Resolves immediately (without throwing) when the signal fires so the\n * caller can check `signal.aborted` and exit cleanly.\n */\nfunction abortableDelay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n const { promise, resolve } = (Promise as PromiseConstructorWithResolvers).withResolvers<void>();\n const timer = setTimeout(resolve, ms);\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return promise.finally(() => signal.removeEventListener(\"abort\", onAbort));\n}\n\nconst STARTUP_WARMUP_TIMEOUT_MS = 20_000;\nconst STARTUP_WARMUP_RETRY_INTERVAL_MS = 30_000;\n\nexport interface StartupReadinessState {\n ready: boolean;\n warmupAttempts: number;\n lastError?: string | null;\n}\n\nexport type StartupReadinessOutcome = \"warmed\" | \"cancelled\" | \"overridden\" | \"search-disabled\";\n\nclass StartupWarmupDegradationError extends Error {\n constructor(code: string) {\n super(`startup search degraded: ${code}`);\n this.name = \"StartupWarmupDegradationError\";\n }\n}\n\nclass StartupSyncPendingError extends Error {\n constructor() {\n super(\"startup search sync is not complete\");\n this.name = \"StartupSyncPendingError\";\n }\n}\n\nexport async function runStartupSearchWarmup(options: {\n signal: AbortSignal;\n isAvailable: () => boolean;\n search: (onDegradation: (code: string) => void) => Promise<unknown>;\n}): Promise<void> {\n let degradationCode: string | undefined;\n await options.search((code) => {\n degradationCode = code;\n });\n if (options.signal.aborted) return;\n if (degradationCode) throw new StartupWarmupDegradationError(degradationCode);\n if (!options.isAvailable()) {\n throw new StartupWarmupDegradationError(\"backend_unavailable\");\n }\n}\n\nexport async function completeStartupReadiness(options: {\n deferredReady: Promise<void>;\n warmup: (signal: AbortSignal) => Promise<unknown>;\n prepareWarmup?: (signal: AbortSignal) => Promise<boolean>;\n state: StartupReadinessState;\n timeoutMs?: number;\n retryIntervalMs?: number;\n override?: boolean;\n skipWarmup?: () => boolean;\n openGate: () => void;\n shutdownSignal?: AbortSignal;\n warn?: (message: string) => void;\n info?: (message: string) => void;\n error?: (message: string) => void;\n}): Promise<StartupReadinessOutcome> {\n const timeoutMs = options.timeoutMs ?? STARTUP_WARMUP_TIMEOUT_MS;\n const retryIntervalMs = options.retryIntervalMs ?? STARTUP_WARMUP_RETRY_INTERVAL_MS;\n const warn = options.warn ?? ((message: string) => log.warn(message));\n const info = options.info ?? ((message: string) => log.info(message));\n const error = options.error ?? ((message: string) => log.error(message));\n\n options.state.ready = false;\n options.state.lastError = null;\n if (options.override) {\n options.openGate();\n options.state.ready = true;\n error(\n \"CRITICAL: emergency readiness override enabled; exposing a cold search backend to traffic\",\n );\n return \"overridden\";\n }\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n\n\n let removeDeferredShutdownListener: () => void = () => undefined;\n const deferredShutdown = new Promise<\"shutdown\">((resolve) => {\n if (options.shutdownSignal?.aborted) {\n resolve(\"shutdown\");\n return;\n }\n const onDeferredShutdown = () => resolve(\"shutdown\");\n options.shutdownSignal?.addEventListener(\"abort\", onDeferredShutdown, { once: true });\n removeDeferredShutdownListener = () =>\n options.shutdownSignal?.removeEventListener(\"abort\", onDeferredShutdown);\n });\n try {\n const deferredOutcome = await Promise.race([\n options.deferredReady.then(() => \"ready\" as const),\n deferredShutdown,\n ]);\n if (deferredOutcome === \"shutdown\") return \"cancelled\";\n } catch (err) {\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n options.state.lastError = err instanceof Error ? err.name : typeof err;\n warn(`Standalone deferred initialization failed; warm-up retries will continue: ${err}`);\n } finally {\n removeDeferredShutdownListener();\n }\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n\n const lifecycleAbort = new AbortController();\n const onShutdown = () => lifecycleAbort.abort(options.shutdownSignal?.reason);\n options.shutdownSignal?.addEventListener(\"abort\", onShutdown, { once: true });\n\n try {\n while (!lifecycleAbort.signal.aborted) {\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n options.state.warmupAttempts += 1;\n const warmupAbort = new AbortController();\n const onLifecycleAbort = () => warmupAbort.abort(lifecycleAbort.signal.reason);\n lifecycleAbort.signal.addEventListener(\"abort\", onLifecycleAbort, { once: true });\n const timeout = (Promise as PromiseConstructorWithResolvers).withResolvers<never>();\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n warmupAbort.abort();\n timeout.reject(new Error(`startup warm-up timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n timer.unref();\n\n try {\n const attempt = async () => {\n if (options.prepareWarmup && !await options.prepareWarmup(warmupAbort.signal)) {\n throw new StartupSyncPendingError();\n }\n return options.warmup(warmupAbort.signal);\n };\n await Promise.race([attempt(), timeout.promise]);\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = null;\n options.openGate();\n options.state.ready = true;\n info(\n `Standalone init gate opened after search warm-up attempt ${options.state.warmupAttempts}`,\n );\n return \"warmed\";\n } catch (err) {\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = timedOut\n ? \"TimeoutError\"\n : err instanceof Error\n ? err.name\n : typeof err;\n warn(\n timedOut\n ? `Standalone startup warm-up attempt ${options.state.warmupAttempts} timed out after ${timeoutMs}ms; retrying in ${retryIntervalMs}ms`\n : `Standalone startup warm-up attempt ${options.state.warmupAttempts} failed (${options.state.lastError}); retrying in ${retryIntervalMs}ms`,\n );\n } finally {\n clearTimeout(timer);\n lifecycleAbort.signal.removeEventListener(\"abort\", onLifecycleAbort);\n }\n\n await abortableDelay(retryIntervalMs, lifecycleAbort.signal);\n }\n return \"cancelled\";\n } finally {\n options.shutdownSignal?.removeEventListener(\"abort\", onShutdown);\n }\n}\n\nasync function cleanupFailedStartup(\n orchestrator: Orchestrator,\n httpServer: EngramAccessHttpServer,\n): Promise<void> {\n try {\n await httpServer.stop();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not stop server: ${err}`);\n }\n\n try {\n await orchestrator.destroy();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not destroy orchestrator: ${err}`);\n }\n}\n\n// ── Server startup ──────────────────────────────────────────────────────────\n\nexport interface ServerResult {\n config: PluginConfig;\n service: EngramAccessService;\n httpServer: EngramAccessHttpServer;\n host: string;\n port: number;\n /** Stop HTTP, cancel startup work, abort deferred init, and destroy the orchestrator. */\n stop: () => Promise<void>;\n /** Cancel any pending startup-sync retry timers. Called automatically on shutdown. */\n cancelStartupSync: () => void;\n /** Abort deferred orchestrator initialization (QMD sync, warmup, cache). */\n abortDeferredInit: () => void;\n}\n\nexport async function startServer(options?: {\n configPath?: string;\n host?: string;\n port?: number;\n authToken?: string;\n}): Promise<ServerResult> {\n initLogger();\n\n // Startup driver-load check (issue #1829): attempt to load the better-sqlite3\n // native binding under THIS process. A wrong-ABI build previously threw inside\n // each projection open, was caught, and returned the same silent null as a\n // missing file — so every memory list fell back to a full-corpus scan with no\n // visible error. Probe once at startup and log LOUDLY. Do not crash: the\n // full-corpus fallback still serves, and the projection-open path records the\n // distinct rate-limited signal + doctor entry on its own.\n const driverProbe = probeBetterSqlite3Driver();\n if (!driverProbe.ok) {\n const detailSuffix = driverProbe.detail ? ` (${driverProbe.detail})` : \"\";\n const abiSuffix = driverProbe.nativeBindingMismatch\n ? \" — the binding was built for a different Node.js ABI; rebuild it (`node scripts/ensure-better-sqlite3.mjs` or `pnpm rebuild better-sqlite3`)\"\n : \"\";\n log.error(\n `better-sqlite3 native driver failed to load under the running process${detailSuffix}${abiSuffix}. SQLite-backed features (memory projection) will fall back to slower full-corpus scans until fixed.`,\n );\n }\n\n const resolvedConfigPath = resolveConfigPath(options?.configPath);\n const fileConfig = loadResolvedConfig(resolvedConfigPath);\n\n const env = envOverrides();\n const { remnic: envRemnic, ...envServer } = env;\n\n // Merge: file < env < cli flags\n const remnicConfig = mergeRemnicConfigForServer(fileConfig.remnic, envRemnic);\n const cliServerConfig: Partial<ServerConfig[\"server\"]> = {};\n if (options?.host !== undefined) cliServerConfig.host = options.host;\n if (options?.port !== undefined) cliServerConfig.port = parseServerPort(options.port, \"options.port\");\n if (options?.authToken !== undefined) cliServerConfig.authToken = options.authToken;\n\n const serverConfig = {\n ...fileConfig.server,\n ...envServer,\n ...cliServerConfig,\n };\n const portSource = cliServerConfig.port !== undefined\n ? \"options.port\"\n : envServer.port !== undefined\n ? \"REMNIC_PORT/ENGRAM_PORT\"\n : \"server.port\";\n const parsedServerConfig = parseServerConfig(serverConfig, { portSource });\n\n const config = parseConfig(remnicConfig);\n const orchestrator = new Orchestrator(config);\n await orchestrator.initialize();\n\n // Start the HTTP server immediately so health checks, MCP handshakes,\n // and liveness probes can connect while deferred init is still running.\n const service = new EngramAccessService(orchestrator);\n const readiness: StartupReadinessState = { ready: false, warmupAttempts: 0, lastError: null };\n\n const authToken = parsedServerConfig.authToken ?? readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\") ?? \"\";\n\n // Connector tokens are loaded dynamically per request via authTokensGetter\n // so that token generate/revoke takes effect without server restart\n if (!authToken && getAllValidTokens().length === 0) {\n log.warn(\"No auth token set — server will reject all requests. Set REMNIC_AUTH_TOKEN, server.authToken in config, or generate tokens with 'remnic token generate'.\");\n }\n // OAuth facade (ChatGPT developer-mode apps): file block < REMNIC_OAUTH_* env.\n // Parsed strictly — invalid values abort startup with a precise message.\n const oauthConfig = applyOAuthEnvOverrides((serverConfig as { oauth?: unknown }).oauth);\n const oauthRequestHandler = buildOAuthRequestHandler(oauthConfig);\n\n\n const httpServer = new EngramAccessHttpServer({\n service,\n host: parsedServerConfig.host,\n port: parsedServerConfig.port,\n authToken: authToken || undefined,\n // Entry-based getter: validation + connector identity from ONE cached\n // snapshot (see tokens.ts). The path policy pins ChatGPT-minted OAuth\n // tokens (connector \"chatgpt\") to the MCP endpoint only — they never\n // authorize REST/admin routes. All other connector tokens keep full\n // access, matching pre-OAuth behavior.\n authTokenEntriesGetter: () => getAllValidTokenEntriesCached(),\n tokenPathPolicy: (connector, pathname) => connector !== \"chatgpt\" || pathname === \"/mcp\",\n readiness: () => readiness,\n principal: parsedServerConfig.principal,\n maxBodyBytes: parsedServerConfig.maxBodyBytes,\n writeRateLimitMaxRequests: parsedServerConfig.writeRateLimitMaxRequests,\n writeRateLimitWindowMs: parsedServerConfig.writeRateLimitWindowMs,\n adminConsoleEnabled: parsedServerConfig.adminConsoleEnabled,\n adminConsolePublicDir: parsedServerConfig.adminConsolePublicDir\n ? path.resolve(expandTildePath(parsedServerConfig.adminConsolePublicDir))\n : undefined,\n adminConsolePrefillToken: parsedServerConfig.adminConsolePrefillToken,\n adminControls: parsedServerConfig.adminConsoleEnabled\n ? createAdminControls(resolvedConfigPath.path, config, parsedServerConfig)\n : undefined,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n emitLegacyTools: config.emitLegacyTools,\n ...(oauthConfig.enabled\n ? {\n externalRequestHandler: oauthRequestHandler,\n resourceMetadataUrl: new URL(\n \"/.well-known/oauth-protected-resource/mcp\",\n oauthConfig.issuerUrl,\n ).href,\n }\n : {}),\n });\n\n let host: string;\n let port: number;\n try {\n ({ host, port } = await httpServer.start());\n } catch (err) {\n await cleanupFailedStartup(orchestrator, httpServer);\n throw err;\n }\n\n // Fire-and-forget: wait for deferred init (QMD probe, collection setup,\n // warmup) then check QMD availability and retry if needed. This does NOT\n // block the server listener — connections are accepted immediately above.\n // An AbortController allows the shutdown handler to cancel pending retries.\n const startupSyncAbort = new AbortController();\n const readinessAbort = new AbortController();\n let startupSyncInFlight: Promise<boolean> | undefined;\n const ensureStartupSync = async (signal: AbortSignal): Promise<boolean> => {\n if (orchestrator.deferredSyncSucceeded) return true;\n if (startupSyncInFlight) return startupSyncInFlight;\n const attempt = orchestrator.startupSearchSync(signal).then((synced) => {\n if (synced) orchestrator.deferredSyncSucceeded = true;\n return synced;\n });\n startupSyncInFlight = attempt;\n try {\n return await attempt;\n } finally {\n if (startupSyncInFlight === attempt) startupSyncInFlight = undefined;\n }\n };\n const readinessTask = completeStartupReadiness({\n deferredReady: orchestrator.deferredReady,\n warmup: (signal) =>\n runStartupSearchWarmup({\n signal,\n isAvailable: () => orchestrator.qmd.isAvailable(),\n search: (onDegradation) =>\n orchestrator.qmd.search(\n \"remnic startup readiness\",\n config.defaultNamespace,\n 1,\n undefined,\n {\n signal,\n onDegradation: (degradation) => onDegradation(degradation.code),\n },\n ),\n }),\n prepareWarmup: ensureStartupSync,\n state: readiness,\n override: parsedServerConfig.readinessOverride,\n skipWarmup: () => orchestrator.qmd.debugStatus() === \"backend=noop\",\n openGate: () => {\n readiness.ready = true;\n },\n shutdownSignal: readinessAbort.signal,\n });\n // Wrap httpServer.stop() so that existing callers also get full lifecycle\n // cleanup: retry timers, deferred init, HTTP listener, and orchestrator.\n const originalStop = httpServer.stop.bind(httpServer);\n let stopPromise: Promise<void> | undefined;\n const stop = async (): Promise<void> => {\n if (stopPromise) return stopPromise;\n stopPromise = (async () => {\n startupSyncAbort.abort();\n readinessAbort.abort();\n orchestrator.abortDeferredInit();\n try {\n await originalStop();\n } finally {\n try {\n await readinessTask;\n } finally {\n await orchestrator.destroy();\n }\n }\n })();\n return stopPromise;\n };\n httpServer.stop = stop;\n\n orchestrator.deferredReady.then(() => {\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync: cancelled before deferred init completed\");\n return;\n }\n\n // Skip retries when search is explicitly disabled via config or when the\n // orchestrator already resolved to a noop backend (e.g. missing collection\n // detected during deferredInitialize). Both cases mean no sync should ever\n // run; scheduling retries would create misleading operational noise and\n // unnecessary background work on every server start.\n if (!config.qmdEnabled || orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync: search disabled or noop backend, skipping retries\");\n return;\n }\n\n // Retry when either: (a) QMD is not available yet (cold-start race), or\n // (b) QMD is available but the deferred init sync step failed silently\n // (e.g., update errors swallowed by backend, throttle skip, transient\n // network failure). Without (b), the daemon permanently serves stale\n // recall after a failed sync despite healthy QMD probe.\n const needsRetry = !orchestrator.qmd.isAvailable() || !orchestrator.deferredSyncSucceeded;\n if (!needsRetry) {\n log.debug(\"QMD startup-sync: deferred init completed successfully, no retries needed\");\n return;\n }\n\n const RETRY_DELAYS_MS = [5_000, 15_000, 30_000, 60_000, 120_000];\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled before retry task started\");\n return;\n }\n (async () => {\n for (const delay of RETRY_DELAYS_MS) {\n await abortableDelay(delay, startupSyncAbort.signal);\n\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled by shutdown\");\n return;\n }\n\n const synced = await ensureStartupSync(startupSyncAbort.signal);\n if (!synced) {\n if (orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync retry: search intentionally disabled; stopping retries\");\n return;\n }\n log.debug(`QMD startup-sync retry: not available yet (next retry in ${RETRY_DELAYS_MS[RETRY_DELAYS_MS.indexOf(delay) + 1] ?? \"n/a\"}ms)`);\n continue;\n }\n\n return; // sync succeeded, stop retrying\n }\n\n log.warn(\"QMD startup-sync retry: exhausted all retries; search index may be stale\");\n })().catch((err: unknown) => {\n log.warn(`QMD startup-sync retry: unexpected error: ${err}`);\n });\n }).catch((err: unknown) => {\n log.warn(`Deferred init error: ${err}`);\n });\n\n return { config, service, httpServer, host, port, stop, cancelStartupSync: () => startupSyncAbort.abort(), abortDeferredInit: () => orchestrator.abortDeferredInit() };\n}\n\n// ── CLI entry point ──────────────────────────────────────────────────────────\n\nconst BOOLEAN_CLI_OPTIONS = new Set([\"help\"]);\nconst VALUE_CLI_OPTIONS = new Set([\"config\", \"host\", \"port\", \"auth-token\"]);\n\nfunction parseCliArgs(argv: string[]): Record<string, string | undefined> {\n const args: Record<string, string | undefined> = {};\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (token === \"-h\") {\n args.help = \"true\";\n continue;\n }\n\n if (token.startsWith(\"--\")) {\n const [key, inlineValue] = token.slice(2).split(/=(.*)/s, 2);\n if (!key) {\n throw new Error(`Invalid option ${token}`);\n }\n\n if (BOOLEAN_CLI_OPTIONS.has(key)) {\n if (inlineValue !== undefined) {\n throw new Error(`Option --${key} does not accept a value`);\n }\n args[key] = \"true\";\n continue;\n }\n\n if (!VALUE_CLI_OPTIONS.has(key)) {\n throw new Error(`Unknown option --${key}`);\n }\n\n const value = inlineValue ?? argv[i + 1];\n if (\n value === undefined ||\n (inlineValue === undefined && value.startsWith(\"--\")) ||\n value.trim() === \"\"\n ) {\n throw new Error(`Missing value for --${key}`);\n }\n\n args[key] = value;\n if (inlineValue === undefined) i++;\n }\n }\n return args;\n}\n\nexport async function cliMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n const args = parseCliArgs(argv);\n\n if (args.help) {\n console.log(`\nremnic-server — Standalone Remnic memory server\n\nUsage:\n remnic-server [options]\n\nOptions:\n --config <path> Path to config file (default: remnic.config.json)\n --host <addr> Bind address (default: 127.0.0.1)\n --port <number> Port number (default: 4318)\n --auth-token <tok> Bearer token for auth (or set REMNIC_AUTH_TOKEN)\n --help Show this help\n\nEnvironment:\n REMNIC_CONFIG_PATH Config file path (ENGRAM_CONFIG_PATH also supported)\n REMNIC_PORT Server port (ENGRAM_PORT also supported)\n REMNIC_HOST Bind address (ENGRAM_HOST also supported)\n REMNIC_AUTH_TOKEN Auth bearer token (ENGRAM_AUTH_TOKEN also supported)\n REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\n Prefill admin UI with REMNIC_AUTH_TOKEN when true\n REMNIC_MEMORY_DIR Override memory directory (ENGRAM_MEMORY_DIR also supported)\n OPENAI_API_KEY OpenAI API key for extraction; ignored when config sets openaiApiKey=false\n`);\n process.exit(0);\n }\n\n const result = await startServer({\n configPath: args.config,\n host: args.host,\n port: args.port === undefined ? undefined : parseServerPort(args.port, \"--port\"),\n authToken: args[\"auth-token\"],\n });\n\n console.log(`Remnic server listening on http://${result.host}:${result.port}`);\n\n // Graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\nReceived ${signal}, shutting down...`);\n await result.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n}\n\n// Auto-run when executed directly\n// Matches direct execution of `node .../remnic-server/dist/index.js` or\n// `node .../remnic-server/src/index.ts`. Package command names are handled by\n// the bin wrappers in ../bin so importing this module cannot start twice.\nif (\n process.argv[1] &&\n /(?:remnic-server|engram-server)[\\\\/](?:dist|src)[\\\\/]index\\.[jt]s$/.test(process.argv[1])\n) {\n cliMain().catch((err) => {\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n}\n","/**\n * OAuth 2.1 authorization-server facade for the standalone Remnic server.\n *\n * Why this exists: ChatGPT developer-mode apps (chatgpt.com) can only talk\n * to remote MCP servers with OAuth, \"No Authentication\", or Mixed auth —\n * there is no static API-key/bearer option in the ChatGPT UI. Remnic's MCP\n * endpoint (`POST /mcp`) is bearer-token protected, so this module lets\n * Remnic act as its OWN authorization server: ChatGPT runs a standard\n * authorization-code + PKCE flow against these endpoints and receives a\n * regular Remnic connector token (connector id `chatgpt`) as the OAuth\n * access token. The existing bearer validation on `/mcp` then works\n * unchanged.\n *\n * Protocol layer: the security-critical endpoints are NOT hand-rolled.\n * `authorizationHandler`, `tokenHandler` (PKCE S256 verification, client\n * authentication, param validation, rate limiting) and the RFC 8414 /\n * RFC 9728 metadata router all come from `@modelcontextprotocol/sdk`.\n * This module contributes only the Remnic-specific pieces:\n *\n * - config parsing (`server.oauth` block + `REMNIC_OAUTH_*` env overrides),\n * - the static single-client store (Remnic pre-registers ChatGPT; the\n * operator pastes the same client id/secret into ChatGPT's app UI),\n * - the pending-approval interaction: `/authorize` renders a page that\n * instructs the operator to run `remnic oauth approve <ref>` locally;\n * the page polls with a per-transaction secret and redirects to the\n * ChatGPT callback once the operator approves,\n * - operator-only pending/approve/deny endpoints (gated by the core\n * bearer authorization passed in from the access server), and\n * - access-token minting into the Remnic token store.\n *\n * Security model (do not weaken):\n * - The approval page NEVER asks for credentials. Approval happens\n * out-of-band via the CLI, which authenticates with the operator\n * bearer token against the local daemon.\n * - `txn` id and `pollSecret` are independent 128-bit random values;\n * the human-readable `ref` shown on the page cannot authorize\n * anything by itself.\n * - Authorization codes are 128-bit, single-use, TTL-capped at 120 s,\n * and bound to client_id + redirect_uri + PKCE challenge + resource.\n * - Redirect URIs are validated by the SDK against the exact\n * pre-registered allowlist (`server.oauth.redirectUris`); an empty\n * allowlist refuses every authorization (setup mode: discovery works,\n * authorization does not).\n * - Secret comparisons go through SHA-256 digests + `timingSafeEqual`,\n * which is constant-time and never throws on length mismatch.\n */\n\nimport { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport express, { type Express, type Request, type Response } from \"express\";\nimport { rateLimit } from \"express-rate-limit\";\nimport { authorizationHandler } from \"@modelcontextprotocol/sdk/server/auth/handlers/authorize.js\";\nimport { tokenHandler } from \"@modelcontextprotocol/sdk/server/auth/handlers/token.js\";\nimport { createOAuthMetadata, mcpAuthMetadataRouter } from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport {\n InvalidGrantError,\n InvalidTokenError,\n ServerError,\n UnsupportedGrantTypeError,\n} from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type { OAuthServerProvider, AuthorizationParams } from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport type { OAuthRegisteredClientsStore } from \"@modelcontextprotocol/sdk/server/auth/clients.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type { OAuthClientInformationFull, OAuthTokens } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { buildTokenEntry, commitTokenEntry, getAllValidTokensCached, log } from \"@remnic/core\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\n/**\n * Token-endpoint client auth methods this facade supports. The SDK's\n * client-auth middleware reads credentials from the request body only,\n * so `client_secret_basic` is intentionally NOT offered: advertising a\n * method the token endpoint would reject is worse than not offering it.\n * ChatGPT supports `client_secret_post` and `none` for predefined\n * clients.\n */\nconst ALLOWED_TOKEN_AUTH_METHODS = [\"client_secret_post\", \"none\"] as const;\nexport type OAuthTokenEndpointAuthMethod = (typeof ALLOWED_TOKEN_AUTH_METHODS)[number];\n\n/** Default TTL for pending authorizations (10 minutes). */\nconst DEFAULT_APPROVAL_TTL_SECONDS = 600;\n\n/** Authorization-code TTL (OAuth 2.1 recommends short single-use codes). */\nconst AUTHORIZATION_CODE_TTL_MS = 120_000;\n\n/** 128 bits of entropy in hex = 32 chars. */\nconst HIGH_ENTROPY_HEX_BYTES = 16;\n\n/** Connector id under which OAuth access tokens are minted. */\nconst CHATGPT_CONNECTOR_ID = \"chatgpt\";\n\n// ── Config schema + parser ──────────────────────────────────────────────────\n\n/**\n * Raw config block as it appears in `server.oauth`. All fields optional at\n * the type level; the parser enforces requirements when `enabled: true`.\n */\nexport interface OAuthConfigInput {\n enabled?: unknown;\n issuerUrl?: unknown;\n clientId?: unknown;\n clientSecret?: unknown;\n tokenEndpointAuthMethod?: unknown;\n redirectUris?: unknown;\n approvalTtlSeconds?: unknown;\n}\n\nexport interface ParsedOAuthConfig {\n enabled: boolean;\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod;\n redirectUris: string[];\n approvalTtlSeconds: number;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Coerce a boolean coming from JSON (any) or env (string). Mirrors the\n * repo's canonical boolean coercion (rules 17, 24): \"true\"/\"1\"/\"yes\"/\"on\"\n * are truthy, \"false\"/\"0\"/\"no\"/\"off\" are falsy. Anything else throws.\n */\nfunction coerceBoolean(value: unknown, source: string): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean (got: ${JSON.stringify(value)})`);\n}\n\nfunction coerceNonEmptyString(value: unknown, source: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return value.trim();\n}\n\nfunction coercePositiveInteger(value: unknown, source: string): number {\n const num = typeof value === \"string\" && value.trim() !== \"\" ? Number(value) : value;\n if (typeof num !== \"number\" || !Number.isInteger(num) || num <= 0) {\n throw new Error(`Invalid ${source}: expected a positive integer (got: ${JSON.stringify(value)})`);\n }\n return num;\n}\n\nfunction coerceAbsoluteHttpsUrl(\n value: unknown,\n source: string,\n opts: { allowHttpForLocalhost: boolean },\n): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`Invalid ${source}: expected a non-empty URL string`);\n }\n const trimmed = value.trim();\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw new Error(`Invalid ${source}: \"${trimmed}\" is not a valid URL`);\n }\n if (parsed.hash || parsed.search) {\n throw new Error(`Invalid ${source}: URL must not include a query string or fragment`);\n }\n const isHttps = parsed.protocol === \"https:\";\n const isLocalhostHttp =\n opts.allowHttpForLocalhost &&\n parsed.protocol === \"http:\" &&\n (parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"localhost\");\n if (!isHttps && !isLocalhostHttp) {\n throw new Error(\n `Invalid ${source}: must be an absolute https:// URL (http:// allowed only for 127.0.0.1/localhost)`,\n );\n }\n return trimmed;\n}\n\nfunction coerceTokenAuthMethod(value: unknown, source: string): OAuthTokenEndpointAuthMethod {\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected one of ${ALLOWED_TOKEN_AUTH_METHODS.join(\", \")}`);\n }\n const trimmed = value.trim();\n const match = ALLOWED_TOKEN_AUTH_METHODS.find((method) => method === trimmed);\n if (!match) {\n throw new Error(\n `Invalid ${source}: \"${trimmed}\" is not allowed. Use one of ${ALLOWED_TOKEN_AUTH_METHODS.join(\", \")}.`,\n );\n }\n return match;\n}\n\nfunction coerceRedirectUris(value: unknown, source: string): string[] {\n if (!Array.isArray(value)) {\n throw new Error(`Invalid ${source}: expected an array of exact redirect URI strings`);\n }\n return value.map((entry, index) =>\n coerceAbsoluteHttpsUrl(entry, `${source}[${index}]`, { allowHttpForLocalhost: true }),\n );\n}\n\nconst DISABLED_OAUTH_CONFIG: ParsedOAuthConfig = {\n enabled: false,\n issuerUrl: \"\",\n clientId: \"\",\n clientSecret: \"\",\n tokenEndpointAuthMethod: \"client_secret_post\",\n redirectUris: [],\n approvalTtlSeconds: DEFAULT_APPROVAL_TTL_SECONDS,\n};\n\n/**\n * Parse the `server.oauth` config block. Throws on invalid input.\n *\n * When disabled (or absent), only `enabled` is consulted — a\n * partially-filled disabled block is legal. When enabled, `issuerUrl`,\n * `clientId`, and (unless auth method is `none`) `clientSecret` are\n * required. `redirectUris` MAY be empty: that is \"setup mode\" — the\n * discovery documents are served so ChatGPT can create the app, but every\n * authorization attempt is refused until the exact per-app callback URL\n * (shown in ChatGPT's app management page) is added to the allowlist.\n */\nexport function parseOAuthConfig(raw: unknown): ParsedOAuthConfig {\n if (raw === undefined) return { ...DISABLED_OAUTH_CONFIG };\n if (!isPlainObject(raw)) {\n throw new Error(\"Invalid server.oauth: expected a JSON object\");\n }\n const input = raw as OAuthConfigInput;\n const enabled = input.enabled === undefined ? false : coerceBoolean(input.enabled, \"server.oauth.enabled\");\n if (!enabled) return { ...DISABLED_OAUTH_CONFIG };\n\n const issuerUrl =\n input.issuerUrl === undefined\n ? \"\"\n : coerceAbsoluteHttpsUrl(input.issuerUrl, \"server.oauth.issuerUrl\", { allowHttpForLocalhost: true });\n const clientId =\n input.clientId === undefined ? \"\" : coerceNonEmptyString(input.clientId, \"server.oauth.clientId\");\n const clientSecret =\n input.clientSecret === undefined ? \"\" : coerceNonEmptyString(input.clientSecret, \"server.oauth.clientSecret\");\n const tokenEndpointAuthMethod =\n input.tokenEndpointAuthMethod === undefined\n ? \"client_secret_post\"\n : coerceTokenAuthMethod(input.tokenEndpointAuthMethod, \"server.oauth.tokenEndpointAuthMethod\");\n const redirectUris =\n input.redirectUris === undefined ? [] : coerceRedirectUris(input.redirectUris, \"server.oauth.redirectUris\");\n const approvalTtlSeconds =\n input.approvalTtlSeconds === undefined\n ? DEFAULT_APPROVAL_TTL_SECONDS\n : coercePositiveInteger(input.approvalTtlSeconds, \"server.oauth.approvalTtlSeconds\");\n\n if (issuerUrl === \"\") {\n throw new Error(\"Invalid server.oauth.issuerUrl: required when enabled (absolute https URL)\");\n }\n if (clientId === \"\") {\n throw new Error(\"Invalid server.oauth.clientId: required when enabled (non-empty string)\");\n }\n if (tokenEndpointAuthMethod !== \"none\" && clientSecret === \"\") {\n throw new Error(\n `Invalid server.oauth.clientSecret: required when enabled and tokenEndpointAuthMethod is \"${tokenEndpointAuthMethod}\"`,\n );\n }\n\n return {\n enabled: true,\n issuerUrl,\n clientId,\n clientSecret,\n tokenEndpointAuthMethod,\n redirectUris,\n approvalTtlSeconds,\n };\n}\n\n// ── Env overrides (REMNIC_OAUTH_*) ───────────────────────────────────────────\n\n/**\n * Build the OAuth config overrides from env vars (REMNIC_OAUTH_*). The\n * merged result goes back through `parseOAuthConfig`, so invalid values\n * throw the same precise messages.\n */\nexport function readOAuthEnvOverrides(): Record<string, unknown> {\n const overrides: Record<string, unknown> = {};\n if (process.env.REMNIC_OAUTH_ENABLED !== undefined) overrides.enabled = process.env.REMNIC_OAUTH_ENABLED;\n if (process.env.REMNIC_OAUTH_ISSUER_URL !== undefined) overrides.issuerUrl = process.env.REMNIC_OAUTH_ISSUER_URL;\n if (process.env.REMNIC_OAUTH_CLIENT_ID !== undefined) overrides.clientId = process.env.REMNIC_OAUTH_CLIENT_ID;\n if (process.env.REMNIC_OAUTH_CLIENT_SECRET !== undefined) {\n overrides.clientSecret = process.env.REMNIC_OAUTH_CLIENT_SECRET;\n }\n if (process.env.REMNIC_OAUTH_TOKEN_AUTH_METHOD !== undefined) {\n overrides.tokenEndpointAuthMethod = process.env.REMNIC_OAUTH_TOKEN_AUTH_METHOD;\n }\n if (process.env.REMNIC_OAUTH_REDIRECT_URIS !== undefined) {\n overrides.redirectUris = process.env.REMNIC_OAUTH_REDIRECT_URIS.split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n }\n if (process.env.REMNIC_OAUTH_APPROVAL_TTL_SECONDS !== undefined) {\n overrides.approvalTtlSeconds = process.env.REMNIC_OAUTH_APPROVAL_TTL_SECONDS;\n }\n return overrides;\n}\n\n/** Merge env overrides over the file block and parse the result. */\nexport function applyOAuthEnvOverrides(fileBlock: unknown): ParsedOAuthConfig {\n // A present-but-non-object `server.oauth` (e.g. `true` or a string) is\n // invalid config, not \"disabled\". Hand it straight to parseOAuthConfig,\n // which throws a precise error, instead of silently coercing it to `{}`\n // and treating OAuth as off (repo rule: reject invalid input).\n if (fileBlock !== undefined && !isPlainObject(fileBlock)) {\n return parseOAuthConfig(fileBlock);\n }\n const overrides = readOAuthEnvOverrides();\n const merged: Record<string, unknown> = isPlainObject(fileBlock) ? { ...fileBlock } : {};\n for (const [key, value] of Object.entries(overrides)) {\n merged[key] = value;\n }\n return parseOAuthConfig(merged);\n}\n\n// ── Small crypto helpers ─────────────────────────────────────────────────────\n\n/**\n * Constant-time string equality via fixed-size SHA-256 digests. Digest\n * comparison never throws on length mismatch and does not leak length\n * or content timing. Hash equality implies content equality.\n */\nfunction timingSafeStringEqual(a: string, b: string): boolean {\n if (typeof a !== \"string\" || typeof b !== \"string\") return false;\n const digestA = createHash(\"sha256\").update(a, \"utf8\").digest();\n const digestB = createHash(\"sha256\").update(b, \"utf8\").digest();\n return timingSafeEqual(digestA, digestB);\n}\n\nfunction randomHex(bytes: number): string {\n return randomBytes(bytes).toString(\"hex\");\n}\n\n/**\n * Human-readable approval ref: 8 random bytes rendered in base36\n * (~62 bits, 13 chars max). Unguessable in practice, but knowing the ref\n * alone never authorizes anything — approval additionally requires the\n * operator bearer token.\n */\nfunction newApprovalRef(): string {\n return BigInt(`0x${randomHex(8)}`).toString(36);\n}\n\n// ── Pending-transaction store ────────────────────────────────────────────────\n\nexport interface PendingTransaction {\n /** High-entropy public id; key for the poll endpoint. */\n txn: string;\n /** Separate high-entropy secret required alongside `txn` to poll. */\n pollSecret: string;\n /** Human-visible approval ref for the CLI. */\n ref: string;\n clientId: string;\n redirectUri: string;\n scopes: string[];\n resource: string | undefined;\n state: string | undefined;\n codeChallenge: string;\n createdAt: number;\n expiresAt: number;\n outcome?: { kind: \"approved\"; code: string } | { kind: \"denied\" };\n}\n\ninterface AuthorizationCodeEntry {\n code: string;\n clientId: string;\n redirectUri: string;\n codeChallenge: string;\n resource: string | undefined;\n expiresAt: number;\n consumed: boolean;\n}\n\nexport class OAuthState {\n private readonly pending = new Map<string, PendingTransaction>();\n private readonly codes = new Map<string, AuthorizationCodeEntry>();\n private readonly config: ParsedOAuthConfig;\n\n constructor(config: ParsedOAuthConfig) {\n this.config = config;\n }\n\n get approvalTtlMs(): number {\n return this.config.approvalTtlSeconds * 1000;\n }\n\n /** Delete expired entries. Run before each lookup. */\n private gc(now: number): void {\n // Expire codes first so the pending check below sees the post-GC code\n // set.\n for (const [key, code] of this.codes) {\n if (code.expiresAt < now) this.codes.delete(key);\n }\n for (const [key, txn] of this.pending) {\n if (txn.expiresAt >= now) continue;\n // Keep an APPROVED transaction alive while its authorization code is\n // still live, even past the pending TTL: an approval made just\n // before the deadline (or under a short approvalTtlSeconds) must\n // still be pollable+redirectable until the code itself expires.\n if (txn.outcome?.kind === \"approved\" && this.codes.has(txn.outcome.code)) continue;\n this.pending.delete(key);\n }\n }\n\n createPending(args: {\n clientId: string;\n redirectUri: string;\n scopes: string[];\n resource: string | undefined;\n state: string | undefined;\n codeChallenge: string;\n }): PendingTransaction {\n const now = Date.now();\n this.gc(now);\n const txn: PendingTransaction = {\n txn: randomHex(HIGH_ENTROPY_HEX_BYTES),\n pollSecret: randomHex(HIGH_ENTROPY_HEX_BYTES),\n ref: newApprovalRef(),\n clientId: args.clientId,\n redirectUri: args.redirectUri,\n scopes: args.scopes,\n resource: args.resource,\n state: args.state,\n codeChallenge: args.codeChallenge,\n createdAt: now,\n expiresAt: now + this.approvalTtlMs,\n };\n this.pending.set(txn.txn, txn);\n return txn;\n }\n\n listPending(): PendingTransaction[] {\n const now = Date.now();\n this.gc(now);\n return Array.from(this.pending.values())\n .filter((txn) => !txn.outcome)\n .sort((a, b) => (a.createdAt === b.createdAt ? a.txn.localeCompare(b.txn) : a.createdAt - b.createdAt));\n }\n\n findByRef(ref: string): PendingTransaction | undefined {\n const now = Date.now();\n this.gc(now);\n for (const txn of this.pending.values()) {\n if (txn.ref === ref) return txn;\n }\n return undefined;\n }\n\n /**\n * Approve a pending transaction by ref. Returns the authorization code.\n * Re-approval of an already-approved txn returns the same code so the\n * operator can recover from a client-side miss. Throws on\n * missing/expired/denied.\n */\n approveByRef(ref: string): { code: string; txn: PendingTransaction } {\n const now = Date.now();\n this.gc(now);\n const txn = this.findByRef(ref);\n if (!txn) throw new Error(\"unknown or expired ref\");\n if (txn.outcome) {\n if (txn.outcome.kind === \"approved\") return { code: txn.outcome.code, txn };\n throw new Error(\"denied\");\n }\n const code = randomHex(HIGH_ENTROPY_HEX_BYTES);\n this.codes.set(code, {\n code,\n clientId: txn.clientId,\n redirectUri: txn.redirectUri,\n codeChallenge: txn.codeChallenge,\n resource: txn.resource,\n expiresAt: now + AUTHORIZATION_CODE_TTL_MS,\n consumed: false,\n });\n txn.outcome = { kind: \"approved\", code };\n return { code, txn };\n }\n\n denyByRef(ref: string): void {\n const now = Date.now();\n this.gc(now);\n const txn = this.findByRef(ref);\n if (!txn) throw new Error(\"unknown or expired ref\");\n if (txn.outcome) {\n if (txn.outcome.kind === \"denied\") return; // idempotent\n throw new Error(\"already approved\");\n }\n txn.outcome = { kind: \"denied\" };\n }\n\n /** Redirect target carrying the code (+ state) back to the client. */\n buildRedirect(txn: PendingTransaction, code: string): string {\n const url = new URL(txn.redirectUri);\n url.searchParams.set(\"code\", code);\n if (txn.state) url.searchParams.set(\"state\", txn.state);\n return url.toString();\n }\n\n /** Poll status; `undefined` on wrong pollSecret. */\n poll(\n txnId: string,\n pollSecret: string,\n ):\n | { status: \"pending\" }\n | { status: \"approved\"; redirect: string }\n | { status: \"denied\" }\n | { status: \"expired\" }\n | undefined {\n const now = Date.now();\n this.gc(now);\n const txn = this.pending.get(txnId);\n if (!txn) return { status: \"expired\" };\n if (!timingSafeStringEqual(txn.pollSecret, pollSecret)) return undefined;\n if (!txn.outcome) {\n if (txn.expiresAt < now) return { status: \"expired\" };\n return { status: \"pending\" };\n }\n if (txn.outcome.kind === \"denied\") return { status: \"denied\" };\n // Approved, but only hand back the redirect while the authorization\n // code is still live. The code TTL (120 s) is shorter than the txn TTL\n // (approvalTtlSeconds, default 600 s), so a slow browser poll could\n // otherwise be redirected with a code that has already expired,\n // failing the token exchange. Once the code is gone (expired or\n // already exchanged), report expired so the client restarts cleanly.\n if (!this.peekCode(txn.outcome.code)) return { status: \"expired\" };\n return { status: \"approved\", redirect: this.buildRedirect(txn, txn.outcome.code) };\n }\n\n /**\n * Read a code entry without consuming it (SDK PKCE verification path).\n * Returns undefined on unknown/expired/consumed.\n */\n peekCode(code: string): AuthorizationCodeEntry | undefined {\n const now = Date.now();\n this.gc(now);\n const entry = this.codes.get(code);\n if (!entry || entry.consumed || entry.expiresAt < now) return undefined;\n return entry;\n }\n\n /**\n * Consume a code after the SDK verified PKCE. Enforces single use and\n * the client/redirect/resource bindings. Returns the entry on success,\n * undefined on any mismatch.\n */\n takeCode(args: {\n code: string;\n clientId: string;\n redirectUri: string | undefined;\n resource: string | undefined;\n }): AuthorizationCodeEntry | undefined {\n const entry = this.peekCode(args.code);\n if (!entry) return undefined;\n // Burn the code before the binding checks: a code that reaches the\n // exchange step is spent regardless of outcome (OAuth 2.1 single use).\n entry.consumed = true;\n if (!timingSafeStringEqual(entry.clientId, args.clientId)) return undefined;\n // The token request MAY omit redirect_uri only when the authorization\n // request used the client's single registered URI; when provided it\n // must match the bound value exactly.\n if (args.redirectUri !== undefined && entry.redirectUri !== args.redirectUri) return undefined;\n if (entry.resource !== undefined) {\n if (args.resource === undefined) return undefined;\n if (entry.resource !== args.resource) return undefined;\n }\n return entry;\n }\n\n /**\n * Un-burn a code consumed by takeCode when the downstream token persist\n * fails (disk full, permissions). Lets the client retry the exchange\n * instead of being forced through a fresh authorize+approve. Only\n * revives an entry still present and within TTL; a binding/PKCE\n * mismatch is NOT revived (that is a genuine client error, and OAuth\n * 2.1 revokes a code presented incorrectly).\n */\n reviveCode(code: string): void {\n const entry = this.codes.get(code);\n if (entry && entry.expiresAt >= Date.now()) {\n entry.consumed = false;\n }\n }\n}\n\n// ── HTML approval page (auto-polling) ────────────────────────────────────────\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nfunction renderApprovalPage(args: { txn: PendingTransaction; issuerUrl: string; command: string }): string {\n const { txn, issuerUrl, command } = args;\n // The page NEVER asks for credentials. It displays the approval ref and\n // CLI instructions, then polls /oauth/authorize/poll with the\n // per-transaction pollSecret. On approval the browser follows the\n // redirect to the client's callback.\n const safeRef = escapeHtml(txn.ref);\n const safeClientId = escapeHtml(txn.clientId);\n const safeRedirect = escapeHtml(txn.redirectUri);\n const safeResource = txn.resource ? escapeHtml(txn.resource) : \"\";\n const safeCommand = escapeHtml(command);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Remnic MCP authorization</title>\n<style>\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; color: #1a1a1a; }\n h1 { font-size: 1.4rem; }\n .ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1.1rem; background: #f4f4f5; padding: 0.25rem 0.5rem; border-radius: 4px; }\n details { margin: 1rem 0; padding: 0.75rem 1rem; background: #fafafa; border: 1px solid #e4e4e7; border-radius: 6px; }\n summary { cursor: pointer; font-weight: 600; }\n code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem; }\n pre { background: #f4f4f5; padding: 0.75rem; border-radius: 4px; overflow-x: auto; }\n #status { margin-top: 1.5rem; padding: 0.75rem 1rem; border-radius: 6px; font-weight: 600; }\n .pending { background: #fef9c3; color: #713f12; }\n .approved { background: #dcfce7; color: #14532d; }\n .denied { background: #fee2e2; color: #7f1d1d; }\n .expired { background: #f3f4f6; color: #374151; }\n</style>\n</head>\n<body>\n<h1>Remnic MCP authorization pending</h1>\n<p>An external application has requested access to your Remnic MCP server.\nOpen a terminal on the Remnic host and run:</p>\n<pre>${safeCommand}</pre>\n<p>Approval ref: <span class=\"ref\">${safeRef}</span></p>\n<details>\n <summary>Request details</summary>\n <ul>\n <li>Client ID: <code>${safeClientId}</code></li>\n <li>Redirect URI: <code>${safeRedirect}</code></li>\n ${safeResource ? `<li>Resource: <code>${safeResource}</code></li>` : \"\"}\n <li>Scopes: <code>${escapeHtml(txn.scopes.join(\" \")) || \"(none requested)\"}</code></li>\n </ul>\n</details>\n<div id=\"status\" class=\"pending\">Waiting for operator approval…</div>\n<script>\n(function () {\n var statusEl = document.getElementById(\"status\");\n var txn = ${JSON.stringify(txn.txn)};\n var secret = ${JSON.stringify(txn.pollSecret)};\n var issuer = ${JSON.stringify(issuerUrl)};\n var done = false;\n function render(result) {\n if (result.status === \"pending\") return;\n done = true;\n if (result.status === \"approved\") {\n statusEl.className = \"approved\";\n statusEl.textContent = \"Approved — redirecting\\\\u2026\";\n window.location.replace(result.redirect);\n } else if (result.status === \"denied\") {\n statusEl.className = \"denied\";\n statusEl.textContent = \"Denied by operator. Return to the calling app to retry or cancel.\";\n } else {\n statusEl.className = \"expired\";\n statusEl.textContent = \"This request expired. Return to the calling app to retry.\";\n }\n }\n function poll() {\n if (done) return;\n fetch(issuer + \"/oauth/authorize/poll\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ txn: txn, pollSecret: secret }),\n })\n .then(function (res) {\n if (!res.ok) throw new Error(\"poll failed\");\n return res.json();\n })\n .then(render)\n .catch(function () { /* transient; next interval retries */ });\n }\n poll();\n setInterval(poll, 3000);\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ── Provider (SDK OAuthServerProvider implementation) ────────────────────────\n\n/**\n * Static single-client provider backed by the pending-approval store and\n * the Remnic token store. All protocol validation (PKCE, client auth,\n * redirect membership, param shapes) happens in the SDK handlers; this\n * provider only implements the Remnic-specific decisions.\n */\nclass RemnicOAuthProvider implements OAuthServerProvider {\n private readonly config: ParsedOAuthConfig;\n private readonly state: OAuthState;\n private readonly staticClient: OAuthClientInformationFull;\n /** Token-store override for tests; production uses the default path. */\n private readonly tokensPath?: string;\n\n constructor(config: ParsedOAuthConfig, state: OAuthState, tokensPath?: string) {\n this.config = config;\n this.state = state;\n this.tokensPath = tokensPath;\n this.staticClient = {\n client_id: config.clientId,\n // With `none`, the client is public: no secret is stored, so the\n // SDK's client-auth middleware will not demand one. Otherwise the\n // secret is REQUIRED on every token request.\n ...(config.tokenEndpointAuthMethod === \"none\" ? {} : { client_secret: config.clientSecret }),\n redirect_uris: config.redirectUris,\n token_endpoint_auth_method: config.tokenEndpointAuthMethod,\n grant_types: [\"authorization_code\"],\n response_types: [\"code\"],\n };\n }\n\n get clientsStore(): OAuthRegisteredClientsStore {\n const client = this.staticClient;\n return {\n getClient: (clientId: string) => (clientId === client.client_id ? client : undefined),\n // No registerClient: dynamic client registration is deliberately\n // NOT supported — the single client is pre-registered via config.\n };\n }\n\n async authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void> {\n const txn = this.state.createPending({\n clientId: client.client_id,\n redirectUri: params.redirectUri,\n scopes: params.scopes ?? [],\n resource: params.resource?.href,\n state: params.state,\n codeChallenge: params.codeChallenge,\n });\n log.info(\n `OAuth authorization pending: ref=${txn.ref} client=${txn.clientId} redirect=${txn.redirectUri} — ` +\n `run \\`remnic oauth approve ${txn.ref}\\` to approve`,\n );\n res\n .status(200)\n .type(\"html\")\n .send(\n renderApprovalPage({\n txn,\n issuerUrl: this.config.issuerUrl.replace(/\\/+$/, \"\"),\n command: `remnic oauth approve ${txn.ref}`,\n }),\n );\n }\n\n async challengeForAuthorizationCode(\n client: OAuthClientInformationFull,\n authorizationCode: string,\n ): Promise<string> {\n const entry = this.state.peekCode(authorizationCode);\n if (!entry || !timingSafeStringEqual(entry.clientId, client.client_id)) {\n throw new InvalidGrantError(\"authorization code is invalid, expired, or already used\");\n }\n return entry.codeChallenge;\n }\n\n async exchangeAuthorizationCode(\n client: OAuthClientInformationFull,\n authorizationCode: string,\n _codeVerifier?: string,\n redirectUri?: string,\n resource?: URL,\n ): Promise<OAuthTokens> {\n const entry = this.state.takeCode({\n code: authorizationCode,\n clientId: client.client_id,\n redirectUri,\n resource: resource?.href,\n });\n if (!entry) {\n throw new InvalidGrantError(\"authorization code is invalid, expired, or already used\");\n }\n // Mint a fresh Remnic connector token as the OAuth access token.\n // commitTokenEntry replaces the previous `chatgpt` entry, so\n // re-linking rotates the token — documented behavior. If the\n // token-store write fails (disk full, permissions), revive the code\n // so the client can retry the exchange instead of being forced\n // through a fresh authorize+approve (AGENTS.md #14 — don't burn old\n // state before the replacement is confirmed).\n const tokenEntry = buildTokenEntry(CHATGPT_CONNECTOR_ID);\n try {\n commitTokenEntry(tokenEntry, this.tokensPath);\n } catch (err) {\n this.state.reviveCode(authorizationCode);\n log.warn(\n `OAuth token persist failed; authorization code revived for retry: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n throw new ServerError(\"failed to persist the issued token; retry the exchange\");\n }\n log.info(`OAuth token issued for connector \"${CHATGPT_CONNECTOR_ID}\" (client=${client.client_id})`);\n return {\n access_token: tokenEntry.token,\n token_type: \"Bearer\",\n };\n }\n\n async exchangeRefreshToken(): Promise<OAuthTokens> {\n throw new UnsupportedGrantTypeError(\n \"refresh_token grant is not supported; re-link the app to rotate the token\",\n );\n }\n\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n const valid = getAllValidTokensCached(this.tokensPath).some((candidate) =>\n timingSafeStringEqual(candidate, token),\n );\n if (!valid) {\n throw new InvalidTokenError(\"unknown or revoked token\");\n }\n return {\n token,\n clientId: this.config.clientId,\n scopes: [],\n };\n }\n}\n\n// ── Express app builder ─────────────────────────────────────────────────────\n\nexport interface OAuthAppBundle {\n app: Express;\n state: OAuthState;\n /** Paths owned by the OAuth facade (exact matches). */\n ownedExactPaths: string[];\n /** Path prefixes owned by the OAuth facade. */\n ownedPathPrefixes: string[];\n}\n\n/**\n * Build the express app hosting all OAuth endpoints.\n *\n * SDK-owned: `/authorize`, `/token`,\n * `/.well-known/oauth-authorization-server`,\n * `/.well-known/oauth-protected-resource/mcp`.\n * Remnic-owned: `/oauth/authorize/poll` (public, secret-gated),\n * `/oauth/pending[...]` (operator-only), plus a bare\n * `/.well-known/oauth-protected-resource` alias for clients that do not\n * implement RFC 9728 path insertion.\n *\n * @param authCtxLookup returns the operator-authorization context the core\n * access server computed for this request; the facade never validates\n * bearer tokens itself.\n */\nexport function buildOAuthApp(\n config: ParsedOAuthConfig,\n authCtxLookup: (req: IncomingMessage) => { authorized: boolean },\n deps?: { tokensPath?: string },\n): OAuthAppBundle {\n const state = new OAuthState(config);\n const provider = new RemnicOAuthProvider(config, state, deps?.tokensPath);\n const issuer = new URL(config.issuerUrl);\n const resourceServerUrl = new URL(\"/mcp\", issuer);\n\n if (config.redirectUris.length === 0) {\n log.warn(\n \"server.oauth.redirectUris is empty — OAuth discovery is live but every authorization will be refused \" +\n \"until the exact callback URL from ChatGPT's app management page is added (setup mode).\",\n );\n }\n\n const app = express();\n app.disable(\"x-powered-by\");\n\n // SDK metadata, truthfully narrowed to what this server actually\n // accepts: a single client-auth method and the authorization_code grant.\n const oauthMetadata = createOAuthMetadata({ provider, issuerUrl: issuer });\n oauthMetadata.token_endpoint_auth_methods_supported = [config.tokenEndpointAuthMethod];\n oauthMetadata.grant_types_supported = [\"authorization_code\"];\n\n // SDK handlers own the protocol-critical endpoints.\n app.use(\"/authorize\", authorizationHandler({ provider }));\n app.use(\"/token\", tokenHandler({ provider }));\n app.use(mcpAuthMetadataRouter({ oauthMetadata, resourceServerUrl, resourceName: \"Remnic\" }));\n\n // Alias: serve the protected-resource document at the bare well-known\n // path too (the SDK mounts only the RFC 9728 path-inserted variant).\n app.get(\"/.well-known/oauth-protected-resource\", (_req: Request, res: Response) => {\n res.status(200).json({\n resource: resourceServerUrl.href,\n authorization_servers: [oauthMetadata.issuer],\n resource_name: \"Remnic\",\n });\n });\n\n // Remnic-specific endpoints below parse JSON bodies.\n app.use(express.json({ limit: \"32kb\" }));\n // Rate limiters for the Remnic-owned endpoints (the SDK already\n // rate-limits /authorize and /token). Two policies:\n // - pollLimiter: generous. The approval page polls every ~3 s for up\n // to approvalTtlSeconds, so a full flow is ~200 polls; 120/min per IP\n // leaves headroom for several concurrent flows without locking the\n // browser out of its own approval.\n // - operatorReadLimiter / operatorDecisionLimiter: operator list vs\n // approve/deny. Both are low-frequency, so tight bounds blunt\n // brute-forcing an approval ref while never impeding a human\n // operator; separate buckets keep listing from starving decisions.\n // Shared limiter shape: deterministic JSON 429 (stable `error` code) so\n // ChatGPT and the CLI get a machine-parseable body, never default HTML.\n const makeLimiter = (max: number) =>\n rateLimit({\n windowMs: 60_000,\n max,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: \"rate_limited\", error_description: \"too many requests; retry later\" },\n });\n const pollLimiter = makeLimiter(120);\n // Read listing gets its own bucket so polling `pending` can never\n // exhaust the decision (approve/deny) capacity below.\n const operatorReadLimiter = makeLimiter(60);\n const operatorDecisionLimiter = makeLimiter(30);\n\n // ── Poll endpoint (public; gated by txn id + pollSecret) ──────────────\n app.post(\"/oauth/authorize/poll\", pollLimiter, (req: Request, res: Response) => {\n const body: unknown = req.body;\n if (\n !isPlainObject(body) ||\n typeof body.txn !== \"string\" ||\n typeof body.pollSecret !== \"string\" ||\n body.txn.length === 0 ||\n body.pollSecret.length === 0\n ) {\n res.status(400).json({ error: \"invalid_request\", error_description: \"txn and pollSecret are required\" });\n return;\n }\n const result = state.poll(body.txn, body.pollSecret);\n if (result === undefined) {\n res.status(401).json({ error: \"invalid_request\", error_description: \"unknown txn or wrong pollSecret\" });\n return;\n }\n res.status(200).json(result);\n });\n\n // ── Operator-only endpoints ────────────────────────────────────────────\n function operatorGuard(req: Request, res: Response): boolean {\n if (authCtxLookup(req).authorized === true) return true;\n res.status(401).json({ error: \"unauthorized\", error_description: \"operator authentication required\" });\n return false;\n }\n\n app.get(\"/oauth/pending\", operatorReadLimiter, (req: Request, res: Response) => {\n if (!operatorGuard(req, res)) return;\n const pending = state.listPending().map((txn) => ({\n ref: txn.ref,\n clientId: txn.clientId,\n redirectUri: txn.redirectUri,\n scopes: txn.scopes,\n resource: txn.resource ?? null,\n createdAt: new Date(txn.createdAt).toISOString(),\n expiresAt: new Date(txn.expiresAt).toISOString(),\n }));\n res.status(200).json({ pending });\n });\n\n function decisionHandler(action: \"approve\" | \"deny\") {\n return (req: Request, res: Response) => {\n if (!operatorGuard(req, res)) return;\n const ref = req.params.ref;\n if (!ref) {\n res.status(400).json({ error: \"invalid_request\", error_description: \"ref is required\" });\n return;\n }\n try {\n if (action === \"approve\") {\n const { txn, code } = state.approveByRef(ref);\n res.status(200).json({ ref, status: \"approved\", redirect: state.buildRedirect(txn, code) });\n } else {\n state.denyByRef(ref);\n res.status(200).json({ ref, status: \"denied\" });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message === \"unknown or expired ref\") {\n res.status(404).json({ error: \"invalid_request\", error_description: message });\n return;\n }\n if (message === \"denied\" || message === \"already approved\") {\n res.status(409).json({ error: \"invalid_request\", error_description: message });\n return;\n }\n log.warn(`OAuth ${action} endpoint unexpected error: ${message}`);\n res.status(500).json({ error: \"server_error\", error_description: \"internal error\" });\n }\n };\n }\n\n app.post(\"/oauth/pending/:ref/approve\", operatorDecisionLimiter, decisionHandler(\"approve\"));\n app.post(\"/oauth/pending/:ref/deny\", operatorDecisionLimiter, decisionHandler(\"deny\"));\n\n return {\n app,\n state,\n ownedExactPaths: [\n \"/authorize\",\n \"/token\",\n \"/.well-known/oauth-authorization-server\",\n \"/.well-known/oauth-protected-resource\",\n \"/.well-known/oauth-protected-resource/mcp\",\n ],\n ownedPathPrefixes: [\"/oauth/\"],\n };\n}\n\n// ── External request handler (mount point) ──────────────────────────────────\n\n/**\n * Adapt the OAuth express app to the core access server's\n * `externalRequestHandler` hook. Ownership is decided by pathname BEFORE\n * delegating to express (deterministic — never inferred from express\n * fallthrough), so non-OAuth requests always continue down the normal\n * core pipeline.\n */\nexport function buildOAuthRequestHandler(\n config: ParsedOAuthConfig,\n deps?: { tokensPath?: string },\n): (req: IncomingMessage, res: ServerResponse, ctx: { authorized: boolean }) => Promise<boolean> {\n if (!config.enabled) {\n return async () => false;\n }\n // Core computes the operator-authorization ctx per request and hands it\n // to the hook; stash it per-request so express handlers can read it\n // without ever re-validating headers themselves.\n const ctxByRequest = new WeakMap<IncomingMessage, { authorized: boolean }>();\n const bundle = buildOAuthApp(config, (req) => ctxByRequest.get(req) ?? { authorized: false }, deps);\n const exactPaths = new Set(bundle.ownedExactPaths);\n\n return (req, res, ctx) => {\n let pathname: string;\n try {\n pathname = new URL(req.url ?? \"/\", \"http://placeholder\").pathname;\n } catch {\n return Promise.resolve(false);\n }\n const owned = exactPaths.has(pathname) || bundle.ownedPathPrefixes.some((prefix) => pathname.startsWith(prefix));\n if (!owned) return Promise.resolve(false);\n\n ctxByRequest.set(req, ctx);\n const { promise, resolve } = Promise.withResolvers<boolean>();\n const finish = () => resolve(true);\n res.on(\"finish\", finish);\n res.on(\"close\", finish);\n // An express app instance is a Node request listener; the third\n // argument runs when no route matched or a handler errored.\n bundle.app(req as Request, res as Response, (err: unknown) => {\n if (!res.headersSent) {\n if (err) {\n log.warn(`OAuth facade error: ${err instanceof Error ? err.message : String(err)}`);\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify({ error: \"server_error\", error_description: \"internal error\" }));\n } else {\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify({ error: \"not_found\", error_description: \"unknown OAuth endpoint\" }));\n }\n }\n resolve(true);\n });\n return promise;\n };\n}\n","/**\n * @remnic/server — environment-sourced server config.\n *\n * Extracted from index.ts (issue #2029): keeps the env → server-config surface\n * in one place and keeps index.ts under its structural size ceiling. Values are\n * returned as raw strings; `parseServerConfig` coerces/validates them.\n */\n\nimport type { ServerConfig } from \"./index.js\";\n\n/**\n * Read an env var by its current `REMNIC_` name, falling back to the legacy\n * `ENGRAM_` name.\n */\nexport function readCompatEnv(primary: string, legacy: string): string | undefined {\n return process.env[primary] ?? process.env[legacy];\n}\n\n/**\n * Collect server-config overrides sourced from environment variables. Merged\n * over file config (file < env < cli) by the server startup path.\n */\nexport function envOverrides(): Partial<ServerConfig[\"server\"]> & { remnic?: Record<string, unknown> } {\n const overrides: Record<string, unknown> = {};\n const remnic: Record<string, unknown> = {};\n\n const port = readCompatEnv(\"REMNIC_PORT\", \"ENGRAM_PORT\");\n const host = readCompatEnv(\"REMNIC_HOST\", \"ENGRAM_HOST\");\n const authToken = readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\");\n const adminConsoleEnabled = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_ENABLED\", \"ENGRAM_ADMIN_CONSOLE_ENABLED\");\n const adminConsolePublicDir = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PUBLIC_DIR\", \"ENGRAM_ADMIN_CONSOLE_PUBLIC_DIR\");\n const adminConsolePrefillToken = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\", \"ENGRAM_ADMIN_CONSOLE_PREFILL_TOKEN\");\n const readinessOverride = process.env.REMNIC_READY_OVERRIDE;\n // issue #2029: size the global write rate limit from env. The standalone\n // server already honors `server.writeRateLimit*` in config; this makes it\n // settable via the launchd/systemd environment without editing config.json.\n const writeRateLimitMaxRequests = readCompatEnv(\n \"REMNIC_WRITE_RATE_LIMIT_MAX_REQUESTS\",\n \"ENGRAM_WRITE_RATE_LIMIT_MAX_REQUESTS\",\n );\n const writeRateLimitWindowMs = readCompatEnv(\n \"REMNIC_WRITE_RATE_LIMIT_WINDOW_MS\",\n \"ENGRAM_WRITE_RATE_LIMIT_WINDOW_MS\",\n );\n if (port) overrides.port = port;\n if (host) overrides.host = host;\n if (authToken) overrides.authToken = authToken;\n if (adminConsoleEnabled) overrides.adminConsoleEnabled = adminConsoleEnabled;\n if (adminConsolePublicDir) overrides.adminConsolePublicDir = adminConsolePublicDir;\n if (adminConsolePrefillToken) overrides.adminConsolePrefillToken = adminConsolePrefillToken;\n if (readinessOverride !== undefined) overrides.readinessOverride = readinessOverride;\n // Use `!== undefined` (not truthiness) so an explicitly-set empty/invalid\n // value still reaches parseServerConfig and is rejected, rather than being\n // silently dropped so file config wins (issue #2029 review).\n if (writeRateLimitMaxRequests !== undefined) overrides.writeRateLimitMaxRequests = writeRateLimitMaxRequests;\n if (writeRateLimitWindowMs !== undefined) overrides.writeRateLimitWindowMs = writeRateLimitWindowMs;\n\n if (process.env.OPENAI_API_KEY) remnic.openaiApiKey = process.env.OPENAI_API_KEY;\n const memoryDir = readCompatEnv(\"REMNIC_MEMORY_DIR\", \"ENGRAM_MEMORY_DIR\");\n if (memoryDir) remnic.memoryDir = memoryDir;\n\n return { ...overrides, ...(Object.keys(remnic).length > 0 ? { remnic } : {}) };\n}\n"],"mappings":"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","names":["log","log"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/oauth.ts","../src/server-env.ts"],"sourcesContent":["/**\n * @remnic/server\n *\n * Standalone Remnic memory server.\n *\n * Loads config from `remnic.config.json` (or env vars), creates an Orchestrator,\n * and starts the HTTP access server with MCP endpoint — no OpenClaw required.\n *\n * Usage:\n * npx --package @remnic/server remnic-server\n * npx --package @remnic/server remnic-server --config ./my-remnic.json\n * npx --package @remnic/server remnic-server --port 4320\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseConfig, isOpenaiApiKeyDisabled, resolveRemnicConfigRecord, Orchestrator, EngramAccessService, EngramAccessHttpServer, initLogger, log, getAllValidTokens, getAllValidTokensCached, getAllValidTokenEntriesCached, expandTildePath, type PluginConfig, type RemnicAdminControls, type RemnicAdminDashboardStatus, type RemnicAdminModelOption, type RemnicAdminConfigPatch } from \"@remnic/core\";\nimport { probeBetterSqlite3Driver } from \"@remnic/core/runtime/better-sqlite\";\nimport { applyOAuthEnvOverrides, buildOAuthRequestHandler } from \"./oauth.js\";\nimport { envOverrides, readCompatEnv } from \"./server-env.js\";\nexport { envOverrides };\n\n// ── Config loading ──────────────────────────────────────────────────────────\n\nexport interface ServerConfig {\n remnic: Record<string, unknown>;\n server: {\n host?: string;\n port?: unknown;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n /** Max write requests per rolling window before 429 write_rate_limited (issue #1937). */\n writeRateLimitMaxRequests?: number;\n /** Rolling window for the write rate limit, in ms (issue #1937). */\n writeRateLimitWindowMs?: number;\n adminConsoleEnabled?: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken?: boolean;\n readinessOverride?: boolean;\n /** OAuth authorization-server facade for ChatGPT dev-mode apps (parsed by oauth.ts). */\n oauth?: unknown;\n };\n}\n\nfunction parseServerPort(value: unknown, source: string): number {\n const port = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof port !== \"number\" ||\n !Number.isInteger(port) ||\n port < 1 ||\n port > 65535\n ) {\n throw new Error(`Invalid ${source}: expected an integer port from 1 to 65535`);\n }\n return port;\n}\n\nfunction parseOptionalString(value: unknown, source: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected a string`);\n }\n return value;\n}\n\nfunction parseOptionalNonEmptyString(value: unknown, source: string): string | undefined {\n const parsed = parseOptionalString(value, source);\n if (parsed === undefined) return undefined;\n if (parsed.trim() === \"\") {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return parsed;\n}\n\nfunction parseOptionalPositiveInteger(value: unknown, source: string): number | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? Number(value.trim()) : value;\n if (\n typeof parsed !== \"number\" ||\n !Number.isInteger(parsed) ||\n parsed < 1\n ) {\n throw new Error(`Invalid ${source}: expected a positive integer`);\n }\n return parsed;\n}\n\nfunction parseOptionalBoolean(value: unknown, source: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean`);\n}\n\nexport interface ParsedServerConfig {\n host: string;\n port: number;\n authToken?: string;\n principal?: string;\n maxBodyBytes?: number;\n writeRateLimitMaxRequests?: number;\n writeRateLimitWindowMs?: number;\n adminConsoleEnabled: boolean;\n adminConsolePublicDir?: string;\n adminConsolePrefillToken: boolean;\n readinessOverride: boolean;\n}\n\nexport function parseServerConfig(\n raw: Partial<ServerConfig[\"server\"]>,\n options?: { portSource?: string },\n): ParsedServerConfig {\n return {\n host: parseOptionalNonEmptyString(raw.host, \"server.host\") ?? \"127.0.0.1\",\n port: raw.port === undefined\n ? 4318\n : parseServerPort(raw.port, options?.portSource ?? \"server.port\"),\n authToken: parseOptionalString(raw.authToken, \"server.authToken\"),\n principal: parseOptionalString(raw.principal, \"server.principal\"),\n maxBodyBytes: parseOptionalPositiveInteger(raw.maxBodyBytes, \"server.maxBodyBytes\"),\n writeRateLimitMaxRequests: parseOptionalPositiveInteger(\n raw.writeRateLimitMaxRequests,\n \"server.writeRateLimitMaxRequests\",\n ),\n writeRateLimitWindowMs: parseOptionalPositiveInteger(\n raw.writeRateLimitWindowMs,\n \"server.writeRateLimitWindowMs\",\n ),\n adminConsoleEnabled: parseOptionalBoolean(raw.adminConsoleEnabled, \"server.adminConsoleEnabled\") ?? false,\n adminConsolePublicDir: parseOptionalString(raw.adminConsolePublicDir, \"server.adminConsolePublicDir\"),\n adminConsolePrefillToken: parseOptionalBoolean(raw.adminConsolePrefillToken, \"server.adminConsolePrefillToken\") ?? false,\n readinessOverride: parseOptionalBoolean(raw.readinessOverride, \"server.readinessOverride\") ?? false,\n };\n}\n\ninterface ResolvedConfigPath {\n path: string;\n explicit: boolean;\n source: string;\n}\n\nfunction resolveUserPath(value: string): string {\n return path.resolve(expandTildePath(value));\n}\n\nfunction resolveConfigPath(cliPath?: string): ResolvedConfigPath {\n if (cliPath) {\n return { path: resolveUserPath(cliPath), explicit: true, source: \"--config\" };\n }\n\n const envPath = readCompatEnv(\"REMNIC_CONFIG_PATH\", \"ENGRAM_CONFIG_PATH\");\n if (envPath) {\n return { path: resolveUserPath(envPath), explicit: true, source: \"REMNIC_CONFIG_PATH/ENGRAM_CONFIG_PATH\" };\n }\n\n const homeDir = process.env.HOME ?? \"~\";\n const candidates = [\n path.join(process.cwd(), \"remnic.config.json\"),\n path.join(process.cwd(), \"engram.config.json\"),\n path.join(homeDir, \".config\", \"remnic\", \"config.json\"),\n path.join(homeDir, \".config\", \"engram\", \"config.json\"),\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return { path: candidate, explicit: false, source: \"auto-discovery\" };\n }\n }\n\n return { path: path.join(homeDir, \".config\", \"remnic\", \"config.json\"), explicit: false, source: \"auto-discovery\" };\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requirePlainConfigBlock(\n raw: Record<string, unknown>,\n key: \"remnic\" | \"engram\" | \"server\",\n configPath: string,\n): Record<string, unknown> | undefined {\n const value = raw[key];\n if (value === undefined) return undefined;\n if (!isPlainRecord(value)) {\n throw new Error(`Invalid config file ${configPath}: ${key} must be a JSON object`);\n }\n return value;\n}\n\nexport function loadConfigFile(configPath: string): ServerConfig {\n const raw = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(raw)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n requirePlainConfigBlock(raw, \"remnic\", configPath);\n requirePlainConfigBlock(raw, \"engram\", configPath);\n const server = requirePlainConfigBlock(raw, \"server\", configPath);\n return {\n remnic: resolveRemnicConfigRecord(raw),\n server: server ?? {},\n };\n}\n\nfunction loadResolvedConfig(resolved: ResolvedConfigPath): ServerConfig {\n if (!fs.existsSync(resolved.path)) {\n if (resolved.explicit) {\n throw new Error(`Config file from ${resolved.source} not found: ${resolved.path}`);\n }\n return { remnic: {}, server: {} };\n }\n\n const stat = fs.statSync(resolved.path);\n if (!stat.isFile()) {\n if (!resolved.explicit) {\n return { remnic: {}, server: {} };\n }\n throw new Error(`Config file from ${resolved.source} is not a regular file: ${resolved.path}`);\n }\n\n return loadConfigFile(resolved.path);\n}\n\nexport function mergeRemnicConfigForServer(\n fileRemnic: Record<string, unknown>,\n envRemnic: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const effectiveEnvRemnic = { ...(envRemnic ?? {}) };\n if (isOpenaiApiKeyDisabled(fileRemnic.openaiApiKey)) {\n // A local/gateway-only deployment can explicitly disable the direct\n // OpenAI client. Preserve that opt-out even when the process has a\n // global OPENAI_API_KEY for unrelated tools.\n delete effectiveEnvRemnic.openaiApiKey;\n }\n return { ...fileRemnic, ...effectiveEnvRemnic };\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nconst WRITABLE_BOOLEAN_CONFIG_KEYS = new Set([\n \"citationsAutoDetect\",\n \"citationsEnabled\",\n \"embeddingFallbackEnabled\",\n \"enrichmentAutoOnCreate\",\n \"enrichmentEnabled\",\n \"feedbackEnabled\",\n \"hostEmbeddingProviderEnabled\",\n \"localLlmDisableThinking\",\n \"localLlmEnabled\",\n \"localLlmFallback\",\n \"localLlmFastEnabled\",\n \"memoryExtensionsEnabled\",\n \"namespacesEnabled\",\n \"qmdEnabled\",\n \"queryExpansionEnabled\",\n \"recallPlannerEnabled\",\n \"recallPlannerLlmEnabled\",\n \"recallPlannerTelemetryEnabled\",\n \"rerankEnabled\",\n]);\n\nconst WRITABLE_STRING_CONFIG_KEYS = new Set([\n \"embeddingFallbackModel\",\n \"embeddingFallbackProvider\",\n \"fastGatewayAgentId\",\n \"gatewayAgentId\",\n \"localLlmFastModel\",\n \"localLlmModel\",\n \"localLlmUrl\",\n \"model\",\n \"modelSource\",\n \"openaiBaseUrl\",\n \"qmdEmbedModel\",\n \"qmdGenerateModel\",\n \"qmdRerankModel\",\n \"recallPlannerModel\",\n]);\n\nfunction isWritableConfigKey(key: string): boolean {\n return WRITABLE_BOOLEAN_CONFIG_KEYS.has(key) || WRITABLE_STRING_CONFIG_KEYS.has(key);\n}\n\nfunction hasEnv(name: string): boolean {\n const value = process.env[name];\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction fileExists(candidate: string): boolean {\n try {\n return fs.existsSync(expandTildePath(candidate));\n } catch {\n return false;\n }\n}\n\nfunction canWriteConfigPath(configPath: string): boolean {\n try {\n if (fs.existsSync(configPath)) {\n fs.accessSync(configPath, fs.constants.W_OK);\n return true;\n }\n fs.accessSync(path.dirname(configPath), fs.constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction writeConfigFileAtomically(configPath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const tmpPath = path.join(\n path.dirname(configPath),\n `.${path.basename(configPath)}.tmp-${process.pid}-${Date.now()}`,\n );\n let completed = false;\n try {\n fs.writeFileSync(tmpPath, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n mode: 0o600,\n });\n fs.renameSync(tmpPath, configPath);\n try {\n fs.chmodSync(configPath, 0o600);\n } catch {\n // Best effort for platforms/filesystems that do not support chmod.\n }\n completed = true;\n } finally {\n if (!completed) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best effort cleanup for failed writes.\n }\n }\n }\n}\n\nfunction readJsonRecordIfPresent(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (!isPlainRecord(parsed)) {\n throw new Error(`Invalid config file ${configPath}: top-level config must be a JSON object`);\n }\n return parsed;\n}\n\nfunction resolveEditableRemnicBlock(root: Record<string, unknown>): Record<string, unknown> {\n if (isPlainRecord(root.remnic)) return root.remnic;\n if (isPlainRecord(root.engram)) return root.engram;\n return root;\n}\n\nfunction normalizePatchValue(key: string, value: unknown): string | boolean | null {\n if (!isWritableConfigKey(key)) {\n throw new Error(`Unsupported admin config key: ${key}`);\n }\n if (value === null) return null;\n if (WRITABLE_BOOLEAN_CONFIG_KEYS.has(key)) {\n if (typeof value === \"boolean\") return value;\n throw new Error(`Invalid ${key}: expected boolean or null`);\n }\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${key}: expected string or null`);\n }\n const normalized = value.trim();\n if (key === \"modelSource\" && normalized !== \"plugin\" && normalized !== \"gateway\") {\n throw new Error(\"Invalid modelSource: expected plugin or gateway\");\n }\n if (\n key === \"embeddingFallbackProvider\" &&\n normalized !== \"auto\" &&\n normalized !== \"openai\" &&\n normalized !== \"local\"\n ) {\n throw new Error(\"Invalid embeddingFallbackProvider: expected auto, openai, or local\");\n }\n return normalized;\n}\n\nfunction publicConfigValues(config: PluginConfig, serverConfig: ParsedServerConfig): Record<string, string | number | boolean | null> {\n return {\n adminConsoleEnabled: serverConfig.adminConsoleEnabled,\n memoryDir: config.memoryDir,\n model: config.model,\n modelSource: config.modelSource,\n gatewayAgentId: config.gatewayAgentId || null,\n fastGatewayAgentId: config.fastGatewayAgentId || null,\n localLlmEnabled: config.localLlmEnabled,\n localLlmUrl: config.localLlmUrl || null,\n localLlmModel: config.localLlmModel || null,\n localLlmFastEnabled: config.localLlmFastEnabled,\n localLlmFastModel: config.localLlmFastModel || null,\n localLlmFallback: config.localLlmFallback,\n localLlmDisableThinking: config.localLlmDisableThinking,\n qmdEnabled: config.qmdEnabled,\n qmdEmbedModel: config.qmdEmbedModel || null,\n qmdRerankModel: config.qmdRerankModel || null,\n qmdGenerateModel: config.qmdGenerateModel || null,\n embeddingFallbackEnabled: config.embeddingFallbackEnabled,\n embeddingFallbackProvider: config.embeddingFallbackProvider,\n embeddingFallbackModel: config.embeddingFallbackModel || null,\n hostEmbeddingProviderEnabled: config.hostEmbeddingProviderEnabled,\n namespacesEnabled: config.namespacesEnabled,\n recallPlannerEnabled: config.recallPlannerEnabled,\n recallPlannerLlmEnabled: config.recallPlannerLlmEnabled,\n recallPlannerModel: config.recallPlannerModel || null,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n queryExpansionEnabled: config.queryExpansionEnabled,\n rerankEnabled: config.rerankEnabled,\n feedbackEnabled: config.feedbackEnabled,\n memoryExtensionsEnabled: config.memoryExtensionsEnabled,\n enrichmentEnabled: config.enrichmentEnabled,\n enrichmentAutoOnCreate: config.enrichmentAutoOnCreate,\n };\n}\n\nfunction configuredModels(config: PluginConfig): RemnicAdminModelOption[] {\n const models = new Map<string, RemnicAdminModelOption>();\n const add = (id: string | undefined, provider: string, label: string, enabled: boolean, isDefault = false, source = \"config\") => {\n const normalized = id?.trim();\n if (!normalized) return;\n const existing = models.get(`${provider}:${normalized}`);\n models.set(`${provider}:${normalized}`, {\n id: normalized,\n provider,\n label,\n detected: existing?.detected ?? true,\n enabled: existing?.enabled || enabled,\n default: existing?.default || isDefault,\n source,\n });\n };\n\n add(config.model, \"openai\", config.model, !isOpenaiApiKeyDisabled(config.openaiApiKey), config.modelSource === \"plugin\");\n add(config.gatewayAgentId, \"gateway\", config.gatewayAgentId, config.modelSource === \"gateway\", config.modelSource === \"gateway\");\n add(config.fastGatewayAgentId, \"gateway\", config.fastGatewayAgentId, config.modelSource === \"gateway\");\n add(config.localLlmModel, \"local\", config.localLlmModel, config.localLlmEnabled, config.localLlmEnabled);\n add(config.localLlmFastModel, \"local\", `${config.localLlmFastModel} (fast)`, config.localLlmFastEnabled);\n add(config.embeddingFallbackModel, config.embeddingFallbackProvider, `${config.embeddingFallbackModel} (embedding fallback)`, config.embeddingFallbackEnabled);\n add(config.qmdEmbedModel, \"qmd\", `${config.qmdEmbedModel} (embed)`, config.qmdEnabled);\n add(config.qmdRerankModel, \"qmd\", `${config.qmdRerankModel} (rerank)`, config.qmdEnabled);\n add(config.qmdGenerateModel, \"qmd\", `${config.qmdGenerateModel} (generate)`, config.qmdEnabled);\n add(config.recallPlannerModel, \"planner\", `${config.recallPlannerModel} (planner)`, config.recallPlannerLlmEnabled);\n\n return [...models.values()].sort((a, b) => `${a.provider}:${a.id}`.localeCompare(`${b.provider}:${b.id}`));\n}\n\nfunction isLikelyOllamaEndpoint(endpoint: string): boolean {\n return /(?:ollama|11434)/i.test(endpoint);\n}\n\nasync function detectOllamaModels(baseUrl: string | undefined): Promise<RemnicAdminModelOption[]> {\n const configuredEndpoint = baseUrl?.trim();\n const envEndpoint = process.env.OLLAMA_HOST?.trim();\n const endpoint =\n configuredEndpoint && isLikelyOllamaEndpoint(configuredEndpoint)\n ? configuredEndpoint\n : envEndpoint;\n if (!endpoint) return [];\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 900);\n try {\n const url = new URL(\"/api/tags\", endpoint.endsWith(\"/\") ? endpoint : `${endpoint}/`);\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) return [];\n const payload = await response.json() as { models?: Array<{ name?: unknown; model?: unknown }> };\n return (payload.models ?? [])\n .map((model) => typeof model.name === \"string\" ? model.name : typeof model.model === \"string\" ? model.model : \"\")\n .filter((name) => name.length > 0)\n .map((name) => ({\n id: name,\n label: name,\n provider: \"ollama\",\n detected: true,\n enabled: true,\n source: \"ollama\",\n endpoint,\n }));\n } catch {\n return [];\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction dashboardHarnesses(config: PluginConfig) {\n const openclawDetected = hasEnv(\"OPENCLAW_HOME\") || hasEnv(\"OPENCLAW_WORKSPACE\") || fileExists(\"~/.openclaw\");\n const codexDetected = hasEnv(\"CODEX_HOME\") || fileExists(\"~/.codex/auth.json\") || fileExists(\"~/.codex\");\n return [\n {\n id: \"remnic-http\",\n label: \"Remnic HTTP API\",\n detected: true,\n enabled: true,\n source: \"server\",\n detail: \"MCP and REST access server\",\n },\n {\n id: \"openclaw\",\n label: \"OpenClaw\",\n detected: openclawDetected,\n enabled: config.modelSource === \"gateway\" || config.hostEmbeddingProviderEnabled,\n source: openclawDetected ? \"host\" : \"not detected\",\n detail: \"Gateway and host adapters\",\n },\n {\n id: \"codex\",\n label: \"Codex\",\n detected: codexDetected,\n enabled: config.citationsEnabled || config.citationsAutoDetect,\n source: codexDetected ? \"host\" : \"not detected\",\n detail: \"Citation-aware adapter\",\n },\n {\n id: \"qmd\",\n label: \"QMD Search\",\n detected: Boolean(config.qmdPath) || config.qmdEnabled,\n enabled: config.qmdEnabled,\n source: config.qmdPath ? \"qmdPath\" : \"config\",\n detail: config.qmdSearchStrategy,\n },\n ];\n}\n\nfunction dashboardProviders(config: PluginConfig) {\n const localLlmUrl = config.localLlmUrl || \"\";\n const gatewayIds = [config.gatewayAgentId, config.fastGatewayAgentId].filter(Boolean).join(\" \");\n const openaiBaseUrl = process.env.OPENAI_BASE_URL || \"\";\n const sageRouterDetected =\n hasEnv(\"SAGE_ROUTER_URL\") ||\n hasEnv(\"SAGE_ROUTER_HOST\") ||\n /sage[-_ ]?router/i.test(`${gatewayIds} ${openaiBaseUrl}`);\n const ollamaDetected = hasEnv(\"OLLAMA_HOST\") || /(?:ollama|11434)/i.test(localLlmUrl);\n const localDetected = Boolean(localLlmUrl.trim());\n\n return [\n {\n id: \"openai\",\n label: \"OpenAI\",\n detected: !isOpenaiApiKeyDisabled(config.openaiApiKey),\n enabled: config.modelSource === \"plugin\" && !isOpenaiApiKeyDisabled(config.openaiApiKey),\n source: !isOpenaiApiKeyDisabled(config.openaiApiKey) ? \"config/env\" : \"disabled\",\n detail: config.model,\n },\n {\n id: \"sage-router\",\n label: \"Sage Router\",\n detected: sageRouterDetected,\n enabled: config.modelSource === \"gateway\" && sageRouterDetected,\n source: sageRouterDetected ? \"gateway/env\" : \"not detected\",\n detail: config.gatewayAgentId || config.fastGatewayAgentId || openaiBaseUrl || \"OpenAI-compatible provider router\",\n },\n {\n id: \"ollama\",\n label: \"Ollama\",\n detected: ollamaDetected,\n enabled: config.localLlmEnabled && ollamaDetected,\n source: ollamaDetected ? \"localLlmUrl/OLLAMA_HOST\" : \"not detected\",\n detail: \"Local or cloud-compatible Ollama endpoint\",\n },\n {\n id: \"local-openai-compatible\",\n label: \"Local OpenAI-compatible\",\n detected: localDetected && !ollamaDetected,\n enabled: config.localLlmEnabled && localDetected && !ollamaDetected,\n source: localDetected ? \"localLlmUrl\" : \"not detected\",\n detail: localLlmUrl || \"Local provider endpoint\",\n },\n ];\n}\n\nfunction dashboardFeatures(config: PluginConfig) {\n return [\n [\"localLlmEnabled\", \"Local LLM\", config.localLlmEnabled],\n [\"localLlmFastEnabled\", \"Fast Local Tier\", config.localLlmFastEnabled],\n [\"qmdEnabled\", \"QMD Search\", config.qmdEnabled],\n [\"embeddingFallbackEnabled\", \"Embedding Fallback\", config.embeddingFallbackEnabled],\n [\"hostEmbeddingProviderEnabled\", \"Host Embeddings\", config.hostEmbeddingProviderEnabled],\n [\"namespacesEnabled\", \"Namespaces\", config.namespacesEnabled],\n [\"recallPlannerEnabled\", \"Recall Planner\", config.recallPlannerEnabled],\n [\"recallPlannerLlmEnabled\", \"Planner LLM\", config.recallPlannerLlmEnabled],\n [\"citationsEnabled\", \"Citations\", config.citationsEnabled],\n [\"citationsAutoDetect\", \"Citation Auto-detect\", config.citationsAutoDetect],\n [\"queryExpansionEnabled\", \"Query Expansion\", config.queryExpansionEnabled],\n [\"rerankEnabled\", \"Rerank\", config.rerankEnabled],\n [\"feedbackEnabled\", \"Feedback\", config.feedbackEnabled],\n [\"memoryExtensionsEnabled\", \"Memory Extensions\", config.memoryExtensionsEnabled],\n [\"enrichmentEnabled\", \"Entity Enrichment\", config.enrichmentEnabled],\n ].map(([key, label, enabled]) => ({\n key: String(key),\n label: String(label),\n enabled: enabled === true,\n writable: WRITABLE_BOOLEAN_CONFIG_KEYS.has(String(key)),\n restartRequired: true,\n }));\n}\n\nexport function createAdminControls(\n configPath: string,\n config: PluginConfig,\n serverConfig: ParsedServerConfig,\n): RemnicAdminControls {\n let restartRequired = false;\n let displayConfig = config;\n const status = async (): Promise<RemnicAdminDashboardStatus> => {\n const models = configuredModels(displayConfig);\n const ollamaModels = await detectOllamaModels(displayConfig.localLlmUrl);\n const modelKeys = new Set(models.map((model) => `${model.provider}:${model.id}`));\n for (const model of ollamaModels) {\n if (!modelKeys.has(`${model.provider}:${model.id}`)) models.push(model);\n }\n return {\n config: {\n path: configPath,\n exists: fs.existsSync(configPath),\n writable: canWriteConfigPath(configPath),\n restartRequired,\n values: publicConfigValues(displayConfig, serverConfig),\n },\n harnesses: dashboardHarnesses(displayConfig),\n providers: dashboardProviders(displayConfig),\n models,\n features: dashboardFeatures(displayConfig),\n };\n };\n\n return {\n status,\n update: async (patch: RemnicAdminConfigPatch): Promise<RemnicAdminDashboardStatus> => {\n if (!isPlainRecord(patch)) {\n throw new Error(\"Admin config patch must be an object\");\n }\n const normalizedEntries = Object.entries(patch).map(([key, value]) => [key, normalizePatchValue(key, value)] as const);\n if (normalizedEntries.length === 0) return status();\n\n const raw = readJsonRecordIfPresent(configPath);\n const target = resolveEditableRemnicBlock(raw);\n for (const [key, value] of normalizedEntries) {\n if (value === null) {\n delete target[key];\n if (target !== raw) delete raw[key];\n } else {\n target[key] = value;\n }\n }\n\n const nextDisplayConfig = parseConfig(resolveRemnicConfigRecord(raw));\n\n writeConfigFileAtomically(configPath, raw);\n displayConfig = nextDisplayConfig;\n restartRequired = true;\n return status();\n },\n };\n}\n\ninterface PromiseResolvers<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\ntype PromiseConstructorWithResolvers = PromiseConstructor & {\n withResolvers<T>(): PromiseResolvers<T>;\n};\n\n/**\n * Like `setTimeout` wrapped in a Promise, but respects an `AbortSignal`.\n * Resolves immediately (without throwing) when the signal fires so the\n * caller can check `signal.aborted` and exit cleanly.\n */\nfunction abortableDelay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n const { promise, resolve } = (Promise as PromiseConstructorWithResolvers).withResolvers<void>();\n const timer = setTimeout(resolve, ms);\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return promise.finally(() => signal.removeEventListener(\"abort\", onAbort));\n}\n\nconst STARTUP_WARMUP_TIMEOUT_MS = 20_000;\nconst STARTUP_WARMUP_RETRY_INTERVAL_MS = 30_000;\n\nexport interface StartupReadinessState {\n ready: boolean;\n warmupAttempts: number;\n lastError?: string | null;\n}\n\nexport type StartupReadinessOutcome = \"warmed\" | \"cancelled\" | \"overridden\" | \"search-disabled\";\n\nclass StartupWarmupDegradationError extends Error {\n constructor(code: string) {\n super(`startup search degraded: ${code}`);\n this.name = \"StartupWarmupDegradationError\";\n }\n}\n\nclass StartupSyncPendingError extends Error {\n constructor() {\n super(\"startup search sync is not complete\");\n this.name = \"StartupSyncPendingError\";\n }\n}\n\nexport async function runStartupSearchWarmup(options: {\n signal: AbortSignal;\n isAvailable: () => boolean;\n search: (onDegradation: (code: string) => void) => Promise<unknown>;\n}): Promise<void> {\n let degradationCode: string | undefined;\n await options.search((code) => {\n degradationCode = code;\n });\n if (options.signal.aborted) return;\n if (degradationCode) throw new StartupWarmupDegradationError(degradationCode);\n if (!options.isAvailable()) {\n throw new StartupWarmupDegradationError(\"backend_unavailable\");\n }\n}\n\nexport async function completeStartupReadiness(options: {\n deferredReady: Promise<void>;\n warmup: (signal: AbortSignal) => Promise<unknown>;\n prepareWarmup?: (signal: AbortSignal) => Promise<boolean>;\n state: StartupReadinessState;\n timeoutMs?: number;\n retryIntervalMs?: number;\n override?: boolean;\n skipWarmup?: () => boolean;\n openGate: () => void;\n shutdownSignal?: AbortSignal;\n warn?: (message: string) => void;\n info?: (message: string) => void;\n error?: (message: string) => void;\n}): Promise<StartupReadinessOutcome> {\n const timeoutMs = options.timeoutMs ?? STARTUP_WARMUP_TIMEOUT_MS;\n const retryIntervalMs = options.retryIntervalMs ?? STARTUP_WARMUP_RETRY_INTERVAL_MS;\n const warn = options.warn ?? ((message: string) => log.warn(message));\n const info = options.info ?? ((message: string) => log.info(message));\n const error = options.error ?? ((message: string) => log.error(message));\n\n options.state.ready = false;\n options.state.lastError = null;\n if (options.override) {\n options.openGate();\n options.state.ready = true;\n error(\n \"CRITICAL: emergency readiness override enabled; exposing a cold search backend to traffic\",\n );\n return \"overridden\";\n }\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n\n\n let removeDeferredShutdownListener: () => void = () => undefined;\n const deferredShutdown = new Promise<\"shutdown\">((resolve) => {\n if (options.shutdownSignal?.aborted) {\n resolve(\"shutdown\");\n return;\n }\n const onDeferredShutdown = () => resolve(\"shutdown\");\n options.shutdownSignal?.addEventListener(\"abort\", onDeferredShutdown, { once: true });\n removeDeferredShutdownListener = () =>\n options.shutdownSignal?.removeEventListener(\"abort\", onDeferredShutdown);\n });\n try {\n const deferredOutcome = await Promise.race([\n options.deferredReady.then(() => \"ready\" as const),\n deferredShutdown,\n ]);\n if (deferredOutcome === \"shutdown\") return \"cancelled\";\n } catch (err) {\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n options.state.lastError = err instanceof Error ? err.name : typeof err;\n warn(`Standalone deferred initialization failed; warm-up retries will continue: ${err}`);\n } finally {\n removeDeferredShutdownListener();\n }\n if (options.shutdownSignal?.aborted) return \"cancelled\";\n\n const lifecycleAbort = new AbortController();\n const onShutdown = () => lifecycleAbort.abort(options.shutdownSignal?.reason);\n options.shutdownSignal?.addEventListener(\"abort\", onShutdown, { once: true });\n\n try {\n while (!lifecycleAbort.signal.aborted) {\n if (options.skipWarmup?.()) {\n options.openGate();\n options.state.ready = true;\n info(\"Standalone init gate opened without search warm-up (search intentionally disabled)\");\n return \"search-disabled\";\n }\n options.state.warmupAttempts += 1;\n const warmupAbort = new AbortController();\n const onLifecycleAbort = () => warmupAbort.abort(lifecycleAbort.signal.reason);\n lifecycleAbort.signal.addEventListener(\"abort\", onLifecycleAbort, { once: true });\n const timeout = (Promise as PromiseConstructorWithResolvers).withResolvers<never>();\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n warmupAbort.abort();\n timeout.reject(new Error(`startup warm-up timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n timer.unref();\n\n try {\n const attempt = async () => {\n if (options.prepareWarmup && !await options.prepareWarmup(warmupAbort.signal)) {\n throw new StartupSyncPendingError();\n }\n return options.warmup(warmupAbort.signal);\n };\n await Promise.race([attempt(), timeout.promise]);\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = null;\n options.openGate();\n options.state.ready = true;\n info(\n `Standalone init gate opened after search warm-up attempt ${options.state.warmupAttempts}`,\n );\n return \"warmed\";\n } catch (err) {\n if (lifecycleAbort.signal.aborted) return \"cancelled\";\n options.state.lastError = timedOut\n ? \"TimeoutError\"\n : err instanceof Error\n ? err.name\n : typeof err;\n warn(\n timedOut\n ? `Standalone startup warm-up attempt ${options.state.warmupAttempts} timed out after ${timeoutMs}ms; retrying in ${retryIntervalMs}ms`\n : `Standalone startup warm-up attempt ${options.state.warmupAttempts} failed (${options.state.lastError}); retrying in ${retryIntervalMs}ms`,\n );\n } finally {\n clearTimeout(timer);\n lifecycleAbort.signal.removeEventListener(\"abort\", onLifecycleAbort);\n }\n\n await abortableDelay(retryIntervalMs, lifecycleAbort.signal);\n }\n return \"cancelled\";\n } finally {\n options.shutdownSignal?.removeEventListener(\"abort\", onShutdown);\n }\n}\n\nasync function cleanupFailedStartup(\n orchestrator: Orchestrator,\n httpServer: EngramAccessHttpServer,\n): Promise<void> {\n try {\n await httpServer.stop();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not stop server: ${err}`);\n }\n\n try {\n await orchestrator.destroy();\n } catch (err) {\n log.warn(`HTTP startup failure cleanup could not destroy orchestrator: ${err}`);\n }\n}\n\n// ── Server startup ──────────────────────────────────────────────────────────\n\nexport interface ServerResult {\n config: PluginConfig;\n service: EngramAccessService;\n httpServer: EngramAccessHttpServer;\n host: string;\n port: number;\n /** Stop HTTP, cancel startup work, abort deferred init, and destroy the orchestrator. */\n stop: () => Promise<void>;\n /** Cancel any pending startup-sync retry timers. Called automatically on shutdown. */\n cancelStartupSync: () => void;\n /** Abort deferred orchestrator initialization (QMD sync, warmup, cache). */\n abortDeferredInit: () => void;\n}\n\nexport async function startServer(options?: {\n configPath?: string;\n host?: string;\n port?: number;\n authToken?: string;\n}): Promise<ServerResult> {\n initLogger();\n\n // Startup driver-load check (issue #1829): attempt to load the better-sqlite3\n // native binding under THIS process. A wrong-ABI build previously threw inside\n // each projection open, was caught, and returned the same silent null as a\n // missing file — so every memory list fell back to a full-corpus scan with no\n // visible error. Probe once at startup and log LOUDLY. Do not crash: the\n // full-corpus fallback still serves, and the projection-open path records the\n // distinct rate-limited signal + doctor entry on its own.\n const driverProbe = probeBetterSqlite3Driver();\n if (!driverProbe.ok) {\n const detailSuffix = driverProbe.detail ? ` (${driverProbe.detail})` : \"\";\n const abiSuffix = driverProbe.nativeBindingMismatch\n ? \" — the binding was built for a different Node.js ABI; rebuild it (`node scripts/ensure-better-sqlite3.mjs` or `pnpm rebuild better-sqlite3`)\"\n : \"\";\n log.error(\n `better-sqlite3 native driver failed to load under the running process${detailSuffix}${abiSuffix}. SQLite-backed features (memory projection) will fall back to slower full-corpus scans until fixed.`,\n );\n }\n\n const resolvedConfigPath = resolveConfigPath(options?.configPath);\n const fileConfig = loadResolvedConfig(resolvedConfigPath);\n\n const env = envOverrides();\n const { remnic: envRemnic, ...envServer } = env;\n\n // Merge: file < env < cli flags\n const remnicConfig = mergeRemnicConfigForServer(fileConfig.remnic, envRemnic);\n const cliServerConfig: Partial<ServerConfig[\"server\"]> = {};\n if (options?.host !== undefined) cliServerConfig.host = options.host;\n if (options?.port !== undefined) cliServerConfig.port = parseServerPort(options.port, \"options.port\");\n if (options?.authToken !== undefined) cliServerConfig.authToken = options.authToken;\n\n const serverConfig = {\n ...fileConfig.server,\n ...envServer,\n ...cliServerConfig,\n };\n const portSource = cliServerConfig.port !== undefined\n ? \"options.port\"\n : envServer.port !== undefined\n ? \"REMNIC_PORT/ENGRAM_PORT\"\n : \"server.port\";\n const parsedServerConfig = parseServerConfig(serverConfig, { portSource });\n\n const config = parseConfig(remnicConfig);\n // Re-init now that config is known. The call at the top of startServer runs\n // BEFORE the config file is read, so it could only ever default `debug` to\n // false — `debug: true` was accepted, documented, and silently inert on the\n // standalone daemon, which is exactly the flag you reach for when the daemon\n // is misbehaving (issue #2209).\n initLogger(undefined, config.debug);\n log.debug(`debug logging enabled from config (${resolvedConfigPath.source})`);\n const orchestrator = new Orchestrator(config);\n await orchestrator.initialize();\n\n // Start the HTTP server immediately so health checks, MCP handshakes,\n // and liveness probes can connect while deferred init is still running.\n const service = new EngramAccessService(orchestrator);\n const readiness: StartupReadinessState = { ready: false, warmupAttempts: 0, lastError: null };\n\n const authToken = parsedServerConfig.authToken ?? readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\") ?? \"\";\n\n // Connector tokens are loaded dynamically per request via authTokensGetter\n // so that token generate/revoke takes effect without server restart\n if (!authToken && getAllValidTokens().length === 0) {\n log.warn(\"No auth token set — server will reject all requests. Set REMNIC_AUTH_TOKEN, server.authToken in config, or generate tokens with 'remnic token generate'.\");\n }\n // OAuth facade (ChatGPT developer-mode apps): file block < REMNIC_OAUTH_* env.\n // Parsed strictly — invalid values abort startup with a precise message.\n const oauthConfig = applyOAuthEnvOverrides((serverConfig as { oauth?: unknown }).oauth);\n const oauthRequestHandler = buildOAuthRequestHandler(oauthConfig);\n\n\n const httpServer = new EngramAccessHttpServer({\n service,\n host: parsedServerConfig.host,\n port: parsedServerConfig.port,\n authToken: authToken || undefined,\n // Entry-based getter: validation + connector identity from ONE cached\n // snapshot (see tokens.ts). The path policy pins ChatGPT-minted OAuth\n // tokens (connector \"chatgpt\") to the MCP endpoint only — they never\n // authorize REST/admin routes. All other connector tokens keep full\n // access, matching pre-OAuth behavior.\n authTokenEntriesGetter: () => getAllValidTokenEntriesCached(),\n tokenPathPolicy: (connector, pathname) => connector !== \"chatgpt\" || pathname === \"/mcp\",\n readiness: () => readiness,\n principal: parsedServerConfig.principal,\n maxBodyBytes: parsedServerConfig.maxBodyBytes,\n writeRateLimitMaxRequests: parsedServerConfig.writeRateLimitMaxRequests,\n writeRateLimitWindowMs: parsedServerConfig.writeRateLimitWindowMs,\n adminConsoleEnabled: parsedServerConfig.adminConsoleEnabled,\n adminConsolePublicDir: parsedServerConfig.adminConsolePublicDir\n ? path.resolve(expandTildePath(parsedServerConfig.adminConsolePublicDir))\n : undefined,\n adminConsolePrefillToken: parsedServerConfig.adminConsolePrefillToken,\n adminControls: parsedServerConfig.adminConsoleEnabled\n ? createAdminControls(resolvedConfigPath.path, config, parsedServerConfig)\n : undefined,\n citationsEnabled: config.citationsEnabled,\n citationsAutoDetect: config.citationsAutoDetect,\n emitLegacyTools: config.emitLegacyTools,\n ...(oauthConfig.enabled\n ? {\n externalRequestHandler: oauthRequestHandler,\n resourceMetadataUrl: new URL(\n \"/.well-known/oauth-protected-resource/mcp\",\n oauthConfig.issuerUrl,\n ).href,\n }\n : {}),\n });\n\n let host: string;\n let port: number;\n try {\n ({ host, port } = await httpServer.start());\n } catch (err) {\n await cleanupFailedStartup(orchestrator, httpServer);\n throw err;\n }\n\n // Fire-and-forget: wait for deferred init (QMD probe, collection setup,\n // warmup) then check QMD availability and retry if needed. This does NOT\n // block the server listener — connections are accepted immediately above.\n // An AbortController allows the shutdown handler to cancel pending retries.\n const startupSyncAbort = new AbortController();\n const readinessAbort = new AbortController();\n let startupSyncInFlight: Promise<boolean> | undefined;\n const ensureStartupSync = async (signal: AbortSignal): Promise<boolean> => {\n if (orchestrator.deferredSyncSucceeded) return true;\n if (startupSyncInFlight) return startupSyncInFlight;\n const attempt = orchestrator.startupSearchSync(signal).then((synced) => {\n if (synced) orchestrator.deferredSyncSucceeded = true;\n return synced;\n });\n startupSyncInFlight = attempt;\n try {\n return await attempt;\n } finally {\n if (startupSyncInFlight === attempt) startupSyncInFlight = undefined;\n }\n };\n const readinessTask = completeStartupReadiness({\n deferredReady: orchestrator.deferredReady,\n warmup: (signal) =>\n runStartupSearchWarmup({\n signal,\n isAvailable: () => orchestrator.qmd.isAvailable(),\n search: (onDegradation) =>\n orchestrator.qmd.search(\n \"remnic startup readiness\",\n config.defaultNamespace,\n 1,\n undefined,\n {\n signal,\n onDegradation: (degradation) => onDegradation(degradation.code),\n },\n ),\n }),\n prepareWarmup: ensureStartupSync,\n state: readiness,\n override: parsedServerConfig.readinessOverride,\n skipWarmup: () => orchestrator.qmd.debugStatus() === \"backend=noop\",\n openGate: () => {\n readiness.ready = true;\n },\n shutdownSignal: readinessAbort.signal,\n });\n // Wrap httpServer.stop() so that existing callers also get full lifecycle\n // cleanup: retry timers, deferred init, HTTP listener, and orchestrator.\n const originalStop = httpServer.stop.bind(httpServer);\n let stopPromise: Promise<void> | undefined;\n const stop = async (): Promise<void> => {\n if (stopPromise) return stopPromise;\n stopPromise = (async () => {\n startupSyncAbort.abort();\n readinessAbort.abort();\n orchestrator.abortDeferredInit();\n try {\n await originalStop();\n } finally {\n try {\n await readinessTask;\n } finally {\n await orchestrator.destroy();\n }\n }\n })();\n return stopPromise;\n };\n httpServer.stop = stop;\n\n orchestrator.deferredReady.then(() => {\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync: cancelled before deferred init completed\");\n return;\n }\n\n // Skip retries when search is explicitly disabled via config or when the\n // orchestrator already resolved to a noop backend (e.g. missing collection\n // detected during deferredInitialize). Both cases mean no sync should ever\n // run; scheduling retries would create misleading operational noise and\n // unnecessary background work on every server start.\n if (!config.qmdEnabled || orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync: search disabled or noop backend, skipping retries\");\n return;\n }\n\n // Retry when either: (a) QMD is not available yet (cold-start race), or\n // (b) QMD is available but the deferred init sync step failed silently\n // (e.g., update errors swallowed by backend, throttle skip, transient\n // network failure). Without (b), the daemon permanently serves stale\n // recall after a failed sync despite healthy QMD probe.\n const needsRetry = !orchestrator.qmd.isAvailable() || !orchestrator.deferredSyncSucceeded;\n if (!needsRetry) {\n log.debug(\"QMD startup-sync: deferred init completed successfully, no retries needed\");\n return;\n }\n\n const RETRY_DELAYS_MS = [5_000, 15_000, 30_000, 60_000, 120_000];\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled before retry task started\");\n return;\n }\n (async () => {\n for (const delay of RETRY_DELAYS_MS) {\n await abortableDelay(delay, startupSyncAbort.signal);\n\n if (startupSyncAbort.signal.aborted) {\n log.debug(\"QMD startup-sync retry: cancelled by shutdown\");\n return;\n }\n\n const synced = await ensureStartupSync(startupSyncAbort.signal);\n if (!synced) {\n if (orchestrator.qmd.debugStatus() === \"backend=noop\") {\n log.debug(\"QMD startup-sync retry: search intentionally disabled; stopping retries\");\n return;\n }\n log.debug(`QMD startup-sync retry: not available yet (next retry in ${RETRY_DELAYS_MS[RETRY_DELAYS_MS.indexOf(delay) + 1] ?? \"n/a\"}ms)`);\n continue;\n }\n\n return; // sync succeeded, stop retrying\n }\n\n log.warn(\"QMD startup-sync retry: exhausted all retries; search index may be stale\");\n })().catch((err: unknown) => {\n log.warn(`QMD startup-sync retry: unexpected error: ${err}`);\n });\n }).catch((err: unknown) => {\n log.warn(`Deferred init error: ${err}`);\n });\n\n return { config, service, httpServer, host, port, stop, cancelStartupSync: () => startupSyncAbort.abort(), abortDeferredInit: () => orchestrator.abortDeferredInit() };\n}\n\n// ── CLI entry point ──────────────────────────────────────────────────────────\n\nconst BOOLEAN_CLI_OPTIONS = new Set([\"help\"]);\nconst VALUE_CLI_OPTIONS = new Set([\"config\", \"host\", \"port\", \"auth-token\"]);\n\nfunction parseCliArgs(argv: string[]): Record<string, string | undefined> {\n const args: Record<string, string | undefined> = {};\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (token === \"-h\") {\n args.help = \"true\";\n continue;\n }\n\n if (token.startsWith(\"--\")) {\n const [key, inlineValue] = token.slice(2).split(/=(.*)/s, 2);\n if (!key) {\n throw new Error(`Invalid option ${token}`);\n }\n\n if (BOOLEAN_CLI_OPTIONS.has(key)) {\n if (inlineValue !== undefined) {\n throw new Error(`Option --${key} does not accept a value`);\n }\n args[key] = \"true\";\n continue;\n }\n\n if (!VALUE_CLI_OPTIONS.has(key)) {\n throw new Error(`Unknown option --${key}`);\n }\n\n const value = inlineValue ?? argv[i + 1];\n if (\n value === undefined ||\n (inlineValue === undefined && value.startsWith(\"--\")) ||\n value.trim() === \"\"\n ) {\n throw new Error(`Missing value for --${key}`);\n }\n\n args[key] = value;\n if (inlineValue === undefined) i++;\n }\n }\n return args;\n}\n\nexport async function cliMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n const args = parseCliArgs(argv);\n\n if (args.help) {\n console.log(`\nremnic-server — Standalone Remnic memory server\n\nUsage:\n remnic-server [options]\n\nOptions:\n --config <path> Path to config file (default: remnic.config.json)\n --host <addr> Bind address (default: 127.0.0.1)\n --port <number> Port number (default: 4318)\n --auth-token <tok> Bearer token for auth (or set REMNIC_AUTH_TOKEN)\n --help Show this help\n\nEnvironment:\n REMNIC_CONFIG_PATH Config file path (ENGRAM_CONFIG_PATH also supported)\n REMNIC_PORT Server port (ENGRAM_PORT also supported)\n REMNIC_HOST Bind address (ENGRAM_HOST also supported)\n REMNIC_AUTH_TOKEN Auth bearer token (ENGRAM_AUTH_TOKEN also supported)\n REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\n Prefill admin UI with REMNIC_AUTH_TOKEN when true\n REMNIC_MEMORY_DIR Override memory directory (ENGRAM_MEMORY_DIR also supported)\n OPENAI_API_KEY OpenAI API key for extraction; ignored when config sets openaiApiKey=false\n`);\n process.exit(0);\n }\n\n const result = await startServer({\n configPath: args.config,\n host: args.host,\n port: args.port === undefined ? undefined : parseServerPort(args.port, \"--port\"),\n authToken: args[\"auth-token\"],\n });\n\n console.log(`Remnic server listening on http://${result.host}:${result.port}`);\n\n // Graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\nReceived ${signal}, shutting down...`);\n await result.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n}\n\n// Auto-run when executed directly\n// Matches direct execution of `node .../remnic-server/dist/index.js` or\n// `node .../remnic-server/src/index.ts`. Package command names are handled by\n// the bin wrappers in ../bin so importing this module cannot start twice.\nif (\n process.argv[1] &&\n /(?:remnic-server|engram-server)[\\\\/](?:dist|src)[\\\\/]index\\.[jt]s$/.test(process.argv[1])\n) {\n cliMain().catch((err) => {\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n}\n","/**\n * OAuth 2.1 authorization-server facade for the standalone Remnic server.\n *\n * Why this exists: ChatGPT developer-mode apps (chatgpt.com) can only talk\n * to remote MCP servers with OAuth, \"No Authentication\", or Mixed auth —\n * there is no static API-key/bearer option in the ChatGPT UI. Remnic's MCP\n * endpoint (`POST /mcp`) is bearer-token protected, so this module lets\n * Remnic act as its OWN authorization server: ChatGPT runs a standard\n * authorization-code + PKCE flow against these endpoints and receives a\n * regular Remnic connector token (connector id `chatgpt`) as the OAuth\n * access token. The existing bearer validation on `/mcp` then works\n * unchanged.\n *\n * Protocol layer: the security-critical endpoints are NOT hand-rolled.\n * `authorizationHandler`, `tokenHandler` (PKCE S256 verification, client\n * authentication, param validation, rate limiting) and the RFC 8414 /\n * RFC 9728 metadata router all come from `@modelcontextprotocol/sdk`.\n * This module contributes only the Remnic-specific pieces:\n *\n * - config parsing (`server.oauth` block + `REMNIC_OAUTH_*` env overrides),\n * - the static single-client store (Remnic pre-registers ChatGPT; the\n * operator pastes the same client id/secret into ChatGPT's app UI),\n * - the pending-approval interaction: `/authorize` renders a page that\n * instructs the operator to run `remnic oauth approve <ref>` locally;\n * the page polls with a per-transaction secret and redirects to the\n * ChatGPT callback once the operator approves,\n * - operator-only pending/approve/deny endpoints (gated by the core\n * bearer authorization passed in from the access server), and\n * - access-token minting into the Remnic token store.\n *\n * Security model (do not weaken):\n * - The approval page NEVER asks for credentials. Approval happens\n * out-of-band via the CLI, which authenticates with the operator\n * bearer token against the local daemon.\n * - `txn` id and `pollSecret` are independent 128-bit random values;\n * the human-readable `ref` shown on the page cannot authorize\n * anything by itself.\n * - Authorization codes are 128-bit, single-use, TTL-capped at 120 s,\n * and bound to client_id + redirect_uri + PKCE challenge + resource.\n * - Redirect URIs are validated by the SDK against the exact\n * pre-registered allowlist (`server.oauth.redirectUris`); an empty\n * allowlist refuses every authorization (setup mode: discovery works,\n * authorization does not).\n * - Secret comparisons go through SHA-256 digests + `timingSafeEqual`,\n * which is constant-time and never throws on length mismatch.\n */\n\nimport { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport express, { type Express, type Request, type Response } from \"express\";\nimport { rateLimit } from \"express-rate-limit\";\nimport { authorizationHandler } from \"@modelcontextprotocol/sdk/server/auth/handlers/authorize.js\";\nimport { tokenHandler } from \"@modelcontextprotocol/sdk/server/auth/handlers/token.js\";\nimport { createOAuthMetadata, mcpAuthMetadataRouter } from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport {\n InvalidGrantError,\n InvalidTokenError,\n ServerError,\n UnsupportedGrantTypeError,\n} from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type { OAuthServerProvider, AuthorizationParams } from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport type { OAuthRegisteredClientsStore } from \"@modelcontextprotocol/sdk/server/auth/clients.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type { OAuthClientInformationFull, OAuthTokens } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { buildTokenEntry, commitTokenEntry, getAllValidTokensCached, log } from \"@remnic/core\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\n/**\n * Token-endpoint client auth methods this facade supports. The SDK's\n * client-auth middleware reads credentials from the request body only,\n * so `client_secret_basic` is intentionally NOT offered: advertising a\n * method the token endpoint would reject is worse than not offering it.\n * ChatGPT supports `client_secret_post` and `none` for predefined\n * clients.\n */\nconst ALLOWED_TOKEN_AUTH_METHODS = [\"client_secret_post\", \"none\"] as const;\nexport type OAuthTokenEndpointAuthMethod = (typeof ALLOWED_TOKEN_AUTH_METHODS)[number];\n\n/** Default TTL for pending authorizations (10 minutes). */\nconst DEFAULT_APPROVAL_TTL_SECONDS = 600;\n\n/** Authorization-code TTL (OAuth 2.1 recommends short single-use codes). */\nconst AUTHORIZATION_CODE_TTL_MS = 120_000;\n\n/** 128 bits of entropy in hex = 32 chars. */\nconst HIGH_ENTROPY_HEX_BYTES = 16;\n\n/** Connector id under which OAuth access tokens are minted. */\nconst CHATGPT_CONNECTOR_ID = \"chatgpt\";\n\n// ── Config schema + parser ──────────────────────────────────────────────────\n\n/**\n * Raw config block as it appears in `server.oauth`. All fields optional at\n * the type level; the parser enforces requirements when `enabled: true`.\n */\nexport interface OAuthConfigInput {\n enabled?: unknown;\n issuerUrl?: unknown;\n clientId?: unknown;\n clientSecret?: unknown;\n tokenEndpointAuthMethod?: unknown;\n redirectUris?: unknown;\n approvalTtlSeconds?: unknown;\n}\n\nexport interface ParsedOAuthConfig {\n enabled: boolean;\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod;\n redirectUris: string[];\n approvalTtlSeconds: number;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Coerce a boolean coming from JSON (any) or env (string). Mirrors the\n * repo's canonical boolean coercion (rules 17, 24): \"true\"/\"1\"/\"yes\"/\"on\"\n * are truthy, \"false\"/\"0\"/\"no\"/\"off\" are falsy. Anything else throws.\n */\nfunction coerceBoolean(value: unknown, source: string): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid ${source}: expected a boolean (got: ${JSON.stringify(value)})`);\n}\n\nfunction coerceNonEmptyString(value: unknown, source: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`Invalid ${source}: expected a non-empty string`);\n }\n return value.trim();\n}\n\nfunction coercePositiveInteger(value: unknown, source: string): number {\n const num = typeof value === \"string\" && value.trim() !== \"\" ? Number(value) : value;\n if (typeof num !== \"number\" || !Number.isInteger(num) || num <= 0) {\n throw new Error(`Invalid ${source}: expected a positive integer (got: ${JSON.stringify(value)})`);\n }\n return num;\n}\n\nfunction coerceAbsoluteHttpsUrl(\n value: unknown,\n source: string,\n opts: { allowHttpForLocalhost: boolean },\n): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`Invalid ${source}: expected a non-empty URL string`);\n }\n const trimmed = value.trim();\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw new Error(`Invalid ${source}: \"${trimmed}\" is not a valid URL`);\n }\n if (parsed.hash || parsed.search) {\n throw new Error(`Invalid ${source}: URL must not include a query string or fragment`);\n }\n const isHttps = parsed.protocol === \"https:\";\n const isLocalhostHttp =\n opts.allowHttpForLocalhost &&\n parsed.protocol === \"http:\" &&\n (parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"localhost\");\n if (!isHttps && !isLocalhostHttp) {\n throw new Error(\n `Invalid ${source}: must be an absolute https:// URL (http:// allowed only for 127.0.0.1/localhost)`,\n );\n }\n return trimmed;\n}\n\nfunction coerceTokenAuthMethod(value: unknown, source: string): OAuthTokenEndpointAuthMethod {\n if (typeof value !== \"string\") {\n throw new Error(`Invalid ${source}: expected one of ${ALLOWED_TOKEN_AUTH_METHODS.join(\", \")}`);\n }\n const trimmed = value.trim();\n const match = ALLOWED_TOKEN_AUTH_METHODS.find((method) => method === trimmed);\n if (!match) {\n throw new Error(\n `Invalid ${source}: \"${trimmed}\" is not allowed. Use one of ${ALLOWED_TOKEN_AUTH_METHODS.join(\", \")}.`,\n );\n }\n return match;\n}\n\nfunction coerceRedirectUris(value: unknown, source: string): string[] {\n if (!Array.isArray(value)) {\n throw new Error(`Invalid ${source}: expected an array of exact redirect URI strings`);\n }\n return value.map((entry, index) =>\n coerceAbsoluteHttpsUrl(entry, `${source}[${index}]`, { allowHttpForLocalhost: true }),\n );\n}\n\nconst DISABLED_OAUTH_CONFIG: ParsedOAuthConfig = {\n enabled: false,\n issuerUrl: \"\",\n clientId: \"\",\n clientSecret: \"\",\n tokenEndpointAuthMethod: \"client_secret_post\",\n redirectUris: [],\n approvalTtlSeconds: DEFAULT_APPROVAL_TTL_SECONDS,\n};\n\n/**\n * Parse the `server.oauth` config block. Throws on invalid input.\n *\n * When disabled (or absent), only `enabled` is consulted — a\n * partially-filled disabled block is legal. When enabled, `issuerUrl`,\n * `clientId`, and (unless auth method is `none`) `clientSecret` are\n * required. `redirectUris` MAY be empty: that is \"setup mode\" — the\n * discovery documents are served so ChatGPT can create the app, but every\n * authorization attempt is refused until the exact per-app callback URL\n * (shown in ChatGPT's app management page) is added to the allowlist.\n */\nexport function parseOAuthConfig(raw: unknown): ParsedOAuthConfig {\n if (raw === undefined) return { ...DISABLED_OAUTH_CONFIG };\n if (!isPlainObject(raw)) {\n throw new Error(\"Invalid server.oauth: expected a JSON object\");\n }\n const input = raw as OAuthConfigInput;\n const enabled = input.enabled === undefined ? false : coerceBoolean(input.enabled, \"server.oauth.enabled\");\n if (!enabled) return { ...DISABLED_OAUTH_CONFIG };\n\n const issuerUrl =\n input.issuerUrl === undefined\n ? \"\"\n : coerceAbsoluteHttpsUrl(input.issuerUrl, \"server.oauth.issuerUrl\", { allowHttpForLocalhost: true });\n const clientId =\n input.clientId === undefined ? \"\" : coerceNonEmptyString(input.clientId, \"server.oauth.clientId\");\n const clientSecret =\n input.clientSecret === undefined ? \"\" : coerceNonEmptyString(input.clientSecret, \"server.oauth.clientSecret\");\n const tokenEndpointAuthMethod =\n input.tokenEndpointAuthMethod === undefined\n ? \"client_secret_post\"\n : coerceTokenAuthMethod(input.tokenEndpointAuthMethod, \"server.oauth.tokenEndpointAuthMethod\");\n const redirectUris =\n input.redirectUris === undefined ? [] : coerceRedirectUris(input.redirectUris, \"server.oauth.redirectUris\");\n const approvalTtlSeconds =\n input.approvalTtlSeconds === undefined\n ? DEFAULT_APPROVAL_TTL_SECONDS\n : coercePositiveInteger(input.approvalTtlSeconds, \"server.oauth.approvalTtlSeconds\");\n\n if (issuerUrl === \"\") {\n throw new Error(\"Invalid server.oauth.issuerUrl: required when enabled (absolute https URL)\");\n }\n if (clientId === \"\") {\n throw new Error(\"Invalid server.oauth.clientId: required when enabled (non-empty string)\");\n }\n if (tokenEndpointAuthMethod !== \"none\" && clientSecret === \"\") {\n throw new Error(\n `Invalid server.oauth.clientSecret: required when enabled and tokenEndpointAuthMethod is \"${tokenEndpointAuthMethod}\"`,\n );\n }\n\n return {\n enabled: true,\n issuerUrl,\n clientId,\n clientSecret,\n tokenEndpointAuthMethod,\n redirectUris,\n approvalTtlSeconds,\n };\n}\n\n// ── Env overrides (REMNIC_OAUTH_*) ───────────────────────────────────────────\n\n/**\n * Build the OAuth config overrides from env vars (REMNIC_OAUTH_*). The\n * merged result goes back through `parseOAuthConfig`, so invalid values\n * throw the same precise messages.\n */\nexport function readOAuthEnvOverrides(): Record<string, unknown> {\n const overrides: Record<string, unknown> = {};\n if (process.env.REMNIC_OAUTH_ENABLED !== undefined) overrides.enabled = process.env.REMNIC_OAUTH_ENABLED;\n if (process.env.REMNIC_OAUTH_ISSUER_URL !== undefined) overrides.issuerUrl = process.env.REMNIC_OAUTH_ISSUER_URL;\n if (process.env.REMNIC_OAUTH_CLIENT_ID !== undefined) overrides.clientId = process.env.REMNIC_OAUTH_CLIENT_ID;\n if (process.env.REMNIC_OAUTH_CLIENT_SECRET !== undefined) {\n overrides.clientSecret = process.env.REMNIC_OAUTH_CLIENT_SECRET;\n }\n if (process.env.REMNIC_OAUTH_TOKEN_AUTH_METHOD !== undefined) {\n overrides.tokenEndpointAuthMethod = process.env.REMNIC_OAUTH_TOKEN_AUTH_METHOD;\n }\n if (process.env.REMNIC_OAUTH_REDIRECT_URIS !== undefined) {\n overrides.redirectUris = process.env.REMNIC_OAUTH_REDIRECT_URIS.split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n }\n if (process.env.REMNIC_OAUTH_APPROVAL_TTL_SECONDS !== undefined) {\n overrides.approvalTtlSeconds = process.env.REMNIC_OAUTH_APPROVAL_TTL_SECONDS;\n }\n return overrides;\n}\n\n/** Merge env overrides over the file block and parse the result. */\nexport function applyOAuthEnvOverrides(fileBlock: unknown): ParsedOAuthConfig {\n // A present-but-non-object `server.oauth` (e.g. `true` or a string) is\n // invalid config, not \"disabled\". Hand it straight to parseOAuthConfig,\n // which throws a precise error, instead of silently coercing it to `{}`\n // and treating OAuth as off (repo rule: reject invalid input).\n if (fileBlock !== undefined && !isPlainObject(fileBlock)) {\n return parseOAuthConfig(fileBlock);\n }\n const overrides = readOAuthEnvOverrides();\n const merged: Record<string, unknown> = isPlainObject(fileBlock) ? { ...fileBlock } : {};\n for (const [key, value] of Object.entries(overrides)) {\n merged[key] = value;\n }\n return parseOAuthConfig(merged);\n}\n\n// ── Small crypto helpers ─────────────────────────────────────────────────────\n\n/**\n * Constant-time string equality via fixed-size SHA-256 digests. Digest\n * comparison never throws on length mismatch and does not leak length\n * or content timing. Hash equality implies content equality.\n */\nfunction timingSafeStringEqual(a: string, b: string): boolean {\n if (typeof a !== \"string\" || typeof b !== \"string\") return false;\n const digestA = createHash(\"sha256\").update(a, \"utf8\").digest();\n const digestB = createHash(\"sha256\").update(b, \"utf8\").digest();\n return timingSafeEqual(digestA, digestB);\n}\n\nfunction randomHex(bytes: number): string {\n return randomBytes(bytes).toString(\"hex\");\n}\n\n/**\n * Human-readable approval ref: 8 random bytes rendered in base36\n * (~62 bits, 13 chars max). Unguessable in practice, but knowing the ref\n * alone never authorizes anything — approval additionally requires the\n * operator bearer token.\n */\nfunction newApprovalRef(): string {\n return BigInt(`0x${randomHex(8)}`).toString(36);\n}\n\n// ── Pending-transaction store ────────────────────────────────────────────────\n\nexport interface PendingTransaction {\n /** High-entropy public id; key for the poll endpoint. */\n txn: string;\n /** Separate high-entropy secret required alongside `txn` to poll. */\n pollSecret: string;\n /** Human-visible approval ref for the CLI. */\n ref: string;\n clientId: string;\n redirectUri: string;\n scopes: string[];\n resource: string | undefined;\n state: string | undefined;\n codeChallenge: string;\n createdAt: number;\n expiresAt: number;\n outcome?: { kind: \"approved\"; code: string } | { kind: \"denied\" };\n}\n\ninterface AuthorizationCodeEntry {\n code: string;\n clientId: string;\n redirectUri: string;\n codeChallenge: string;\n resource: string | undefined;\n expiresAt: number;\n consumed: boolean;\n}\n\nexport class OAuthState {\n private readonly pending = new Map<string, PendingTransaction>();\n private readonly codes = new Map<string, AuthorizationCodeEntry>();\n private readonly config: ParsedOAuthConfig;\n\n constructor(config: ParsedOAuthConfig) {\n this.config = config;\n }\n\n get approvalTtlMs(): number {\n return this.config.approvalTtlSeconds * 1000;\n }\n\n /** Delete expired entries. Run before each lookup. */\n private gc(now: number): void {\n // Expire codes first so the pending check below sees the post-GC code\n // set.\n for (const [key, code] of this.codes) {\n if (code.expiresAt < now) this.codes.delete(key);\n }\n for (const [key, txn] of this.pending) {\n if (txn.expiresAt >= now) continue;\n // Keep an APPROVED transaction alive while its authorization code is\n // still live, even past the pending TTL: an approval made just\n // before the deadline (or under a short approvalTtlSeconds) must\n // still be pollable+redirectable until the code itself expires.\n if (txn.outcome?.kind === \"approved\" && this.codes.has(txn.outcome.code)) continue;\n this.pending.delete(key);\n }\n }\n\n createPending(args: {\n clientId: string;\n redirectUri: string;\n scopes: string[];\n resource: string | undefined;\n state: string | undefined;\n codeChallenge: string;\n }): PendingTransaction {\n const now = Date.now();\n this.gc(now);\n const txn: PendingTransaction = {\n txn: randomHex(HIGH_ENTROPY_HEX_BYTES),\n pollSecret: randomHex(HIGH_ENTROPY_HEX_BYTES),\n ref: newApprovalRef(),\n clientId: args.clientId,\n redirectUri: args.redirectUri,\n scopes: args.scopes,\n resource: args.resource,\n state: args.state,\n codeChallenge: args.codeChallenge,\n createdAt: now,\n expiresAt: now + this.approvalTtlMs,\n };\n this.pending.set(txn.txn, txn);\n return txn;\n }\n\n listPending(): PendingTransaction[] {\n const now = Date.now();\n this.gc(now);\n return Array.from(this.pending.values())\n .filter((txn) => !txn.outcome)\n .sort((a, b) => (a.createdAt === b.createdAt ? a.txn.localeCompare(b.txn) : a.createdAt - b.createdAt));\n }\n\n findByRef(ref: string): PendingTransaction | undefined {\n const now = Date.now();\n this.gc(now);\n for (const txn of this.pending.values()) {\n if (txn.ref === ref) return txn;\n }\n return undefined;\n }\n\n /**\n * Approve a pending transaction by ref. Returns the authorization code.\n * Re-approval of an already-approved txn returns the same code so the\n * operator can recover from a client-side miss. Throws on\n * missing/expired/denied.\n */\n approveByRef(ref: string): { code: string; txn: PendingTransaction } {\n const now = Date.now();\n this.gc(now);\n const txn = this.findByRef(ref);\n if (!txn) throw new Error(\"unknown or expired ref\");\n if (txn.outcome) {\n if (txn.outcome.kind === \"approved\") return { code: txn.outcome.code, txn };\n throw new Error(\"denied\");\n }\n const code = randomHex(HIGH_ENTROPY_HEX_BYTES);\n this.codes.set(code, {\n code,\n clientId: txn.clientId,\n redirectUri: txn.redirectUri,\n codeChallenge: txn.codeChallenge,\n resource: txn.resource,\n expiresAt: now + AUTHORIZATION_CODE_TTL_MS,\n consumed: false,\n });\n txn.outcome = { kind: \"approved\", code };\n return { code, txn };\n }\n\n denyByRef(ref: string): void {\n const now = Date.now();\n this.gc(now);\n const txn = this.findByRef(ref);\n if (!txn) throw new Error(\"unknown or expired ref\");\n if (txn.outcome) {\n if (txn.outcome.kind === \"denied\") return; // idempotent\n throw new Error(\"already approved\");\n }\n txn.outcome = { kind: \"denied\" };\n }\n\n /** Redirect target carrying the code (+ state) back to the client. */\n buildRedirect(txn: PendingTransaction, code: string): string {\n const url = new URL(txn.redirectUri);\n url.searchParams.set(\"code\", code);\n if (txn.state) url.searchParams.set(\"state\", txn.state);\n return url.toString();\n }\n\n /** Poll status; `undefined` on wrong pollSecret. */\n poll(\n txnId: string,\n pollSecret: string,\n ):\n | { status: \"pending\" }\n | { status: \"approved\"; redirect: string }\n | { status: \"denied\" }\n | { status: \"expired\" }\n | undefined {\n const now = Date.now();\n this.gc(now);\n const txn = this.pending.get(txnId);\n if (!txn) return { status: \"expired\" };\n if (!timingSafeStringEqual(txn.pollSecret, pollSecret)) return undefined;\n if (!txn.outcome) {\n if (txn.expiresAt < now) return { status: \"expired\" };\n return { status: \"pending\" };\n }\n if (txn.outcome.kind === \"denied\") return { status: \"denied\" };\n // Approved, but only hand back the redirect while the authorization\n // code is still live. The code TTL (120 s) is shorter than the txn TTL\n // (approvalTtlSeconds, default 600 s), so a slow browser poll could\n // otherwise be redirected with a code that has already expired,\n // failing the token exchange. Once the code is gone (expired or\n // already exchanged), report expired so the client restarts cleanly.\n if (!this.peekCode(txn.outcome.code)) return { status: \"expired\" };\n return { status: \"approved\", redirect: this.buildRedirect(txn, txn.outcome.code) };\n }\n\n /**\n * Read a code entry without consuming it (SDK PKCE verification path).\n * Returns undefined on unknown/expired/consumed.\n */\n peekCode(code: string): AuthorizationCodeEntry | undefined {\n const now = Date.now();\n this.gc(now);\n const entry = this.codes.get(code);\n if (!entry || entry.consumed || entry.expiresAt < now) return undefined;\n return entry;\n }\n\n /**\n * Consume a code after the SDK verified PKCE. Enforces single use and\n * the client/redirect/resource bindings. Returns the entry on success,\n * undefined on any mismatch.\n */\n takeCode(args: {\n code: string;\n clientId: string;\n redirectUri: string | undefined;\n resource: string | undefined;\n }): AuthorizationCodeEntry | undefined {\n const entry = this.peekCode(args.code);\n if (!entry) return undefined;\n // Burn the code before the binding checks: a code that reaches the\n // exchange step is spent regardless of outcome (OAuth 2.1 single use).\n entry.consumed = true;\n if (!timingSafeStringEqual(entry.clientId, args.clientId)) return undefined;\n // The token request MAY omit redirect_uri only when the authorization\n // request used the client's single registered URI; when provided it\n // must match the bound value exactly.\n if (args.redirectUri !== undefined && entry.redirectUri !== args.redirectUri) return undefined;\n if (entry.resource !== undefined) {\n if (args.resource === undefined) return undefined;\n if (entry.resource !== args.resource) return undefined;\n }\n return entry;\n }\n\n /**\n * Un-burn a code consumed by takeCode when the downstream token persist\n * fails (disk full, permissions). Lets the client retry the exchange\n * instead of being forced through a fresh authorize+approve. Only\n * revives an entry still present and within TTL; a binding/PKCE\n * mismatch is NOT revived (that is a genuine client error, and OAuth\n * 2.1 revokes a code presented incorrectly).\n */\n reviveCode(code: string): void {\n const entry = this.codes.get(code);\n if (entry && entry.expiresAt >= Date.now()) {\n entry.consumed = false;\n }\n }\n}\n\n// ── HTML approval page (auto-polling) ────────────────────────────────────────\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nfunction renderApprovalPage(args: { txn: PendingTransaction; issuerUrl: string; command: string }): string {\n const { txn, issuerUrl, command } = args;\n // The page NEVER asks for credentials. It displays the approval ref and\n // CLI instructions, then polls /oauth/authorize/poll with the\n // per-transaction pollSecret. On approval the browser follows the\n // redirect to the client's callback.\n const safeRef = escapeHtml(txn.ref);\n const safeClientId = escapeHtml(txn.clientId);\n const safeRedirect = escapeHtml(txn.redirectUri);\n const safeResource = txn.resource ? escapeHtml(txn.resource) : \"\";\n const safeCommand = escapeHtml(command);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Remnic MCP authorization</title>\n<style>\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; color: #1a1a1a; }\n h1 { font-size: 1.4rem; }\n .ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1.1rem; background: #f4f4f5; padding: 0.25rem 0.5rem; border-radius: 4px; }\n details { margin: 1rem 0; padding: 0.75rem 1rem; background: #fafafa; border: 1px solid #e4e4e7; border-radius: 6px; }\n summary { cursor: pointer; font-weight: 600; }\n code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem; }\n pre { background: #f4f4f5; padding: 0.75rem; border-radius: 4px; overflow-x: auto; }\n #status { margin-top: 1.5rem; padding: 0.75rem 1rem; border-radius: 6px; font-weight: 600; }\n .pending { background: #fef9c3; color: #713f12; }\n .approved { background: #dcfce7; color: #14532d; }\n .denied { background: #fee2e2; color: #7f1d1d; }\n .expired { background: #f3f4f6; color: #374151; }\n</style>\n</head>\n<body>\n<h1>Remnic MCP authorization pending</h1>\n<p>An external application has requested access to your Remnic MCP server.\nOpen a terminal on the Remnic host and run:</p>\n<pre>${safeCommand}</pre>\n<p>Approval ref: <span class=\"ref\">${safeRef}</span></p>\n<details>\n <summary>Request details</summary>\n <ul>\n <li>Client ID: <code>${safeClientId}</code></li>\n <li>Redirect URI: <code>${safeRedirect}</code></li>\n ${safeResource ? `<li>Resource: <code>${safeResource}</code></li>` : \"\"}\n <li>Scopes: <code>${escapeHtml(txn.scopes.join(\" \")) || \"(none requested)\"}</code></li>\n </ul>\n</details>\n<div id=\"status\" class=\"pending\">Waiting for operator approval…</div>\n<script>\n(function () {\n var statusEl = document.getElementById(\"status\");\n var txn = ${JSON.stringify(txn.txn)};\n var secret = ${JSON.stringify(txn.pollSecret)};\n var issuer = ${JSON.stringify(issuerUrl)};\n var done = false;\n function render(result) {\n if (result.status === \"pending\") return;\n done = true;\n if (result.status === \"approved\") {\n statusEl.className = \"approved\";\n statusEl.textContent = \"Approved — redirecting\\\\u2026\";\n window.location.replace(result.redirect);\n } else if (result.status === \"denied\") {\n statusEl.className = \"denied\";\n statusEl.textContent = \"Denied by operator. Return to the calling app to retry or cancel.\";\n } else {\n statusEl.className = \"expired\";\n statusEl.textContent = \"This request expired. Return to the calling app to retry.\";\n }\n }\n function poll() {\n if (done) return;\n fetch(issuer + \"/oauth/authorize/poll\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ txn: txn, pollSecret: secret }),\n })\n .then(function (res) {\n if (!res.ok) throw new Error(\"poll failed\");\n return res.json();\n })\n .then(render)\n .catch(function () { /* transient; next interval retries */ });\n }\n poll();\n setInterval(poll, 3000);\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ── Provider (SDK OAuthServerProvider implementation) ────────────────────────\n\n/**\n * Static single-client provider backed by the pending-approval store and\n * the Remnic token store. All protocol validation (PKCE, client auth,\n * redirect membership, param shapes) happens in the SDK handlers; this\n * provider only implements the Remnic-specific decisions.\n */\nclass RemnicOAuthProvider implements OAuthServerProvider {\n private readonly config: ParsedOAuthConfig;\n private readonly state: OAuthState;\n private readonly staticClient: OAuthClientInformationFull;\n /** Token-store override for tests; production uses the default path. */\n private readonly tokensPath?: string;\n\n constructor(config: ParsedOAuthConfig, state: OAuthState, tokensPath?: string) {\n this.config = config;\n this.state = state;\n this.tokensPath = tokensPath;\n this.staticClient = {\n client_id: config.clientId,\n // With `none`, the client is public: no secret is stored, so the\n // SDK's client-auth middleware will not demand one. Otherwise the\n // secret is REQUIRED on every token request.\n ...(config.tokenEndpointAuthMethod === \"none\" ? {} : { client_secret: config.clientSecret }),\n redirect_uris: config.redirectUris,\n token_endpoint_auth_method: config.tokenEndpointAuthMethod,\n grant_types: [\"authorization_code\"],\n response_types: [\"code\"],\n };\n }\n\n get clientsStore(): OAuthRegisteredClientsStore {\n const client = this.staticClient;\n return {\n getClient: (clientId: string) => (clientId === client.client_id ? client : undefined),\n // No registerClient: dynamic client registration is deliberately\n // NOT supported — the single client is pre-registered via config.\n };\n }\n\n async authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void> {\n const txn = this.state.createPending({\n clientId: client.client_id,\n redirectUri: params.redirectUri,\n scopes: params.scopes ?? [],\n resource: params.resource?.href,\n state: params.state,\n codeChallenge: params.codeChallenge,\n });\n log.info(\n `OAuth authorization pending: ref=${txn.ref} client=${txn.clientId} redirect=${txn.redirectUri} — ` +\n `run \\`remnic oauth approve ${txn.ref}\\` to approve`,\n );\n res\n .status(200)\n .type(\"html\")\n .send(\n renderApprovalPage({\n txn,\n issuerUrl: this.config.issuerUrl.replace(/\\/+$/, \"\"),\n command: `remnic oauth approve ${txn.ref}`,\n }),\n );\n }\n\n async challengeForAuthorizationCode(\n client: OAuthClientInformationFull,\n authorizationCode: string,\n ): Promise<string> {\n const entry = this.state.peekCode(authorizationCode);\n if (!entry || !timingSafeStringEqual(entry.clientId, client.client_id)) {\n throw new InvalidGrantError(\"authorization code is invalid, expired, or already used\");\n }\n return entry.codeChallenge;\n }\n\n async exchangeAuthorizationCode(\n client: OAuthClientInformationFull,\n authorizationCode: string,\n _codeVerifier?: string,\n redirectUri?: string,\n resource?: URL,\n ): Promise<OAuthTokens> {\n const entry = this.state.takeCode({\n code: authorizationCode,\n clientId: client.client_id,\n redirectUri,\n resource: resource?.href,\n });\n if (!entry) {\n throw new InvalidGrantError(\"authorization code is invalid, expired, or already used\");\n }\n // Mint a fresh Remnic connector token as the OAuth access token.\n // commitTokenEntry replaces the previous `chatgpt` entry, so\n // re-linking rotates the token — documented behavior. If the\n // token-store write fails (disk full, permissions), revive the code\n // so the client can retry the exchange instead of being forced\n // through a fresh authorize+approve (AGENTS.md #14 — don't burn old\n // state before the replacement is confirmed).\n const tokenEntry = buildTokenEntry(CHATGPT_CONNECTOR_ID);\n try {\n commitTokenEntry(tokenEntry, this.tokensPath);\n } catch (err) {\n this.state.reviveCode(authorizationCode);\n log.warn(\n `OAuth token persist failed; authorization code revived for retry: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n throw new ServerError(\"failed to persist the issued token; retry the exchange\");\n }\n log.info(`OAuth token issued for connector \"${CHATGPT_CONNECTOR_ID}\" (client=${client.client_id})`);\n return {\n access_token: tokenEntry.token,\n token_type: \"Bearer\",\n };\n }\n\n async exchangeRefreshToken(): Promise<OAuthTokens> {\n throw new UnsupportedGrantTypeError(\n \"refresh_token grant is not supported; re-link the app to rotate the token\",\n );\n }\n\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n const valid = getAllValidTokensCached(this.tokensPath).some((candidate) =>\n timingSafeStringEqual(candidate, token),\n );\n if (!valid) {\n throw new InvalidTokenError(\"unknown or revoked token\");\n }\n return {\n token,\n clientId: this.config.clientId,\n scopes: [],\n };\n }\n}\n\n// ── Express app builder ─────────────────────────────────────────────────────\n\nexport interface OAuthAppBundle {\n app: Express;\n state: OAuthState;\n /** Paths owned by the OAuth facade (exact matches). */\n ownedExactPaths: string[];\n /** Path prefixes owned by the OAuth facade. */\n ownedPathPrefixes: string[];\n}\n\n/**\n * Build the express app hosting all OAuth endpoints.\n *\n * SDK-owned: `/authorize`, `/token`,\n * `/.well-known/oauth-authorization-server`,\n * `/.well-known/oauth-protected-resource/mcp`.\n * Remnic-owned: `/oauth/authorize/poll` (public, secret-gated),\n * `/oauth/pending[...]` (operator-only), plus a bare\n * `/.well-known/oauth-protected-resource` alias for clients that do not\n * implement RFC 9728 path insertion.\n *\n * @param authCtxLookup returns the operator-authorization context the core\n * access server computed for this request; the facade never validates\n * bearer tokens itself.\n */\nexport function buildOAuthApp(\n config: ParsedOAuthConfig,\n authCtxLookup: (req: IncomingMessage) => { authorized: boolean },\n deps?: { tokensPath?: string },\n): OAuthAppBundle {\n const state = new OAuthState(config);\n const provider = new RemnicOAuthProvider(config, state, deps?.tokensPath);\n const issuer = new URL(config.issuerUrl);\n const resourceServerUrl = new URL(\"/mcp\", issuer);\n\n if (config.redirectUris.length === 0) {\n log.warn(\n \"server.oauth.redirectUris is empty — OAuth discovery is live but every authorization will be refused \" +\n \"until the exact callback URL from ChatGPT's app management page is added (setup mode).\",\n );\n }\n\n const app = express();\n app.disable(\"x-powered-by\");\n\n // SDK metadata, truthfully narrowed to what this server actually\n // accepts: a single client-auth method and the authorization_code grant.\n const oauthMetadata = createOAuthMetadata({ provider, issuerUrl: issuer });\n oauthMetadata.token_endpoint_auth_methods_supported = [config.tokenEndpointAuthMethod];\n oauthMetadata.grant_types_supported = [\"authorization_code\"];\n\n // SDK handlers own the protocol-critical endpoints.\n app.use(\"/authorize\", authorizationHandler({ provider }));\n app.use(\"/token\", tokenHandler({ provider }));\n app.use(mcpAuthMetadataRouter({ oauthMetadata, resourceServerUrl, resourceName: \"Remnic\" }));\n\n // Alias: serve the protected-resource document at the bare well-known\n // path too (the SDK mounts only the RFC 9728 path-inserted variant).\n app.get(\"/.well-known/oauth-protected-resource\", (_req: Request, res: Response) => {\n res.status(200).json({\n resource: resourceServerUrl.href,\n authorization_servers: [oauthMetadata.issuer],\n resource_name: \"Remnic\",\n });\n });\n\n // Remnic-specific endpoints below parse JSON bodies.\n app.use(express.json({ limit: \"32kb\" }));\n // Rate limiters for the Remnic-owned endpoints (the SDK already\n // rate-limits /authorize and /token). Two policies:\n // - pollLimiter: generous. The approval page polls every ~3 s for up\n // to approvalTtlSeconds, so a full flow is ~200 polls; 120/min per IP\n // leaves headroom for several concurrent flows without locking the\n // browser out of its own approval.\n // - operatorReadLimiter / operatorDecisionLimiter: operator list vs\n // approve/deny. Both are low-frequency, so tight bounds blunt\n // brute-forcing an approval ref while never impeding a human\n // operator; separate buckets keep listing from starving decisions.\n // Shared limiter shape: deterministic JSON 429 (stable `error` code) so\n // ChatGPT and the CLI get a machine-parseable body, never default HTML.\n const makeLimiter = (max: number) =>\n rateLimit({\n windowMs: 60_000,\n max,\n standardHeaders: true,\n legacyHeaders: false,\n message: { error: \"rate_limited\", error_description: \"too many requests; retry later\" },\n });\n const pollLimiter = makeLimiter(120);\n // Read listing gets its own bucket so polling `pending` can never\n // exhaust the decision (approve/deny) capacity below.\n const operatorReadLimiter = makeLimiter(60);\n const operatorDecisionLimiter = makeLimiter(30);\n\n // ── Poll endpoint (public; gated by txn id + pollSecret) ──────────────\n app.post(\"/oauth/authorize/poll\", pollLimiter, (req: Request, res: Response) => {\n const body: unknown = req.body;\n if (\n !isPlainObject(body) ||\n typeof body.txn !== \"string\" ||\n typeof body.pollSecret !== \"string\" ||\n body.txn.length === 0 ||\n body.pollSecret.length === 0\n ) {\n res.status(400).json({ error: \"invalid_request\", error_description: \"txn and pollSecret are required\" });\n return;\n }\n const result = state.poll(body.txn, body.pollSecret);\n if (result === undefined) {\n res.status(401).json({ error: \"invalid_request\", error_description: \"unknown txn or wrong pollSecret\" });\n return;\n }\n res.status(200).json(result);\n });\n\n // ── Operator-only endpoints ────────────────────────────────────────────\n function operatorGuard(req: Request, res: Response): boolean {\n if (authCtxLookup(req).authorized === true) return true;\n res.status(401).json({ error: \"unauthorized\", error_description: \"operator authentication required\" });\n return false;\n }\n\n app.get(\"/oauth/pending\", operatorReadLimiter, (req: Request, res: Response) => {\n if (!operatorGuard(req, res)) return;\n const pending = state.listPending().map((txn) => ({\n ref: txn.ref,\n clientId: txn.clientId,\n redirectUri: txn.redirectUri,\n scopes: txn.scopes,\n resource: txn.resource ?? null,\n createdAt: new Date(txn.createdAt).toISOString(),\n expiresAt: new Date(txn.expiresAt).toISOString(),\n }));\n res.status(200).json({ pending });\n });\n\n function decisionHandler(action: \"approve\" | \"deny\") {\n return (req: Request, res: Response) => {\n if (!operatorGuard(req, res)) return;\n const ref = req.params.ref;\n if (!ref) {\n res.status(400).json({ error: \"invalid_request\", error_description: \"ref is required\" });\n return;\n }\n try {\n if (action === \"approve\") {\n const { txn, code } = state.approveByRef(ref);\n res.status(200).json({ ref, status: \"approved\", redirect: state.buildRedirect(txn, code) });\n } else {\n state.denyByRef(ref);\n res.status(200).json({ ref, status: \"denied\" });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message === \"unknown or expired ref\") {\n res.status(404).json({ error: \"invalid_request\", error_description: message });\n return;\n }\n if (message === \"denied\" || message === \"already approved\") {\n res.status(409).json({ error: \"invalid_request\", error_description: message });\n return;\n }\n log.warn(`OAuth ${action} endpoint unexpected error: ${message}`);\n res.status(500).json({ error: \"server_error\", error_description: \"internal error\" });\n }\n };\n }\n\n app.post(\"/oauth/pending/:ref/approve\", operatorDecisionLimiter, decisionHandler(\"approve\"));\n app.post(\"/oauth/pending/:ref/deny\", operatorDecisionLimiter, decisionHandler(\"deny\"));\n\n return {\n app,\n state,\n ownedExactPaths: [\n \"/authorize\",\n \"/token\",\n \"/.well-known/oauth-authorization-server\",\n \"/.well-known/oauth-protected-resource\",\n \"/.well-known/oauth-protected-resource/mcp\",\n ],\n ownedPathPrefixes: [\"/oauth/\"],\n };\n}\n\n// ── External request handler (mount point) ──────────────────────────────────\n\n/**\n * Adapt the OAuth express app to the core access server's\n * `externalRequestHandler` hook. Ownership is decided by pathname BEFORE\n * delegating to express (deterministic — never inferred from express\n * fallthrough), so non-OAuth requests always continue down the normal\n * core pipeline.\n */\nexport function buildOAuthRequestHandler(\n config: ParsedOAuthConfig,\n deps?: { tokensPath?: string },\n): (req: IncomingMessage, res: ServerResponse, ctx: { authorized: boolean }) => Promise<boolean> {\n if (!config.enabled) {\n return async () => false;\n }\n // Core computes the operator-authorization ctx per request and hands it\n // to the hook; stash it per-request so express handlers can read it\n // without ever re-validating headers themselves.\n const ctxByRequest = new WeakMap<IncomingMessage, { authorized: boolean }>();\n const bundle = buildOAuthApp(config, (req) => ctxByRequest.get(req) ?? { authorized: false }, deps);\n const exactPaths = new Set(bundle.ownedExactPaths);\n\n return (req, res, ctx) => {\n let pathname: string;\n try {\n pathname = new URL(req.url ?? \"/\", \"http://placeholder\").pathname;\n } catch {\n return Promise.resolve(false);\n }\n const owned = exactPaths.has(pathname) || bundle.ownedPathPrefixes.some((prefix) => pathname.startsWith(prefix));\n if (!owned) return Promise.resolve(false);\n\n ctxByRequest.set(req, ctx);\n const { promise, resolve } = Promise.withResolvers<boolean>();\n const finish = () => resolve(true);\n res.on(\"finish\", finish);\n res.on(\"close\", finish);\n // An express app instance is a Node request listener; the third\n // argument runs when no route matched or a handler errored.\n bundle.app(req as Request, res as Response, (err: unknown) => {\n if (!res.headersSent) {\n if (err) {\n log.warn(`OAuth facade error: ${err instanceof Error ? err.message : String(err)}`);\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify({ error: \"server_error\", error_description: \"internal error\" }));\n } else {\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify({ error: \"not_found\", error_description: \"unknown OAuth endpoint\" }));\n }\n }\n resolve(true);\n });\n return promise;\n };\n}\n","/**\n * @remnic/server — environment-sourced server config.\n *\n * Extracted from index.ts (issue #2029): keeps the env → server-config surface\n * in one place and keeps index.ts under its structural size ceiling. Values are\n * returned as raw strings; `parseServerConfig` coerces/validates them.\n */\n\nimport type { ServerConfig } from \"./index.js\";\n\n/**\n * Read an env var by its current `REMNIC_` name, falling back to the legacy\n * `ENGRAM_` name.\n */\nexport function readCompatEnv(primary: string, legacy: string): string | undefined {\n return process.env[primary] ?? process.env[legacy];\n}\n\n/**\n * Collect server-config overrides sourced from environment variables. Merged\n * over file config (file < env < cli) by the server startup path.\n */\nexport function envOverrides(): Partial<ServerConfig[\"server\"]> & { remnic?: Record<string, unknown> } {\n const overrides: Record<string, unknown> = {};\n const remnic: Record<string, unknown> = {};\n\n const port = readCompatEnv(\"REMNIC_PORT\", \"ENGRAM_PORT\");\n const host = readCompatEnv(\"REMNIC_HOST\", \"ENGRAM_HOST\");\n const authToken = readCompatEnv(\"REMNIC_AUTH_TOKEN\", \"ENGRAM_AUTH_TOKEN\");\n const adminConsoleEnabled = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_ENABLED\", \"ENGRAM_ADMIN_CONSOLE_ENABLED\");\n const adminConsolePublicDir = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PUBLIC_DIR\", \"ENGRAM_ADMIN_CONSOLE_PUBLIC_DIR\");\n const adminConsolePrefillToken = readCompatEnv(\"REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN\", \"ENGRAM_ADMIN_CONSOLE_PREFILL_TOKEN\");\n const readinessOverride = process.env.REMNIC_READY_OVERRIDE;\n // issue #2029: size the global write rate limit from env. The standalone\n // server already honors `server.writeRateLimit*` in config; this makes it\n // settable via the launchd/systemd environment without editing config.json.\n const writeRateLimitMaxRequests = readCompatEnv(\n \"REMNIC_WRITE_RATE_LIMIT_MAX_REQUESTS\",\n \"ENGRAM_WRITE_RATE_LIMIT_MAX_REQUESTS\",\n );\n const writeRateLimitWindowMs = readCompatEnv(\n \"REMNIC_WRITE_RATE_LIMIT_WINDOW_MS\",\n \"ENGRAM_WRITE_RATE_LIMIT_WINDOW_MS\",\n );\n if (port) overrides.port = port;\n if (host) overrides.host = host;\n if (authToken) overrides.authToken = authToken;\n if (adminConsoleEnabled) overrides.adminConsoleEnabled = adminConsoleEnabled;\n if (adminConsolePublicDir) overrides.adminConsolePublicDir = adminConsolePublicDir;\n if (adminConsolePrefillToken) overrides.adminConsolePrefillToken = adminConsolePrefillToken;\n if (readinessOverride !== undefined) overrides.readinessOverride = readinessOverride;\n // Use `!== undefined` (not truthiness) so an explicitly-set empty/invalid\n // value still reaches parseServerConfig and is rejected, rather than being\n // silently dropped so file config wins (issue #2029 review).\n if (writeRateLimitMaxRequests !== undefined) overrides.writeRateLimitMaxRequests = writeRateLimitMaxRequests;\n if (writeRateLimitWindowMs !== undefined) overrides.writeRateLimitWindowMs = writeRateLimitWindowMs;\n\n if (process.env.OPENAI_API_KEY) remnic.openaiApiKey = process.env.OPENAI_API_KEY;\n const memoryDir = readCompatEnv(\"REMNIC_MEMORY_DIR\", \"ENGRAM_MEMORY_DIR\");\n if (memoryDir) remnic.memoryDir = memoryDir;\n\n return { ...overrides, ...(Object.keys(remnic).length > 0 ? { remnic } : {}) };\n}\n"],"mappings":"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","names":["log","log"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/server",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.35.1",
|
|
4
4
|
"description": "Standalone Remnic memory server — HTTP + MCP without OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
28
|
"express": "^4.21.2",
|
|
29
29
|
"express-rate-limit": "^8.5.2",
|
|
30
|
-
"@remnic/core": "^9.
|
|
30
|
+
"@remnic/core": "^9.35.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/express": "^4.17.21",
|
|
34
34
|
"tsup": "^8.5.1",
|
|
35
35
|
"typescript": "^5.9.3",
|
|
36
|
-
"@remnic/core": "^9.
|
|
36
|
+
"@remnic/core": "^9.35.1"
|
|
37
37
|
},
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"repository": {
|