@datasynx/agentic-ai-cartography 2.6.0 → 2.7.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scanners/spi.ts","../src/scanners/types.ts","../src/scanners/bookmarks.ts","../src/scanners/installed-apps.ts","../src/scanners/ports.ts","../src/scanners/confidence.ts","../src/scanners/connections.ts","../src/scanners/service-config.ts","../src/scanners/registry.ts","../src/scanners/loader.ts","../src/discovery/local.ts","../src/compliance/rulesets/baseline.ts","../src/compliance/rulesets/cis.ts","../src/compliance/rulesets/soc2.ts","../src/compliance/rulesets/iso27001.ts","../src/compliance/rulesets/registry.ts","../src/sinks/stdout.ts","../src/sinks/webhook.ts","../src/sinks/providers.ts","../src/sinks/provider-sink.ts","../src/sinks/index.ts","../src/drift.ts","../src/orgkey.ts","../src/anonymize.ts","../src/mcp/server.ts","../src/nlq/resolve.ts","../src/mcp/transports.ts","../src/semantic/hash.ts","../src/semantic/embeddings.ts","../src/semantic/store.ts","../src/semantic/search.ts","../src/store/sqlite.ts","../src/central/ingest.ts","../src/central/merge.ts","../src/central/anonymization.ts","../src/central/server.ts","../src/central/quota.ts","../src/mcp/start.ts"],"sourcesContent":["/**\n * Public Scanner SPI — the documented extension contract for out-of-tree\n * `@datasynx/scanner-*` packages.\n *\n * A plugin package default-exports `definePlugin({ name, register })`. Its\n * `register(api)` calls `api.registerScanner(scanner)` for each scanner it\n * provides. The core validates, namespaces (`plugin:<pkg>:<id>`), command-gates\n * and freezes each registered scanner — a plugin never imports core internals\n * beyond the published types here. Loading is opt-in (`config.plugins`) and a\n * broken plugin is isolated, never aborting discovery.\n */\n\nimport { z } from 'zod';\nimport type { Scanner } from './types.js';\n\n/** The only surface a plugin's `register()` touches. */\nexport interface ScannerPluginApi {\n /** Register a scanner the host will namespace, command-gate, and freeze. */\n registerScanner(scanner: Scanner): void;\n}\n\n/** The default-exported shape a `@datasynx/scanner-*` package implements. */\nexport interface ScannerPlugin {\n /** Human-readable plugin name (informational; the package name is the identity). */\n name: string;\n /** Register the plugin's scanners through the host-provided API. */\n register(api: ScannerPluginApi): void;\n}\n\n/**\n * Identity helper a plugin author default-exports. It only narrows the type so\n * editors guide authoring; it adds no runtime behaviour.\n */\nexport function definePlugin(plugin: ScannerPlugin): ScannerPlugin {\n return plugin;\n}\n\n/**\n * Zod shape validating a {@link Scanner} produced by an external plugin.\n * `platforms` uses the concrete `Platform` literals so an invalid platform is\n * rejected at registration; `detect`/`scan` are validated as callables only\n * (their async signatures cannot be expressed in zod) — behavioural safety comes\n * from the gated `ctx.run` wrapper plus the per-scanner runtime try/catch.\n */\nexport const ScannerShape = z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n platforms: z.union([\n z.literal('all'),\n z.array(z.enum(['linux', 'darwin', 'win32'])).nonempty(),\n ]),\n allowedCommands: z.array(z.string().min(1)).optional(),\n detect: z.custom<Scanner['detect']>((v) => typeof v === 'function', {\n message: 'detect must be a function',\n }),\n scan: z.custom<Scanner['scan']>((v) => typeof v === 'function', {\n message: 'scan must be a function',\n }),\n});\n\n/**\n * Validate an unknown value as a {@link Scanner}; throws `ZodError` on mismatch.\n * Returns the original value (the parsed result discards the live function\n * references), so the caller keeps the real `detect`/`scan` closures.\n */\nexport function validateScanner(value: unknown): Scanner {\n ScannerShape.parse(value);\n return value as Scanner;\n}\n","/**\n * Scanner plugin contract.\n *\n * A scanner detects whether it applies to the current machine, then produces a\n * read-only {@link ScanResult}: deterministic nodes/edges where the data is\n * structured enough, plus an optional raw report for an LLM to classify further.\n *\n * Modeled on Steampipe plugins / Backstage processors: new sources can be added\n * (in-tree or via `@datasynx/scanner-*` packages) and registered without forking\n * the core. Each scanner declares the commands it needs, feeding the safety layer.\n */\n\nimport type { Platform } from '../platform.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport type { BookmarkHost } from '../bookmarks.js';\nimport { checkReadOnly } from '../allowlist.js';\nimport { validateScanner } from './spi.js';\n\nexport interface ScanContext {\n /** Optional focus hint from the caller (e.g. tool names to look for). */\n hint?: string;\n /** The current platform. */\n platform: Platform;\n /** Allowlist-gated command runner (returns '' on error/blocked). */\n run: (cmd: string, opts?: { timeout?: number; env?: NodeJS.ProcessEnv }) => string;\n /**\n * Injectable seam: resolve a command to its path ('' if absent). Defaults to the\n * real `commandExists` when omitted. Lets scanners run deterministically in tests.\n */\n commandExists?: (cmd: string) => string;\n /** Injectable seam: raw listening-port output. Defaults to `scanListeningPorts`. */\n scanListeningPorts?: () => string;\n /** Injectable seam: raw established-connection output (3.2). Defaults to `scanEstablishedConnections`. */\n scanEstablishedConnections?: () => string;\n /** Injectable seam: cross-platform file search (3.2). Defaults to `findFiles`. */\n findFiles?: (dirs: string[], patterns: string[], maxDepth: number, limit: number) => string;\n /** Injectable seam: browser-bookmark host source. Defaults to `scanAllBookmarks`. */\n scanBookmarks?: () => Promise<BookmarkHost[]>;\n}\n\nexport interface ScanResult {\n /** Deterministically classified nodes. */\n nodes: DiscoveryNode[];\n /** Deterministically classified edges. */\n edges: DiscoveryEdge[];\n /** Optional raw text report for LLM-driven classification. */\n report?: string;\n}\n\nexport interface Scanner {\n /** Stable id, e.g. \"bookmarks\", \"installed-apps\", \"cloud-aws\". */\n id: string;\n /** Human-readable title. */\n title: string;\n /** Platforms this scanner supports, or 'all'. */\n platforms: Platform[] | 'all';\n /** Read-only commands this scanner may run (declared for the safety layer/docs). */\n allowedCommands?: string[];\n /** Cheap check whether the scanner applies here (e.g. a CLI is installed). */\n detect(ctx: ScanContext): boolean | Promise<boolean>;\n /** Perform the read-only scan. */\n scan(ctx: ScanContext): Promise<ScanResult>;\n}\n\n/** A typed registry of scanners with lazy, platform-aware selection. */\nexport class ScannerRegistry {\n private scanners = new Map<string, Scanner>();\n\n register(scanner: Scanner): this {\n if (this.scanners.has(scanner.id)) throw new Error(`scanner already registered: ${scanner.id}`);\n this.scanners.set(scanner.id, scanner);\n return this;\n }\n\n /**\n * Register a {@link Scanner} produced by an external plugin package. Validates\n * the shape (throws `ZodError` on mismatch), namespaces the id to\n * `plugin:<pkg>:<id>` (avoiding collisions with built-ins and across plugins),\n * wraps `scan()` so the scanner's `ctx.run` is gated by its declared\n * `allowedCommands` intersected with the central read-only allowlist\n * ({@link checkReadOnly}), and freezes the wrapper. Reuses the duplicate-id\n * guard in {@link register}.\n *\n * The gated runner delegates the actual execution to the host-supplied\n * `ctx.run` (the platform runner), so the global read-only floor still applies\n * and `allowedCommands` is a *second*, scanner-scoped least-privilege boundary.\n * A command runs only if its leading executable is declared AND the whole\n * command passes `checkReadOnly`; otherwise the runner returns `''` (the\n * documented \"blocked → ''\" contract).\n */\n registerExternal(pkg: string, scanner: Scanner): this {\n const valid = validateScanner(scanner);\n const id = `plugin:${pkg}:${valid.id}`;\n const declared = valid.allowedCommands ?? [];\n const wrapped: Scanner = Object.freeze({\n id,\n title: valid.title,\n platforms: valid.platforms,\n allowedCommands: declared,\n detect: (ctx: ScanContext) => valid.detect(ctx),\n scan: (ctx: ScanContext) =>\n valid.scan({\n ...ctx,\n run: (cmd, opts) => {\n const exe = cmd.trim().split(/\\s+/)[0] ?? '';\n if (!declared.includes(exe)) return '';\n if (!checkReadOnly(cmd).allowed) return '';\n return ctx.run(cmd, opts);\n },\n }),\n });\n return this.register(wrapped);\n }\n\n get(id: string): Scanner | undefined {\n return this.scanners.get(id);\n }\n\n list(): Scanner[] {\n return [...this.scanners.values()];\n }\n\n /** Scanners whose `platforms` include the given platform. */\n forPlatform(platform: Platform): Scanner[] {\n return this.list().filter((s) => s.platforms === 'all' || s.platforms.includes(platform));\n }\n}\n","import type { Scanner, ScanResult } from './types.js';\nimport type { DiscoveryNode } from '../types.js';\nimport { scanAllBookmarks } from '../bookmarks.js';\n\n/**\n * Hostname substrings that indicate a personal site — never catalogued, and the\n * hard floor for the 2.10 sharing policy (a personal host is forced to `'none'`\n * regardless of policy). Single-sourced here and shared via {@link isPersonalHost}\n * so the never-share list never forks.\n */\nexport const PERSONAL = [\n 'facebook.', 'instagram.', 'twitter.', 'x.com', 'tiktok.', 'reddit.', 'youtube.', 'netflix.',\n 'spotify.', 'twitch.', 'pinterest.', 'snapchat.', 'whatsapp.', 'amazon.', 'ebay.', 'aliexpress.',\n 'cnn.', 'bbc.', 'nytimes.', 'espn.', 'booking.', 'airbnb.', 'tripadvisor.', 'wikipedia.',\n];\n\n/** True when `host` matches a PERSONAL substring (case-insensitive). */\nexport function isPersonalHost(host: string): boolean {\n const h = host.toLowerCase();\n return PERSONAL.some((p) => h.includes(p));\n}\n\n/** Well-known business/SaaS hostnames → catalogued as saas_tool. */\nconst BUSINESS = [\n 'github.', 'gitlab.', 'bitbucket.', 'atlassian.', 'jira.', 'confluence.', 'notion.', 'linear.',\n 'slack.', 'zoom.', 'figma.', 'miro.', 'vercel.', 'netlify.', 'heroku.', 'datadog', 'sentry.',\n 'grafana.', 'pagerduty.', 'aws.amazon.', 'console.cloud.google', 'portal.azure', 'cloudflare.',\n 'hubspot.', 'salesforce.', 'stripe.', 'twilio.', 'sendgrid.', 'mailchimp.', 'segment.', 'mixpanel.',\n 'amplitude.', 'looker.', 'tableau.', 'snowflake.', 'databricks.', 'mongodb.', 'redis.', 'elastic.',\n 'openai.', 'anthropic.', 'huggingface.', 'docker.', 'npmjs.', 'pypi.', 'circleci.', 'travis-ci.',\n 'jenkins.', 'terraform.', 'hashicorp.', 'okta.', 'auth0.', '1password.', 'asana.', 'trello.', 'monday.',\n];\n\nfunction classify(hostname: string): { type: DiscoveryNode['type']; confidence: number } | null {\n const h = hostname.toLowerCase();\n if (isPersonalHost(h)) return null;\n if (BUSINESS.some((b) => h.includes(b))) return { type: 'saas_tool', confidence: 0.7 };\n // Internal/custom hosts (IPs or *.company.tld with non-standard ports) → web_service\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(h) || /\\.(internal|local|corp|lan)\\b/.test(h)) {\n return { type: 'web_service', confidence: 0.6 };\n }\n return null; // unknown public host — leave for LLM-driven classification\n}\n\nexport const bookmarksScanner: Scanner = {\n id: 'bookmarks',\n title: 'Browser bookmarks',\n platforms: 'all',\n detect: () => true,\n async scan(ctx): Promise<ScanResult> {\n const hosts = await (ctx.scanBookmarks ?? scanAllBookmarks)();\n const seen = new Set<string>();\n const nodes: DiscoveryNode[] = [];\n for (const host of hosts) {\n const klass = classify(host.hostname);\n if (!klass) continue;\n const id = `${klass.type}:${host.hostname}`;\n if (seen.has(id)) continue;\n seen.add(id);\n nodes.push({\n id, type: klass.type, name: host.hostname, discoveredVia: 'bookmark',\n confidence: klass.confidence, tags: ['bookmark'],\n metadata: { protocol: host.protocol, ...(host.port ? { port: host.port } : {}) },\n });\n }\n return { nodes, edges: [] };\n },\n};\n","import type { Scanner, ScanResult } from './types.js';\nimport type { DiscoveryNode } from '../types.js';\nimport { commandExists } from '../platform.js';\n\n/** Known CLIs/tools grouped by category — detected deterministically via `commandExists`. */\nconst KNOWN_TOOLS: Record<string, string[]> = {\n ide: ['code', 'code-insiders', 'cursor', 'windsurf', 'zed', 'nvim', 'vim', 'emacs', 'idea', 'webstorm', 'pycharm', 'goland', 'datagrip', 'clion', 'rider', 'phpstorm'],\n 'dev-tool': ['git', 'gh', 'docker', 'docker-compose', 'podman', 'kubectl', 'helm', 'terraform', 'ansible', 'vagrant', 'packer', 'consul', 'vault', 'nomad'],\n runtime: ['node', 'npm', 'pnpm', 'yarn', 'bun', 'deno', 'python', 'python3', 'pip', 'poetry', 'ruby', 'rails', 'java', 'mvn', 'gradle', 'go', 'cargo', 'rustc', 'php', 'composer', 'dotnet'],\n database: ['psql', 'mysql', 'mongosh', 'redis-cli', 'sqlite3', 'clickhouse-client'],\n cloud: ['aws', 'gcloud', 'az', 'heroku', 'fly', 'vercel', 'netlify', 'wrangler', 'supabase'],\n browser: ['google-chrome', 'chromium', 'firefox', 'brave', 'opera'],\n observability: ['prometheus', 'grafana-cli', 'datadog-agent', 'newrelic-agent'],\n};\n\nexport const installedAppsScanner: Scanner = {\n id: 'installed-apps',\n title: 'Installed apps & developer tools',\n platforms: 'all',\n allowedCommands: ['which', 'command', 'Get-Command'],\n detect: () => true,\n async scan(ctx): Promise<ScanResult> {\n const nodes: DiscoveryNode[] = [];\n const hintTerms = (ctx.hint ?? '').toLowerCase().split(/[\\s,]+/).filter(Boolean);\n for (const [category, tools] of Object.entries(KNOWN_TOOLS)) {\n for (const tool of tools) {\n const path = (ctx.commandExists ?? commandExists)(tool);\n if (!path) continue;\n const boosted = hintTerms.some((t) => tool.includes(t));\n nodes.push({\n id: `saas_tool:${tool}`,\n type: 'saas_tool',\n name: tool,\n discoveredVia: 'installed-app',\n confidence: boosted ? 0.95 : 0.9,\n tags: [category],\n metadata: { category, path },\n });\n }\n }\n return { nodes, edges: [] };\n },\n};\n","import type { Scanner, ScanResult } from './types.js';\nimport type { DiscoveryNode, NodeType } from '../types.js';\nimport { scanListeningPorts } from '../platform.js';\n\n/** Well-known listening ports → node type + service name. */\nexport const PORT_MAP: Record<number, { type: NodeType; service: string }> = {\n 5432: { type: 'database_server', service: 'postgresql' },\n 3306: { type: 'database_server', service: 'mysql' },\n 1433: { type: 'database_server', service: 'sqlserver' },\n 27017: { type: 'database_server', service: 'mongodb' },\n 9200: { type: 'database_server', service: 'elasticsearch' },\n 6379: { type: 'cache_server', service: 'redis' },\n 11211: { type: 'cache_server', service: 'memcached' },\n 9092: { type: 'message_broker', service: 'kafka' },\n 5672: { type: 'message_broker', service: 'rabbitmq' },\n 4222: { type: 'message_broker', service: 'nats' },\n 9090: { type: 'web_service', service: 'prometheus' },\n 3000: { type: 'web_service', service: 'http-app' },\n 8080: { type: 'web_service', service: 'http-app' },\n 8000: { type: 'web_service', service: 'http-app' },\n 80: { type: 'web_service', service: 'http' },\n 443: { type: 'web_service', service: 'https' },\n 8200: { type: 'web_service', service: 'vault' },\n 8500: { type: 'web_service', service: 'consul' },\n 2379: { type: 'web_service', service: 'etcd' },\n 5601: { type: 'web_service', service: 'kibana' },\n 15672: { type: 'web_service', service: 'rabbitmq-management' },\n};\n\n/** Extract distinct listening port numbers from ss/lsof/PowerShell output. */\nexport function extractListeningPorts(raw: string): number[] {\n const ports = new Set<number>();\n for (const m of raw.matchAll(/[:.](\\d{2,5})\\b/g)) {\n const p = Number(m[1]);\n if (p in PORT_MAP) ports.add(p);\n }\n return [...ports];\n}\n\nexport const portsScanner: Scanner = {\n id: 'local-ports',\n title: 'Local listening ports',\n platforms: 'all',\n allowedCommands: ['ss', 'lsof', 'Get-NetTCPConnection'],\n detect: () => true,\n async scan(ctx): Promise<ScanResult> {\n const raw = (ctx.scanListeningPorts ?? scanListeningPorts)();\n const nodes: DiscoveryNode[] = [];\n for (const port of extractListeningPorts(raw)) {\n const { type, service } = PORT_MAP[port]!;\n nodes.push({\n id: `${type}:localhost:${port}`,\n type,\n name: `${service} (:${port})`,\n discoveredVia: 'listening-port',\n confidence: 0.9,\n tags: ['local', service],\n metadata: { port, service, host: 'localhost' },\n });\n }\n return { nodes, edges: [] };\n },\n};\n","/**\n * Confidence rubric for inferred dependency edges (3.2).\n *\n * A single source of truth so every scanner that infers an edge draws its\n * `confidence` from the same strictly-ordered tier set, and downstream consumers\n * (compliance 3.4, anomaly 3.6) can rank evidence quality consistently. All values\n * are in `(0,1]`; confidence is never fabricated — an absent evidence source yields\n * no edge rather than a low-confidence guess.\n */\n\n/** Evidence tiers for inferred dependency edges, strongest first. */\nexport type EvidenceKind =\n | 'established-connection'\n | 'config-declared'\n | 'connection-string'\n | 'co-location';\n\n/** Single source of truth for edge confidence. Strictly ordered, all in (0,1]. */\nexport const CONFIDENCE: Record<EvidenceKind, number> = {\n 'established-connection': 0.85,\n 'config-declared': 0.7,\n 'connection-string': 0.6,\n 'co-location': 0.4,\n};\n\n/** Build a self-describing, auditable evidence string with an ISO-8601 UTC stamp. */\nexport function evidenceLine(kind: EvidenceKind, detail: string): string {\n return `[${kind}] ${detail} @ ${new Date().toISOString()}`;\n}\n","/**\n * Established-connections scanner (3.2).\n *\n * Reads the host's live TCP connections (read-only) and infers `connects_to`\n * edges from a single local `host:localhost` node to any recognized listening\n * service (`${type}:localhost:${port}` from {@link PORT_MAP}). The server-side\n * node is only kept when `portsScanner` also mapped that port in the same run —\n * the in-batch endpoint gate in `runLocalDiscovery` drops edges to ports nothing\n * is listening on (graceful degradation, never an error).\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport { PORT_MAP } from './ports.js';\nimport { CONFIDENCE, evidenceLine } from './confidence.js';\nimport { scanEstablishedConnections } from '../platform.js';\n\nexport interface EstablishedConn { localPort: number; remoteHost: string; remotePort: number; }\n\nconst PORT_RE = /^\\d{1,5}$/;\n\n/** Parse a `host:port` token (IPv4 or `[v6]:port`) into `{ host, port }` or null. */\nfunction splitHostPort(token: string): { host: string; port: number } | null {\n const t = token.trim();\n if (!t) return null;\n const idx = t.lastIndexOf(':');\n if (idx <= 0) return null;\n const host = t.slice(0, idx).replace(/^\\[|\\]$/g, '');\n const portStr = t.slice(idx + 1);\n if (!PORT_RE.test(portStr)) return null;\n const port = Number(portStr);\n if (port < 1 || port > 65535) return null;\n return { host, port };\n}\n\n/**\n * Parse established connections from `ss -tnp`, `lsof -nP`, or PowerShell output\n * (pure, host-independent). Deduplicates and silently ignores unparseable rows.\n *\n * Recognized shapes:\n * - ss: `ESTAB 0 0 127.0.0.1:54321 127.0.0.1:5432 …`\n * - lsof: `node 1 u … TCP 127.0.0.1:54321->127.0.0.1:5432 (ESTABLISHED)`\n * - PS: `127.0.0.1:54321 -> 127.0.0.1:5432`\n */\nexport function parseEstablished(raw: string): EstablishedConn[] {\n const out: EstablishedConn[] = [];\n const seen = new Set<string>();\n const push = (local: { host: string; port: number }, remote: { host: string; port: number }): void => {\n const key = `${local.port}->${remote.host}:${remote.port}`;\n if (seen.has(key)) return;\n seen.add(key);\n out.push({ localPort: local.port, remoteHost: remote.host, remotePort: remote.port });\n };\n\n for (const line of raw.split(/\\r?\\n/)) {\n const l = line.trim();\n if (!l) continue;\n\n // lsof / PowerShell arrow form: local->remote or local -> remote\n const arrow = l.match(/([^\\s]+:\\d{1,5})\\s*->\\s*([^\\s(]+:\\d{1,5})/);\n if (arrow) {\n const local = splitHostPort(arrow[1]);\n const remote = splitHostPort(arrow[2]);\n if (local && remote) push(local, remote);\n continue;\n }\n\n // ss form: whitespace-separated columns; the last two host:port columns are local+peer.\n if (/^ESTAB\\b/i.test(l) || /\\bESTAB\\b/i.test(l)) {\n const cols = l.split(/\\s+/).filter(Boolean);\n const hostPorts = cols\n .map((c) => splitHostPort(c))\n .filter((x): x is { host: string; port: number } => x !== null);\n if (hostPorts.length >= 2) {\n const local = hostPorts[hostPorts.length - 2];\n const remote = hostPorts[hostPorts.length - 1];\n push(local, remote);\n }\n }\n }\n return out;\n}\n\nexport const connectionsScanner: Scanner = {\n id: 'local-connections',\n title: 'Local established connections',\n platforms: 'all',\n allowedCommands: ['ss', 'lsof', 'netstat', 'Get-NetTCPConnection'],\n detect: () => true,\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const conns = parseEstablished((ctx.scanEstablishedConnections ?? scanEstablishedConnections)());\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n if (conns.length === 0) return { nodes, edges };\n\n // One stable self node so the in-batch endpoint gate can anchor client-side edges.\n const selfId = 'host:localhost';\n nodes.push({\n id: selfId,\n type: 'host',\n name: 'localhost',\n discoveredVia: 'established-connection',\n confidence: 0.9,\n metadata: { host: 'localhost' },\n tags: ['local'],\n });\n\n const seenEdge = new Set<string>();\n for (const c of conns) {\n const svc = PORT_MAP[c.remotePort];\n if (!svc) continue; // only connect to recognized services\n const targetId = `${svc.type}:localhost:${c.remotePort}`;\n if (seenEdge.has(targetId)) continue;\n seenEdge.add(targetId);\n edges.push({\n sourceId: selfId,\n targetId,\n relationship: 'connects_to',\n evidence: evidenceLine('established-connection', `${c.localPort} -> ${c.remoteHost}:${c.remotePort}`),\n confidence: CONFIDENCE['established-connection'],\n });\n }\n return { nodes, edges };\n },\n};\n","/**\n * Service & reverse-proxy config scanner (3.2).\n *\n * Infers *declared* dependency edges from local config without running any service:\n * - connection-string env vars (`DATABASE_URL`, `REDIS_URL`, …) → `reads_from` /\n * `depends_on` to `${type}:localhost:${port}`, confidence `connection-string` (0.6);\n * - nginx `upstream` / `proxy_pass` host:port → `depends_on`, confidence `config-declared` (0.7);\n * - docker-compose `depends_on:` → `container:<svc> depends_on container:<dep>`, 0.7.\n *\n * **Security boundary:** connection strings routinely embed credentials and\n * `sanitizeUntrusted` (applied later in `insertEdge`) is NOT a credential filter, so\n * this scanner redacts userinfo / `password=` *before* the value reaches `evidence`.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge, EdgeRelationship } from '../types.js';\nimport { PORT_MAP } from './ports.js';\nimport { CONFIDENCE, evidenceLine } from './confidence.js';\nimport { findFiles, IS_WIN } from '../platform.js';\n\n/** Connection-string env var names worth inferring an edge from. */\nconst CONN_VAR_RE = /^(DATABASE_URL|[A-Z0-9_]*_DATABASE_URL|REDIS_URL|[A-Z0-9_]*_REDIS_URL|MONGO_URL|MONGODB_URI|AMQP_URL|[A-Z0-9_]*_URL)$/;\n\n/**\n * Connection-string scheme → well-known port. Deliberately **excludes** `http`/\n * `https`: a generic `FOO_URL=https://example.com` is a web link, not a service\n * dependency, and inferring `reads_from` from it would fabricate edges (e.g. CI's\n * `GITHUB_SERVER_URL`). Only datastore/broker schemes qualify.\n */\nconst SCHEME_PORT: Record<string, number> = {\n postgres: 5432, postgresql: 5432, mysql: 3306, mongodb: 27017, redis: 6379,\n amqp: 5672, kafka: 9092, elasticsearch: 9200,\n};\n\n/** Directories to search for service config, platform-appropriate + cwd. */\nfunction configDirs(): string[] {\n const dirs = [process.cwd()];\n if (!IS_WIN) dirs.push('/etc/nginx', '/etc/nginx/conf.d');\n return dirs;\n}\n\n/**\n * Replace credentials in a connection string with `****` (the security boundary).\n * Strips the `user:pass@` userinfo and any `password=`/`pwd=` query token.\n */\nexport function redactConnectionString(url: string): string {\n return url\n // Match the userinfo up to the *last* `@` before the path (`[^/\\s]+`, greedy),\n // so a password containing a literal `@` (e.g. `root:p@ss@host`) is fully redacted.\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/)([^/\\s]+)@/i, (_m, scheme: string) => `${scheme}****@`)\n .replace(/\\b(password|pwd)=([^&\\s]+)/gi, '$1=****');\n}\n\n/**\n * Parse nginx `upstream { server host:port; }` and `proxy_pass http://host:port`\n * directives into `{ host, port }` targets (pure, host-independent).\n */\nexport function parseNginxUpstreams(raw: string): { host: string; port: number }[] {\n const out: { host: string; port: number }[] = [];\n const seen = new Set<string>();\n const add = (host: string, port: number): void => {\n const key = `${host}:${port}`;\n if (seen.has(key)) return;\n seen.add(key);\n out.push({ host, port });\n };\n for (const m of raw.matchAll(/\\bserver\\s+([a-zA-Z0-9_.-]+):(\\d{1,5})/g)) add(m[1], Number(m[2]));\n for (const m of raw.matchAll(/\\bproxy_pass\\s+https?:\\/\\/([a-zA-Z0-9_.-]+):(\\d{1,5})/g)) add(m[1], Number(m[2]));\n return out;\n}\n\n/**\n * Parse docker-compose `depends_on:` blocks (both list and map form) into a\n * `{ service, dependsOn[] }` array (pure). Indentation-based, dependency-free.\n */\nexport function parseComposeDeps(raw: string): { service: string; dependsOn: string[] }[] {\n const lines = raw.split(/\\r?\\n/);\n const result: { service: string; dependsOn: string[] }[] = [];\n let inServices = false;\n let servicesIndent = -1;\n let current: { service: string; dependsOn: string[] } | null = null;\n let serviceIndent = -1;\n let inDeps = false;\n let depsIndent = -1;\n\n const indentOf = (s: string): number => s.length - s.trimStart().length;\n\n for (const rawLine of lines) {\n if (!rawLine.trim() || rawLine.trim().startsWith('#')) continue;\n const indent = indentOf(rawLine);\n const trimmed = rawLine.trim();\n\n if (/^services:\\s*$/.test(trimmed)) { inServices = true; servicesIndent = indent; continue; }\n if (!inServices) continue;\n if (indent <= servicesIndent && !/^services:/.test(trimmed)) { inServices = false; current = null; inDeps = false; continue; }\n\n // A service header: `<name>:` at the services' child indent. This always ends\n // any open depends_on block (dep items are *more* indented than the service),\n // so it must take precedence even when inDeps is still true.\n const svcMatch = trimmed.match(/^([a-zA-Z0-9._-]+):\\s*$/);\n const isServiceHeader = !!svcMatch && indent > servicesIndent && (serviceIndent === -1 || indent === serviceIndent);\n if (isServiceHeader) {\n current = { service: svcMatch![1], dependsOn: [] };\n result.push(current);\n serviceIndent = indent;\n inDeps = false;\n continue;\n }\n if (!current) continue;\n\n if (/^depends_on:/.test(trimmed)) {\n inDeps = true;\n depsIndent = indent;\n // inline list form: depends_on: [a, b]\n const inline = trimmed.match(/^depends_on:\\s*\\[(.*)\\]\\s*$/);\n if (inline) {\n for (const dep of inline[1].split(',').map((d) => d.trim().replace(/['\"]/g, '')).filter(Boolean)) {\n current.dependsOn.push(dep);\n }\n inDeps = false;\n }\n continue;\n }\n\n if (inDeps) {\n if (indent <= depsIndent) { inDeps = false; }\n else {\n const listItem = trimmed.match(/^-\\s*([a-zA-Z0-9._-]+)\\s*$/); // - dep\n const mapItem = trimmed.match(/^([a-zA-Z0-9._-]+):\\s*$/); // dep: (long form)\n const dep = listItem?.[1] ?? mapItem?.[1];\n if (dep) { if (!current.dependsOn.includes(dep)) current.dependsOn.push(dep); continue; }\n }\n }\n }\n return result;\n}\n\n/**\n * Map a connection-string env var to a candidate edge (pure). Returns the\n * relationship, the target service port, and a **credential-redacted** evidence\n * string — or null when the scheme/port is not recognized.\n */\nexport function parseConnectionString(name: string, url: string):\n { relationship: EdgeRelationship; service: string; port: number; evidence: string } | null {\n const m = url.match(/^([a-z][a-z0-9+.-]*):\\/\\//i);\n if (!m) return null;\n const scheme = m[1].toLowerCase();\n if (!(scheme in SCHEME_PORT)) return null; // datastore/broker schemes only — never http(s)\n const portMatch = url.match(/:\\/\\/[^/]*?:(\\d{1,5})(?:[/?]|$)/);\n const port = portMatch ? Number(portMatch[1]) : SCHEME_PORT[scheme];\n if (!port || !(port in PORT_MAP)) return null;\n\n // postgres/mysql/mongo/redis → reads_from (conservative; writes_to is never\n // inferred to avoid fabricating certainty). amqp/kafka → depends_on.\n const relationship: EdgeRelationship =\n scheme === 'amqp' || scheme === 'kafka' ? 'depends_on' : 'reads_from';\n const redacted = redactConnectionString(url);\n return {\n relationship,\n service: PORT_MAP[port].service,\n port,\n evidence: evidenceLine('connection-string', `${name}=${redacted}`),\n };\n}\n\n/**\n * True only when an env var's **value** is a real datastore/broker connection\n * string (not merely a `*_URL`-shaped name). Value-based so a generic web `*_URL`\n * (e.g. CI's `GITHUB_SERVER_URL`) never makes the scanner self-activate.\n */\nfunction hasConnVar(env: string): boolean {\n for (const line of env.split(/\\r?\\n/)) {\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const name = line.slice(0, eq).trim();\n if (!CONN_VAR_RE.test(name)) continue;\n if (parseConnectionString(name, line.slice(eq + 1).trim())) return true;\n }\n return false;\n}\n\nexport const serviceConfigScanner: Scanner = {\n id: 'service-config',\n title: 'Service & reverse-proxy config',\n platforms: 'all',\n allowedCommands: ['cat', 'grep', 'find', 'printenv', 'ls', 'Get-ChildItem'],\n detect(ctx: ScanContext): boolean {\n const files = (ctx.findFiles ?? findFiles)(configDirs(), ['*.conf', 'nginx.conf', 'docker-compose.y*ml', 'compose.y*ml'], 4, 50);\n if (files.trim().length > 0) return true;\n return hasConnVar(ctx.run('printenv'));\n },\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const selfId = 'host:localhost';\n let selfEmitted = false;\n const emitSelf = (): void => {\n if (selfEmitted) return;\n selfEmitted = true;\n nodes.push({ id: selfId, type: 'host', name: 'localhost', discoveredVia: 'service-config', confidence: 0.9, metadata: { host: 'localhost' }, tags: ['local'] });\n };\n\n // ── Connection-string env vars ──────────────────────────────────────────\n for (const line of ctx.run('printenv').split(/\\r?\\n/)) {\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const name = line.slice(0, eq).trim();\n const value = line.slice(eq + 1).trim();\n if (!CONN_VAR_RE.test(name)) continue;\n const parsed = parseConnectionString(name, value);\n if (!parsed) continue;\n emitSelf();\n edges.push({\n sourceId: selfId,\n targetId: `${PORT_MAP[parsed.port].type}:localhost:${parsed.port}`,\n relationship: parsed.relationship,\n evidence: parsed.evidence,\n confidence: CONFIDENCE['connection-string'],\n });\n }\n\n // ── nginx upstream / proxy_pass ─────────────────────────────────────────\n const findFilesFn = ctx.findFiles ?? findFiles;\n const nginxFiles = findFilesFn(configDirs(), ['*.conf', 'nginx.conf'], 4, 50)\n .split(/\\r?\\n/).map((s) => s.trim()).filter(Boolean);\n for (const file of nginxFiles) {\n const content = ctx.run(`cat \"${file}\"`);\n if (!content) continue;\n for (const { host, port } of parseNginxUpstreams(content)) {\n if (!(port in PORT_MAP)) continue;\n emitSelf();\n edges.push({\n sourceId: selfId,\n targetId: `${PORT_MAP[port].type}:localhost:${port}`,\n relationship: 'depends_on',\n evidence: evidenceLine('config-declared', `nginx ${file}: ${host}:${port}`),\n confidence: CONFIDENCE['config-declared'],\n });\n }\n }\n\n // ── docker-compose depends_on ───────────────────────────────────────────\n const composeFiles = findFilesFn(configDirs(), ['docker-compose.y*ml', 'compose.y*ml'], 4, 50)\n .split(/\\r?\\n/).map((s) => s.trim()).filter(Boolean);\n for (const file of composeFiles) {\n const content = ctx.run(`cat \"${file}\"`);\n if (!content) continue;\n for (const { service, dependsOn } of parseComposeDeps(content)) {\n if (dependsOn.length === 0) continue;\n const srcId = `container:${service}`;\n nodes.push({ id: srcId, type: 'container', name: service, discoveredVia: 'service-config', confidence: 0.7, metadata: { compose: file }, tags: ['compose'] });\n for (const dep of dependsOn) {\n const depId = `container:${dep}`;\n nodes.push({ id: depId, type: 'container', name: dep, discoveredVia: 'service-config', confidence: 0.7, metadata: { compose: file }, tags: ['compose'] });\n edges.push({\n sourceId: srcId,\n targetId: depId,\n relationship: 'depends_on',\n evidence: evidenceLine('config-declared', `compose ${file}: ${service} depends_on ${dep}`),\n confidence: CONFIDENCE['config-declared'],\n });\n }\n }\n }\n\n return { nodes, edges };\n },\n};\n","import { ScannerRegistry } from './types.js';\nimport { bookmarksScanner } from './bookmarks.js';\nimport { installedAppsScanner } from './installed-apps.js';\nimport { portsScanner } from './ports.js';\nimport { cloudAwsScanner } from './cloud-aws.js';\nimport { cloudGcpScanner } from './cloud-gcp.js';\nimport { cloudAzureScanner } from './cloud-azure.js';\nimport { k8sScanner } from './k8s.js';\nimport { databasesScanner } from './databases.js';\nimport { connectionsScanner } from './connections.js';\nimport { serviceConfigScanner } from './service-config.js';\n\nexport { ScannerRegistry } from './types.js';\nexport type { Scanner, ScanContext, ScanResult } from './types.js';\nexport { bookmarksScanner } from './bookmarks.js';\nexport { installedAppsScanner } from './installed-apps.js';\nexport { portsScanner, extractListeningPorts, PORT_MAP } from './ports.js';\nexport { cloudAwsScanner } from './cloud-aws.js';\nexport { cloudGcpScanner } from './cloud-gcp.js';\nexport { cloudAzureScanner } from './cloud-azure.js';\nexport { k8sScanner } from './k8s.js';\nexport { databasesScanner } from './databases.js';\nexport { connectionsScanner, parseEstablished } from './connections.js';\nexport type { EstablishedConn } from './connections.js';\nexport {\n serviceConfigScanner, parseNginxUpstreams, parseComposeDeps, parseConnectionString, redactConnectionString,\n} from './service-config.js';\nexport { CONFIDENCE, evidenceLine } from './confidence.js';\nexport type { EvidenceKind } from './confidence.js';\nexport { safeJson, parseScanHint, buildReport } from './cloud-util.js';\nexport type { ScanHintParams } from './cloud-util.js';\nexport { definePlugin, validateScanner, ScannerShape } from './spi.js';\nexport type { ScannerPlugin, ScannerPluginApi } from './spi.js';\n\n/** A registry pre-loaded with the built-in deterministic scanners. */\nexport function defaultRegistry(): ScannerRegistry {\n return new ScannerRegistry()\n .register(bookmarksScanner)\n .register(installedAppsScanner)\n .register(portsScanner)\n .register(cloudAwsScanner)\n .register(cloudGcpScanner)\n .register(cloudAzureScanner)\n .register(k8sScanner)\n .register(databasesScanner)\n .register(connectionsScanner)\n .register(serviceConfigScanner);\n}\n","/**\n * Opt-in scanner-plugin loader.\n *\n * Resolves the explicitly configured plugin package names (consent-first: no\n * filesystem auto-scan), dynamically `import()`s each in its own try/catch, and\n * registers its scanners through {@link ScannerRegistry.registerExternal}\n * (validated, namespaced, command-gated, frozen). One bad plugin — a\n * non-resolvable name, a missing/invalid default export, a `register()` that\n * throws, or a malformed scanner that fails zod validation — is logged via\n * `logWarn` and skipped. It never aborts discovery (optional-deps degrade\n * pattern, mirroring `src/semantic/search.ts`).\n */\n\nimport { logInfo, logWarn } from '../logger.js';\nimport type { ScannerRegistry } from './types.js';\nimport type { ScannerPlugin, ScannerPluginApi } from './spi.js';\n\n/**\n * Load each configured plugin package into the given registry. Returns the\n * package names that loaded and registered successfully (for audit/reporting).\n */\nexport async function loadPlugins(\n registry: ScannerRegistry,\n pkgs: readonly string[],\n): Promise<string[]> {\n const loaded: string[] = [];\n for (const pkg of pkgs) {\n try {\n const mod = (await import(pkg)) as { default?: ScannerPlugin };\n const plugin = mod.default;\n if (!plugin || typeof plugin.register !== 'function') {\n logWarn('scanner plugin missing default export', { pkg });\n continue;\n }\n const api: ScannerPluginApi = {\n registerScanner: (scanner) => {\n registry.registerExternal(pkg, scanner);\n },\n };\n plugin.register(api);\n loaded.push(pkg);\n logInfo('scanner plugin loaded', { pkg, name: plugin.name });\n } catch (err) {\n logWarn('scanner plugin load failed (skipped)', {\n pkg,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return loaded;\n}\n","/**\n * Deterministic, LLM-free local discovery.\n *\n * Runs every applicable scanner from a {@link ScannerRegistry}, deduplicates and\n * persists the resulting nodes/edges, and returns counts. This is what powers the\n * MCP `run_discovery` tool without requiring any model — the host LLM can then\n * enrich the catalog via the read tools. (The Claude-driven loop in agent.ts\n * remains available as the optional, richer turnkey path.)\n */\n\nimport type { CartographyDB } from '../db.js';\nimport type { DiscoveryNode, DiscoveryEdge, Contributor } from '../types.js';\nimport { diffTopology } from '../diff.js';\nimport type { TopologyInput, TopologyDelta } from '../diff.js';\nimport { PLATFORM, run, commandExists, scanListeningPorts, scanEstablishedConnections, findFiles } from '../platform.js';\nimport { scanAllBookmarks } from '../bookmarks.js';\nimport { defaultRegistry, ScannerRegistry } from '../scanners/registry.js';\nimport { loadPlugins } from '../scanners/loader.js';\nimport type { ScanContext } from '../scanners/types.js';\n\nexport interface LocalDiscoveryOptions {\n hint?: string;\n registry?: ScannerRegistry;\n /**\n * Opt-in scanner plugin package names to load (consent-first). Honoured only\n * when no explicit `registry` is supplied — an injected registry is used\n * verbatim, so tests and advanced callers stay in full control.\n */\n plugins?: string[];\n /** Called after each scanner with a short progress line. */\n onProgress?: (line: string) => void;\n /**\n * Override individual {@link ScanContext} fields. Production omits this and the\n * real platform helpers are used; tests inject deterministic sources\n * (`commandExists`, `scanListeningPorts`, `scanBookmarks`) so the built-in\n * scanners run against known inputs without depending on the host.\n */\n ctx?: Partial<ScanContext>;\n /**\n * `'replace'` (default) preserves today's append-all behavior: every scanned\n * node/edge is upserted/inserted into the session. `'update'` (2.1 incremental\n * discovery) reads the session's prior state, diffs it against the current scan,\n * and applies only the delta — upserting changed/added, pruning removed (nodes\n * and edges), and stamping `last_scanned_at`. Same `session_id` either way.\n */\n mode?: 'replace' | 'update';\n}\n\nexport interface LocalDiscoveryResult {\n nodes: number;\n edges: number;\n scanners: string[];\n /** Present only in `'update'` mode: the delta applied to the session (2.1). */\n delta?: TopologyDelta;\n}\n\n/**\n * Lift scanner `DiscoveryNode`/`DiscoveryEdge` output into the `NodeRow`/`EdgeRow`\n * shape {@link diffTopology} compares. Only the `DRIFT_FIELDS` (node) and the\n * `(sourceId, targetId, relationship)` logical key (edge) are read by the diff, so\n * `id`/`depth`/`discoveredAt` carry placeholders the diff ignores. Nodes keep the\n * raw `id` so they key against the prior state's `getNodes` ids correctly.\n */\nfunction projectScan(sessionId: string, nodes: DiscoveryNode[], edges: DiscoveryEdge[]): TopologyInput {\n const ts = new Date().toISOString();\n return {\n nodes: nodes.map((n) => ({\n ...n, sessionId, discoveredAt: ts, depth: 0,\n tags: n.tags ?? [], metadata: n.metadata ?? {},\n })),\n edges: edges.map((e) => ({ ...e, id: '', sessionId, discoveredAt: ts })),\n };\n}\n\nexport async function runLocalDiscovery(\n db: CartographyDB,\n sessionId: string,\n opts: LocalDiscoveryOptions = {},\n): Promise<LocalDiscoveryResult> {\n const registry = opts.registry ?? defaultRegistry();\n // Load opt-in plugins only onto the default registry — never mutate an\n // explicitly injected one. A broken plugin is logged + skipped, never fatal.\n if (!opts.registry && opts.plugins && opts.plugins.length > 0) {\n await loadPlugins(registry, opts.plugins);\n }\n const ctx: ScanContext = { hint: opts.hint, platform: PLATFORM, run, commandExists, scanListeningPorts, scanEstablishedConnections, findFiles, scanBookmarks: scanAllBookmarks, ...opts.ctx };\n\n const nodes = new Map<string, DiscoveryNode>();\n const edges: DiscoveryEdge[] = [];\n const ran: string[] = [];\n\n for (const scanner of registry.forPlatform(PLATFORM)) {\n try {\n if (!(await scanner.detect(ctx))) continue;\n const result = await scanner.scan(ctx);\n ran.push(scanner.id);\n for (const node of result.nodes) {\n // Keep the highest-confidence record on id collision.\n const prev = nodes.get(node.id);\n if (!prev || node.confidence > prev.confidence) nodes.set(node.id, node);\n }\n edges.push(...result.edges);\n opts.onProgress?.(`${scanner.title}: +${result.nodes.length} nodes`);\n } catch (err) {\n opts.onProgress?.(`${scanner.title}: failed (${err instanceof Error ? err.message : String(err)})`);\n }\n }\n\n // 2.9: derive a Contributor once from the (attributed) session row so the same\n // logical node accumulates one contributor per machine instead of being clobbered.\n const session = db.getSession(sessionId);\n const baseAttribution: Omit<Contributor, 'confidence'> | undefined = session?.machineId\n ? {\n machineId: session.machineId,\n hostname: session.hostname ?? 'unknown-host',\n user: session.user ?? 'unknown-user',\n organization: session.tenant,\n at: new Date().toISOString(),\n }\n : undefined;\n\n // Only persist edges whose endpoints exist (matches the replace-mode invariant).\n const validEdges = edges.filter((e) => nodes.has(e.sourceId) && nodes.has(e.targetId));\n\n if (opts.mode === 'update') {\n // Incremental rescan (2.1): diff the current scan against the session's prior\n // state and apply only the delta — keeping the same session_id, pruning gone\n // entities, and preserving 2.9 contributors on unchanged nodes (they are not\n // re-upserted) while appending the running machine's contributor to changed/\n // added nodes via `attribution`.\n const prior: TopologyInput = { nodes: db.getNodes(sessionId), edges: db.getEdges(sessionId) };\n const current = projectScan(sessionId, [...nodes.values()], validEdges);\n const delta = diffTopology(prior, current);\n db.applyTopologyDelta(\n sessionId, delta,\n baseAttribution ? { ...baseAttribution, confidence: 0.5 } : undefined,\n );\n return { nodes: current.nodes.length, edges: current.edges.length, scanners: ran, delta };\n }\n\n // replace (default): existing append-all behavior, unchanged.\n for (const node of nodes.values()) {\n db.upsertNode(\n sessionId, node, 0,\n baseAttribution ? { ...baseAttribution, confidence: node.confidence } : undefined,\n );\n }\n for (const edge of validEdges) db.insertEdge(sessionId, edge);\n\n return { nodes: nodes.size, edges: validEdges.length, scanners: ran };\n}\n\n/**\n * Adapter matching the MCP `DiscoveryFn` signature. When no explicit `registry`\n * is provided, the opt-in `plugins` list is loaded onto the default registry on\n * each discovery run (so late-installed plugins are picked up).\n */\nexport function localDiscoveryFn(registry?: ScannerRegistry, plugins?: string[]) {\n return async (db: CartographyDB, sessionId: string, opts: { hint?: string; mode?: 'replace' | 'update' }) => {\n const r = await runLocalDiscovery(db, sessionId, { hint: opts.hint, mode: opts.mode, registry, plugins });\n return { nodes: r.nodes, edges: r.edges, ...(r.delta ? { delta: r.delta } : {}) };\n };\n}\n","/**\n * Baseline compliance ruleset (3.4) — the acceptance target.\n *\n * Scored against signals available today (node type/group, domain, confidence,\n * owner, metadata DSNs). Validated by `RulesetSchema.parse` at load (fail-fast).\n */\n\nimport { RulesetSchema } from '../types.js';\nimport type { Ruleset } from '../types.js';\n\nexport const baseline: Ruleset = RulesetSchema.parse({\n name: 'baseline',\n version: '1.0.0',\n framework: 'baseline',\n description: 'Deterministic baseline hygiene controls scored against signals available today.',\n rules: [\n {\n id: 'BASE-1', control: 'BASE-1', framework: 'baseline', severity: 'medium',\n title: 'Asset has an owner', rationale: 'Every asset should have a clear owning team/person for accountability.',\n scope: {},\n check: { any: [\n { field: 'owner', op: 'present' },\n { field: 'domain', op: 'present' },\n { field: 'tags', op: 'matches', pattern: 'owner_key' },\n { field: 'metadataKeys', op: 'matches', pattern: 'owner_key' },\n ] },\n },\n {\n id: 'BASE-2', control: 'BASE-2', framework: 'baseline', severity: 'low',\n title: 'Service/data asset is assigned a business domain', rationale: 'Domain assignment enables blast-radius and ownership analysis.',\n scope: { groups: ['data', 'web'] },\n check: { field: 'domain', op: 'present' },\n },\n {\n id: 'BASE-3', control: 'BASE-3', framework: 'baseline', severity: 'high',\n title: 'Critical asset is discovered with adequate confidence', rationale: 'Low-confidence critical assets indicate incomplete or unreliable discovery.',\n scope: { groups: ['data', 'infra'] },\n check: { field: 'confidence', op: 'gte', value: 0.5 },\n },\n {\n id: 'BASE-4', control: 'BASE-4', framework: 'baseline', severity: 'critical',\n title: 'No embedded credentials / plaintext DSN in metadata', rationale: 'A connection string with embedded credentials is a direct secret-exposure risk.',\n scope: {},\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'dsn_with_credentials' } },\n },\n {\n id: 'BASE-5', control: 'BASE-5', framework: 'baseline', severity: 'medium',\n title: 'Data store carries an acceptable quality score', rationale: 'Where a quality score exists, a low score flags an under-governed data store.',\n scope: { groups: ['data'] },\n applicableWhen: { field: 'qualityScore', op: 'present' },\n check: { field: 'qualityScore', op: 'gte', value: 50 },\n },\n ],\n});\n","/**\n * CIS starter subset (3.4) — NOT a certified CIS Benchmark. A small, illustrative\n * subset that resolves the `cis` ruleset name; full coverage is plugin/community work.\n */\n\nimport { RulesetSchema } from '../types.js';\nimport type { Ruleset } from '../types.js';\n\nexport const cis: Ruleset = RulesetSchema.parse({\n name: 'cis',\n version: '0.1.0',\n framework: 'CIS',\n description: 'CIS starter subset — illustrative controls, not a certified benchmark.',\n rules: [\n {\n id: 'CIS-1.1', control: 'CIS-1.1', framework: 'CIS', severity: 'critical',\n title: 'No plaintext credentials in service configuration', rationale: 'CIS hardening forbids embedded secrets in config/metadata.',\n scope: {},\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'dsn_with_credentials' } },\n },\n {\n id: 'CIS-2.1', control: 'CIS-2.1', framework: 'CIS', severity: 'high',\n title: 'Data store is not publicly exposed', rationale: 'Data stores should not bind to public/0.0.0.0 addresses.',\n scope: { groups: ['data'] },\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'public_exposure' } },\n },\n {\n id: 'CIS-3.1', control: 'CIS-3.1', framework: 'CIS', severity: 'medium',\n title: 'Asset inventory has an accountable owner', rationale: 'CIS asset management requires an assigned owner.',\n scope: {},\n check: { any: [{ field: 'owner', op: 'present' }, { field: 'metadataKeys', op: 'matches', pattern: 'owner_key' }] },\n },\n ],\n});\n","/**\n * SOC 2 starter subset (3.4) — NOT a certified SOC 2 control set. Illustrative\n * controls that resolve the `soc2` ruleset name; full coverage is community work.\n */\n\nimport { RulesetSchema } from '../types.js';\nimport type { Ruleset } from '../types.js';\n\nexport const soc2: Ruleset = RulesetSchema.parse({\n name: 'soc2',\n version: '0.1.0',\n framework: 'SOC2',\n description: 'SOC 2 starter subset — illustrative controls, not a certified control set.',\n rules: [\n {\n id: 'CC6.1', control: 'CC6.1', framework: 'SOC2', severity: 'critical',\n title: 'Logical access — no embedded credentials', rationale: 'CC6.1 logical access controls preclude plaintext secrets in config.',\n scope: {},\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'dsn_with_credentials' } },\n },\n {\n id: 'CC6.6', control: 'CC6.6', framework: 'SOC2', severity: 'high',\n title: 'Boundary protection — data stores not public', rationale: 'CC6.6 requires protection of system boundaries from external access.',\n scope: { groups: ['data'] },\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'public_exposure' } },\n },\n {\n id: 'CC1.2', control: 'CC1.2', framework: 'SOC2', severity: 'medium',\n title: 'Accountability — asset has an owner', rationale: 'CC1.2 establishes accountability; assets need an assigned owner.',\n scope: {},\n check: { any: [{ field: 'owner', op: 'present' }, { field: 'domain', op: 'present' }] },\n },\n ],\n});\n","/**\n * ISO/IEC 27001 starter subset (3.4) — NOT a certified Annex A control set.\n * Illustrative controls that resolve the `iso27001` ruleset name.\n */\n\nimport { RulesetSchema } from '../types.js';\nimport type { Ruleset } from '../types.js';\n\nexport const iso27001: Ruleset = RulesetSchema.parse({\n name: 'iso27001',\n version: '0.1.0',\n framework: 'ISO27001',\n description: 'ISO/IEC 27001 Annex A starter subset — illustrative, not certified.',\n rules: [\n {\n id: 'A.8.1', control: 'A.8.1', framework: 'ISO27001', severity: 'medium',\n title: 'Inventory of assets — ownership assigned', rationale: 'A.8.1 requires an inventory of assets each with an identified owner.',\n scope: {},\n check: { any: [{ field: 'owner', op: 'present' }, { field: 'metadataKeys', op: 'matches', pattern: 'owner_key' }] },\n },\n {\n id: 'A.8.12', control: 'A.8.12', framework: 'ISO27001', severity: 'critical',\n title: 'Data leakage prevention — no embedded secrets', rationale: 'A.8.12 mandates measures against data leakage such as exposed credentials.',\n scope: {},\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'dsn_with_credentials' } },\n },\n {\n id: 'A.8.20', control: 'A.8.20', framework: 'ISO27001', severity: 'high',\n title: 'Network security — data stores not publicly exposed', rationale: 'A.8.20 requires networks to be secured; data stores should not be public.',\n scope: { groups: ['data'] },\n check: { not: { field: 'metadataValues', op: 'matches', pattern: 'public_exposure' } },\n },\n ],\n});\n","/**\n * Bundled ruleset registry (3.4). All rulesets are plain data, validated at load.\n */\n\nimport type { Ruleset } from '../types.js';\nimport { baseline } from './baseline.js';\nimport { cis } from './cis.js';\nimport { soc2 } from './soc2.js';\nimport { iso27001 } from './iso27001.js';\n\nconst RULESETS: Record<string, Ruleset> = { baseline, cis, soc2, iso27001 };\n\n/** Resolve a bundled ruleset by name, or `undefined` (callers degrade, never throw). */\nexport function getRuleset(name: string): Ruleset | undefined {\n return RULESETS[name];\n}\n\n/** List the bundled rulesets (name/version/framework/rule count) for help + degrade messages. */\nexport function listRulesets(): Array<{ name: string; version: string; framework: string; ruleCount: number }> {\n return Object.values(RULESETS).map((r) => ({ name: r.name, version: r.version, framework: r.framework, ruleCount: r.rules.length }));\n}\n\nexport { baseline, cis, soc2, iso27001 };\n","import type { DriftSink } from './types.js';\nimport type { DriftAlert } from '../types.js';\nimport { redactValue } from '../tools.js';\nimport { logInfo } from '../logger.js';\n\n/**\n * Default sink: writes one severity-tagged, credential-redacted JSON line to\n * **stdout** (the data channel) and a one-line diagnostic to **stderr**, keeping\n * the stdout/stderr discipline the rest of the CLI follows. Fully local — no\n * network egress.\n */\nexport class StdoutSink implements DriftSink {\n readonly name = 'stdout';\n\n async emit(alert: DriftAlert): Promise<void> {\n process.stdout.write(JSON.stringify(redactValue(alert)) + '\\n');\n logInfo('drift alert emitted', { sink: this.name, severity: alert.severity, items: alert.items.length });\n }\n}\n","import type { DriftSink } from './types.js';\nimport type { DriftAlert } from '../types.js';\nimport { redactValue, stripSensitive } from '../tools.js';\nimport { logWarn, logError } from '../logger.js';\n\nexport interface WebhookSinkOptions {\n url: string;\n token?: string;\n timeoutMs?: number;\n}\n\n/** Loopback hosts for which a plaintext `http:` webhook is permitted (local dev). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n\n/** Injectable fetch (defaults to the global) — lets tests deliver without a socket. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<{ ok: boolean; status: number }>;\n\nexport interface PostJsonOptions {\n url: string;\n body: unknown;\n /** Extra request headers (e.g. provider auth). `content-type: application/json` is always set. */\n headers?: Record<string, string>;\n timeoutMs?: number;\n /** Sink name for structured logs (never logs the url/token/body). */\n sinkName: string;\n /** Injected fetch for tests; defaults to the global. */\n fetchImpl?: FetchLike;\n}\n\n/**\n * Shared outbound JSON POST carrying the load-bearing sink hardening, reused by\n * {@link WebhookSink} and the Slack/PagerDuty/Jira provider sinks so there is exactly\n * one egress code path:\n * - refuses a non-`https:` / non-loopback target ({@link isSecureWebhookUrl}, SSRF/plaintext guard);\n * - `AbortSignal.timeout` bounded;\n * - **never throws** for a transient failure (logs and resolves so the runner continues);\n * - logs only `stripSensitive(url)` (host:port) — never the full url, headers, token, or body.\n * The body must already be redaction-safe (callers pass `redactValue(...)` output or a\n * provider payload derived from it).\n */\nexport async function postJson(opts: PostJsonOptions): Promise<void> {\n const doFetch = opts.fetchImpl ?? (typeof fetch === 'function' ? (fetch as unknown as FetchLike) : undefined);\n if (!doFetch) {\n logWarn('sink unavailable: global fetch missing', { sink: opts.sinkName });\n return;\n }\n if (!opts.url) {\n logWarn('sink unavailable: no url configured', { sink: opts.sinkName });\n return;\n }\n if (!isSecureWebhookUrl(opts.url)) {\n logWarn('sink refused: insecure scheme (use https:// or a loopback host)', {\n sink: opts.sinkName,\n host: stripSensitive(opts.url),\n });\n return;\n }\n try {\n const res = await doFetch(opts.url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...(opts.headers ?? {}) },\n body: JSON.stringify(opts.body),\n signal: AbortSignal.timeout(opts.timeoutMs ?? 10_000),\n });\n if (!res.ok) {\n logError('sink delivery failed', { sink: opts.sinkName, host: stripSensitive(opts.url), status: res.status });\n }\n } catch (err) {\n logError('sink delivery failed', {\n sink: opts.sinkName,\n host: stripSensitive(opts.url),\n reason: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\n/**\n * True if `url` is safe to POST to: `https:`, or `http:` only to a loopback host,\n * or any scheme when the documented `CARTOGRAPHY_ALLOW_INSECURE_SYNC=1` escape\n * hatch (test-only) is set. An unparseable URL is treated as insecure. Mirrors the\n * 2.11 `pushDeltas` guard so the drift feature never silently exfiltrates over the\n * wire in plaintext.\n */\nexport function isSecureWebhookUrl(url: string, env: NodeJS.ProcessEnv = process.env): boolean {\n if (env.CARTOGRAPHY_ALLOW_INSECURE_SYNC === '1') return true;\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n if (parsed.protocol === 'https:') return true;\n if (parsed.protocol === 'http:' && LOOPBACK_HOSTS.has(parsed.hostname)) return true;\n return false;\n}\n\n/**\n * Outbound sink: POSTs the alert as JSON to an operator-configured endpoint. The\n * first and only outbound network surface of the drift feature — off by default\n * (constructed only when a `webhook` sink is explicitly configured). Hardened:\n * - the body is **always** `redactValue(alert)`, so no `user:password@` secret\n * leaves the process;\n * - only `stripSensitive(url)` (host:port) is ever logged — never the full URL,\n * the bearer token, or the body;\n * - the target must be `https:` (SSRF / plaintext-exfil guard) — a plaintext\n * `http:` URL is refused unless the host is loopback or the documented\n * `CARTOGRAPHY_ALLOW_INSECURE_SYNC=1` escape hatch is set (mirrors `pushDeltas`);\n * - degrades to a logged no-op when `fetch` is unavailable or the URL is empty;\n * - never throws (the runner owns retry/abort policy).\n */\nexport class WebhookSink implements DriftSink {\n readonly name = 'webhook';\n\n constructor(private readonly opts: WebhookSinkOptions) {}\n\n async emit(alert: DriftAlert): Promise<void> {\n const { url, token, timeoutMs } = this.opts;\n await postJson({\n url,\n body: redactValue(alert),\n ...(token ? { headers: { authorization: `Bearer ${token}` } } : {}),\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n sinkName: this.name,\n });\n }\n}\n","/**\n * Pure provider payload mappers (4.4): a classified {@link DriftAlert} → the JSON\n * shape Slack / PagerDuty / Jira each expect. Deterministic, no I/O, no secrets —\n * they operate on the **already-`redactValue`'d** alert the sinks pass in, and read\n * only structured fields (severity/counts/item refs), never raw node metadata. The\n * delivery + auth + SSRF hardening lives in `provider-sink.ts`/`webhook.ts`; these\n * are just shape transforms, so they are trivially snapshot-testable.\n */\n\nimport type { DriftAlert, DriftAlertItem, Severity } from '../types.js';\n\n/** Cap on per-item lines included in a message body (keeps payloads bounded). */\nconst MAX_ITEMS = 20;\n\nconst SEVERITY_EMOJI: Record<Severity, string> = { info: '🟢', warning: '🟡', critical: '🔴' };\n\nfunction headline(alert: DriftAlert): string {\n const s = alert.summary;\n return `${s.nodesAdded}+ / ${s.nodesRemoved}- / ${s.nodesChanged}~ nodes, ${s.edgesAdded}+ / ${s.edgesRemoved}- edges`;\n}\n\nfunction itemLine(it: DriftAlertItem): string {\n const sec = it.securityFields?.length ? ` [security: ${it.securityFields.join(', ')}]` : '';\n const fields = it.changedFields?.length ? ` (${it.changedFields.join(', ')})` : '';\n return `${it.severity.toUpperCase()} · ${it.kind} · ${it.label}${fields}${sec}`;\n}\n\nfunction bodyText(alert: DriftAlert): string {\n const lines = alert.items.slice(0, MAX_ITEMS).map(itemLine);\n const more = alert.items.length > MAX_ITEMS ? [`…and ${alert.items.length - MAX_ITEMS} more`] : [];\n return [headline(alert), '', ...lines, ...more].join('\\n');\n}\n\n// ── Slack (incoming webhook payload) ────────────────────────────────────────────\n\nexport interface SlackMessage {\n text: string;\n blocks: unknown[];\n}\n\n/** Map an alert to a Slack incoming-webhook message (Block Kit). The webhook URL is the secret. */\nexport function formatSlack(alert: DriftAlert): SlackMessage {\n const title = `${SEVERITY_EMOJI[alert.severity]} Topology drift — ${alert.severity}`;\n return {\n text: `${title}: ${headline(alert)}`,\n blocks: [\n { type: 'header', text: { type: 'plain_text', text: title, emoji: true } },\n { type: 'section', text: { type: 'mrkdwn', text: '```' + bodyText(alert) + '```' } },\n { type: 'context', elements: [{ type: 'mrkdwn', text: `base ${alert.base.sessionId} → current ${alert.current.sessionId} · ${alert.generatedAt}` }] },\n ],\n };\n}\n\n// ── PagerDuty (Events API v2 enqueue) ───────────────────────────────────────────\n\n/** PagerDuty severity enum. Our `info|warning|critical` map onto `info|warning|critical`. */\nconst PD_SEVERITY: Record<Severity, 'info' | 'warning' | 'critical'> = {\n info: 'info', warning: 'warning', critical: 'critical',\n};\n\nexport interface PagerDutyEvent {\n routing_key: string;\n event_action: 'trigger';\n dedup_key: string;\n payload: {\n summary: string;\n source: string;\n severity: 'info' | 'warning' | 'critical';\n timestamp: string;\n custom_details: Record<string, unknown>;\n };\n}\n\n/** Map an alert to a PagerDuty Events API v2 `trigger`. `routingKey` rides in the body (PD's auth model). */\nexport function formatPagerDuty(alert: DriftAlert, routingKey: string): PagerDutyEvent {\n return {\n routing_key: routingKey,\n event_action: 'trigger',\n // Stable per base→current pair so repeated alerts for the same delta de-duplicate.\n dedup_key: `cartograph-drift:${alert.base.sessionId}:${alert.current.sessionId}`,\n payload: {\n summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,\n source: 'cartograph',\n severity: PD_SEVERITY[alert.severity],\n timestamp: alert.generatedAt,\n custom_details: {\n summary: alert.summary,\n items: alert.items.slice(0, MAX_ITEMS).map((it) => ({\n kind: it.kind, ref: it.ref, severity: it.severity,\n ...(it.changedFields ? { changedFields: it.changedFields } : {}),\n ...(it.securityFields ? { securityFields: it.securityFields } : {}),\n })),\n },\n },\n };\n}\n\n// ── Jira (REST API v2 create-issue) ─────────────────────────────────────────────\n\nexport interface JiraIssue {\n fields: {\n project: { key: string };\n issuetype: { name: string };\n summary: string;\n description: string;\n };\n}\n\nexport interface JiraOptions {\n project: string;\n issueType?: string;\n}\n\n/** Map an alert to a Jira create-issue body. Auth (Basic email:token) is applied by the sink, not here. */\nexport function formatJira(alert: DriftAlert, opts: JiraOptions): JiraIssue {\n return {\n fields: {\n project: { key: opts.project },\n issuetype: { name: opts.issueType ?? 'Task' },\n summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,\n description: bodyText(alert) + `\\n\\nbase ${alert.base.sessionId} → current ${alert.current.sessionId}\\ngenerated ${alert.generatedAt}`,\n },\n };\n}\n","/**\n * Provider drift sinks (4.4): Slack / PagerDuty / Jira. Each is a thin {@link DriftSink}\n * that (1) redacts the alert (`redactValue`), (2) maps it to the provider payload via the\n * pure adapters in `providers.ts`, and (3) delivers it through the shared {@link postJson}\n * helper — inheriting the *exact* `WebhookSink` hardening (https-or-loopback SSRF guard,\n * bounded timeout, never-throw, `stripSensitive` log-only). They differ only in where the\n * provider places its secret: Slack in the URL, PagerDuty as a `routing_key` in the body,\n * Jira as an `Authorization: Basic` header.\n */\n\nimport type { DriftSink } from './types.js';\nimport type { DriftAlert } from '../types.js';\nimport { redactValue } from '../tools.js';\nimport { postJson, type FetchLike } from './webhook.js';\nimport { formatSlack, formatPagerDuty, formatJira } from './providers.js';\n\n/** Default PagerDuty Events API v2 ingest endpoint. */\nexport const PAGERDUTY_ENQUEUE_URL = 'https://events.pagerduty.com/v2/enqueue';\n\ninterface BaseSinkOptions {\n url: string;\n timeoutMs?: number;\n /** Injected fetch for tests; defaults to the global. */\n fetchImpl?: FetchLike;\n}\n\nfunction deliver(name: string, url: string, body: unknown, opts: BaseSinkOptions, headers?: Record<string, string>): Promise<void> {\n return postJson({\n url,\n body,\n sinkName: name,\n ...(headers ? { headers } : {}),\n ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n\n/** Slack incoming webhook. The configured `url` is the secret; no auth header is sent. */\nexport class SlackSink implements DriftSink {\n readonly name = 'slack';\n constructor(private readonly opts: BaseSinkOptions) {}\n async emit(alert: DriftAlert): Promise<void> {\n await deliver(this.name, this.opts.url, formatSlack(redactValue(alert) as DriftAlert), this.opts);\n }\n}\n\nexport interface PagerDutySinkOptions extends BaseSinkOptions {\n /** Events API v2 routing key (rides in the body, PagerDuty's auth model). */\n routingKey: string;\n}\n\n/** PagerDuty Events API v2 `trigger`. `url` defaults to {@link PAGERDUTY_ENQUEUE_URL}. */\nexport class PagerDutySink implements DriftSink {\n readonly name = 'pagerduty';\n constructor(private readonly opts: PagerDutySinkOptions) {}\n async emit(alert: DriftAlert): Promise<void> {\n const body = formatPagerDuty(redactValue(alert) as DriftAlert, this.opts.routingKey);\n await deliver(this.name, this.opts.url || PAGERDUTY_ENQUEUE_URL, body, this.opts);\n }\n}\n\nexport interface JiraSinkOptions extends BaseSinkOptions {\n /** Jira account email (basic-auth user). */\n email: string;\n /** Jira API token (basic-auth pass). */\n token: string;\n /** Target project key (e.g. \"OPS\"). */\n project: string;\n /** Issue type name (default \"Task\"). */\n issueType?: string;\n}\n\n/** Jira REST API v2 create-issue, authenticated with `Authorization: Basic base64(email:token)`. */\nexport class JiraSink implements DriftSink {\n readonly name = 'jira';\n constructor(private readonly opts: JiraSinkOptions) {}\n async emit(alert: DriftAlert): Promise<void> {\n const body = formatJira(redactValue(alert) as DriftAlert, {\n project: this.opts.project,\n ...(this.opts.issueType ? { issueType: this.opts.issueType } : {}),\n });\n const auth = Buffer.from(`${this.opts.email}:${this.opts.token}`).toString('base64');\n const base = this.opts.url.replace(/\\/+$/, '');\n await deliver(this.name, `${base}/rest/api/2/issue`, body, this.opts, { authorization: `Basic ${auth}` });\n }\n}\n","import type { DriftConfig } from '../types.js';\nimport type { DriftSink } from './types.js';\nimport { StdoutSink } from './stdout.js';\nimport { WebhookSink } from './webhook.js';\nimport { SlackSink, PagerDutySink, JiraSink, PAGERDUTY_ENQUEUE_URL } from './provider-sink.js';\nimport { logWarn } from '../logger.js';\n\n/**\n * Construct sinks from config. Absent/empty config → `[new StdoutSink()]` (the\n * local default). Secrets (webhook `token`, Jira token, PagerDuty routing key) fall\n * back to `CARTOGRAPHY_DRIFT_TOKEN` when not given explicitly (mirroring\n * `CARTOGRAPHY_HTTP_TOKEN`). A provider entry missing a required field is skipped\n * with a warning rather than aborting the others — graceful degradation.\n */\nexport function buildSinks(drift?: DriftConfig): DriftSink[] {\n const configs = drift?.sinks && drift.sinks.length > 0 ? drift.sinks : [{ type: 'stdout' as const }];\n const envSecret = process.env.CARTOGRAPHY_DRIFT_TOKEN;\n const sinks: DriftSink[] = [];\n for (const s of configs) {\n const timeoutMs = s.timeoutMs;\n switch (s.type) {\n case 'webhook':\n if (!s.url) { logWarn('drift sink skipped: webhook requires a url', { sink: s.type }); break; }\n sinks.push(new WebhookSink({ url: s.url, token: s.token ?? envSecret, timeoutMs }));\n break;\n case 'slack':\n if (!s.url) { logWarn('drift sink skipped: slack requires a webhook url', { sink: s.type }); break; }\n sinks.push(new SlackSink({ url: s.url, timeoutMs }));\n break;\n case 'pagerduty': {\n const routingKey = s.routingKey ?? s.token ?? envSecret;\n if (!routingKey) { logWarn('drift sink skipped: pagerduty requires a routingKey (or CARTOGRAPHY_DRIFT_TOKEN)', { sink: s.type }); break; }\n sinks.push(new PagerDutySink({ url: s.url ?? PAGERDUTY_ENQUEUE_URL, routingKey, timeoutMs }));\n break;\n }\n case 'jira': {\n const token = s.token ?? envSecret;\n if (!s.url || !s.email || !s.project || !token) { logWarn('drift sink skipped: jira requires url, email, project and a token', { sink: s.type }); break; }\n sinks.push(new JiraSink({ url: s.url, email: s.email, token, project: s.project, issueType: s.issueType, timeoutMs }));\n break;\n }\n default:\n sinks.push(new StdoutSink());\n }\n }\n return sinks.length > 0 ? sinks : [new StdoutSink()];\n}\n\nexport { StdoutSink } from './stdout.js';\nexport { WebhookSink, isSecureWebhookUrl, postJson } from './webhook.js';\nexport { SlackSink, PagerDutySink, JiraSink, PAGERDUTY_ENQUEUE_URL } from './provider-sink.js';\nexport { formatSlack, formatPagerDuty, formatJira } from './providers.js';\nexport type { DriftSink } from './types.js';\nexport type { WebhookSinkOptions, PostJsonOptions, FetchLike } from './webhook.js';\nexport type { PagerDutySinkOptions, JiraSinkOptions } from './provider-sink.js';\nexport type { SlackMessage, PagerDutyEvent, JiraIssue, JiraOptions } from './providers.js';\n","/**\n * Drift classification + dispatch (3.1).\n *\n * Layered on top of the pure topology delta (`diffTopology` → `db.diffSessions`):\n * `classifyDrift` turns a `TopologyDiff` into a severity-ranked `DriftAlert`\n * (deterministic, DB-agnostic, exhaustively unit-testable), and `runDrift` is the\n * one-shot runner — the seam scheduled discovery (2.5) calls after a scan — that\n * resolves the latest two sessions, classifies, threshold-filters, fans out to the\n * configured sinks (one sink's failure never aborts the others), and writes an\n * audit event. No new tables, no migration; read-only except the audit row.\n */\n\nimport type {\n TopologyDiff, DriftAlert, DriftAlertItem, Severity, NodeChange, CartographyConfig,\n} from './types.js';\nimport { SEVERITIES } from './types.js';\nimport type { CartographyDB } from './db.js';\nimport { stableStringify } from './diff.js';\nimport { redactValue } from './tools.js';\nimport { buildSinks } from './sinks/index.js';\nimport { logInfo, logWarn, logError } from './logger.js';\nimport { SECURITY_METADATA_KEYS } from './types.js';\n\n/** Return the highest severity among items; `info` for an empty list. */\nexport function maxSeverity(items: DriftAlertItem[]): Severity {\n let rank = 0;\n for (const it of items) {\n const r = SEVERITIES.indexOf(it.severity);\n if (r > rank) rank = r;\n }\n return SEVERITIES[rank];\n}\n\nconst SECURITY_KEY_SET = new Set<string>(SECURITY_METADATA_KEYS);\n\n/**\n * Names of the metadata keys whose value changed and which warrant escalation to\n * `critical`. Empty unless the node-changed touched `metadata` and a\n * security-relevant key (case-insensitive) actually differs before→after.\n */\nexport function securityRelevantChange(change: NodeChange): string[] {\n if (!change.changedFields.includes('metadata')) return [];\n const before = change.before.metadata ?? {};\n const after = change.after.metadata ?? {};\n const keys = new Set<string>([...Object.keys(before), ...Object.keys(after)]);\n const triggered: string[] = [];\n for (const k of keys) {\n if (!SECURITY_KEY_SET.has(k.toLowerCase())) continue;\n if (stableStringify((before as Record<string, unknown>)[k]) !== stableStringify((after as Record<string, unknown>)[k])) {\n triggered.push(k);\n }\n }\n return triggered.sort();\n}\n\n/** Logical reference for an edge item: `source -rel-> target`. */\nconst edgeRef = (sourceId: string, rel: string, targetId: string): string =>\n `${sourceId} -${rel}-> ${targetId}`;\n\n/**\n * Classify a topology diff into a severity-tagged alert. Pure & deterministic\n * (only `generatedAt` depends on `now`). Rules:\n * - node-removed, edge-removed → warning (lost capability / connectivity)\n * - node-changed touching a security metadata → critical\n * key\n * - node-changed (other fields) → info\n * - node-added, edge-added → info\n * Overall severity = maxSeverity(items).\n */\nexport function classifyDrift(diff: TopologyDiff, now: Date = new Date()): DriftAlert {\n const items: DriftAlertItem[] = [];\n\n for (const n of diff.nodes.added) {\n items.push({ kind: 'node-added', ref: n.id, label: n.name, nodeType: n.type, severity: 'info' });\n }\n for (const n of diff.nodes.removed) {\n items.push({ kind: 'node-removed', ref: n.id, label: n.name, nodeType: n.type, severity: 'warning' });\n }\n for (const c of diff.nodes.changed) {\n const securityFields = securityRelevantChange(c);\n const severity: Severity = securityFields.length > 0 ? 'critical' : 'info';\n items.push({\n kind: 'node-changed',\n ref: c.id,\n label: c.after.name,\n nodeType: c.after.type,\n severity,\n changedFields: c.changedFields,\n ...(securityFields.length > 0 ? { securityFields } : {}),\n });\n }\n for (const e of diff.edges.added) {\n items.push({\n kind: 'edge-added',\n ref: edgeRef(e.sourceId, e.relationship, e.targetId),\n label: `${e.sourceId} → ${e.targetId}`,\n severity: 'info',\n });\n }\n for (const e of diff.edges.removed) {\n items.push({\n kind: 'edge-removed',\n ref: edgeRef(e.sourceId, e.relationship, e.targetId),\n label: `${e.sourceId} → ${e.targetId}`,\n severity: 'warning',\n });\n }\n\n return {\n base: diff.base,\n current: diff.current,\n summary: diff.summary,\n severity: maxSeverity(items),\n items,\n generatedAt: now.toISOString(),\n };\n}\n\n/** Drop items strictly below `min`; recompute the overall severity over what's kept. */\nexport function filterBySeverity(alert: DriftAlert, min: Severity): DriftAlert {\n const minRank = SEVERITIES.indexOf(min);\n const items = alert.items.filter((it) => SEVERITIES.indexOf(it.severity) >= minRank);\n return { ...alert, items, severity: maxSeverity(items) };\n}\n\nexport interface RunDriftOptions { base?: string; current?: string; minSeverity?: Severity; }\n\n/**\n * Resolve base/current (newest-first `getSessions`, like the CLI/MCP surfaces),\n * classify, filter below `minSeverity`, dispatch to all configured sinks\n * (`Promise.allSettled` — one sink failure never aborts the others), and record an\n * audit event. Returns the alert, or `null` when fewer than two sessions exist\n * (graceful no-op). The seam scheduled discovery (2.5) calls post-scan.\n */\nexport async function runDrift(\n db: CartographyDB, config: CartographyConfig, opts: RunDriftOptions = {},\n): Promise<DriftAlert | null> {\n // getSessions() is newest-first (rowid DESC): [0] = latest, [1] = previous.\n const sessions = db.getSessions();\n const currentId = opts.current ?? sessions[0]?.id;\n const baseId = opts.base ?? sessions[1]?.id;\n if (!baseId || !currentId) {\n logInfo('drift: fewer than two sessions, skipping');\n return null;\n }\n if (baseId === currentId) {\n logWarn('drift: base and current session are the same, skipping', { session: currentId });\n return null;\n }\n\n const diff = db.diffSessions(baseId, currentId);\n let alert = classifyDrift(diff);\n alert = filterBySeverity(alert, opts.minSeverity ?? config.drift?.minSeverity ?? 'info');\n\n const sinks = buildSinks(config.drift);\n const results = await Promise.allSettled(sinks.map((s) => s.emit(alert)));\n results.forEach((r, i) => {\n if (r.status === 'rejected') {\n logError('drift sink rejected', {\n sink: sinks[i]?.name,\n reason: r.reason instanceof Error ? r.reason.message : String(r.reason),\n });\n }\n });\n\n // Audit (no migration): reuses existing activity_events columns.\n db.insertEvent(currentId, {\n eventType: 'drift_alert_dispatched',\n process: 'drift',\n pid: process.pid,\n command: JSON.stringify(redactValue({\n base: baseId,\n current: currentId,\n severity: alert.severity,\n items: alert.items.length,\n sinks: sinks.map((s) => s.name),\n })),\n });\n\n return alert;\n}\n","/**\n * Org-key lifecycle for the 2.10 anonymization layer.\n *\n * The org key is the single secret that makes pseudonyms deterministic across\n * machines (so a central merge collapses the same host to one token) and\n * admin-reversible (only the key holder can invert a pseudonym). It lives on disk\n * at `~/.cartography/org-key` (mode 0600), **never** in the SQLite catalog, never\n * in any log line, and never in an export. The returned key is HKDF-namespaced by\n * `organization`, so two organizations sharing one machine derive non-colliding\n * keys (and therefore non-colliding tokens + isolated reversal maps).\n *\n * Zero new dependencies: HMAC/AES/HKDF/randomBytes all come from `node:crypto`.\n */\n\nimport { randomBytes, hkdfSync } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { logInfo, logWarn } from './logger.js';\n\nexport interface OrgKeyOptions {\n /** Organization namespace; absent ⇒ the `'default'` namespace. */\n organization?: string;\n /** Override the on-disk key path (tests / non-default data dirs). */\n keyPath?: string;\n}\n\n/** Default org-key file path: `~/.cartography/org-key`. */\nexport function orgKeyPath(home: string = homedir()): string {\n return join(home, '.cartography', 'org-key');\n}\n\n/** Bytes of the org-key file: 32 random bytes stored hex. */\nconst KEY_BYTES = 32;\n\n/** Read the raw file secret, or lazily create it (32 random bytes, hex, mode 0600). */\nfunction loadFileSecret(path: string): Buffer {\n if (existsSync(path)) {\n // Warn (never throw) if the key file is group/world-readable — it must be 0600.\n try {\n const mode = statSync(path).mode & 0o777;\n if (mode & 0o077) {\n logWarn('org-key file is not 0600 — restrict it: chmod 600 ' + path);\n }\n } catch {\n /* stat failure is non-fatal; fall through to the read */\n }\n const hex = readFileSync(path, 'utf8').trim();\n const buf = Buffer.from(hex, 'hex');\n if (buf.length === KEY_BYTES) return buf;\n // Corrupt/short key material: regenerate rather than derive a weak key.\n logWarn('org-key file was malformed — regenerating a fresh org key');\n }\n mkdirSync(dirname(path), { recursive: true });\n const secret = randomBytes(KEY_BYTES);\n writeFileSync(path, secret.toString('hex'), { mode: 0o600 });\n logInfo('created org key at ~/.cartography/org-key (mode 0600) — distribute to admins out-of-band');\n return secret;\n}\n\n/**\n * Load (or lazily create) the org key, HKDF-namespaced by `organization`. The\n * on-disk secret is a single random value; per-org keys are derived from it so\n * the same file serves multiple orgs without collision. Pure of side effects\n * beyond the lazy file creation; never logs the key material.\n */\nexport function loadOrgKey(opts: OrgKeyOptions = {}): Buffer {\n const path = opts.keyPath ?? orgKeyPath();\n const secret = loadFileSecret(path);\n const info = opts.organization && opts.organization.trim() ? opts.organization.trim() : 'default';\n return Buffer.from(hkdfSync('sha256', secret, Buffer.alloc(0), info, KEY_BYTES));\n}\n\n/**\n * Rotate the org key: overwrite the on-disk secret with fresh random bytes and\n * return the new derived key. Reversal entries written under the old key become\n * unrecoverable by design — callers are warned at the stderr level.\n */\nexport function rotateOrgKey(opts: OrgKeyOptions = {}): Buffer {\n const path = opts.keyPath ?? orgKeyPath();\n mkdirSync(dirname(path), { recursive: true });\n const secret = randomBytes(KEY_BYTES);\n writeFileSync(path, secret.toString('hex'), { mode: 0o600 });\n logWarn('org key rotated — pseudonym_reversal entries created under the previous key are now UNRECOVERABLE');\n const info = opts.organization && opts.organization.trim() ? opts.organization.trim() : 'default';\n return Buffer.from(hkdfSync('sha256', secret, Buffer.alloc(0), info, KEY_BYTES));\n}\n\n/** Subkey for HMAC pseudonym tokens (distinct domain from the reversal cipher key). */\nexport function hmacKey(orgKey: Buffer): Buffer {\n return Buffer.from(hkdfSync('sha256', orgKey, Buffer.from('cartography-hmac-v1'), 'hmac', 32));\n}\n\n/** Subkey for AES-256-GCM reversal-map encryption (distinct domain from the HMAC key). */\nexport function reversalKey(orgKey: Buffer): Buffer {\n return Buffer.from(hkdfSync('sha256', orgKey, Buffer.from('cartography-reversal-v1'), 'reversal', 32));\n}\n","/**\n * Org-keyed, admin-reversible pseudonymization (2.10 `anonymized` sharing level).\n *\n * Identifying fragments — private IPs, file paths, `user@host` pairs, and bare\n * hostnames — are replaced by a deterministic token\n * `anon:{kind}:{base32(first 12 bytes of HMAC-SHA256(hmacKey, plaintext))}`\n * so the same input + org key always yields the same token (a central merge then\n * collapses the same host across machines). The token is one-way for anyone\n * without the org key (HMAC preimage resistance + a 32-byte secret); an admin\n * holding the key can invert it via an AES-256-GCM reversal map.\n *\n * Topology shape is preserved: {@link pseudonymize} mirrors `redactValue`\n * (`src/tools.ts`) — it walks structure, rewriting only leaf strings; it never\n * adds, removes, or reorders keys/array elements.\n *\n * Zero new dependencies — all crypto is `node:crypto`.\n */\n\nimport { createHmac, createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport { hmacKey, reversalKey } from './orgkey.js';\nimport { logError } from './logger.js';\nimport type { CartographyDB } from './db.js';\n\n/** The kinds of identifying fragment we tokenize. The prefix keeps namespaces legible + non-colliding. */\nexport type FragmentKind = 'host' | 'user' | 'path' | 'ip';\n\n/**\n * RFC-1918 private IPv4 ranges: 10/8, 172.16/12, 192.168/16. Only private IPs are\n * pseudonymized — public IPs are not identifying of an employee's machine and are\n * left intact (so topology against public infra still reads).\n */\nexport const PRIVATE_IP =\n /\\b(?:10(?:\\.\\d{1,3}){3}|192\\.168(?:\\.\\d{1,3}){2}|172\\.(?:1[6-9]|2\\d|3[01])(?:\\.\\d{1,3}){2})\\b/g;\n\n/** A hostname/domain label run, e.g. `db-01.internal.acme.lan` (≥2 dot-separated labels). */\nconst HOSTNAME = /\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}\\b/gi;\n\n/** An absolute POSIX path (`/home/alice/...`) or Windows path (`C:\\Users\\alice\\...`). */\nconst POSIX_PATH = /(?:^|(?<=\\s|=|:|\"|'|\\())(\\/[A-Za-z0-9._-]+(?:\\/[A-Za-z0-9._-]+)+)/g;\nconst WIN_PATH = /\\b[A-Za-z]:\\\\[A-Za-z0-9._\\\\-]+/g;\n\n/** RFC-4648 base32 (no padding) of the first 12 bytes of a digest → 20 lowercase chars. */\nconst B32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';\nfunction base32(buf: Buffer): string {\n let bits = 0;\n let value = 0;\n let out = '';\n for (const byte of buf) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += B32_ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n if (bits > 0) out += B32_ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\n/** GCM ciphertext layout: base64( iv[12] ‖ tag[16] ‖ ciphertext ). */\nfunction encryptPlaintext(plaintext: string, key: Buffer): string {\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n return Buffer.concat([iv, tag, ct]).toString('base64');\n}\n\nfunction decryptPlaintext(encoded: string, key: Buffer): string | undefined {\n try {\n const raw = Buffer.from(encoded, 'base64');\n const iv = raw.subarray(0, 12);\n const tag = raw.subarray(12, 28);\n const ct = raw.subarray(28);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');\n } catch {\n // GCM auth failure (tampered / wrong key) — fail closed, never throw raw.\n return undefined;\n }\n}\n\n/**\n * Deterministic token for one identifying fragment. When `db` is supplied, the\n * plaintext is AES-256-GCM-encrypted under `reversalKey(orgKey)` and persisted so\n * an admin can later invert the token. Idempotent: the token is deterministic, so\n * re-encrypting the same fragment `INSERT OR REPLACE`s the same row.\n */\nexport function pseudonymizeFragment(plaintext: string, kind: FragmentKind, orgKey: Buffer, db?: CartographyDB): string {\n const digest = createHmac('sha256', hmacKey(orgKey)).update(plaintext).digest();\n const token = `anon:${kind}:${base32(digest.subarray(0, 12))}`;\n if (db) db.saveReversal(token, encryptPlaintext(plaintext, reversalKey(orgKey)));\n return token;\n}\n\n/**\n * Pseudonymize the identifying fragments inside a single string — private IPs,\n * file paths, `user@host` pairs, and bare hostnames — leaving structure-irrelevant\n * text intact. Order matters: paths and IPs are matched before hostnames so an IP\n * or a path segment is never mis-tokenized as a host.\n */\nexport function pseudonymizeString(s: string, orgKey: Buffer, db?: CartographyDB): string {\n let out = s;\n // 1. Private IPs (most specific; also subsumed by paths/hosts if left late).\n out = out.replace(PRIVATE_IP, (m) => pseudonymizeFragment(m, 'ip', orgKey, db));\n // 2. Windows + POSIX absolute paths.\n out = out.replace(WIN_PATH, (m) => pseudonymizeFragment(m, 'path', orgKey, db));\n out = out.replace(POSIX_PATH, (m) => pseudonymizeFragment(m, 'path', orgKey, db));\n // 3. user@host — tokenize the user and the host independently so both invert.\n out = out.replace(/\\b([a-z0-9._-]+)@((?:[a-z0-9-]+\\.)+[a-z]{2,})\\b/gi, (_m, user: string, host: string) =>\n `${pseudonymizeFragment(user, 'user', orgKey, db)}@${pseudonymizeFragment(host, 'host', orgKey, db)}`,\n );\n // 4. Remaining bare hostnames/domains.\n out = out.replace(HOSTNAME, (m) => pseudonymizeFragment(m, 'host', orgKey, db));\n return out;\n}\n\n/**\n * Recursive, structure-preserving walker — same shape as `redactValue`\n * (`src/tools.ts`): strings → {@link pseudonymizeString}, arrays → map,\n * objects → per-value recurse, primitives → unchanged. Object keys are left\n * verbatim (they are schema, not data), so topology shape is invariant.\n */\nexport function pseudonymize(value: unknown, orgKey: Buffer, db?: CartographyDB): unknown {\n if (typeof value === 'string') return pseudonymizeString(value, orgKey, db);\n if (Array.isArray(value)) return value.map((v) => pseudonymize(v, orgKey, db));\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = pseudonymize(v, orgKey, db);\n return out;\n }\n return value;\n}\n\n/**\n * Admin reversal: decrypt the original plaintext behind a pseudonym token, or\n * `undefined` when the token is unknown or the ciphertext fails GCM authentication\n * (tampered or produced under a different / rotated org key).\n */\nexport function reversePseudonym(token: string, orgKey: Buffer, db: CartographyDB): string | undefined {\n const encoded = db.getReversal(token);\n if (encoded == null) return undefined;\n const plaintext = decryptPlaintext(encoded, reversalKey(orgKey));\n if (plaintext === undefined) {\n logError('reversePseudonym: ciphertext failed authentication (tampered or wrong/rotated org key)');\n }\n return plaintext;\n}\n","/**\n * The Cartography MCP server — the package's primary, LLM-agnostic interface.\n *\n * It exposes the discovered infrastructure topology as Model Context Protocol\n * **Resources** (read-only context, progressive disclosure), a small set of query\n * **Tools** (parameterized lookups), and reusable **Prompts**. Any MCP host —\n * Claude Code, Cursor, Cline, Windsurf, the Vercel AI SDK, LangGraph — can drive\n * it; the package never needs to know which model is in use.\n */\n\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { CartographyDB, deriveSessionName, normalizeTenant } from '../db.js';\nimport type { GraphSummary, OrgSummary } from '../db.js';\nimport { defaultConfig, NODE_TYPES, NODE_TYPE_GROUPS } from '../types.js';\nimport type { NodeRow } from '../types.js';\nimport type { TopologyDelta } from '../diff.js';\nimport { classifyDrift, filterBySeverity } from '../drift.js';\nimport { redactValue } from '../tools.js';\nimport { authorize, AuthorizationError } from '../auth/rbac.js';\nimport type { Principal } from '../auth/types.js';\nimport { getRuleset, listRulesets } from '../compliance/rulesets/registry.js';\nimport { parseNlQuery, executeNlQuery } from '../nlq/index.js';\n\nconst SERVER_NAME = 'cartography';\nconst SERVER_VERSION = '2.7.0';\n\nconst SERVICE_TYPES = NODE_TYPE_GROUPS.web;\nconst DATA_TYPES = NODE_TYPE_GROUPS.data;\n\n/** A pluggable search backend; defaults to lexical search, can be upgraded to semantic. */\nexport type SearchFn = (\n db: CartographyDB,\n sessionId: string,\n query: string,\n opts: { types?: readonly string[]; limit: number },\n) => Promise<Array<{ node: NodeRow; score?: number }>>;\n\n/** A pluggable discovery backend invoked by the `run_discovery` tool. */\nexport type DiscoveryFn = (\n db: CartographyDB,\n sessionId: string,\n opts: { hint?: string; mode?: 'replace' | 'update' },\n) => Promise<{ nodes: number; edges: number; delta?: TopologyDelta }>;\n\nexport interface CreateMcpServerOptions {\n /** Database instance. If omitted, one is opened at `config.dbPath`. */\n db?: CartographyDB;\n /** Path to the SQLite catalog (used when `db` is not provided). */\n dbPath?: string;\n /** Session to serve: a session id, or `'latest'` (default) for the newest discovery. */\n session?: string | 'latest';\n /**\n * Tenant/organization whose topology this server serves. Defaults to DEFAULT_TENANT\n * (`'local'`). Session resolution is scoped to this tenant, so a server bound to one\n * tenant never surfaces another tenant's sessions/nodes/edges. This is a data-scoping\n * partition, not an authorization boundary (RBAC is Phase 4).\n */\n tenant?: string;\n /** Semantic/lexical search backend. Defaults to lexical `searchNodes`. */\n search?: SearchFn;\n /** Discovery backend for `run_discovery`/`refresh`. Optional. */\n discovery?: DiscoveryFn;\n /**\n * Central-collector org (2.12). When set, `get_summary` returns the org-wide,\n * cross-machine merged summary (`db.getOrgSummary(org)`) instead of the single-\n * session view. Used by server-mode; unset preserves the local single-session\n * behaviour exactly. The org is normalized to a tenant.\n */\n org?: string;\n /**\n * The authenticated principal (4.5 RBAC). When set, mutating tools (`run_discovery`)\n * are gated by role: a `viewer` is refused with a forbidden error. Read tools are\n * unaffected (any principal is at least `viewer`). Unset → no role gating (the\n * transport already handled 401, or it's an in-process/stdio caller).\n */\n principal?: Principal;\n}\n\nconst lexicalSearch: SearchFn = async (db, sessionId, query, opts) =>\n db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));\n\n/** Compact projection of a node for tool results (token-economical). */\nfunction compactNode(n: NodeRow): Record<string, unknown> {\n return {\n id: n.id,\n type: n.type,\n name: n.name,\n confidence: n.confidence,\n ...(n.domain ? { domain: n.domain } : {}),\n ...(n.tags.length ? { tags: n.tags } : {}),\n };\n}\n\nfunction json(data: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] };\n}\n\n/** True for the org-wide (central-collector) summary shape. */\nfunction isOrgSummary(s: GraphSummary | OrgSummary): s is OrgSummary {\n return 'org' in s;\n}\n\nfunction summaryText(s: GraphSummary | OrgSummary): string {\n const header = isOrgSummary(s)\n ? `# Organization topology — org ${s.org} (${s.contributors} contributor${s.contributors === 1 ? '' : 's'})`\n : `# Infrastructure topology — session ${s.sessionId}`;\n const lines = [\n header,\n ``,\n `Totals: ${s.totals.nodes} nodes, ${s.totals.edges} edges`,\n ``,\n `Nodes by type:`,\n ...Object.entries(s.nodesByType).sort((a, b) => b[1] - a[1]).map(([t, c]) => ` - ${t}: ${c}`),\n ``,\n `Nodes by domain:`,\n ...Object.entries(s.nodesByDomain).sort((a, b) => b[1] - a[1]).map(([d, c]) => ` - ${d}: ${c}`),\n ``,\n `Edges by relationship:`,\n ...Object.entries(s.edgesByRelationship).sort((a, b) => b[1] - a[1]).map(([r, c]) => ` - ${r}: ${c}`),\n ``,\n `Most connected:`,\n ...s.topConnected.map((n) => ` - ${n.id} (${n.type}) — degree ${n.degree}`),\n // 3.6 anomalies — single-session GraphSummary only.\n ...(!isOrgSummary(s)\n ? [``, `Anomalies (${s.anomalies.length}):`,\n ...(s.anomalies.length === 0\n ? [' - none']\n : s.anomalies.slice(0, 20).map((a) => ` - [${a.severity}] ${a.kind}: ${a.nodeId} — ${a.reason}`))]\n : []),\n // 3.3 cost section — only for the single-session GraphSummary, and only when costs exist.\n ...(!isOrgSummary(s) && s.costByDomain.length\n ? [``, `Cost by domain:`, ...s.costByDomain.map((c) => ` - ${c.domain} [${c.currency}/${c.period}]: ${c.total} (${c.nodes} nodes)`)]\n : []),\n ...(!isOrgSummary(s) && s.costCoverage.withCost\n ? [``, `Cost coverage: ${s.costCoverage.withCost}/${s.costCoverage.total} nodes attributed`]\n : []),\n ``,\n `Read cartography://nodes/{id} or cartography://dependencies/{id} for detail.`,\n ];\n return lines.join('\\n');\n}\n\n/**\n * Build a fully-configured Cartography MCP server. Call `.connect(transport)` to run it.\n */\nexport function createMcpServer(opts: CreateMcpServerOptions = {}): McpServer {\n const db = opts.db ?? new CartographyDB(opts.dbPath ?? defaultConfig().dbPath);\n const search = opts.search ?? lexicalSearch;\n const tenant = normalizeTenant(opts.tenant);\n // Central-collector org (2.12): when set, get_summary serves the org-wide merged\n // view; otherwise the single-session view is preserved unchanged.\n const org = opts.org !== undefined ? normalizeTenant(opts.org) : undefined;\n\n /**\n * Resolve the served session id at call time (so late discoveries are picked up),\n * scoped to the served `tenant`. An explicit session id must belong to the served\n * tenant or it resolves to undefined — so a client bound to tenant A can never read\n * a session owned by tenant B, even by naming its id.\n */\n const resolveSession = (): string | undefined => {\n if (opts.session && opts.session !== 'latest') {\n const s = db.getSession(opts.session);\n return s && s.tenant === tenant ? s.id : undefined;\n }\n return db.getLatestSession('discover', tenant)?.id ?? db.getLatestSession(undefined, tenant)?.id;\n };\n\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n {\n capabilities: { resources: { subscribe: true, listChanged: true }, tools: {}, prompts: {}, logging: {} },\n instructions:\n 'Cartography exposes a discovered infrastructure/SaaS topology. Start by reading ' +\n 'cartography://graph/summary for a low-token overview, then drill into specific nodes ' +\n 'via cartography://nodes/{id} or query with the query_infrastructure / get_dependencies tools.',\n },\n );\n\n // ── Resources (read-only context, progressive disclosure) ──────────────────\n\n server.registerResource(\n 'graph-summary',\n 'cartography://graph/summary',\n { title: 'Topology summary', description: 'Low-token aggregate index of the whole landscape — read this first.', mimeType: 'text/markdown' },\n (uri) => {\n if (org !== undefined) {\n return { contents: [{ uri: uri.href, mimeType: 'text/markdown', text: summaryText(db.getOrgSummary(org)) }] };\n }\n const sid = resolveSession();\n if (!sid) return { contents: [{ uri: uri.href, mimeType: 'text/markdown', text: 'No discovery session found. Run discovery first.' }] };\n return { contents: [{ uri: uri.href, mimeType: 'text/markdown', text: summaryText(db.getGraphSummary(sid)) }] };\n },\n );\n\n server.registerResource(\n 'cost-summary',\n 'cartography://cost/summary',\n { title: 'Cost summary', description: 'Cost rolled up by domain and owner (currency/period-bucketed).', mimeType: 'application/json' },\n (uri) => {\n const sid = resolveSession();\n if (!sid) return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ error: 'No discovery session found.' }) }] };\n const s = db.getGraphSummary(sid);\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ costByDomain: s.costByDomain, costByOwner: s.costByOwner, costCoverage: s.costCoverage }, null, 2) }] };\n },\n );\n\n server.registerResource(\n 'nodes-index',\n 'cartography://nodes',\n { title: 'Node index', description: 'Lightweight list of all nodes (id, type, name only).', mimeType: 'application/json' },\n (uri) => {\n const sid = resolveSession();\n const nodes = sid ? db.getNodes(sid) : [];\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ count: nodes.length, nodes: nodes.map((n) => ({ id: n.id, type: n.type, name: n.name })) }, null, 2) }] };\n },\n );\n\n server.registerResource(\n 'node-detail',\n new ResourceTemplate('cartography://nodes/{id}', { list: undefined }),\n { title: 'Node detail', description: 'Full node record plus its incident edges.', mimeType: 'application/json' },\n (uri, variables) => {\n const sid = resolveSession();\n const id = decodeURIComponent(String(variables['id']));\n const node = sid ? db.getNode(sid, id) : undefined;\n if (!node) return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ error: `node not found: ${id}` }) }] };\n const edges = db.getEdges(sid!).filter((e) => e.sourceId === id || e.targetId === id);\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ node, edges }, null, 2) }] };\n },\n );\n\n const typedListResource = (name: string, uri: string, title: string, types: readonly string[]) =>\n server.registerResource(name, uri, { title, description: `Nodes of type: ${types.join(', ')}.`, mimeType: 'application/json' }, (u) => {\n const sid = resolveSession();\n const nodes = sid ? db.getNodesByType(sid, types) : [];\n return { contents: [{ uri: u.href, mimeType: 'application/json', text: JSON.stringify({ count: nodes.length, nodes: nodes.map(compactNode) }, null, 2) }] };\n });\n\n typedListResource('services', 'cartography://services', 'Services', SERVICE_TYPES);\n typedListResource('databases', 'cartography://databases', 'Data stores', DATA_TYPES);\n\n server.registerResource(\n 'dependencies',\n new ResourceTemplate('cartography://dependencies/{id}', { list: undefined }),\n { title: 'Dependencies', description: 'Transitive downstream dependencies of a node.', mimeType: 'application/json' },\n (uri, variables) => {\n const sid = resolveSession();\n const id = decodeURIComponent(String(variables['id']));\n if (!sid) return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ error: 'no session' }) }] };\n const r = db.getDependencies(sid, id, { direction: 'downstream', maxDepth: 8 });\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({\n root: id,\n count: r.nodes.length,\n nodes: r.nodes.map((n) => ({ ...compactNode(n), depth: n.depth })),\n edges: r.edges.map((e) => ({ from: e.sourceId, to: e.targetId, rel: e.relationship, confidence: e.confidence, evidence: e.evidence })),\n }, null, 2) }] };\n },\n );\n\n server.registerResource(\n 'sessions',\n 'cartography://sessions',\n { title: 'Discovery sessions', description: 'All discovery sessions in the catalog.', mimeType: 'application/json' },\n (uri) => ({ contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(db.getSessions(tenant), null, 2) }] }),\n );\n\n // ── Tools (model-controlled queries) ───────────────────────────────────────\n\n /** Annotations shared by every read-only query tool (untrusted hints, never a security boundary). */\n const readOnly = { readOnlyHint: true, openWorldHint: false } as const;\n\n server.registerTool(\n 'get_summary',\n { title: 'Get topology summary', description: 'Low-token overview of the whole landscape (counts, types, domains, most-connected, anomalies).', inputSchema: {}, annotations: readOnly },\n () => {\n if (org !== undefined) return json(db.getOrgSummary(org));\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n return json(db.getGraphSummary(sid));\n },\n );\n\n server.registerTool(\n 'get_cost_summary',\n { title: 'Get cost summary', description: 'FinOps rollup: cost by domain and owner, currency/period-bucketed (3.3).', inputSchema: {}, annotations: readOnly },\n () => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const s = db.getGraphSummary(sid);\n return json({ costByDomain: s.costByDomain, costByOwner: s.costByOwner, costCoverage: s.costCoverage });\n },\n );\n\n server.registerTool(\n 'query_infrastructure',\n {\n title: 'Query infrastructure',\n description: 'Search the topology by name/id/domain (optionally filtered by node type). Returns compact node records.',\n inputSchema: {\n query: z.string().describe('Free-text query, e.g. \"postgres\", \"auth\", \"github\"'),\n types: z.array(z.enum(NODE_TYPES)).optional().describe('Restrict to these node types'),\n limit: z.number().int().min(1).max(200).default(25).optional(),\n },\n annotations: readOnly,\n },\n async (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const results = await search(db, sid, args.query, { types: args.types, limit: args.limit ?? 25 });\n return json({ count: results.length, results: results.map((r) => ({ ...compactNode(r.node), ...(r.score !== undefined ? { score: r.score } : {}) })) });\n },\n );\n\n server.registerTool(\n 'search_topology',\n {\n title: 'Search topology (semantic)',\n description: 'Find nodes related to a concept by meaning (semantic search when available, lexical otherwise).',\n inputSchema: { query: z.string(), limit: z.number().int().min(1).max(100).default(10).optional() },\n annotations: readOnly,\n },\n async (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const results = await search(db, sid, args.query, { limit: args.limit ?? 10 });\n return json({ count: results.length, results: results.map((r) => ({ ...compactNode(r.node), ...(r.score !== undefined ? { score: r.score } : {}) })) });\n },\n );\n\n server.registerTool(\n 'list_services',\n {\n title: 'List services',\n description: 'List discovered services or data stores.',\n inputSchema: { kind: z.enum(['services', 'databases', 'all']).default('all').optional() },\n annotations: readOnly,\n },\n (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const kind = args.kind ?? 'all';\n const types = kind === 'services' ? SERVICE_TYPES : kind === 'databases' ? DATA_TYPES : [...SERVICE_TYPES, ...DATA_TYPES];\n return json(db.getNodesByType(sid, types).map(compactNode));\n },\n );\n\n server.registerTool(\n 'get_node',\n { title: 'Get node', description: 'Fetch a single node with its incident edges.', inputSchema: { id: z.string() }, annotations: readOnly },\n (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const node = db.getNode(sid, args.id);\n if (!node) return json({ error: `node not found: ${args.id}` });\n const edges = db.getEdges(sid).filter((e) => e.sourceId === args.id || e.targetId === args.id);\n return json({ node, edges });\n },\n );\n\n server.registerTool(\n 'get_dependencies',\n {\n title: 'Get dependencies',\n description: 'Traverse the dependency graph from a node (downstream/upstream/both) with a depth limit.',\n inputSchema: {\n id: z.string(),\n direction: z.enum(['downstream', 'upstream', 'both']).default('downstream').optional(),\n maxDepth: z.number().int().min(1).max(64).default(8).optional(),\n minConfidence: z.number().min(0).max(1).optional().describe('Drop edges below this confidence (0..1).'),\n },\n annotations: readOnly,\n },\n (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const r = db.getDependencies(sid, args.id, { direction: args.direction ?? 'downstream', maxDepth: args.maxDepth ?? 8 });\n const minConfidence = args.minConfidence ?? 0;\n return json({\n root: r.root ? compactNode(r.root) : null,\n direction: r.direction,\n count: r.nodes.length,\n nodes: r.nodes.map((n) => ({ ...compactNode(n), depth: n.depth })),\n edges: r.edges\n .filter((e) => e.confidence >= minConfidence)\n .map((e) => ({ from: e.sourceId, to: e.targetId, rel: e.relationship, confidence: e.confidence, evidence: e.evidence })),\n });\n },\n );\n\n server.registerTool(\n 'query_natural_language',\n {\n title: 'Query in natural language',\n description:\n 'Answer a plain-English topology question (e.g. \"services that depend on the payments DB\"). ' +\n 'Deterministically parses the question into a structured intent, then anchors via search and ' +\n 'traverses dependencies, applying any node-type filter to the results. Echoes the parsed ' +\n 'intent for explainability. Read-only, LLM-free.',\n inputSchema: {\n query: z.string().min(1).max(1000).describe('Natural-language question about the topology'),\n maxDepth: z.number().int().min(1).max(64).default(8).optional(),\n },\n annotations: readOnly,\n },\n async (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const intent = parseNlQuery(args.query);\n const r = await executeNlQuery(db, sid, search, intent, { maxDepth: args.maxDepth ?? 8 });\n return json({\n intent: {\n query: r.intent.query, subjectQuery: r.intent.subjectQuery,\n relation: r.intent.relation, direction: r.intent.direction ?? null,\n typeFilter: r.intent.typeFilter ?? null, degraded: r.intent.degraded,\n },\n anchors: r.anchors.map((a) => ({ ...compactNode(a.node), ...(a.score !== undefined ? { score: a.score } : {}) })),\n count: r.nodes.length,\n nodes: r.nodes.map((n) => ({ ...compactNode(n), ...(n.depth !== undefined ? { depth: n.depth } : {}) })),\n paths: r.paths.map((e) => ({ from: e.sourceId, to: e.targetId, rel: e.relationship })),\n });\n },\n );\n\n server.registerTool(\n 'diff_topology',\n {\n title: 'Diff topology (drift detection)',\n description:\n 'Compare two discovery sessions and report added/removed/changed nodes and added/removed edges, ' +\n 'plus newly-appearing structural anomalies (3.6). ' +\n 'Defaults to the two most recent sessions (base = second-most-recent, current = most-recent).',\n inputSchema: {\n base: z.string().optional().describe('Baseline session id (default: second-most-recent session)'),\n current: z.string().optional().describe('Current session id (default: most-recent session)'),\n },\n annotations: readOnly,\n },\n (args) => {\n // getSessions(tenant) is ordered newest-first (rowid DESC): [0] = latest, [1] = previous.\n const sessions = db.getSessions(tenant);\n const currentId = args.current ?? sessions[0]?.id;\n const baseId = args.base ?? sessions[1]?.id;\n if (!baseId || !currentId) return json({ error: 'Need at least two discovery sessions to diff.' });\n if (baseId === currentId) return json({ error: 'Base and current session are the same.' });\n const d = db.diffSessions(baseId, currentId);\n return json({\n base: d.base,\n current: d.current,\n summary: d.summary,\n nodes: {\n added: d.nodes.added.map(compactNode),\n removed: d.nodes.removed.map(compactNode),\n changed: d.nodes.changed.map((c) => ({ ...compactNode(c.after), changedFields: c.changedFields })),\n },\n edges: {\n added: d.edges.added.map((e) => ({ from: e.sourceId, to: e.targetId, rel: e.relationship })),\n removed: d.edges.removed.map((e) => ({ from: e.sourceId, to: e.targetId, rel: e.relationship })),\n },\n anomalies: {\n added: d.anomalies.added,\n baseCount: d.anomalies.base.length,\n currentCount: d.anomalies.current.length,\n },\n });\n },\n );\n\n server.registerTool(\n 'classify_drift',\n {\n title: 'Classify drift (severity-ranked drift detection)',\n description:\n 'Compare two discovery sessions and return a severity-classified drift alert ' +\n '(info|warning|critical per item plus an overall severity). Defaults to the two most recent. ' +\n 'Read-only: never dispatches to sinks.',\n inputSchema: {\n base: z.string().optional().describe('Baseline session id (default: second-most-recent)'),\n current: z.string().optional().describe('Current session id (default: most-recent)'),\n minSeverity: z.enum(['info', 'warning', 'critical']).optional().describe('Drop items below this severity'),\n },\n annotations: readOnly,\n },\n (args) => {\n // getSessions(tenant) is newest-first (rowid DESC): [0] = latest, [1] = previous.\n const sessions = db.getSessions(tenant);\n const currentId = args.current ?? sessions[0]?.id;\n const baseId = args.base ?? sessions[1]?.id;\n if (!baseId || !currentId) return json({ error: 'Need at least two discovery sessions to diff.' });\n if (baseId === currentId) return json({ error: 'Base and current session are the same.' });\n let alert = classifyDrift(db.diffSessions(baseId, currentId));\n if (args.minSeverity) alert = filterBySeverity(alert, args.minSeverity);\n return json(redactValue(alert));\n },\n );\n\n server.registerTool(\n 'score_compliance',\n {\n title: 'Score compliance',\n description:\n 'Grade the served session against a compliance ruleset (baseline/cis/soc2/iso27001 starter ' +\n 'sets) and list gaps with the node ids that caused them. Read-only; never throws.',\n inputSchema: {\n ruleset: z.string().default('baseline').optional().describe('Ruleset name (default: baseline)'),\n session: z.string().optional().describe('Session id (default: the served session)'),\n },\n annotations: readOnly,\n },\n (args) => {\n const sid = args.session ?? resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const name = args.ruleset ?? 'baseline';\n const rs = getRuleset(name);\n if (!rs) return json({ error: `Unknown ruleset: ${name}`, available: listRulesets().map((r) => r.name) });\n return json(db.scoreSession(sid, rs));\n },\n );\n\n server.registerTool(\n 'get_activity_events',\n {\n title: 'Get activity events (audit trail)',\n description: 'Recent executed tool calls and their result sizes for the current session.',\n inputSchema: { limit: z.number().int().min(1).max(500).default(50).optional() },\n annotations: readOnly,\n },\n (args) => {\n const sid = resolveSession();\n if (!sid) return json({ error: 'No discovery session found.' });\n const events = db.getEvents(sid).slice(-(args.limit ?? 50));\n return json({ count: events.length, events });\n },\n );\n\n if (opts.discovery) {\n const discovery = opts.discovery;\n server.registerTool(\n 'run_discovery',\n {\n title: 'Run discovery',\n description: 'Scan the local system (read-only) and update the catalog. Returns counts of nodes/edges found. Pass `update: true` to rescan the served session in place and return the delta (2.1 incremental discovery).',\n inputSchema: {\n hint: z.string().optional().describe('Optional focus, e.g. tool names to look for'),\n update: z.boolean().optional().describe('Rescan the served session in place and return the delta instead of creating a new session'),\n },\n // Scans read-only but writes results to the local catalog, so not a read-only tool; never destructive.\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },\n },\n async (args) => {\n // RBAC (4.5): run_discovery mutates the catalog → requires `operator`+.\n if (opts.principal) {\n try {\n authorize(opts.principal, 'discovery');\n } catch (err) {\n if (err instanceof AuthorizationError) return json({ error: `forbidden: role '${opts.principal.role}' may not run discovery (operator required)` });\n throw err;\n }\n }\n let sid = resolveSession();\n if (args.update) {\n // Incremental rescan needs an existing served session; never silently create one.\n if (!sid) return json({ error: 'No session to update; run discovery first.' });\n } else if (!sid) {\n sid = db.createSession('discover', defaultConfig(), tenant);\n }\n const result = await discovery(db, sid, { hint: args.hint, mode: args.update ? 'update' : 'replace' });\n // Give the session a deterministic, human-friendly name once it has content.\n // The `!sess.name` guard means an in-place rescan never clobbers a name.\n const sess = db.getSession(sid);\n if (sess && !sess.name) db.setSessionName(sid, deriveSessionName(db.getGraphSummary(sid), sess.startedAt));\n server.server.sendResourceUpdated({ uri: 'cartography://graph/summary' }).catch((err: unknown) => {\n process.stderr.write(`[cartography-mcp] resource update notification failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n });\n server.server.sendResourceListChanged?.();\n return json({\n session: sid, nodes: result.nodes, edges: result.edges,\n ...(result.delta ? { summary: result.delta.summary } : {}),\n });\n },\n );\n }\n\n // ── Prompts (user-controlled templates) ────────────────────────────────────\n\n server.registerPrompt(\n 'audit-attack-surface',\n { title: 'Audit attack surface', description: 'Review the discovered topology for externally-reachable services and risky dependencies.' },\n () => ({\n messages: [{\n role: 'user', content: { type: 'text', text:\n 'Read cartography://graph/summary and cartography://services. Identify externally-reachable ' +\n 'services, data stores with broad inbound dependencies, and any node with low confidence that ' +\n 'warrants verification. Use get_dependencies to assess blast radius. Summarize the attack surface ' +\n 'and concrete hardening recommendations.' } }],\n }),\n );\n\n server.registerPrompt(\n 'map-service-dependencies',\n {\n title: 'Map service dependencies',\n description: 'Produce a dependency map for a given service.',\n argsSchema: { service: z.string().describe('Service node id or name') },\n },\n (args) => ({\n messages: [{\n role: 'user', content: { type: 'text', text:\n `Use query_infrastructure to locate \"${args.service}\", then get_dependencies (direction=both) to ` +\n `map everything it depends on and everything that depends on it. Present the result as a clear ` +\n `dependency tree and call out single points of failure.` } }],\n }),\n );\n\n server.registerPrompt(\n 'compare-environments',\n { title: 'Compare environments', description: 'Summarize infrastructure drift between two discovery snapshots.' },\n () => ({\n messages: [{\n role: 'user', content: { type: 'text', text:\n 'Call diff_topology to compare the two most recent discovery sessions. Summarize what was added, ' +\n 'removed, and changed. Flag newly externally-reachable services and removed dependencies that could ' +\n 'indicate an outage or decommission. Recommend what an operator should verify.' } }],\n }),\n );\n\n server.registerPrompt(\n 'onboard-to-system',\n { title: 'Onboard to system', description: 'Explain the system landscape to a new engineer.' },\n () => ({\n messages: [{\n role: 'user', content: { type: 'text', text:\n 'Read cartography://graph/summary, then cartography://services and cartography://databases. ' +\n 'Write a concise onboarding briefing for a new engineer: what the major systems are, how they ' +\n 'connect, which data stores are central, and where to look first.' } }],\n }),\n );\n\n server.registerPrompt(\n 'find-single-points-of-failure',\n { title: 'Find single points of failure', description: 'Rank chokepoints whose loss has the largest blast radius.' },\n () => ({\n messages: [{\n role: 'user', content: { type: 'text', text:\n 'Call get_summary and read topConnected (the most-connected nodes). For each, call get_dependencies ' +\n '(direction=both) to measure how many services depend on it. Identify single points of failure — nodes ' +\n 'whose loss would disconnect or degrade the largest blast radius — rank them by impact, and recommend ' +\n 'redundancy or mitigation for each.' } }],\n }),\n );\n\n server.registerPrompt(\n 'generate-runbook',\n {\n title: 'Generate operations runbook',\n description: 'Produce an operations/onboarding runbook from the topology.',\n argsSchema: { service: z.string().optional().describe('Optional service id/name to scope the runbook') },\n },\n (args) => ({\n messages: [{\n role: 'user', content: { type: 'text', text: args.service\n ? `Use query_infrastructure to locate \"${args.service}\", then get_dependencies (direction=both). Write an ` +\n `operations runbook for it: purpose, upstream/downstream dependencies, startup/shutdown order, health ` +\n `checks, common failure modes, and escalation steps.`\n : 'Read cartography://graph/summary, then call get_summary and list_services. Write a system-wide operations ' +\n 'runbook: major components, how they connect, critical data stores, startup/shutdown order, health checks, ' +\n 'and where an on-call engineer should look first.' } }],\n }),\n );\n\n return server;\n}\n","/**\n * Natural-language topology query resolver (3.5) — deterministic, LLM-free.\n *\n * Turns a plain-English question (\"services that depend on the payments DB\") into a\n * structured intent, then composes the existing primitives — the injected `SearchFn`\n * (semantic↔lexical degradation inherited for free) + `db.getDependencies` + a\n * post-traversal type filter — and echoes the parsed intent for explainability.\n * No LLM, no new exec path, read-only. The NL string is sanitized + length-clamped\n * (ReDoS-safe: only linear, anchored patterns).\n */\n\nimport type { CartographyDB } from '../db.js';\nimport type { NodeRow, EdgeRow, NodeType } from '../types.js';\nimport { NODE_TYPE_GROUPS } from '../types.js';\nimport type { SearchFn } from '../mcp/server.js';\nimport { sanitizeUntrusted } from '../sanitize.js';\nimport { logDebug } from '../logger.js';\n\n/** The relationship the operator asked about, in resolver-native terms. */\nexport type NlRelation = 'depends-on' | 'depended-on-by' | 'connected-to' | 'list';\n\n/** Maps a parsed relation to the `db.getDependencies` direction. Single source of truth. */\nexport const RELATION_TO_DIRECTION = {\n 'depends-on': 'downstream',\n 'depended-on-by': 'upstream',\n 'connected-to': 'both',\n 'list': undefined,\n} as const satisfies Record<NlRelation, 'downstream' | 'upstream' | 'both' | undefined>;\n\n/** Structured intent parsed from a natural-language question. */\nexport interface NlIntent {\n readonly query: string;\n readonly subjectQuery: string;\n readonly relation: NlRelation;\n readonly direction?: 'downstream' | 'upstream' | 'both';\n /** Concrete node types the *result* set is restricted to (post-traversal), if any. */\n readonly typeFilter?: readonly NodeType[];\n /** True when no relation pattern matched and we degraded to a search-only list. */\n readonly degraded: boolean;\n}\n\nexport interface NlQueryResult {\n readonly intent: NlIntent;\n readonly anchors: Array<{ node: NodeRow; score?: number }>;\n readonly nodes: Array<NodeRow & { depth?: number }>;\n readonly paths: EdgeRow[];\n}\n\nexport interface NlQueryOptions {\n readonly maxDepth?: number;\n readonly anchorLimit?: number;\n}\n\nconst MAX_QUERY_LEN = 1000;\n\n/** Plural/group words → concrete node-type sets (applied to the *result* set). */\nconst GROUP_WORDS: ReadonlyArray<readonly [RegExp, readonly NodeType[]]> = [\n [/\\bservices?\\b/i, NODE_TYPE_GROUPS.web],\n [/\\b(?:databases?|datastores?|data ?stores?)\\b/i, NODE_TYPE_GROUPS.data],\n [/\\b(?:queues?|topics?|brokers?|messaging)\\b/i, NODE_TYPE_GROUPS.messaging],\n [/\\b(?:hosts?|containers?|pods?|clusters?|infra(?:structure)?)\\b/i, NODE_TYPE_GROUPS.infra],\n [/\\bsaas\\b/i, NODE_TYPE_GROUPS.saas],\n];\n\n// Relation patterns (bounded, anchored, no nested quantifiers → ReDoS-free). Each\n// captures a named `subject` group; UPSTREAM also has an implicit leading clause.\nconst UPSTREAM: RegExp[] = [\n /^(?:.*?)\\b(?:that|which)\\s+depends?\\s+(?:on|upon)\\s+(?<subject>.+)$/i,\n /^(?:.*?)\\bdepending\\s+(?:on|upon)\\s+(?<subject>.+)$/i,\n /^(?:.*?)\\b(?:that|which)\\s+relies?\\s+on\\s+(?<subject>.+)$/i,\n /^\\s*what\\s+depends?\\s+(?:on|upon)\\s+(?<subject>.+)$/i,\n];\nconst DOWNSTREAM: RegExp[] = [\n /\\bwhat\\s+(?:does|do)\\s+(?<subject>.+?)\\s+depend\\s+(?:on|upon)\\b/i,\n /^(?<subject>.+?)['’]s\\s+dependencies\\b/i,\n /\\bdependencies\\s+of\\s+(?<subject>.+)$/i,\n /^(?<subject>.+?)\\s+dependencies\\b/i,\n /^(?<subject>.+?)\\s+depends?\\s+(?:on|upon)\\s+.*$/i,\n];\nconst CONNECTED: RegExp[] = [\n /\\bwhat\\s+is\\s+connected\\s+to\\s+(?<subject>.+)$/i,\n /\\b(?:connected|related|linked)\\s+to\\s+(?<subject>.+)$/i,\n /\\btalks?\\s+to\\s+(?<subject>.+)$/i,\n];\n\nfunction firstMatch(query: string, pats: RegExp[]): string | null {\n for (const re of pats) {\n const m = query.match(re);\n if (m?.groups?.subject) return m.groups.subject;\n }\n return null;\n}\n\nfunction cleanSubject(s: string): string {\n return s.replace(/[?.!,]+\\s*$/g, '').replace(/^\\s*(?:the|a|an|my|our|all)\\s+/i, '').trim();\n}\n\nfunction detectTypeFilter(text: string): readonly NodeType[] | undefined {\n const set = new Set<NodeType>();\n for (const [re, types] of GROUP_WORDS) if (re.test(text)) for (const t of types) set.add(t);\n return set.size > 0 ? [...set] : undefined;\n}\n\n/** Parse a natural-language question into a structured {@link NlIntent}. Pure & deterministic. */\nexport function parseNlQuery(raw: string): NlIntent {\n const query = sanitizeUntrusted(raw).slice(0, MAX_QUERY_LEN).trim();\n\n let relation: NlRelation = 'list';\n let subj = firstMatch(query, UPSTREAM);\n if (subj !== null) relation = 'depended-on-by';\n else if ((subj = firstMatch(query, DOWNSTREAM)) !== null) relation = 'depends-on';\n else if ((subj = firstMatch(query, CONNECTED)) !== null) relation = 'connected-to';\n\n const degraded = subj === null;\n const subjectQuery = cleanSubject(subj ?? query);\n // Type filter from the non-subject portion (relational) or the whole query (list),\n // so \"DB\" inside the subject never pollutes a \"services …\" result filter.\n const scanText = subj === null ? query : query.replace(subj, ' ');\n const typeFilter = detectTypeFilter(scanText);\n\n // optional LLM assist seam: a future AgentProvider could re-parse `query` here when relation === 'list'.\n return { query, subjectQuery, relation, direction: RELATION_TO_DIRECTION[relation], typeFilter, degraded };\n}\n\n/** Execute a parsed intent: anchor via `search`, traverse, then filter results by type. */\nexport async function executeNlQuery(\n db: CartographyDB,\n sessionId: string,\n search: SearchFn,\n intent: NlIntent,\n opts: NlQueryOptions = {},\n): Promise<NlQueryResult> {\n const anchorLimit = opts.anchorLimit ?? 5;\n\n // 'list': search-only — apply the type filter to the anchor search (no traversal).\n if (intent.relation === 'list') {\n const anchors = await search(db, sessionId, intent.subjectQuery, { types: intent.typeFilter, limit: anchorLimit });\n logDebug('nlq.execute', { relation: 'list', typeFilter: intent.typeFilter?.length ?? 0, anchors: anchors.length });\n return { intent, anchors, nodes: anchors.map((a) => a.node), paths: [] };\n }\n\n // Relational: anchor on the subject WITHOUT a type filter (the anchor is the subject,\n // not the result type); apply the type filter to the *traversal results*.\n const anchors = await search(db, sessionId, intent.subjectQuery, { limit: anchorLimit });\n if (anchors.length === 0) {\n logDebug('nlq.execute', { relation: intent.relation, anchors: 0, results: 0 });\n return { intent, anchors, nodes: [], paths: [] };\n }\n const root = anchors[0].node; // top hit (search orders by score, then confidence)\n const trav = db.getDependencies(sessionId, root.id, { direction: intent.direction, maxDepth: opts.maxDepth ?? 8 });\n const allow = intent.typeFilter ? new Set<string>(intent.typeFilter) : undefined;\n const nodes = allow ? trav.nodes.filter((n) => allow.has(n.type)) : trav.nodes;\n\n logDebug('nlq.execute', {\n relation: intent.relation, direction: intent.direction,\n typeFilter: intent.typeFilter?.length ?? 0, anchors: anchors.length, results: nodes.length,\n });\n return { intent, anchors, nodes, paths: trav.edges };\n}\n\n/** Convenience: parse + execute in one call. */\nexport async function resolveNlQuery(\n db: CartographyDB, sessionId: string, search: SearchFn, raw: string, opts?: NlQueryOptions,\n): Promise<NlQueryResult> {\n return executeNlQuery(db, sessionId, search, parseNlQuery(raw), opts);\n}\n","/**\n * Transport bindings for the Cartography MCP server.\n *\n * - **stdio**: the local-first default — zero network, every client supports it.\n * - **Streamable HTTP**: a single `/mcp` endpoint for team/remote use, bound to\n * localhost with DNS-rebinding protection. The deprecated SSE transport is not used.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport http from 'node:http';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { assertSafeBind, bearerToken, defaultAllowedHosts } from '../api/auth.js';\nimport { resolvePrincipal } from '../auth/identity.js';\nimport type { CredentialStore, Principal } from '../auth/types.js';\n\n/** Two principals are the same iff subject, tenant, and role all match. */\nfunction samePrincipal(a: Principal, b: Principal): boolean {\n return a.subject === b.subject && a.tenant === b.tenant && a.role === b.role;\n}\n\n/** Connect a server over stdio (resolves when the transport closes). */\nexport async function runStdio(server: McpServer): Promise<void> {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nexport interface HttpOptions {\n port?: number;\n host?: string;\n /** Extra allowed Host headers (defaults to localhost:port variants). */\n allowedHosts?: string[];\n /** Allowed Origin headers (defaults to none → same-origin only). */\n allowedOrigins?: string[];\n /**\n * Shared secret required in the `Authorization: Bearer <token>` header.\n * Mandatory when binding a non-loopback host; optional (and enforced when\n * present) on loopback.\n */\n token?: string;\n /**\n * Central-collector ingest hook (2.12). When set, an authenticated `POST /ingest`\n * **write** route is exposed: it inherits the *same* bearer auth and non-loopback\n * host-allowlist guards as `/mcp` (the route adds no separate hardening path), size-\n * caps the body, parses JSON, and returns the hook's `{ status, body }`. When unset,\n * `/ingest` 404s exactly like any other path — the collector stays dark by default.\n */\n onIngest?: (body: unknown) => { status: number; body: unknown; headers?: Record<string, string> };\n /**\n * RBAC (4.5). When `store` holds credentials, the transport runs in RBAC mode: a\n * request's bearer token must resolve to a {@link Principal} (else 401), and the\n * principal is passed to `factory(principal)` so the per-session server is pinned to\n * the principal's tenant and gates mutating tools by role. Without a populated store\n * the legacy shared-token / open-loopback behavior is preserved.\n */\n auth?: { store?: CredentialStore; required?: boolean };\n /** Tenant assigned to implicit (shared/loopback) admin principals. */\n defaultTenant?: string;\n /**\n * Readiness probe (4.7). When set, `GET /readyz` calls it: 200 when `ready`, else 503.\n * `GET /healthz` (liveness) is always 200. Both are PUBLIC (no auth) for k8s/LB probes.\n */\n readiness?: () => { ready: boolean; detail?: Record<string, unknown> };\n}\n\n/** Max ingest body size (5 MB). A larger POST /ingest is refused with 413 before parsing. */\nconst MAX_INGEST_BYTES = 5 * 1024 * 1024;\n\n/**\n * Read a size-capped request body. On overflow it stops buffering and drains the\n * rest of the stream (rather than resetting the socket), so the caller can still\n * write a clean 413 response that the client receives.\n */\nasync function readCappedBody(req: http.IncomingMessage, cap: number): Promise<{ overflow: boolean; value: unknown }> {\n const chunks: Buffer[] = [];\n let total = 0;\n let overflow = false;\n for await (const chunk of req) {\n if (overflow) continue; // drain remaining data without buffering\n const buf = chunk as Buffer;\n total += buf.length;\n if (total > cap) { overflow = true; chunks.length = 0; continue; }\n chunks.push(buf);\n }\n if (overflow) return { overflow: true, value: undefined };\n if (chunks.length === 0) return { overflow: false, value: undefined };\n try { return { overflow: false, value: JSON.parse(Buffer.concat(chunks).toString('utf8')) }; }\n catch { return { overflow: false, value: undefined }; }\n}\n\nasync function readJsonBody(req: http.IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n if (chunks.length === 0) return undefined;\n try { return JSON.parse(Buffer.concat(chunks).toString('utf8')); } catch { return undefined; }\n}\n\n/**\n * Start a Streamable HTTP server. A fresh MCP server instance is created per\n * session via `factory`, so multiple clients can connect concurrently.\n */\nexport async function runHttp(factory: (principal?: Principal) => McpServer, opts: HttpOptions = {}): Promise<http.Server> {\n const host = opts.host ?? '127.0.0.1';\n const port = opts.port ?? 3737;\n\n // CVE-2025-66414 + mandatory-token guards (shared with the API server, src/api/auth.ts).\n assertSafeBind({ host, port, ...(opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}), ...(opts.token ? { token: opts.token } : {}) });\n\n const allowedHosts = opts.allowedHosts ?? defaultAllowedHosts(host, port);\n const token = opts.token;\n const authStore = opts.auth?.store;\n const defaultTenant = opts.defaultTenant;\n const resolveAuth = (header: string | undefined): Principal | undefined => resolvePrincipal(bearerToken(header), {\n ...(authStore ? { store: authStore } : {}),\n ...(token ? { sharedToken: token } : {}),\n ...(defaultTenant ? { defaultTenant } : {}),\n ...(opts.auth?.required ? { required: true } : {}),\n });\n // Each session records the principal that created it, so a reused session can never be\n // driven by a *different* principal (RBAC: prevents cross-tenant / privilege hijack via a\n // stolen/leaked mcp-session-id — the per-request token still has to belong to the same\n // principal that opened the session).\n const transports = new Map<string, { transport: StreamableHTTPServerTransport; principal: Principal }>();\n\n const httpServer = http.createServer(async (req, res) => {\n try {\n const url = req.url ?? '';\n // Health/readiness probes (4.7) — PUBLIC (before auth), for container/LB orchestration.\n // Exact-path match on the parsed pathname (not the raw url, not startsWith) so\n // `/healthz/../mcp` etc. can't reach them, and the body carries ONLY the status (the\n // readiness `detail` — schema version / error text — is for local logs, never an\n // unauthenticated disclosure oracle).\n const probePath = new URL(url || '/', 'http://probe').pathname;\n if (probePath === '/healthz') { res.writeHead(200, { 'content-type': 'application/json' }).end('{\"status\":\"ok\"}'); return; }\n if (probePath === '/readyz') {\n const r = opts.readiness ? opts.readiness() : { ready: true };\n res.writeHead(r.ready ? 200 : 503, { 'content-type': 'application/json' })\n .end(JSON.stringify({ status: r.ready ? 'ready' : 'unready' }));\n return;\n }\n\n const isIngest = url.startsWith('/ingest') && opts.onIngest !== undefined;\n // Only `/mcp` (read) and, when enabled, `/ingest` (write) are routed; all else 404s\n // — so an unset `onIngest` keeps the collector dark (the write surface never appears).\n if (!url.startsWith('/mcp') && !isIngest) { res.writeHead(404, { 'content-type': 'application/json' }).end('{\"error\":\"not found\"}'); return; }\n\n // Authenticate before touching any session/transport state OR ingest. RBAC (4.5):\n // resolve the bearer token to a principal; 401 if it doesn't resolve. The ingest\n // route inherits the SAME check + non-loopback guards as /mcp — no separate path.\n const principal = resolveAuth(req.headers['authorization']);\n if (!principal) {\n res.writeHead(401, { 'content-type': 'application/json', 'www-authenticate': 'Bearer' })\n .end('{\"error\":\"unauthorized\"}');\n return;\n }\n\n // Central-collector ingest (2.12): an authenticated write route, dark unless onIngest is set.\n if (isIngest) {\n // DNS-rebinding protection (CVE-2025-66414): /mcp gets this from the SDK\n // transport's `allowedHosts`, but /ingest returns before the transport runs,\n // so it must validate the Host header itself to inherit the SAME guard.\n const hostHeader = (req.headers['host'] ?? '').toLowerCase();\n if (!allowedHosts.some((h) => h.toLowerCase() === hostHeader)) {\n res.writeHead(403, { 'content-type': 'application/json' }).end('{\"error\":\"host not allowed\"}');\n return;\n }\n const onIngest = opts.onIngest!;\n if (req.method !== 'POST') {\n res.writeHead(405, { 'content-type': 'application/json', 'allow': 'POST' }).end('{\"error\":\"method not allowed\"}');\n return;\n }\n const { overflow, value } = await readCappedBody(req, MAX_INGEST_BYTES);\n if (overflow) { res.writeHead(413, { 'content-type': 'application/json' }).end('{\"error\":\"payload too large\"}'); return; }\n const out = onIngest(value);\n res.writeHead(out.status, { 'content-type': 'application/json', ...(out.headers ?? {}) }).end(JSON.stringify(out.body));\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n const existing = sessionId ? transports.get(sessionId) : undefined;\n\n if (existing) {\n // RBAC: the principal reusing this session must be the one that created it.\n // Otherwise a second valid credential could drive another principal's\n // tenant-pinned, role-bearing session by presenting its mcp-session-id.\n if (!samePrincipal(existing.principal, principal)) {\n res.writeHead(403, { 'content-type': 'application/json' }).end('{\"error\":\"session belongs to a different principal\"}');\n return;\n }\n const body = req.method === 'POST' ? await readJsonBody(req) : undefined;\n await existing.transport.handleRequest(req, res, body);\n return;\n }\n\n if (req.method !== 'POST') {\n res.writeHead(400, { 'content-type': 'application/json' }).end('{\"error\":\"missing or unknown mcp-session-id\"}');\n return;\n }\n\n // New session: initialize a transport + server instance.\n const body = await readJsonBody(req);\n const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableDnsRebindingProtection: true,\n allowedHosts,\n ...(opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {}),\n onsessioninitialized: (id: string) => { transports.set(id, { transport, principal }); },\n });\n transport.onclose = () => { if (transport.sessionId) transports.delete(transport.sessionId); };\n // Bind the resolved principal to the session's server (tenant pinning + role gating).\n await factory(principal).connect(transport);\n await transport.handleRequest(req, res, body);\n } catch (err) {\n process.stderr.write(`[cartography-mcp] HTTP request failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n if (!res.headersSent) res.writeHead(500, { 'content-type': 'application/json' }).end('{\"error\":\"internal error\"}');\n }\n });\n\n await new Promise<void>((resolve) => httpServer.listen(port, host, resolve));\n return httpServer;\n}\n","/** FNV-1a (32-bit) — the shared content hash used by the embeddings and vector store. */\nexport function fnv1a(s: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n","/**\n * Embedding providers for semantic search.\n *\n * The default provider runs a small sentence-transformer locally via\n * `@huggingface/transformers` (no API key, offline after first download), keeping\n * the package LLM-agnostic. Everything is a lazy import so installs that never use\n * semantic search pay no cost and need no native model.\n */\n\nimport { fnv1a } from './hash.js';\n\n/** Produces fixed-dimension embeddings for a batch of texts. */\nexport interface EmbeddingProvider {\n readonly id: string;\n readonly dimensions: number;\n embed(texts: string[]): Promise<Float32Array[]>;\n}\n\n/**\n * Local sentence-transformer embedder (Xenova/all-MiniLM-L6-v2, 384 dims).\n * Returns `undefined` if `@huggingface/transformers` is not installed or the\n * model cannot be loaded, so callers can fall back to lexical search.\n */\nexport async function createLocalEmbedder(\n model = 'Xenova/all-MiniLM-L6-v2',\n): Promise<EmbeddingProvider | undefined> {\n try {\n // Cast the specifier to defeat static module resolution: the type build must\n // not require this optional native dep to be installed. We assert the minimal\n // surface we use instead — the runtime import is unchanged.\n const tf = (await import('@huggingface/transformers' as string)) as {\n pipeline(\n task: string,\n model: string,\n ): Promise<(text: string, opts: { pooling: string; normalize: boolean }) => Promise<{ data: unknown }>>;\n };\n const extractor = await tf.pipeline('feature-extraction', model);\n return {\n id: `local:${model}`,\n dimensions: 384,\n async embed(texts: string[]): Promise<Float32Array[]> {\n const out: Float32Array[] = [];\n for (const text of texts) {\n const tensor = await extractor(text, { pooling: 'mean', normalize: true });\n out.push(Float32Array.from(tensor.data as Iterable<number>));\n }\n return out;\n },\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * A deterministic, dependency-free hashing embedder (bag-of-character-ngrams).\n * Not as good as a transformer, but offline, instant, and useful as a fallback\n * and for tests. Produces L2-normalized vectors.\n */\nexport function createHashEmbedder(dimensions = 256): EmbeddingProvider {\n return {\n id: `hash:${dimensions}`,\n dimensions,\n async embed(texts: string[]): Promise<Float32Array[]> {\n return texts.map((text) => hashEmbed(text, dimensions));\n },\n };\n}\n\nfunction hashEmbed(text: string, dim: number): Float32Array {\n const v = new Float32Array(dim);\n const tokens = text.toLowerCase().match(/[a-z0-9]+/g) ?? [];\n for (const tok of tokens) {\n // hash the token and its char trigrams into buckets\n for (const gram of [tok, ...trigrams(tok)]) {\n const h = fnv1a(gram);\n v[h % dim] += 1;\n }\n }\n let norm = 0;\n for (const x of v) norm += x * x;\n norm = Math.sqrt(norm) || 1;\n for (let i = 0; i < dim; i++) v[i] = v[i]! / norm;\n return v;\n}\n\nfunction trigrams(s: string): string[] {\n if (s.length < 3) return [];\n const out: string[] = [];\n for (let i = 0; i <= s.length - 3; i++) out.push(s.slice(i, i + 3));\n return out;\n}\n","/**\n * Vector store backed by `sqlite-vec`. Stores one embedding per node in a `vec0`\n * virtual table living inside the same SQLite catalog, with incremental indexing\n * (content-hashed) so re-runs only embed what changed.\n */\n\nimport type { CartographyDB } from '../db.js';\nimport type { NodeRow } from '../types.js';\nimport type { EmbeddingProvider } from './embeddings.js';\nimport { fnv1a } from './hash.js';\n\n/** Text used to represent a node for embedding. */\nexport function nodeText(n: NodeRow): string {\n const desc = typeof n.metadata?.['description'] === 'string' ? (n.metadata['description'] as string) : '';\n const category = typeof n.metadata?.['category'] === 'string' ? (n.metadata['category'] as string) : '';\n return [n.name, n.id.replace(/[:_]/g, ' '), `type ${n.type}`, n.domain ?? '', n.subDomain ?? '', category, n.tags.join(' '), desc]\n .filter(Boolean).join(' — ');\n}\n\nfunction hash(s: string): string {\n return fnv1a(s).toString(16);\n}\n\nfunction toBuffer(v: Float32Array): Buffer {\n return Buffer.from(v.buffer, v.byteOffset, v.byteLength);\n}\n\nexport class VectorStore {\n private loaded = false;\n\n constructor(private db: CartographyDB, private embedder: EmbeddingProvider) {}\n\n /** Load sqlite-vec and ensure the schema exists. Returns false if unavailable. */\n async init(): Promise<boolean> {\n if (this.loaded) return true;\n try {\n const conn = this.db.rawConnection();\n // Cast the specifier so the type build doesn't require this optional native\n // dep to be installed; runtime import is unchanged.\n const sqliteVec = (await import('sqlite-vec' as string)) as { load(db: unknown): void };\n sqliteVec.load(conn);\n conn.exec(`\n CREATE TABLE IF NOT EXISTS vec_index (\n rowid INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n node_id TEXT NOT NULL,\n hash TEXT NOT NULL,\n UNIQUE(session_id, node_id)\n );\n CREATE TABLE IF NOT EXISTS vec_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);\n `);\n // Recreate the vector table if the embedding dimensions changed.\n const dimRow = conn.prepare(\"SELECT value FROM vec_meta WHERE key = 'dims'\").get() as { value: string } | undefined;\n const dims = this.embedder.dimensions;\n if (dimRow && Number(dimRow.value) !== dims) {\n conn.exec('DROP TABLE IF EXISTS vec_nodes; DELETE FROM vec_index;');\n }\n conn.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS vec_nodes USING vec0(embedding float[${dims}])`);\n conn.prepare('INSERT OR REPLACE INTO vec_meta(key, value) VALUES (?, ?)').run('dims', String(dims));\n conn.prepare('INSERT OR REPLACE INTO vec_meta(key, value) VALUES (?, ?)').run('embedder', this.embedder.id);\n this.loaded = true;\n return true;\n } catch {\n return false;\n }\n }\n\n /** Incrementally embed and index any new/changed nodes for a session. */\n async index(sessionId: string): Promise<{ embedded: number; total: number }> {\n if (!(await this.init())) return { embedded: 0, total: 0 };\n const conn = this.db.rawConnection();\n const nodes = this.db.getNodes(sessionId);\n\n const getRow = conn.prepare('SELECT rowid, hash FROM vec_index WHERE session_id = ? AND node_id = ?');\n const insIndex = conn.prepare('INSERT INTO vec_index (session_id, node_id, hash) VALUES (?, ?, ?)');\n const updHash = conn.prepare('UPDATE vec_index SET hash = ? WHERE rowid = ?');\n const delVec = conn.prepare('DELETE FROM vec_nodes WHERE rowid = ?');\n const insVec = conn.prepare('INSERT INTO vec_nodes (rowid, embedding) VALUES (?, ?)');\n\n const pending: Array<{ rowid: bigint; text: string }> = [];\n for (const n of nodes) {\n const text = nodeText(n);\n const h = hash(`${this.embedder.id}:${text}`);\n const existing = getRow.get(sessionId, n.id) as { rowid: number; hash: string } | undefined;\n if (existing) {\n if (existing.hash === h) continue;\n updHash.run(h, existing.rowid);\n delVec.run(BigInt(existing.rowid));\n pending.push({ rowid: BigInt(existing.rowid), text });\n } else {\n const info = insIndex.run(sessionId, n.id, h);\n pending.push({ rowid: BigInt(info.lastInsertRowid as number), text });\n }\n }\n\n if (pending.length > 0) {\n const vectors = await this.embedder.embed(pending.map((p) => p.text));\n const tx = conn.transaction(() => {\n pending.forEach((p, i) => insVec.run(p.rowid, toBuffer(vectors[i]!)));\n });\n tx();\n }\n return { embedded: pending.length, total: nodes.length };\n }\n\n /** k-nearest-neighbour search within a session. Returns node ids + distances. */\n async search(sessionId: string, query: string, k: number): Promise<Array<{ nodeId: string; distance: number }>> {\n if (!(await this.init())) return [];\n await this.index(sessionId);\n const conn = this.db.rawConnection();\n const [qv] = await this.embedder.embed([query]);\n if (!qv) return [];\n\n // Pure-vec KNN scan (most compatible form), then map rowids and filter by\n // session in JS. Over-fetch so cross-session neighbours don't crowd out hits.\n const overfetch = Math.max(k * 5, k);\n const knn = conn.prepare(\n 'SELECT rowid, distance FROM vec_nodes WHERE embedding MATCH ? ORDER BY distance LIMIT ?',\n ).all(toBuffer(qv), overfetch) as Array<{ rowid: number; distance: number }>;\n\n const meta = conn.prepare('SELECT node_id AS nodeId, session_id AS sessionId FROM vec_index WHERE rowid = ?');\n const out: Array<{ nodeId: string; distance: number }> = [];\n for (const row of knn) {\n const m = meta.get(row.rowid) as { nodeId: string; sessionId: string } | undefined;\n if (m && m.sessionId === sessionId) out.push({ nodeId: m.nodeId, distance: row.distance });\n if (out.length >= k) break;\n }\n return out;\n }\n}\n","/**\n * Semantic search backend for the MCP server. Wraps a {@link VectorStore} and\n * degrades gracefully to lexical search when embeddings/sqlite-vec are unavailable\n * or return nothing.\n */\n\nimport type { CartographyDB } from '../db.js';\nimport type { NodeRow } from '../types.js';\nimport type { SearchFn } from '../mcp/server.js';\nimport type { EmbeddingProvider } from './embeddings.js';\nimport { createLocalEmbedder } from './embeddings.js';\nimport { VectorStore } from './store.js';\n\nconst lexical = (db: CartographyDB, sessionId: string, query: string, opts: { types?: readonly string[]; limit: number }) =>\n db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));\n\n/** A lexical-only {@link SearchFn} — the fallback when no embedder/vector store is available. */\nconst lexicalSearch = (): SearchFn => async (d, sid, q, opts) => lexical(d, sid, q, opts);\n\n/** Options for {@link createSemanticSearch}. */\nexport interface SemanticSearchOptions {\n /** Logger for mode/degradation diagnostics (stderr). No-op if omitted. */\n log?: (msg: string) => void;\n}\n\n/**\n * Build a {@link SearchFn} that prefers semantic (vector) search and falls back to\n * lexical. Pass an explicit embedder, or let it lazily load the local transformer\n * (returns a lexical-only function if none is available). Pass `opts.log` to record\n * which mode was resolved — semantic readiness or the reason it degraded to lexical.\n */\nexport async function createSemanticSearch(\n db: CartographyDB,\n embedder?: EmbeddingProvider,\n opts: SemanticSearchOptions = {},\n): Promise<SearchFn> {\n const log = opts.log;\n const provider = embedder ?? (await createLocalEmbedder());\n if (!provider) {\n log?.('semantic search: embeddings unavailable (@huggingface/transformers not installed or failed to load) — using lexical search');\n return lexicalSearch();\n }\n const store = new VectorStore(db, provider);\n const ok = await store.init();\n if (!ok) {\n log?.('semantic search: vector store unavailable (sqlite-vec not installed or failed to load) — using lexical search');\n return lexicalSearch();\n }\n log?.('semantic search: ready');\n\n return async (d, sid, query, queryOpts): Promise<Array<{ node: NodeRow; score?: number }>> => {\n const hits = await store.search(sid, query, queryOpts.limit);\n if (hits.length === 0) return lexical(d, sid, query, queryOpts);\n // Materialize only the hit nodes, not the whole session.\n const byId = d.getNodesByIds(sid, hits.map((h) => h.nodeId));\n const results: Array<{ node: NodeRow; score?: number }> = [];\n for (const h of hits) {\n const node = byId.get(h.nodeId);\n if (!node) continue; // vector outlived its node (deleted since last index)\n if (queryOpts.types && queryOpts.types.length > 0 && !queryOpts.types.includes(node.type)) continue;\n // cosine distance → similarity score in [0,1]\n results.push({ node, score: Math.max(0, 1 - h.distance / 2) });\n }\n return results.length > 0 ? results : lexical(d, sid, query, queryOpts);\n };\n}\n\nexport { VectorStore } from './store.js';\nexport { createLocalEmbedder, createHashEmbedder } from './embeddings.js';\nexport type { EmbeddingProvider } from './embeddings.js';\n","/**\n * `SqliteStoreBackend` — the default (and currently only) {@link StoreBackend}\n * implementation for the central collector (2.12).\n *\n * It is a thin adapter over `CartographyDB`: the schema, migrations, and merge SQL\n * all live in `src/db.ts` (the single owner of the catalog), so this class only\n * forwards the org-scoped central operations. Constructing it adds no new state and\n * no new schema — the zero-config local SQLite path is byte-for-byte unchanged.\n *\n * A graph/Postgres backend (4.3) implements the same interface without changing the\n * ingest orchestration that consumes it.\n */\n\nimport type { CartographyDB, OrgSummary } from '../db.js';\nimport type { DiscoveryNode, DiscoveryEdge, Contributor } from '../types.js';\nimport type { StoreBackend, NodeIdentity } from './backend.js';\n\nexport class SqliteStoreBackend implements StoreBackend {\n constructor(private readonly db: CartographyDB) {}\n\n upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): 'created' | 'merged' {\n return this.db.upsertCentralNode(org, node, identity, contributor);\n }\n\n insertEdge(org: string, edge: DiscoveryEdge): void {\n this.db.insertCentralEdge(org, edge);\n }\n\n getSummary(org: string): OrgSummary {\n return this.db.getOrgSummary(org);\n }\n\n getContributors(globalId: string): Contributor[] {\n return this.db.getContributorsByGlobalId(globalId);\n }\n\n /**\n * No-op: the wrapped `CartographyDB` is owned by the caller (it is shared with the\n * read-side MCP server in server-mode), so the backend never closes it. The caller\n * closes the `CartographyDB` directly.\n */\n close(): void {\n /* connection lifecycle is owned by the caller */\n }\n}\n","/**\n * Ingest orchestration for the central collector (2.12).\n *\n * Consumes the 2.11 PUSH ENVELOPE verbatim —\n * `{ schemaVersion: 1, org?, items: [{ contentHash, kind: 'node'|'edge', payload }] }`\n * — and merges it into the tenant-partitioned central store. The pipeline is:\n *\n * 1. **Validate** the envelope shape (zod). A malformed envelope is rejected whole;\n * nothing is persisted (the HTTP layer turns this into a 400).\n * 2. **Re-validate anonymization** per node (`revalidateAnonymized`) — the client is\n * not trusted. In `reject` mode a node with any identifying fragment is dropped\n * and counted; in `strip` mode it is scrubbed and persisted. Either way a\n * structured WARN is logged with the violation path + action.\n * 3. **Merge** each node by `(org, globalId)` primary + `(org, contentHash)` secondary\n * with a contributor union (`store.upsertNode`), and insert edges (`store.insertEdge`)\n * only when both endpoints survived re-validation.\n * 4. **Log** one structured INFO with the per-ingest counts.\n *\n * The contributor `at` is stamped server-side (`new Date().toISOString()`) — never\n * trusted from the client. The envelope MAY carry a `contributor`/`anonymizationLevel`\n * extension (2.9/2.10); absent, the collector derives a conservative default and\n * re-validates at the `anonymized` level (the safe assumption).\n */\n\nimport { z } from 'zod';\nimport { NodeSchema, EdgeSchema } from '../types.js';\nimport type { Contributor } from '../types.js';\nimport { logInfo, logWarn } from '../logger.js';\nimport type { StoreBackend } from '../store/backend.js';\nimport { computeIdentity } from './merge.js';\nimport { revalidateAnonymized } from './anonymization.js';\nimport type { AnonymizationLevel } from './anonymization.js';\n\n/** Wire-format version this collector accepts (the 2.11 `PUSH_SCHEMA_VERSION`). */\nexport const INGEST_SCHEMA_VERSION = 1 as const;\n\n/** Hard cap on items per envelope — bounds memory before the per-delta transaction. */\nconst MAX_ITEMS = 50_000;\n\n/** Optional source-attribution extension an envelope may carry (2.9). */\nconst ContributorSchema = z.object({\n machineId: z.string().min(1),\n hostname: z.string().default('unknown'),\n user: z.string().default('unknown'),\n confidence: z.number().min(0).max(1).default(0.5),\n});\n\n/**\n * The ingest envelope — the 2.11 push contract, plus optional `contributor` /\n * `anonymizationLevel` extension fields (read when present; never required). `org`\n * is optional in the wire format; the collector falls back to its `defaultOrg`.\n */\nexport const IngestEnvelopeSchema = z.object({\n schemaVersion: z.literal(INGEST_SCHEMA_VERSION),\n org: z.string().min(1).max(128).optional(),\n items: z.array(z.object({\n contentHash: z.string(),\n kind: z.enum(['node', 'edge']),\n payload: z.unknown(),\n })).max(MAX_ITEMS),\n // Extensions (forward-compatible; 2.11 does not yet send these).\n contributor: ContributorSchema.optional(),\n anonymizationLevel: z.enum(['none', 'anonymized', 'full']).optional(),\n});\nexport type IngestEnvelope = z.infer<typeof IngestEnvelopeSchema>;\n\n/** Per-ingest outcome counts (the 200 response body). */\nexport interface IngestResult {\n org: string;\n /** Nodes persisted (created + merged). */\n accepted: number;\n /** Nodes that collapsed onto an existing logical node. */\n merged: number;\n /** Nodes dropped for anonymization violations (reject mode). */\n rejected: number;\n /** Edges persisted. */\n edges: number;\n /** Total anonymization violations detected across all nodes. */\n violations: number;\n}\n\nexport interface IngestOptions {\n /** `reject` (default) drops nodes with violations; `strip` scrubs and keeps them. */\n anonMode?: 'reject' | 'strip';\n /** Tenant used when the envelope omits `org`. */\n defaultOrg?: string;\n}\n\n/**\n * Ingest one validated envelope into the store. Returns the outcome counts.\n * The caller (HTTP handler) wraps this in try/catch; the store's per-node upsert is\n * itself transactional, so a single bad node never half-writes a row.\n */\nexport function ingestEnvelope(store: StoreBackend, envelope: IngestEnvelope, opts: IngestOptions = {}): IngestResult {\n const anonMode = opts.anonMode ?? 'reject';\n const org = envelope.org ?? opts.defaultOrg ?? 'local';\n const level: AnonymizationLevel = envelope.anonymizationLevel ?? 'anonymized';\n const at = new Date().toISOString();\n const contributor: Contributor = {\n machineId: envelope.contributor?.machineId ?? 'unknown',\n hostname: envelope.contributor?.hostname ?? 'unknown',\n user: envelope.contributor?.user ?? 'unknown',\n organization: org,\n at,\n confidence: envelope.contributor?.confidence ?? 0.5,\n };\n\n let accepted = 0;\n let merged = 0;\n let rejected = 0;\n let edges = 0;\n let violations = 0;\n /** Node ids that survived re-validation — an edge persists only if both endpoints did. */\n const acceptedNodeIds = new Set<string>();\n\n for (const item of envelope.items) {\n if (item.kind !== 'node') continue;\n const parsed = NodeSchema.safeParse(item.payload);\n if (!parsed.success) {\n rejected += 1;\n logWarn('ingest: dropped malformed node payload', { org, contentHash: item.contentHash });\n continue;\n }\n const node = parsed.data;\n const check = revalidateAnonymized(node, level, anonMode);\n if (check.violations.length > 0) {\n violations += check.violations.length;\n // The node id cannot be safely scrubbed — masking it to `***` would destroy the\n // merge identity (global_id derives from id). So an id-borne violation forces a\n // reject even in `strip` mode; strip only ever masks the safely-maskable leaves.\n const idViolation = check.violations.some((v) => v.path === 'id');\n if (anonMode === 'reject' || idViolation) {\n rejected += 1;\n logWarn('ingest: rejected node with un-anonymized fragments', {\n org, nodeId: node.id, action: 'reject',\n mode: anonMode, idViolation,\n kinds: check.violations.map((v) => `${v.path}:${v.kind}`),\n });\n continue;\n }\n logWarn('ingest: scrubbed un-anonymized fragments from node', {\n org, nodeId: node.id, action: 'strip',\n kinds: check.violations.map((v) => `${v.path}:${v.kind}`),\n });\n }\n const safe = check.node;\n const identity = computeIdentity(org, safe);\n const outcome = store.upsertNode(org, safe, identity, { ...contributor, confidence: safe.confidence });\n accepted += 1;\n if (outcome === 'merged') merged += 1;\n acceptedNodeIds.add(safe.id);\n }\n\n for (const item of envelope.items) {\n if (item.kind !== 'edge') continue;\n const parsed = EdgeSchema.safeParse(item.payload);\n if (!parsed.success) {\n logWarn('ingest: dropped malformed edge payload', { org, contentHash: item.contentHash });\n continue;\n }\n const edge = parsed.data;\n // Persist an edge only when both endpoints survived this envelope's node ingest.\n // An edge-only envelope (no nodes) trusts its endpoints exist from a prior ingest.\n if (acceptedNodeIds.size > 0 && (!acceptedNodeIds.has(edge.sourceId) || !acceptedNodeIds.has(edge.targetId))) {\n continue;\n }\n store.insertEdge(org, edge);\n edges += 1;\n }\n\n logInfo('ingest', { org, accepted, merged, rejected, edges, violations, level, anonMode });\n return { org, accepted, merged, rejected, edges, violations };\n}\n","/**\n * Global-identity merge core for the central collector (2.12) — pure, no I/O.\n *\n * The merge *keys* (`normalizeId`, `contentHash`, `keyMetaOf`, `globalId`) already\n * exist in `src/db.ts` from the 2.9 identity work; this module re-exports them so\n * the central collector has one import surface, and adds {@link computeIdentity} —\n * the small adapter that turns one incoming `DiscoveryNode` into the precomputed\n * `(globalId, contentHash)` pair the {@link StoreBackend} merge consumes.\n *\n * The merge *resolution* (primary by `(org, globalId)`, secondary by\n * `(org, contentHash)`, contributor union, max-confidence) lives in the store\n * (`CartographyDB.upsertCentralNode`) because it needs the existing row — keeping\n * the SQL next to the schema. This module is the deterministic key derivation that\n * both the client (push) and the server (ingest) must agree on.\n */\n\nimport { normalizeId, contentHash, keyMetaOf, globalId } from '../db.js';\nimport type { DiscoveryNode } from '../types.js';\nimport type { NodeIdentity } from '../store/backend.js';\n\nexport { normalizeId, contentHash, keyMetaOf, globalId };\n\n/**\n * Derive the precomputed merge identity for one incoming node under `org`:\n * - `globalId` = `{org}:{normalizeId(node.id)}` (primary key; the org-scope ensures\n * two organizations never collapse onto one logical node).\n * - `contentHash` = sha256 over `type + normalized name + sorted key-meta` (secondary\n * key; catches `id` drift between machines for the same logical resource).\n *\n * Pure and deterministic: the same node + org always yields the same identity, on\n * any machine. This is the contract the client (2.11 push) and the server (ingest)\n * share so a content hash computed on the client matches the one the server recomputes.\n */\nexport function computeIdentity(org: string, node: DiscoveryNode): NodeIdentity {\n return {\n globalId: globalId(org, node.id),\n contentHash: contentHash(node.type, node.name, keyMetaOf(node.metadata ?? {})),\n };\n}\n","/**\n * Server-side anonymization re-validation for the central collector (2.12).\n *\n * **Don't trust the client.** The client (2.10) is supposed to pseudonymize\n * identifying fragments before pushing at the `anonymized` sharing level, but the\n * collector independently re-checks every incoming payload and rejects or scrubs any\n * un-anonymized identifying fragment it finds. This is defense-in-depth: a buggy,\n * outdated, or malicious client cannot leak raw identifiers into the org-wide store.\n *\n * The walker mirrors `redactValue`/`pseudonymize` (it rewrites only leaf strings;\n * structure is invariant). What it flags at the `anonymized` level:\n * - **private-ip** — RFC-1918 / loopback IPv4 (`src/anonymize.ts:PRIVATE_IP` + 127/8).\n * - **absolute-path**— POSIX (`/home/alice/...`) or Windows (`C:\\Users\\alice\\...`) paths.\n * - **username** — the user segment of `/home/<u>`, `/Users/<u>`, `C:\\Users\\<u>`.\n * - **hostname** — multi-label FQDNs (`db-01.internal.acme.lan`), AND the known\n * 2.10 residual: **bare single-label internal hostnames** (e.g. `db-prod-01`) that\n * the client's multi-label-only HOSTNAME regex never tokenizes. A token already in\n * `anon:{kind}:{base32}` form is recognized as anonymized and never flagged.\n *\n * At the `none` / `full` levels nothing is claimed to be anonymized, so re-validation\n * is a no-op (returns no violations): the employee consented to share at that level.\n */\n\nimport { PRIVATE_IP } from '../anonymize.js';\nimport type { DiscoveryNode } from '../types.js';\n\n/** The anonymization level claimed by an ingest envelope (mirrors `SharingLevel`). */\nexport type AnonymizationLevel = 'none' | 'anonymized' | 'full';\n\n/** A detected un-anonymized identifying fragment, with its location for logging. */\nexport interface AnonViolation {\n /** JSON path to the offending leaf, e.g. `metadata.host`. */\n path: string;\n kind: 'hostname' | 'username' | 'absolute-path' | 'private-ip';\n}\n\n/** Loopback IPv4 (127.0.0.0/8) — identifying of a local machine; client never tokenizes it. */\nconst LOOPBACK_IP = /\\b127(?:\\.\\d{1,3}){3}\\b/g;\n\n/** Multi-label FQDN/domain (≥2 dot-separated labels) — the shape 2.10 tokenizes. */\nconst FQDN = /\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}\\b/gi;\n\n/** POSIX absolute path and the user segment of a home dir. */\nconst POSIX_PATH = /(?:^|(?<=\\s|=|:|\"|'|\\())(\\/[A-Za-z0-9._-]+(?:\\/[A-Za-z0-9._-]+)+)/g;\nconst WIN_PATH = /\\b[A-Za-z]:\\\\[A-Za-z0-9._\\\\-]+/g;\nconst HOME_USER = /(?:\\/home\\/|\\/Users\\/|[A-Za-z]:\\\\Users\\\\)([A-Za-z0-9._-]+)/g;\n\n/**\n * A bare single-label internal hostname — the known 2.10 residual. We only flag a\n * single label when it *looks* like an internal host: it contains a hyphen or a\n * digit run (e.g. `db-01`, `web2`, `prod-db`) so we do not false-positive ordinary\n * English words used as a `name`. An already-pseudonymized `anon:...` token has a\n * colon and is excluded by construction. Matched only when the whole string (after\n * trimming) is one such label — embedded prose is left alone to avoid over-scrubbing.\n */\nconst BARE_INTERNAL_HOST = /^[a-z0-9]+(?:-[a-z0-9]+)+$|^[a-z]+\\d+$|^\\d+[a-z]+$/i;\n\n/** A pseudonym token produced by 2.10 (`anon:host:abc…`) — already anonymized. */\nconst ANON_TOKEN = /^anon:(?:host|user|path|ip):[a-z2-7]+$/;\n\n/** Detect identifying fragments in a single leaf string at the `anonymized` level. */\nfunction violationsInString(s: string, path: string): AnonViolation[] {\n const out: AnonViolation[] = [];\n const trimmed = s.trim();\n if (trimmed === '' || ANON_TOKEN.test(trimmed)) return out;\n\n if (PRIVATE_IP.test(s) || LOOPBACK_IP.test(s)) out.push({ path, kind: 'private-ip' });\n PRIVATE_IP.lastIndex = 0; LOOPBACK_IP.lastIndex = 0;\n\n if (HOME_USER.test(s)) out.push({ path, kind: 'username' });\n HOME_USER.lastIndex = 0;\n\n if (WIN_PATH.test(s) || POSIX_PATH.test(s)) out.push({ path, kind: 'absolute-path' });\n WIN_PATH.lastIndex = 0; POSIX_PATH.lastIndex = 0;\n\n if (FQDN.test(s)) out.push({ path, kind: 'hostname' });\n FQDN.lastIndex = 0;\n\n // The residual: a bare single-label internal hostname not already caught above.\n if (out.length === 0 && BARE_INTERNAL_HOST.test(trimmed)) out.push({ path, kind: 'hostname' });\n\n return out;\n}\n\n/**\n * Recursively collect every anonymization violation in a value at the given level.\n * Pure. At `none`/`full` returns `[]` (no anonymization is claimed). Object keys are\n * schema, not data, so only values are inspected; `path` accumulates the location.\n */\nexport function findAnonViolations(value: unknown, level: AnonymizationLevel, path = ''): AnonViolation[] {\n if (level !== 'anonymized') return [];\n return collect(value, path);\n}\n\nfunction collect(value: unknown, path: string): AnonViolation[] {\n if (typeof value === 'string') return violationsInString(value, path || '(root)');\n if (Array.isArray(value)) return value.flatMap((v, i) => collect(v, `${path}[${i}]`));\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>).flatMap(([k, v]) => collect(v, path ? `${path}.${k}` : k));\n }\n return [];\n}\n\n/** Mask every leaf string that contains a violation to `***` (structure-preserving). */\nfunction scrub(value: unknown, path: string): unknown {\n if (typeof value === 'string') return violationsInString(value, path || '(root)').length > 0 ? '***' : value;\n if (Array.isArray(value)) return value.map((v, i) => scrub(v, `${path}[${i}]`));\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = scrub(v, path ? `${path}.${k}` : k);\n return out;\n }\n return value;\n}\n\n/**\n * Re-validate one node against its claimed anonymization level (2.12).\n *\n * - `mode: 'reject'` (default, strict) — returns the node unchanged plus the\n * violations; the caller drops a node with any violation and never persists it.\n * - `mode: 'strip'` (lenient) — returns a sanitized node (offending leaves masked to\n * `***`) plus the violations (for logging), so the topology shape survives while\n * the identifying fragments do not.\n *\n * Pure: no I/O, no logging (the caller logs). At `none`/`full` it is a pass-through.\n */\nexport function revalidateAnonymized(\n node: DiscoveryNode,\n level: AnonymizationLevel,\n mode: 'reject' | 'strip',\n): { node: DiscoveryNode; violations: AnonViolation[] } {\n const violations = [\n ...findAnonViolations(node.name, level, 'name'),\n ...findAnonViolations(node.id, level, 'id'),\n ...findAnonViolations(node.metadata ?? {}, level, 'metadata'),\n ...findAnonViolations(node.tags ?? [], level, 'tags'),\n ...findAnonViolations(node.domain ?? '', level, 'domain'),\n ...findAnonViolations(node.subDomain ?? '', level, 'subDomain'),\n ];\n if (violations.length === 0 || mode === 'reject') return { node, violations };\n // strip mode: mask the offending leaves while preserving structure.\n const scrubbed: DiscoveryNode = {\n ...node,\n name: scrub(node.name, 'name') as string,\n metadata: scrub(node.metadata ?? {}, 'metadata') as Record<string, unknown>,\n tags: (node.tags ?? []).map((t, i) => scrub(t, `tags[${i}]`) as string),\n ...(node.domain != null ? { domain: scrub(node.domain, 'domain') as string } : {}),\n ...(node.subDomain != null ? { subDomain: scrub(node.subDomain, 'subDomain') as string } : {}),\n };\n return { node: scrubbed, violations };\n}\n","/**\n * The central-collector ingest HTTP surface (2.12).\n *\n * {@link createIngestHandler} produces the request handler that the Streamable-HTTP\n * transport mounts at `POST /ingest` when server-mode is on. It is deliberately a\n * pure `(body) => { status, body }` function with no socket of its own, so it can be\n * unit-tested directly without binding a port (the transport owns auth, the\n * non-loopback host allowlist, and the body size cap — see `src/mcp/transports.ts`).\n *\n * Validation is fail-closed: a malformed envelope yields 400 (no writes); an internal\n * error yields 500; only a valid envelope runs `ingestEnvelope` and returns 200 with\n * the {@link IngestResult}. The bearer token is enforced by the transport before this\n * handler ever runs, so the handler never sees (and never logs) the token.\n */\n\nimport type { StoreBackend } from '../store/backend.js';\nimport { normalizeTenant } from '../db.js';\nimport { IngestEnvelopeSchema, ingestEnvelope } from './ingest.js';\nimport type { IngestResult, IngestOptions } from './ingest.js';\nimport type { RateLimiter } from './quota.js';\nimport { logWarn } from '../logger.js';\n\n/** A transport-agnostic HTTP-ish response: a status code, a JSON-serializable body, optional headers. */\nexport interface IngestResponse {\n status: number;\n body: unknown;\n /** Extra response headers (e.g. `Retry-After` on a 429). */\n headers?: Record<string, string>;\n}\n\nexport type IngestHandler = (body: unknown) => IngestResponse;\n\nexport interface IngestHandlerOptions extends IngestOptions {\n /** Per-org ingest rate limiter (4.7 backpressure). Over-quota → 429 + Retry-After. */\n quota?: RateLimiter;\n}\n\n/**\n * Build the `/ingest` handler over a {@link StoreBackend}. The handler validates the\n * 2.11 push envelope, runs ingest (re-validating anonymization first), and maps the\n * outcome to an HTTP status:\n * - 400 — envelope failed `IngestEnvelopeSchema` (no writes).\n * - 500 — ingest threw (the store's per-node transaction rolls that node back).\n * - 200 — {@link IngestResult}.\n */\nexport function createIngestHandler(store: StoreBackend, opts: IngestHandlerOptions = {}): IngestHandler {\n const quota = opts.quota;\n return (body: unknown): IngestResponse => {\n const parsed = IngestEnvelopeSchema.safeParse(body);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`);\n logWarn('ingest: rejected invalid envelope', { issues });\n return { status: 400, body: { error: 'invalid envelope', issues } };\n }\n // Backpressure (4.7): per-org token bucket. Refuse over-quota BEFORE any write.\n if (quota) {\n // Key on the SAME normalized tenant the store writes under (`ensureCentralSession`),\n // so a client cannot evade the limit (or balloon the bucket Map) by rotating raw\n // org strings that all collapse to one tenant — e.g. \"acme\", \"ACME \", \"acme#1\".\n const org = normalizeTenant(parsed.data.org ?? opts.defaultOrg);\n const decision = quota.take(org);\n if (!decision.allowed) {\n logWarn('ingest: rate limited', { org, retryAfterSec: decision.retryAfterSec });\n return { status: 429, body: { error: 'too many requests' }, headers: { 'retry-after': String(decision.retryAfterSec) } };\n }\n }\n try {\n const result: IngestResult = ingestEnvelope(store, parsed.data, opts);\n return { status: 200, body: result };\n } catch (err) {\n logWarn('ingest: failed', { error: err instanceof Error ? err.message : String(err) });\n return { status: 500, body: { error: 'ingest failed' } };\n }\n };\n}\n","/**\n * Ingest backpressure for the central collector (4.7).\n *\n * A pure, in-memory **per-org token-bucket** rate limiter. The networked `POST /ingest`\n * write endpoint must protect the shared store from a runaway or hostile client; over-quota\n * requests are refused with `429 + Retry-After` rather than admitted. Deterministic given an\n * injected clock, so it is unit-testable without real time. In-process only (a multi-replica\n * deployment would front it with a shared limiter; documented in the runbook).\n */\n\nexport interface QuotaConfig {\n /** Bucket capacity = max burst, and the number of tokens refilled over one `refillMs` window. */\n capacity: number;\n /** Milliseconds over which a fully-drained bucket refills to `capacity`. */\n refillMs: number;\n}\n\n/** Sensible default: 120 ingests / minute / org (burst 120). */\nexport const DEFAULT_INGEST_QUOTA: QuotaConfig = { capacity: 120, refillMs: 60_000 };\n\nexport interface QuotaDecision {\n allowed: boolean;\n /** Seconds to wait before retrying — populated only when `!allowed` (≥1). */\n retryAfterSec: number;\n}\n\ninterface Bucket {\n tokens: number;\n last: number;\n}\n\n/** Hard cap on tracked keys — defense-in-depth against an unbounded key space. */\nconst MAX_KEYS = 10_000;\n\nexport class RateLimiter {\n private readonly buckets = new Map<string, Bucket>();\n\n constructor(\n private readonly cfg: QuotaConfig = DEFAULT_INGEST_QUOTA,\n private readonly now: () => number = () => Date.now(),\n ) {}\n\n /** Consume one token for `key`. Returns whether the request is allowed (+ Retry-After when not). */\n take(key: string): QuotaDecision {\n const t = this.now();\n const ratePerMs = this.cfg.capacity / this.cfg.refillMs;\n let b = this.buckets.get(key);\n if (!b) {\n // Bound the Map: callers should already pass a normalized, low-cardinality key, but\n // evict the oldest-inserted entry if the space is somehow exhausted (no unbounded growth).\n if (this.buckets.size >= MAX_KEYS) {\n const oldest = this.buckets.keys().next().value;\n if (oldest !== undefined) this.buckets.delete(oldest);\n }\n b = { tokens: this.cfg.capacity, last: t };\n this.buckets.set(key, b);\n }\n // Refill proportional to elapsed time, capped at capacity.\n b.tokens = Math.min(this.cfg.capacity, b.tokens + Math.max(0, t - b.last) * ratePerMs);\n b.last = t;\n\n if (b.tokens >= 1) {\n b.tokens -= 1;\n return { allowed: true, retryAfterSec: 0 };\n }\n const waitMs = (1 - b.tokens) / ratePerMs;\n return { allowed: false, retryAfterSec: Math.max(1, Math.ceil(waitMs / 1000)) };\n }\n}\n","/**\n * Shared entry logic for launching the Cartography MCP server, used by both the\n * dedicated `cartography-mcp` binary and the `mcp` CLI sub-command.\n *\n * Wires in semantic search (lazy, falls back to lexical) and deterministic local\n * discovery (no LLM required). All logging goes to stderr so stdout stays a clean\n * MCP protocol channel.\n */\n\nimport { CartographyDB, normalizeTenant } from '../db.js';\nimport { defaultConfig } from '../types.js';\nimport { createMcpServer } from './server.js';\nimport type { SearchFn } from './server.js';\nimport { runStdio, runHttp } from './transports.js';\nimport { createSemanticSearch } from '../semantic/search.js';\nimport { localDiscoveryFn } from '../discovery/local.js';\nimport { SqliteStoreBackend } from '../store/sqlite.js';\nimport { SqliteCredentialStore } from '../auth/identity.js';\nimport type { Principal } from '../auth/types.js';\nimport { createIngestHandler } from '../central/server.js';\nimport { RateLimiter, DEFAULT_INGEST_QUOTA } from '../central/quota.js';\nimport type { QuotaConfig } from '../central/quota.js';\nimport { SCHEMA_VERSION } from '../db.js';\n\nconst EXPECTED_USER_VERSION = SCHEMA_VERSION;\n\nexport interface StartMcpOptions {\n dbPath?: string;\n session?: string | 'latest';\n /** Tenant/organization whose topology to serve. Defaults to `'local'`. */\n tenant?: string;\n /** `http` runs Streamable HTTP; otherwise stdio (default). */\n transport?: 'stdio' | 'http';\n port?: number;\n host?: string;\n /** Trusted Host headers when binding a non-loopback host (DNS-rebinding allowlist). */\n allowedHosts?: string[];\n /** Bearer token required on HTTP requests. Mandatory for a non-loopback bind. */\n token?: string;\n /** Enable semantic (vector) search. Default true; degrades to lexical if unavailable. */\n semantic?: boolean;\n /** Opt-in scanner plugin package names to load for `run_discovery` (or `CARTOGRAPHY_PLUGINS`). */\n plugins?: string[];\n /**\n * Run as a central collector (2.12): expose the authenticated `POST /ingest` write\n * route and serve the org-wide `get_summary`. OFF by default — the collector is\n * self-hostable and strictly opt-in (no phone-home). Server-mode implies HTTP.\n */\n serverMode?: boolean;\n /** Anonymization re-validation mode for ingest: `reject` (default) or `strip`. */\n anonMode?: 'reject' | 'strip';\n /** Reject unauthenticated requests even on loopback (RBAC `required` mode). */\n authRequired?: boolean;\n /** Per-org ingest rate limit (4.7 backpressure). Defaults to DEFAULT_INGEST_QUOTA. */\n ingestQuota?: QuotaConfig;\n /** Logger (stderr). */\n log?: (msg: string) => void;\n}\n\nexport interface ParsedMcpArgs extends StartMcpOptions {\n /** `--help`/`-h` was passed; the caller should print usage and exit 0. */\n help?: boolean;\n}\n\n/**\n * Parse the `cartography-mcp` argv into StartMcpOptions. Kept here (not in the\n * binary) so it can be unit-tested without triggering the binary's side effects.\n */\nexport function parseMcpArgs(argv: string[]): ParsedMcpArgs {\n const opts: ParsedMcpArgs = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === '--http') opts.transport = 'http';\n else if (a === '--stdio') opts.transport = 'stdio';\n else if (a === '--no-semantic') opts.semantic = false;\n else if (a === '--port') opts.port = Number(argv[++i]);\n else if (a === '--host') opts.host = argv[++i];\n else if (a === '--allowed-hosts') opts.allowedHosts = (argv[++i] ?? '').split(',').map((h) => h.trim()).filter(Boolean);\n else if (a === '--token') opts.token = argv[++i];\n else if (a === '--db') opts.dbPath = argv[++i];\n else if (a === '--session') opts.session = argv[++i];\n else if (a === '--tenant' || a === '--org') opts.tenant = argv[++i];\n else if (a === '--plugins') opts.plugins = (argv[++i] ?? '').split(',').map((p) => p.trim()).filter(Boolean);\n else if (a === '--server-mode') opts.serverMode = true;\n else if (a === '--anon-mode') { const m = argv[++i]; if (m === 'reject' || m === 'strip') opts.anonMode = m; }\n else if (a === '--auth-required') opts.authRequired = true;\n else if (a === '--help' || a === '-h') opts.help = true;\n }\n return opts;\n}\n\nexport async function startMcp(opts: StartMcpOptions = {}): Promise<void> {\n const log = opts.log ?? ((m: string) => process.stderr.write(m + '\\n'));\n const db = new CartographyDB(opts.dbPath ?? defaultConfig().dbPath);\n\n let search: SearchFn | undefined;\n if (opts.semantic !== false) {\n search = await createSemanticSearch(db, undefined, { log });\n }\n const plugins =\n opts.plugins ??\n (process.env['CARTOGRAPHY_PLUGINS']\n ? process.env['CARTOGRAPHY_PLUGINS'].split(',').map((p) => p.trim()).filter(Boolean)\n : []);\n const discovery = localDiscoveryFn(undefined, plugins);\n\n const tenant = normalizeTenant(opts.tenant);\n\n // Central collector (2.12): server-mode exposes an authenticated /ingest write route\n // and serves the org-wide summary. OFF unless --server-mode; never enabled by env.\n // Server-mode implies HTTP (the ingest surface is networked).\n const serverMode = opts.serverMode === true;\n // RBAC (4.5): the credential store is always wired; it only enforces once an admin has\n // added credentials. When a principal is bound (HTTP RBAC mode), the per-session server\n // is pinned to the principal's tenant and gates run_discovery by role.\n const authStore = new SqliteCredentialStore(db);\n const rbacActive = authStore.count() > 0;\n const factory = (principal?: Principal) => {\n // In RBAC mode the principal's tenant is authoritative for BOTH session-scoped reads\n // AND the server-mode org-wide summary — otherwise a principal of org B connecting to a\n // collector started `--tenant A` would receive A's cross-machine org aggregate.\n const scopeTenant = rbacActive && principal ? principal.tenant : tenant;\n const orgArg = serverMode ? scopeTenant : undefined;\n return createMcpServer({\n db,\n session: opts.session ?? 'latest',\n tenant: scopeTenant,\n search,\n discovery,\n ...(principal ? { principal } : {}),\n ...(orgArg !== undefined ? { org: orgArg } : {}),\n });\n };\n\n const transport = serverMode ? 'http' : opts.transport;\n\n if (transport === 'http') {\n const port = opts.port ?? 3737;\n const host = opts.host ?? '127.0.0.1';\n // CARTOGRAPHY_CENTRAL_TOKEN is accepted as an alias for the collector's bearer.\n const token = opts.token ?? process.env['CARTOGRAPHY_HTTP_TOKEN'] ?? process.env['CARTOGRAPHY_CENTRAL_TOKEN'];\n let onIngest: ((body: unknown) => { status: number; body: unknown; headers?: Record<string, string> }) | undefined;\n if (serverMode) {\n const store = new SqliteStoreBackend(db);\n const anonMode = opts.anonMode ?? 'reject';\n // Ingest backpressure (4.7): per-org token bucket → 429 + Retry-After when exceeded.\n const quota = new RateLimiter(opts.ingestQuota ?? DEFAULT_INGEST_QUOTA);\n onIngest = createIngestHandler(store, { anonMode, defaultOrg: tenant, quota });\n }\n // Readiness (4.7): the store is ready when the catalog is reachable at the expected schema.\n const readiness = (): { ready: boolean; detail?: Record<string, unknown> } => {\n try {\n const v = db.rawConnection().pragma('user_version', { simple: true }) as number;\n return { ready: v === EXPECTED_USER_VERSION, detail: { schema: v } };\n } catch (err) {\n return { ready: false, detail: { error: err instanceof Error ? err.message : String(err) } };\n }\n };\n await runHttp(factory, {\n port,\n host,\n readiness,\n auth: { store: authStore, ...(opts.authRequired ? { required: true } : {}) },\n defaultTenant: tenant,\n ...(opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}),\n ...(token ? { token } : {}),\n ...(onIngest ? { onIngest } : {}),\n });\n const modeNote = serverMode ? ` [central collector: POST /ingest enabled, anon-mode=${opts.anonMode ?? 'reject'}]` : '';\n log(`Cartography MCP server (Streamable HTTP) on http://${host}:${port}/mcp${token ? ' (auth: bearer token required)' : ''} (tenant: ${tenant})${modeNote}`);\n } else {\n log(`Cartography MCP server (stdio) ready (tenant: ${tenant})`);\n await runStdio(factory());\n }\n}\n"],"mappings":"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","names":["randomBytes","randomBytes","z","anchors","z","body","lexicalSearch","globalId","z","POSIX_PATH","WIN_PATH","MAX_ITEMS","z"]}