diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 675d209..7eeaf19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,9 @@ jobs: - name: Verify npm package metadata run: pnpm package:check + - name: Generate release verification artifacts + run: pnpm release:artifacts + commitlint: name: Commitlint if: github.event_name == 'pull_request' diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 3e6a130..a259164 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -73,6 +73,15 @@ jobs: - name: Verify npm package metadata run: pnpm package:check + - name: Generate release verification artifacts + run: pnpm release:artifacts + + - name: Upload release verification artifacts + uses: actions/upload-artifact@v4 + with: + name: mimir-release-${{ inputs.version }} + path: release-artifacts/ + - name: Publish run: npm publish --access public --provenance env: diff --git a/.gitignore b/.gitignore index bfb1d72..f040c77 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ coverage/ .env .DS_Store private/** -.kb/storage/ -.kb/cache/ +.kb/ +.mimir/ *.tgz +release-artifacts/ diff --git a/AGENTS.md b/AGENTS.md index 84df71b..c497722 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,9 @@ - `kb init` and `kb install-skill` must keep generated local Mimir state ignored in target repositories. By default, add `.kb/`, `.mimir/`, and private raw-document paths to the target repository `.gitignore`. +- Keep confidentiality features low-friction: local-only network policy, redaction before + indexing, metadata-only access logs, bounded MCP retrieval, and `security-audit` should work + from default config. - Use Context7 before changing dependencies or public APIs that rely on external libraries. - Run `pnpm validate` before opening a release pull request or publishing. It covers Biome, TypeScript, Vitest, build output, production CLI/MCP smoke tests, and npm package @@ -31,6 +34,8 @@ - `src/mcp.ts` exposes Mimir as an MCP stdio server for agents. - `src/gitignore.ts` owns target-repository `.gitignore` entries for local generated Mimir state. +- `src/security.ts`, `src/network.ts`, `src/redaction.ts`, and `src/access-log.ts` own the + privacy and confidentiality hardening layer. - `skills/mimir/SKILL.md` is the bundled portable agent skill. - `.kb/`, `.mimir/`, and project `private/` folders are local user data or generated agent state in target repositories and must not be committed. diff --git a/CHANGELOG.md b/CHANGELOG.md index fa9aeb1..b38d4dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.3.0 - 2026-06-28 + +- Add confidentiality hardening defaults: local-only Ollama network policy, built-in + redaction before indexing, metadata-only access logs, and bounded MCP retrieval. +- Add `kb security-audit` for zero-telemetry, network, redaction, gitignore, storage, and + MCP posture checks. +- Add `kb destroy-index --yes` to remove generated vector indexes. +- Add release verification artifacts: npm tarball, SHA256 checksums, SBOM, and manifest. +- Document air-gapped operation, threat model, MCP hardening, and secure deletion limits. + ## 0.2.1 - 2026-06-28 - Add GitHub Sponsors funding metadata and document suggested sponsor tiers. diff --git a/README.md b/README.md index fdf1b04..5df9d1a 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ pnpm exec kb ingest pnpm exec kb search "vendor invoice status" pnpm exec kb ask "What do the documents prove?" pnpm exec kb audit +pnpm exec kb security-audit pnpm exec kb status ``` @@ -121,6 +122,7 @@ npx kb ingest npx kb search "vendor invoice status" npx kb ask "What do the documents prove?" npx kb audit +npx kb security-audit npx kb status ``` @@ -157,6 +159,7 @@ MCP tools exposed: - `mimir_search` - `mimir_ask` - `mimir_audit` +- `mimir_security_audit` Print the bundled skill path from the installed package: @@ -175,12 +178,40 @@ your-project/ .kb/config.json # local config .kb/sources.txt # optional extra source paths .kb/storage/ # generated LanceDB index + .kb/access.log # metadata-only access log ``` The package never ships project documents. `kb init` adds gitignore entries for `.kb/` and `private/**`, and `kb install-skill` keeps `.mimir/` ignored as generated local agent state. +## Confidentiality Defaults + +Mimir is designed for private repositories and sensitive local evidence. + +- Zero telemetry: no analytics or document content is sent to JCode Labs. +- Local-only network policy: Ollama must be on loopback by default. +- Redaction before indexing: common secrets and identifiers are redacted before chunks are + embedded and stored. +- Metadata-only access logs: query hashes and action metadata are logged, not raw queries. +- MCP is read-focused and bounded by `mcpMaxTopK`. +- Generated local state is ignored by Git. + +Run: + +```bash +pnpm exec kb security-audit --strict +``` + +Remove the generated vector index: + +```bash +pnpm exec kb destroy-index --yes +``` + +For air-gapped operation, release verification, secure deletion limits, and threat model details, +read [`SECURITY-HARDENING.md`](./SECURITY-HARDENING.md). + ## Supported Files - Markdown: `.md`, `.mdx` @@ -200,10 +231,19 @@ state. "rawDir": "private", "storageDir": ".kb/storage", "sourcesFile": ".kb/sources.txt", + "accessLogPath": ".kb/access.log", "tableName": "chunks", "ollamaHost": "http://localhost:11434", + "networkPolicy": "local-only", "embedModel": "nomic-embed-text", "llmModel": "gemma4:latest", + "redaction": { + "enabled": true, + "builtIn": true, + "patterns": [] + }, + "accessLog": true, + "mcpMaxTopK": 10, "topK": 5, "chunkSize": 1200, "chunkOverlap": 150 @@ -215,9 +255,15 @@ Environment overrides: - `KB_RAW_DIR` - `KB_STORAGE_DIR` - `KB_SOURCES_FILE` +- `KB_ACCESS_LOG_PATH` - `KB_OLLAMA_HOST` +- `KB_NETWORK_POLICY` - `KB_EMBED_MODEL` - `KB_LLM_MODEL` +- `KB_REDACTION_ENABLED` +- `KB_REDACTION_BUILT_IN` +- `KB_ACCESS_LOG` +- `KB_MCP_MAX_TOP_K` - `KB_TOP_K` - `KB_CHUNK_SIZE` - `KB_CHUNK_OVERLAP` @@ -235,6 +281,9 @@ const answer = await ask("What documents support the project timeline?") ## Privacy - Embeddings and answers use local Ollama by default. +- Remote Ollama hosts are blocked unless `networkPolicy` explicitly allows them. +- Built-in redaction runs before indexing by default. +- Access logs store query hashes, not raw queries. - The vector index is stored locally. - Raw private documents should stay in the target repository's ignored `private/` folder. - Do not put secrets or scans inside this package repository. diff --git a/SECURITY-HARDENING.md b/SECURITY-HARDENING.md new file mode 100644 index 0000000..f534555 --- /dev/null +++ b/SECURITY-HARDENING.md @@ -0,0 +1,156 @@ +# Mimir Security Hardening + +Mimir is a local-first knowledge base for private project documents. It is built to minimize +data movement, but it is not a certified high-assurance system. + +## Current Guarantees + +- Zero telemetry: Mimir does not send usage analytics or document content to JCode Labs. +- Local-only network policy by default: document text can only be sent to loopback Ollama hosts + unless the repository explicitly opts in to broader network access. +- Redaction before indexing: built-in DLP patterns redact common secrets and identifiers before + chunks are embedded and stored. +- Metadata-only access logs: access logs contain action metadata and query hashes, not raw + queries or retrieved text. +- Generated local state is ignored by Git: `.kb/`, `.mimir/`, and `private/**` are ignored by + default. +- MCP is read-focused: destructive tools are not exposed over MCP, and MCP retrieval is capped by + `mcpMaxTopK`. +- npm releases are published with provenance from the protected GitHub Actions workflow. +- Release artifacts include a package tarball, SHA256 checksums, SBOM, and manifest. + +## Threat Model + +Mimir protects against accidental repository leaks, accidental remote LLM usage, accidental secret +indexing, and weak release traceability. + +Mimir does not protect against a compromised local machine, malicious dependencies already present +in the runtime, a user with filesystem access to the same checkout, or forensic recovery from an +unencrypted disk. + +## At-Rest Encryption + +Native encrypted LanceDB storage is not implemented yet. For sensitive environments, put the +repository and `.kb/` on an encrypted volume: + +- macOS: FileVault or an encrypted APFS volume. +- Linux: LUKS, fscrypt, or an encrypted VM disk. +- Containers/VMs: mount `.kb/` on an encrypted host volume. + +`kb destroy-index --yes` removes generated index files, but secure deletion on SSDs and copy-on-write +filesystems cannot be guaranteed without encrypted storage and key destruction. + +## Air-Gapped Operation + +Prepare artifacts on an internet-connected build machine: + +```bash +pnpm install --frozen-lockfile +pnpm build +pnpm release:artifacts +``` + +Move the generated tarball from `release-artifacts/` into the offline environment and install it: + +```bash +pnpm add -D ./jcode.labs-mimir-.tgz +pnpm exec kb init +pnpm exec kb ingest +``` + +Ollama and the required models must also be preloaded inside the offline environment. + +## Zero Network Posture + +Default config: + +```json +{ + "ollamaHost": "http://localhost:11434", + "networkPolicy": "local-only" +} +``` + +Allowed policies: + +- `local-only`: only loopback hosts such as `localhost` and `127.0.0.1`. +- `allow-private`: loopback and private LAN hosts. +- `allow-any`: any host. Use only when the remote endpoint is explicitly trusted. + +Run: + +```bash +pnpm exec kb security-audit --strict +``` + +## DLP Redaction + +Built-in redaction is enabled by default for common secret and identifier shapes: private keys, +JWTs, API tokens, emails, IBANs, and card-like numbers. + +Custom patterns can be added in `.kb/config.json`: + +```json +{ + "redaction": { + "enabled": true, + "builtIn": true, + "patterns": [ + { + "name": "internal_case_id", + "pattern": "CASE-[0-9]+", + "replacement": "[CASE]" + } + ] + } +} +``` + +Redaction changes the indexed text, not the raw files under `private/`. + +## MCP Hardening + +MCP gives an agent access to retrieved private context. Use it only for agents running under the +same trust boundary as the repository. + +Mimir MCP defaults: + +- read-focused tools only; +- no index deletion tool exposed over MCP; +- bounded retrieval through `mcpMaxTopK`; +- metadata-only access logging. + +For team use, prefer one checkout per user or per role. Mimir does not implement RBAC. + +## Release Verification + +The protected npm workflow runs validation, generates release artifacts, and publishes with +provenance: + +```bash +npm publish --access public --provenance +``` + +Release artifacts include: + +- npm tarball; +- `SHA256SUMS`; +- CycloneDX SBOM; +- `release-manifest.json`. + +Verify checksums offline with: + +```bash +sha256sum -c SHA256SUMS +``` + +On macOS: + +```bash +shasum -a 256 -c SHA256SUMS +``` + +## External Audit Status + +No external security audit has been completed yet. Treat Mimir as useful hardening for private +developer workflows, not as military-grade certified software. diff --git a/dist/access-log.d.ts b/dist/access-log.d.ts new file mode 100644 index 0000000..c2e31c2 --- /dev/null +++ b/dist/access-log.d.ts @@ -0,0 +1,10 @@ +import type { Config } from "./types.js"; +export interface AccessLogEvent { + action: "ingest" | "search" | "ask" | "destroy-index"; + query?: string; + topK?: number; + resultCount?: number; + redactions?: number; +} +export declare function recordAccess(config: Config, event: AccessLogEvent): Promise; +//# sourceMappingURL=access-log.d.ts.map \ No newline at end of file diff --git a/dist/access-log.d.ts.map b/dist/access-log.d.ts.map new file mode 100644 index 0000000..2329ddb --- /dev/null +++ b/dist/access-log.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"access-log.d.ts","sourceRoot":"","sources":["../src/access-log.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvF"} \ No newline at end of file diff --git a/dist/access-log.js b/dist/access-log.js new file mode 100644 index 0000000..2acd8d4 --- /dev/null +++ b/dist/access-log.js @@ -0,0 +1,29 @@ +import { createHash } from "node:crypto"; +import { appendFile, mkdir } from "node:fs/promises"; +import path from "node:path"; +export async function recordAccess(config, event) { + if (!config.accessLog) { + return; + } + try { + await mkdir(path.dirname(config.accessLogPath), { recursive: true }); + await appendFile(config.accessLogPath, `${JSON.stringify(toLogLine(event))}\n`, "utf8"); + } + catch { + // Access logging is best-effort so read-only workspaces do not block local use. + } +} +function toLogLine(event) { + return { + timestamp: new Date().toISOString(), + action: event.action, + queryHash: event.query ? hashQuery(event.query) : undefined, + topK: event.topK, + resultCount: event.resultCount, + redactions: event.redactions, + }; +} +function hashQuery(query) { + return createHash("sha256").update(query).digest("hex"); +} +//# sourceMappingURL=access-log.js.map \ No newline at end of file diff --git a/dist/access-log.js.map b/dist/access-log.js.map new file mode 100644 index 0000000..996e2cb --- /dev/null +++ b/dist/access-log.js.map @@ -0,0 +1 @@ +{"version":3,"file":"access-log.js","sourceRoot":"","sources":["../src/access-log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,IAAI,MAAM,WAAW,CAAA;AAW5B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,KAAqB;IACtE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAM;IACR,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACpE,MAAM,UAAU,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,gFAAgF;IAClF,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAqB;IACtC,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;QAC3D,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;KAC7B,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzD,CAAC"} \ No newline at end of file diff --git a/dist/cli.js b/dist/cli.js index c34a770..3bd4eea 100755 --- a/dist/cli.js +++ b/dist/cli.js @@ -2,10 +2,12 @@ import { Command } from "commander"; import pc from "picocolors"; import { loadConfig } from "./config.js"; +import { destroyIndex } from "./destroy.js"; import { audit, ingest } from "./ingest.js"; import { initProject } from "./init.js"; import { serveMcp } from "./mcp.js"; import { ask, search } from "./query.js"; +import { securityAudit } from "./security.js"; import { bundledSkillPath, installSkill } from "./skill.js"; import { countRows } from "./store.js"; import { VERSION } from "./version.js"; @@ -34,7 +36,7 @@ program .option("--rebuild", "Accepted for compatibility; ingest always rebuilds the local index.") .action(async () => { const result = await ingest({ cwd: process.cwd(), rebuild: true }); - console.log(pc.green(`Done. indexedFiles=${result.indexedFiles} chunks=${result.chunks} skippedFiles=${result.skippedFiles} errors=${result.errors.length}`)); + console.log(pc.green(`Done. indexedFiles=${result.indexedFiles} chunks=${result.chunks} skippedFiles=${result.skippedFiles} redactions=${result.redactions} errors=${result.errors.length}`)); for (const error of result.errors) { console.error(pc.red(` - ${error.path}: ${error.message}`)); } @@ -105,10 +107,60 @@ program console.log(`rawDir=${config.rawDir}`); console.log(`storageDir=${config.storageDir}`); console.log(`sourcesFile=${config.sourcesFile}`); + console.log(`accessLogPath=${config.accessLogPath}`); + console.log(`networkPolicy=${config.networkPolicy}`); console.log(`embedModel=${config.embedModel}`); console.log(`llmModel=${config.llmModel}`); + console.log(`redactionEnabled=${config.redaction.enabled}`); + console.log(`accessLog=${config.accessLog}`); + console.log(`mcpMaxTopK=${config.mcpMaxTopK}`); console.log(`chunksIndexed=${rows}`); }); +program + .command("security-audit") + .description("Show local privacy, network, redaction, MCP, and gitignore posture.") + .option("--json", "Print machine-readable JSON.") + .option("--strict", "Exit with code 1 when warnings are present.") + .action(async (options) => { + const report = await securityAudit(process.cwd()); + if (options.json) { + console.log(JSON.stringify(report, null, 2)); + } + else { + console.log(`zeroTelemetry=${report.zeroTelemetry}`); + console.log(`networkPolicy=${report.network.policy}`); + console.log(`ollamaHost=${report.network.ollamaHost}`); + console.log(`ollamaHostClassification=${report.network.classification}`); + console.log(`redactionEnabled=${report.redaction.enabled}`); + console.log(`redactionBuiltIn=${report.redaction.builtIn}`); + console.log(`accessLog=${report.accessLog.enabled}`); + console.log(`accessLogStoresRawQueries=${report.accessLog.storesRawQueries}`); + console.log(`storageGitIgnored=${report.storage.gitIgnored}`); + console.log(`mcpMaxTopK=${report.mcp.maxTopK}`); + console.log(`mcpDestructiveToolsExposed=${report.mcp.destructiveToolsExposed}`); + for (const warning of report.warnings) { + console.log(pc.yellow(`warning: ${warning}`)); + } + } + if (options.strict && report.warnings.length > 0) { + process.exitCode = 1; + } +}); +program + .command("destroy-index") + .description("Remove the generated local vector index from .kb/storage.") + .option("--yes", "Confirm deletion without an interactive prompt.") + .action(async (options) => { + if (!options.yes) { + console.error(pc.red("Refusing to delete the index without --yes.")); + process.exitCode = 1; + return; + } + const result = await destroyIndex(process.cwd()); + console.log(`storageDir=${result.storageDir}`); + console.log(`removed=${result.removed}`); + console.log(result.note); +}); program .command("serve-mcp") .description("Start the MCP server over stdio for Claude, Codex, and other MCP-compatible agents.") diff --git a/dist/cli.js.map b/dist/cli.js.map index 9b3e08e..e6654f3 100644 --- a/dist/cli.js.map +++ b/dist/cli.js.map @@ -1 +1 @@ -{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/config.d.ts.map b/dist/config.d.ts.map index 4a1187c..96ee57f 100644 --- a/dist/config.d.ts.map +++ b/dist/config.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAmBxC,wBAAgB,eAAe,CAAC,KAAK,SAAgB,GAAG,MAAM,CAc7D;AAED,wBAAsB,UAAU,CAAC,KAAK,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BvE"} \ No newline at end of file +{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAuCxC,wBAAgB,eAAe,CAAC,KAAK,SAAgB,GAAG,MAAM,CAc7D;AAED,wBAAsB,UAAU,CAAC,KAAK,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCvE"} \ No newline at end of file diff --git a/dist/config.js b/dist/config.js index c757946..64a485f 100644 --- a/dist/config.js +++ b/dist/config.js @@ -6,10 +6,28 @@ const rawConfigSchema = z.object({ rawDir: z.string().default("private"), storageDir: z.string().default(".kb/storage"), sourcesFile: z.string().default(".kb/sources.txt"), + accessLogPath: z.string().default(".kb/access.log"), tableName: z.string().default("chunks"), ollamaHost: z.string().default("http://localhost:11434"), + networkPolicy: z.enum(["local-only", "allow-private", "allow-any"]).default("local-only"), embedModel: z.string().default("nomic-embed-text"), llmModel: z.string().default("gemma4:latest"), + redaction: z + .object({ + enabled: z.boolean().default(true), + builtIn: z.boolean().default(true), + patterns: z + .array(z.object({ + name: z.string().min(1), + pattern: z.string().min(1), + flags: z.string().optional(), + replacement: z.string().optional(), + })) + .default([]), + }) + .default({ enabled: true, builtIn: true, patterns: [] }), + accessLog: z.boolean().default(true), + mcpMaxTopK: z.number().int().positive().default(10), topK: z.number().int().positive().default(5), chunkSize: z.number().int().positive().default(1200), chunkOverlap: z.number().int().nonnegative().default(150), @@ -44,10 +62,15 @@ export async function loadConfig(start = process.cwd()) { rawDir: resolveFromRoot(projectRoot, withEnv.rawDir), storageDir: resolveFromRoot(projectRoot, withEnv.storageDir), sourcesFile: resolveFromRoot(projectRoot, withEnv.sourcesFile), + accessLogPath: resolveFromRoot(projectRoot, withEnv.accessLogPath), tableName: withEnv.tableName, ollamaHost: withEnv.ollamaHost, + networkPolicy: withEnv.networkPolicy, embedModel: withEnv.embedModel, llmModel: withEnv.llmModel, + redaction: withEnv.redaction, + accessLog: withEnv.accessLog, + mcpMaxTopK: withEnv.mcpMaxTopK, topK: withEnv.topK, chunkSize: withEnv.chunkSize, chunkOverlap: withEnv.chunkOverlap, @@ -62,14 +85,40 @@ function applyEnv(config) { rawDir: process.env.KB_RAW_DIR ?? config.rawDir, storageDir: process.env.KB_STORAGE_DIR ?? config.storageDir, sourcesFile: process.env.KB_SOURCES_FILE ?? config.sourcesFile, + accessLogPath: process.env.KB_ACCESS_LOG_PATH ?? config.accessLogPath, ollamaHost: process.env.KB_OLLAMA_HOST ?? config.ollamaHost, + networkPolicy: readNetworkPolicyEnv("KB_NETWORK_POLICY", config.networkPolicy), embedModel: process.env.KB_EMBED_MODEL ?? config.embedModel, llmModel: process.env.KB_LLM_MODEL ?? config.llmModel, + redaction: { + ...config.redaction, + enabled: readBooleanEnv("KB_REDACTION_ENABLED", config.redaction.enabled), + builtIn: readBooleanEnv("KB_REDACTION_BUILT_IN", config.redaction.builtIn), + }, + accessLog: readBooleanEnv("KB_ACCESS_LOG", config.accessLog), + mcpMaxTopK: readPositiveIntEnv("KB_MCP_MAX_TOP_K", config.mcpMaxTopK), topK: readPositiveIntEnv("KB_TOP_K", config.topK), chunkSize: readPositiveIntEnv("KB_CHUNK_SIZE", config.chunkSize), chunkOverlap: readNonNegativeIntEnv("KB_CHUNK_OVERLAP", config.chunkOverlap), }; } +function readNetworkPolicyEnv(name, fallback) { + const raw = process.env[name]; + if (raw === "local-only" || raw === "allow-private" || raw === "allow-any") { + return raw; + } + return fallback; +} +function readBooleanEnv(name, fallback) { + const raw = process.env[name]?.toLowerCase(); + if (raw === "1" || raw === "true" || raw === "yes") { + return true; + } + if (raw === "0" || raw === "false" || raw === "no") { + return false; + } + return fallback; +} function readPositiveIntEnv(name, fallback) { const raw = process.env[name]; if (!raw) { diff --git a/dist/config.js.map b/dist/config.js.map index 406350b..99a1dea 100644 --- a/dist/config.js.map +++ b/dist/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/destroy.d.ts b/dist/destroy.d.ts new file mode 100644 index 0000000..4421081 --- /dev/null +++ b/dist/destroy.d.ts @@ -0,0 +1,3 @@ +import type { DestroyIndexResult } from "./types.js"; +export declare function destroyIndex(cwd?: string): Promise; +//# sourceMappingURL=destroy.d.ts.map \ No newline at end of file diff --git a/dist/destroy.d.ts.map b/dist/destroy.d.ts.map new file mode 100644 index 0000000..106314a --- /dev/null +++ b/dist/destroy.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../src/destroy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD,wBAAsB,YAAY,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAYnF"} \ No newline at end of file diff --git a/dist/destroy.js b/dist/destroy.js new file mode 100644 index 0000000..858759e --- /dev/null +++ b/dist/destroy.js @@ -0,0 +1,16 @@ +import { existsSync } from "node:fs"; +import { rm } from "node:fs/promises"; +import { recordAccess } from "./access-log.js"; +import { loadConfig } from "./config.js"; +export async function destroyIndex(cwd = process.cwd()) { + const config = await loadConfig(cwd); + const existed = existsSync(config.storageDir); + await recordAccess(config, { action: "destroy-index" }); + await rm(config.storageDir, { recursive: true, force: true }); + return { + storageDir: config.storageDir, + removed: existed, + note: "Generated index removed. For forensic deletion guarantees, keep .kb/ on an encrypted volume and rotate/destroy the volume key.", + }; +} +//# sourceMappingURL=destroy.js.map \ No newline at end of file diff --git a/dist/destroy.js.map b/dist/destroy.js.map new file mode 100644 index 0000000..2abfaa9 --- /dev/null +++ b/dist/destroy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"destroy.js","sourceRoot":"","sources":["../src/destroy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACpD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAA;IACpC,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IAE7C,MAAM,YAAY,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAA;IACvD,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAE7D,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,gIAAgI;KACvI,CAAA;AACH,CAAC"} \ No newline at end of file diff --git a/dist/embeddings.d.ts.map b/dist/embeddings.d.ts.map index b8edcde..2ed43b8 100644 --- a/dist/embeddings.d.ts.map +++ b/dist/embeddings.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAkBrF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAM/E"} \ No newline at end of file +{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAmBrF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAM/E"} \ No newline at end of file diff --git a/dist/embeddings.js b/dist/embeddings.js index 5c150f3..ad9f116 100644 --- a/dist/embeddings.js +++ b/dist/embeddings.js @@ -1,8 +1,10 @@ import { Ollama } from "ollama"; +import { assertNetworkPolicy } from "./network.js"; export async function embedTexts(texts, config) { if (texts.length === 0) { return []; } + assertNetworkPolicy(config); const client = new Ollama({ host: config.ollamaHost }); const response = await client.embed({ model: config.embedModel, diff --git a/dist/embeddings.js.map b/dist/embeddings.js.map index ceee6d2..2e23048 100644 --- a/dist/embeddings.js.map +++ b/dist/embeddings.js.map @@ -1 +1 @@ -{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAG/B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAe,EAAE,MAAc;IAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,KAAK,EAAE,MAAM,CAAC,UAAU;QACxB,KAAK,EAAE,KAAK;KACb,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,YAAY,KAAK,CAAC,MAAM,yBAAyB,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,GAAG,CACrF,CAAA;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,UAAU,CAAA;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,MAAc;IAC1D,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"} \ No newline at end of file +{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAGlD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAe,EAAE,MAAc;IAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC3B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,KAAK,EAAE,MAAM,CAAC,UAAU;QACxB,KAAK,EAAE,KAAK;KACb,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,YAAY,KAAK,CAAC,MAAM,yBAAyB,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,GAAG,CACrF,CAAA;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,UAAU,CAAA;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,MAAc;IAC1D,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"} \ No newline at end of file diff --git a/dist/files.js b/dist/files.js index 2b7dcb1..bcad32b 100644 --- a/dist/files.js +++ b/dist/files.js @@ -30,7 +30,7 @@ export async function listSourceFiles(config) { onlyFiles: true, dot: false, followSymbolicLinks: false, - ignore: ["**/.git/**", "**/node_modules/**", "**/.kb/storage/**"], + ignore: ["**/.git/**", "**/node_modules/**", "**/.kb/**", "**/.mimir/**"], }); for (const absolutePath of entries) { const extension = path.extname(absolutePath).toLowerCase(); diff --git a/dist/files.js.map b/dist/files.js.map index 3ea4e2e..417c890 100644 --- a/dist/files.js.map +++ b/dist/files.js.map @@ -1 +1 @@ -{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,WAAW,CAAA;AAG1B,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IAC1C,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;CACP,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAA;IAE3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,SAAQ;QACV,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE;YAC/B,GAAG,EAAE,IAAI;YACT,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;YACf,GAAG,EAAE,KAAK;YACV,mBAAmB,EAAE,KAAK;YAC1B,MAAM,EAAE,CAAC,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,CAAC;SAClE,CAAC,CAAA;QAEF,KAAK,MAAM,YAAY,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAA;YAC1D,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzC,SAAQ;YACV,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,CAAA;YACrC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAA;YAC3C,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE;gBACtB,YAAY;gBACZ,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC;gBAC7D,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACxE,SAAS;gBACT,KAAK,EAAE,IAAI,CAAC,IAAI;gBAChB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC5D,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAA;AACzF,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc;IACvC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC7B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAC1D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,SAAQ;QACV,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAA;IAC5F,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file +{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts index 5a7ca13..d9e3472 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,9 +1,12 @@ export { loadConfig } from "./config.js"; +export { destroyIndex } from "./destroy.js"; export { audit, ingest } from "./ingest.js"; export { initProject } from "./init.js"; export { serveMcp } from "./mcp.js"; export { ask, search } from "./query.js"; +export { redactText } from "./redaction.js"; +export { securityAudit } from "./security.js"; export { bundledSkillPath, installSkill } from "./skill.js"; -export type { AskResult, AuditReport, Config, IngestResult, SearchResult, } from "./types.js"; +export type { AskResult, AuditReport, Config, DestroyIndexResult, IngestResult, SearchResult, SecurityAuditReport, } from "./types.js"; export { VERSION } from "./version.js"; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map index 384fdd2..0942db1 100644 --- a/dist/index.d.ts.map +++ b/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC3D,YAAY,EACV,SAAS,EACT,WAAW,EACX,MAAM,EACN,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC3D,YAAY,EACV,SAAS,EACT,WAAW,EACX,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index a32210d..febe14d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,8 +1,11 @@ export { loadConfig } from "./config.js"; +export { destroyIndex } from "./destroy.js"; export { audit, ingest } from "./ingest.js"; export { initProject } from "./init.js"; export { serveMcp } from "./mcp.js"; export { ask, search } from "./query.js"; +export { redactText } from "./redaction.js"; +export { securityAudit } from "./security.js"; export { bundledSkillPath, installSkill } from "./skill.js"; export { VERSION } from "./version.js"; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index dd4e59b..726faba 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAQ3D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAU3D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/dist/ingest.d.ts.map b/dist/ingest.d.ts.map index 45ebf90..a845844 100644 --- a/dist/ingest.d.ts.map +++ b/dist/ingest.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../src/ingest.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAwB,MAAM,YAAY,CAAA;AAIhG,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+C/E;AAED,wBAAsB,KAAK,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkCrE"} \ No newline at end of file +{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../src/ingest.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,YAAY,EAIb,MAAM,YAAY,CAAA;AAInB,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4D/E;AAED,wBAAsB,KAAK,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkCrE"} \ No newline at end of file diff --git a/dist/ingest.js b/dist/ingest.js index 8d38457..670defe 100644 --- a/dist/ingest.js +++ b/dist/ingest.js @@ -1,8 +1,10 @@ +import { recordAccess } from "./access-log.js"; import { chunkDocument } from "./chunking.js"; import { loadConfig } from "./config.js"; import { embedTexts } from "./embeddings.js"; import { listSourceFiles } from "./files.js"; import { parseFile } from "./parsing.js"; +import { redactText, totalRedactions } from "./redaction.js"; import { openRowsTable, writeRows } from "./store.js"; const EMBED_BATCH_SIZE = 32; export async function ingest(options = {}) { @@ -10,11 +12,14 @@ export async function ingest(options = {}) { const files = await listSourceFiles(config); const allChunks = []; const errors = []; + const redactionCounts = []; let skippedFiles = 0; for (const file of files) { try { const parsed = await parseFile(file); - const chunks = chunkDocument(parsed, config.chunkSize, config.chunkOverlap); + const redacted = redactText(parsed.text, config); + redactionCounts.push(...redacted.counts); + const chunks = chunkDocument({ ...parsed, text: redacted.text }, config.chunkSize, config.chunkOverlap); if (chunks.length === 0) { skippedFiles += 1; } @@ -40,10 +45,16 @@ export async function ingest(options = {}) { } } await writeRows(rows, config); + await recordAccess(config, { + action: "ingest", + resultCount: rows.length, + redactions: totalRedactions(redactionCounts), + }); return { indexedFiles: new Set(rows.map((row) => row.relativePath)).size, chunks: rows.length, skippedFiles, + redactions: totalRedactions(redactionCounts), errors, }; } diff --git a/dist/ingest.js.map b/dist/ingest.js.map index 3f02932..36b612a 100644 --- a/dist/ingest.js.map +++ b/dist/ingest.js.map @@ -1 +1 @@ -{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../src/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAGrD,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAE3B,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,UAAyB,EAAE;IACtD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACrE,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAA;IAC3C,MAAM,SAAS,GAAgB,EAAE,CAAA;IACjC,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,IAAI,YAAY,GAAG,CAAC,CAAA;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;YACpC,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;YAC3E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,YAAY,IAAI,CAAC,CAAA;YACnB,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAA;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,IAAI,CAAC,YAAY;gBACvB,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAgB,EAAE,CAAA;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,gBAAgB,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAA;QACtD,MAAM,UAAU,GAAG,MAAM,UAAU,CACjC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAChC,MAAM,CACP,CAAA;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;YAChC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC,CAAA;YAC3F,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAE7B,OAAO;QACL,YAAY,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI;QAC/D,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY;QACZ,MAAM;KACP,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC7C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAA;IAC3C,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;IAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,YAAY,EAAE,EAAE;YAChB,cAAc;YACd,gBAAgB,EAAE,cAAc;YAChC,YAAY,EAAE,EAAE;YAChB,WAAW,EAAE,CAAC;SACf,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAoC,CAAA;IAC7F,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACvE,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;IAC5C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAEzC,OAAO;QACL,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;aAChC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;aACtC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAClD,cAAc;QACd,gBAAgB,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxE,YAAY,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;QAC9E,WAAW,EAAE,IAAI,CAAC,MAAM;KACzB,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../src/ingest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/init.d.ts.map b/dist/init.d.ts.map index f2b4adc..2a9f3a4 100644 --- a/dist/init.d.ts.map +++ b/dist/init.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAkBA,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwCxE"} \ No newline at end of file +{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AA2BA,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwCxE"} \ No newline at end of file diff --git a/dist/init.js b/dist/init.js index 4f59a38..5418e0a 100644 --- a/dist/init.js +++ b/dist/init.js @@ -6,10 +6,19 @@ const DEFAULT_CONFIG = { rawDir: "private", storageDir: ".kb/storage", sourcesFile: ".kb/sources.txt", + accessLogPath: ".kb/access.log", tableName: "chunks", ollamaHost: "http://localhost:11434", + networkPolicy: "local-only", embedModel: "nomic-embed-text", llmModel: "gemma4:latest", + redaction: { + enabled: true, + builtIn: true, + patterns: [], + }, + accessLog: true, + mcpMaxTopK: 10, topK: 5, chunkSize: 1200, chunkOverlap: 150, diff --git a/dist/init.js.map b/dist/init.js.map index aa1d15d..e744441 100644 --- a/dist/init.js.map +++ b/dist/init.js.map @@ -1 +1 @@ -{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,SAAS;IACjB,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,iBAAiB;IAC9B,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,wBAAwB;IACpC,UAAU,EAAE,kBAAkB;IAC9B,QAAQ,EAAE,eAAe;IACzB,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,IAAI;IACf,YAAY,EAAE,GAAG;CAClB,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IAC7C,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACvC,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;IAClD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACnF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;IACnD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,SAAS,CACb,WAAW,EACX,8FAA8F,EAC9F,MAAM,CACP,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IACrD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,CACb,UAAU,EACV,6FAA6F,EAC7F,MAAM,CACP,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,MAAM,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"} \ No newline at end of file +{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,SAAS;IACjB,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,iBAAiB;IAC9B,aAAa,EAAE,gBAAgB;IAC/B,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,wBAAwB;IACpC,aAAa,EAAE,YAAY;IAC3B,UAAU,EAAE,kBAAkB;IAC9B,QAAQ,EAAE,eAAe;IACzB,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,EAAE;KACb;IACD,SAAS,EAAE,IAAI;IACf,UAAU,EAAE,EAAE;IACd,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,IAAI;IACf,YAAY,EAAE,GAAG;CAClB,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IAC7C,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACvC,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;IAClD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACnF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;IACnD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,SAAS,CACb,WAAW,EACX,8FAA8F,EAC9F,MAAM,CACP,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IACrD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,CACb,UAAU,EACV,6FAA6F,EAC7F,MAAM,CACP,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,MAAM,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"} \ No newline at end of file diff --git a/dist/mcp.d.ts.map b/dist/mcp.d.ts.map index afd7bc2..025724f 100644 --- a/dist/mcp.d.ts.map +++ b/dist/mcp.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AASA,wBAAsB,QAAQ,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoEjE"} \ No newline at end of file +{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAUA,wBAAsB,QAAQ,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFjE"} \ No newline at end of file diff --git a/dist/mcp.js b/dist/mcp.js index ed1145d..043c30f 100644 --- a/dist/mcp.js +++ b/dist/mcp.js @@ -4,6 +4,7 @@ import { z } from "zod"; import { loadConfig } from "./config.js"; import { audit } from "./ingest.js"; import { ask, search } from "./query.js"; +import { securityAudit } from "./security.js"; import { countRows } from "./store.js"; import { VERSION } from "./version.js"; export async function serveMcp(cwd = process.cwd()) { @@ -25,6 +26,9 @@ export async function serveMcp(cwd = process.cwd()) { sourcesFile: config.sourcesFile, embedModel: config.embedModel, llmModel: config.llmModel, + networkPolicy: config.networkPolicy, + redactionEnabled: config.redaction.enabled, + mcpMaxTopK: config.mcpMaxTopK, chunksIndexed, }; return textResult(output); @@ -36,7 +40,7 @@ export async function serveMcp(cwd = process.cwd()) { query: z.string().min(1), topK: z.number().int().positive().optional(), }), - }, async ({ query, topK }) => textResult(await search(query, searchOptions(cwd, topK)))); + }, async ({ query, topK }) => textResult(await search(query, await searchOptions(cwd, topK)))); server.registerTool("mimir_ask", { title: "Mimir Ask", description: "Answer a question using local retrieved passages and the configured Ollama model.", @@ -44,12 +48,17 @@ export async function serveMcp(cwd = process.cwd()) { query: z.string().min(1), topK: z.number().int().positive().optional(), }), - }, async ({ query, topK }) => textResult(await ask(query, searchOptions(cwd, topK)))); + }, async ({ query, topK }) => textResult(await ask(query, await searchOptions(cwd, topK)))); server.registerTool("mimir_audit", { title: "Mimir Audit", description: "Compare supported source files on disk with the current vector index.", inputSchema: z.object({}), }, async () => textResult(await audit(cwd))); + server.registerTool("mimir_security_audit", { + title: "Mimir Security Audit", + description: "Show local privacy, network, redaction, MCP, and gitignore posture.", + inputSchema: z.object({}), + }, async () => textResult(await securityAudit(cwd))); await server.connect(new StdioServerTransport()); } function textResult(value) { @@ -62,7 +71,9 @@ function textResult(value) { ], }; } -function searchOptions(cwd, topK) { - return topK === undefined ? { cwd } : { cwd, topK }; +async function searchOptions(cwd, topK) { + const config = await loadConfig(cwd); + const boundedTopK = Math.min(topK ?? config.topK, config.mcpMaxTopK); + return { cwd, topK: boundedTopK }; } //# sourceMappingURL=mcp.js.map \ No newline at end of file diff --git a/dist/mcp.js.map b/dist/mcp.js.map index 26d5e7f..49807dd 100644 --- a/dist/mcp.js.map +++ b/dist/mcp.js.map @@ -1 +1 @@ -{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/network.d.ts b/dist/network.d.ts new file mode 100644 index 0000000..a3f9ad2 --- /dev/null +++ b/dist/network.d.ts @@ -0,0 +1,4 @@ +import type { Config, HostClassification } from "./types.js"; +export declare function assertNetworkPolicy(config: Config): void; +export declare function classifyHost(input: string): HostClassification; +//# sourceMappingURL=network.d.ts.map \ No newline at end of file diff --git a/dist/network.d.ts.map b/dist/network.d.ts.map new file mode 100644 index 0000000..abf4861 --- /dev/null +++ b/dist/network.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE5D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAsBxD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAkB9D"} \ No newline at end of file diff --git a/dist/network.js b/dist/network.js new file mode 100644 index 0000000..d43ffb9 --- /dev/null +++ b/dist/network.js @@ -0,0 +1,59 @@ +import { isIP } from "node:net"; +export function assertNetworkPolicy(config) { + const classification = classifyHost(config.ollamaHost); + if (config.networkPolicy === "allow-any") { + return; + } + if (config.networkPolicy === "local-only" && classification.kind !== "loopback") { + throw new Error(`Refusing to send document text to non-local Ollama host "${config.ollamaHost}". Set networkPolicy to "allow-private" or "allow-any" only if this is intentional.`); + } + if (config.networkPolicy === "allow-private" && + classification.kind !== "loopback" && + classification.kind !== "private") { + throw new Error(`Refusing to send document text to remote Ollama host "${config.ollamaHost}". Set networkPolicy to "allow-any" only if this is intentional.`); + } +} +export function classifyHost(input) { + let url; + try { + url = new URL(input); + } + catch { + return { kind: "invalid", host: input }; + } + const host = url.hostname.replace(/^\[(.*)\]$/, "$1").toLowerCase(); + if (isLoopbackHost(host)) { + return { kind: "loopback", host }; + } + if (isPrivateHost(host)) { + return { kind: "private", host }; + } + return { kind: "remote", host }; +} +function isLoopbackHost(host) { + if (host === "localhost" || host === "::1") { + return true; + } + if (isIP(host) === 4) { + return host.startsWith("127."); + } + return false; +} +function isPrivateHost(host) { + if (host === "host.docker.internal" || host.endsWith(".local")) { + return true; + } + if (isIP(host) === 4) { + const parts = host.split(".").map((part) => Number.parseInt(part, 10)); + const [first = 0, second = 0] = parts; + return (first === 10 || + (first === 172 && second >= 16 && second <= 31) || + (first === 192 && second === 168) || + (first === 169 && second === 254)); + } + if (isIP(host) === 6) { + return host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80"); + } + return false; +} +//# sourceMappingURL=network.js.map \ No newline at end of file diff --git a/dist/network.js.map b/dist/network.js.map new file mode 100644 index 0000000..d0cb015 --- /dev/null +++ b/dist/network.js.map @@ -0,0 +1 @@ +{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/query.d.ts.map b/dist/query.d.ts.map index 95d3e50..d2f9275 100644 --- a/dist/query.d.ts.map +++ b/dist/query.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAUxE,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAoBhG;AAED,wBAAsB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CAuCxF"} \ No newline at end of file +{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAUxE,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA2BhG;AAED,wBAAsB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CA+CxF"} \ No newline at end of file diff --git a/dist/query.js b/dist/query.js index f7f71ee..723ea27 100644 --- a/dist/query.js +++ b/dist/query.js @@ -1,6 +1,8 @@ import { Ollama } from "ollama"; +import { recordAccess } from "./access-log.js"; import { loadConfig } from "./config.js"; import { embedText } from "./embeddings.js"; +import { assertNetworkPolicy } from "./network.js"; import { openRowsTable } from "./store.js"; export async function search(query, options = {}) { const config = await loadConfig(String(options.cwd ?? process.cwd())); @@ -13,13 +15,20 @@ export async function search(query, options = {}) { .vectorSearch(vector) .limit(options.topK ?? config.topK) .toArray()); - return rows.map((row) => ({ + const results = rows.map((row) => ({ source: row.source, relativePath: row.relativePath, chunkIndex: row.chunkIndex, text: row.text, distance: typeof row._distance === "number" ? row._distance : null, })); + await recordAccess(config, { + action: "search", + query, + topK: options.topK ?? config.topK, + resultCount: results.length, + }); + return results; } export async function ask(query, options = {}) { const config = await loadConfig(String(options.cwd ?? process.cwd())); @@ -33,6 +42,7 @@ export async function ask(query, options = {}) { const context = sources .map((source, index) => `[${index + 1}] ${source.relativePath}#${source.chunkIndex}\n${source.text}`) .join("\n\n---\n\n"); + assertNetworkPolicy(config); const client = new Ollama({ host: config.ollamaHost }); const response = await client.chat({ model: config.llmModel, @@ -48,6 +58,12 @@ export async function ask(query, options = {}) { ], stream: false, }); + await recordAccess(config, { + action: "ask", + query, + topK: options.topK ?? config.topK, + resultCount: sources.length, + }); return { answer: response.message.content, sources, diff --git a/dist/query.js.map b/dist/query.js.map index e72bd6d..43be57a 100644 --- a/dist/query.js.map +++ b/dist/query.js.map @@ -1 +1 @@ -{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAW1C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAa,EAAE,UAAyB,EAAE;IACrE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACrE,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK;SACtB,YAAY,CAAC,MAAM,CAAC;SACpB,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;SAClC,OAAO,EAAE,CAAgB,CAAA;IAE5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;KACnE,CAAC,CAAC,CAAA;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,KAAa,EAAE,UAAyB,EAAE;IAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACrE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAE5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,MAAM,EAAE,2EAA2E;YACnF,OAAO;SACR,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,OAAO;SACpB,GAAG,CACF,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAChB,IAAI,KAAK,GAAG,CAAC,KAAK,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,IAAI,EAAE,CAC/E;SACA,IAAI,CAAC,aAAa,CAAC,CAAA;IAEtB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC;QACjC,KAAK,EAAE,MAAM,CAAC,QAAQ;QACtB,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,QAAQ;gBACd,OAAO,EACL,8HAA8H;aACjI;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,cAAc,KAAK,iBAAiB,OAAO,EAAE;aACvD;SACF;QACD,MAAM,EAAE,KAAK;KACd,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;QAChC,OAAO;KACR,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/redaction.d.ts b/dist/redaction.d.ts new file mode 100644 index 0000000..f2e09d5 --- /dev/null +++ b/dist/redaction.d.ts @@ -0,0 +1,7 @@ +import type { Config, RedactionCount } from "./types.js"; +export declare function redactText(input: string, config: Config): { + text: string; + counts: RedactionCount[]; +}; +export declare function totalRedactions(counts: RedactionCount[]): number; +//# sourceMappingURL=redaction.d.ts.map \ No newline at end of file diff --git a/dist/redaction.d.ts.map b/dist/redaction.d.ts.map new file mode 100644 index 0000000..f839d9e --- /dev/null +++ b/dist/redaction.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AAoC1E,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAyB5C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAEhE"} \ No newline at end of file diff --git a/dist/redaction.js b/dist/redaction.js new file mode 100644 index 0000000..90fd683 --- /dev/null +++ b/dist/redaction.js @@ -0,0 +1,63 @@ +const BUILT_IN_PATTERNS = [ + { + name: "private_key", + pattern: "-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----", + flags: "g", + }, + { + name: "jwt", + pattern: "\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b", + flags: "g", + }, + { + name: "api_token", + pattern: "\\b(?:sk|pk|ghp|gho|github_pat|npm)_[A-Za-z0-9_=-]{20,}\\b|\\b[A-Za-z0-9_-]{32,}\\.[A-Za-z0-9_-]{16,}\\.[A-Za-z0-9_-]{16,}\\b", + flags: "g", + }, + { + name: "email", + pattern: "\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b", + flags: "gi", + }, + { + name: "iban", + pattern: "\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b", + flags: "g", + }, + { + name: "credit_card", + pattern: "\\b(?:\\d[ -]*?){13,19}\\b", + flags: "g", + }, +]; +export function redactText(input, config) { + if (!config.redaction.enabled) { + return { text: input, counts: [] }; + } + let text = input; + const counts = []; + const patterns = [ + ...(config.redaction.builtIn ? BUILT_IN_PATTERNS : []), + ...config.redaction.patterns, + ]; + for (const pattern of patterns) { + const regexp = compilePattern(pattern); + let count = 0; + text = text.replace(regexp, () => { + count += 1; + return pattern.replacement ?? `[REDACTED_${pattern.name.toUpperCase()}]`; + }); + if (count > 0) { + counts.push({ name: pattern.name, count }); + } + } + return { text, counts }; +} +export function totalRedactions(counts) { + return counts.reduce((total, entry) => total + entry.count, 0); +} +function compilePattern(pattern) { + const flags = pattern.flags?.includes("g") ? pattern.flags : `${pattern.flags ?? ""}g`; + return new RegExp(pattern.pattern, flags); +} +//# sourceMappingURL=redaction.js.map \ No newline at end of file diff --git a/dist/redaction.js.map b/dist/redaction.js.map new file mode 100644 index 0000000..ba06185 --- /dev/null +++ b/dist/redaction.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAuB;IAC5C;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,8EAA8E;QACvF,KAAK,EAAE,GAAG;KACX;IACD;QACE,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,2DAA2D;QACpE,KAAK,EAAE,GAAG;KACX;IACD;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EACL,+HAA+H;QACjI,KAAK,EAAE,GAAG;KACX;IACD;QACE,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,8CAA8C;QACvD,KAAK,EAAE,IAAI;KACZ;IACD;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,qCAAqC;QAC9C,KAAK,EAAE,GAAG;KACX;IACD;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,4BAA4B;QACrC,KAAK,EAAE,GAAG;KACX;CACF,CAAA;AAED,MAAM,UAAU,UAAU,CACxB,KAAa,EACb,MAAc;IAEd,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IACpC,CAAC;IAED,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,MAAM,MAAM,GAAqB,EAAE,CAAA;IACnC,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ;KAC7B,CAAA;IAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;YAC/B,KAAK,IAAI,CAAC,CAAA;YACV,OAAO,OAAO,CAAC,WAAW,IAAI,aAAa,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAA;QAC1E,CAAC,CAAC,CAAA;QACF,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAwB;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,SAAS,cAAc,CAAC,OAAyB;IAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,GAAG,CAAA;IACtF,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC"} \ No newline at end of file diff --git a/dist/security.d.ts b/dist/security.d.ts new file mode 100644 index 0000000..01ad6b4 --- /dev/null +++ b/dist/security.d.ts @@ -0,0 +1,3 @@ +import type { SecurityAuditReport } from "./types.js"; +export declare function securityAudit(cwd?: string): Promise; +//# sourceMappingURL=security.d.ts.map \ No newline at end of file diff --git a/dist/security.d.ts.map b/dist/security.d.ts.map new file mode 100644 index 0000000..602b96e --- /dev/null +++ b/dist/security.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD,wBAAsB,aAAa,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqErF"} \ No newline at end of file diff --git a/dist/security.js b/dist/security.js new file mode 100644 index 0000000..a54a20f --- /dev/null +++ b/dist/security.js @@ -0,0 +1,86 @@ +import { existsSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { loadConfig } from "./config.js"; +import { classifyHost } from "./network.js"; +export async function securityAudit(cwd = process.cwd()) { + const config = await loadConfig(cwd); + const gitignore = await readGitignore(config.projectRoot); + const network = classifyHost(config.ollamaHost); + const warnings = []; + const kbIgnored = hasGitignoreEntry(gitignore, ".kb/"); + const mimirIgnored = hasGitignoreEntry(gitignore, ".mimir/"); + const privateIgnored = hasGitignoreEntry(gitignore, "private/**"); + if (config.networkPolicy === "allow-any") { + warnings.push("networkPolicy is allow-any; document text can be sent to a remote Ollama host."); + } + if (config.networkPolicy === "local-only" && network.kind !== "loopback") { + warnings.push("networkPolicy is local-only but ollamaHost is not loopback."); + } + if (!config.redaction.enabled) { + warnings.push("Redaction is disabled; secrets and identifiers may be embedded in the index."); + } + if (!kbIgnored) { + warnings.push(".kb/ is not ignored by Git."); + } + if (!mimirIgnored) { + warnings.push(".mimir/ is not ignored by Git."); + } + if (!privateIgnored) { + warnings.push("private/** is not ignored by Git."); + } + return { + projectRoot: config.projectRoot, + zeroTelemetry: true, + network: { + policy: config.networkPolicy, + ollamaHost: config.ollamaHost, + host: network.host, + classification: network.kind, + }, + redaction: { + enabled: config.redaction.enabled, + builtIn: config.redaction.builtIn, + customPatterns: config.redaction.patterns.map((pattern) => pattern.name), + }, + accessLog: { + enabled: config.accessLog, + path: config.accessLogPath, + storesRawQueries: false, + }, + storage: { + path: config.storageDir, + gitIgnored: kbIgnored, + encryptedAtRest: "external-required", + }, + mcp: { + maxTopK: config.mcpMaxTopK, + destructiveToolsExposed: false, + }, + gitignore: { + kbIgnored, + mimirIgnored, + privateIgnored, + }, + recommendations: [ + "Run Mimir inside an encrypted disk, VM, or container volume for at-rest encryption.", + "Use npm provenance, release checksums, and the generated SBOM for release verification.", + "Use one repository checkout per trust boundary; Mimir does not implement multi-user RBAC.", + ], + warnings, + }; +} +async function readGitignore(projectRoot) { + const gitignorePath = path.join(projectRoot, ".gitignore"); + if (!existsSync(gitignorePath)) { + return new Set(); + } + return new Set((await readFile(gitignorePath, "utf8")) + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean)); +} +function hasGitignoreEntry(lines, entry) { + return lines.has(entry); +} +//# sourceMappingURL=security.js.map \ No newline at end of file diff --git a/dist/security.js.map b/dist/security.js.map new file mode 100644 index 0000000..b75784e --- /dev/null +++ b/dist/security.js.map @@ -0,0 +1 @@ +{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts index 38a30f4..616f552 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -4,14 +4,39 @@ export interface Config { rawDir: string; storageDir: string; sourcesFile: string; + accessLogPath: string; tableName: string; ollamaHost: string; + networkPolicy: NetworkPolicy; embedModel: string; llmModel: string; + redaction: RedactionConfig; + accessLog: boolean; + mcpMaxTopK: number; topK: number; chunkSize: number; chunkOverlap: number; } +export type NetworkPolicy = "local-only" | "allow-private" | "allow-any"; +export interface RedactionConfig { + enabled: boolean; + builtIn: boolean; + patterns: RedactionPattern[]; +} +export interface RedactionPattern { + name: string; + pattern: string; + flags?: string | undefined; + replacement?: string | undefined; +} +export interface RedactionCount { + name: string; + count: number; +} +export interface HostClassification { + kind: "loopback" | "private" | "remote" | "invalid"; + host: string; +} export interface SourceFile { absolutePath: string; relativePath: string; @@ -46,6 +71,7 @@ export interface IngestResult { indexedFiles: number; chunks: number; skippedFiles: number; + redactions: number; errors: Array<{ path: string; message: string; @@ -76,4 +102,45 @@ export interface AuditReport { staleInIndex: string[]; totalChunks: number; } +export interface DestroyIndexResult { + storageDir: string; + removed: boolean; + note: string; +} +export interface SecurityAuditReport { + projectRoot: string; + zeroTelemetry: true; + network: { + policy: NetworkPolicy; + ollamaHost: string; + host: string; + classification: HostClassification["kind"]; + }; + redaction: { + enabled: boolean; + builtIn: boolean; + customPatterns: string[]; + }; + accessLog: { + enabled: boolean; + path: string; + storesRawQueries: false; + }; + storage: { + path: string; + gitIgnored: boolean; + encryptedAtRest: "external-required"; + }; + mcp: { + maxTopK: number; + destructiveToolsExposed: false; + }; + gitignore: { + kbIgnored: boolean; + mimirIgnored: boolean; + privateIgnored: boolean; + }; + recommendations: string[]; + warnings: string[]; +} //# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/types.d.ts.map b/dist/types.d.ts.map index 5773a7a..e04e208 100644 --- a/dist/types.d.ts.map +++ b/dist/types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;CACpB"} \ No newline at end of file +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/version.d.ts b/dist/version.d.ts index d6d58cb..f7aae6a 100644 --- a/dist/version.d.ts +++ b/dist/version.d.ts @@ -1,2 +1,2 @@ -export declare const VERSION = "0.2.1"; +export declare const VERSION = "0.3.0"; //# sourceMappingURL=version.d.ts.map \ No newline at end of file diff --git a/dist/version.js b/dist/version.js index da208e6..b7312b3 100644 --- a/dist/version.js +++ b/dist/version.js @@ -1,2 +1,2 @@ -export const VERSION = "0.2.1"; +export const VERSION = "0.3.0"; //# sourceMappingURL=version.js.map \ No newline at end of file diff --git a/package.json b/package.json index 7d602c8..58fbb62 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jcode.labs/mimir", - "version": "0.2.1", + "version": "0.3.0", "description": "Mimir: open-source local-first memory and retrieval for private project knowledge.", "type": "module", "license": "MIT", @@ -42,7 +42,10 @@ "files": [ "dist", "skills", - "README.md" + "README.md", + "SECURITY-HARDENING.md", + "CHANGELOG.md", + "SECURITY.md" ], "publishConfig": { "access": "public" @@ -55,9 +58,10 @@ "lint": "biome ci .", "lint:fix": "biome check --write .", "package:check": "publint", + "release:artifacts": "node scripts/release-artifacts.mjs", "smoke": "node scripts/smoke.mjs", "test": "vitest run", - "validate": "pnpm lint && pnpm check && pnpm test && pnpm build && pnpm smoke && pnpm package:check" + "validate": "pnpm lint && pnpm check && pnpm test && pnpm build && pnpm smoke && pnpm package:check && pnpm release:artifacts" }, "dependencies": { "@lancedb/lancedb": "^0.30.0", diff --git a/scripts/release-artifacts.mjs b/scripts/release-artifacts.mjs new file mode 100644 index 0000000..737e853 --- /dev/null +++ b/scripts/release-artifacts.mjs @@ -0,0 +1,138 @@ +import { spawnSync } from "node:child_process" +import { createHash, randomUUID } from "node:crypto" +import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises" +import path from "node:path" +import { fileURLToPath } from "node:url" +import YAML from "yaml" + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..") +const artifactsDir = path.join(repoRoot, "release-artifacts") +const packageJson = JSON.parse(await readFile(path.join(repoRoot, "package.json"), "utf8")) + +await rm(artifactsDir, { recursive: true, force: true }) +await mkdir(artifactsDir, { recursive: true }) + +const pack = run("npm", ["pack", "--json", "--pack-destination", artifactsDir]) +const packed = JSON.parse(pack.stdout) +const tarball = packed[0]?.filename +if (!tarball) { + throw new Error(`npm pack did not return a tarball filename: ${pack.stdout}`) +} + +const sbomFile = `${packageJson.name.replace("/", "-").replace("@", "")}-${packageJson.version}.sbom.cdx.json` +await writeFile( + path.join(artifactsDir, sbomFile), + `${JSON.stringify(await buildCycloneDxSbom(), null, 2)}\n`, + "utf8", +) + +const checksums = await sha256Files(artifactsDir) +await writeFile( + path.join(artifactsDir, "SHA256SUMS"), + `${checksums.map((entry) => `${entry.sha256} ${entry.file}`).join("\n")}\n`, + "utf8", +) + +await writeFile( + path.join(artifactsDir, "release-manifest.json"), + `${JSON.stringify( + { + package: packageJson.name, + version: packageJson.version, + tarball, + sbom: sbomFile, + checksums: "SHA256SUMS", + provenance: "npm publish --provenance is enforced by the protected GitHub Actions workflow.", + }, + null, + 2, + )}\n`, + "utf8", +) + +console.log(`Release artifacts written to ${path.relative(repoRoot, artifactsDir)}`) + +function run(command, args) { + const result = spawnSync(command, args, { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }) + if (result.status !== 0) { + throw new Error( + `Command failed: ${command} ${args.join(" ")}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`, + ) + } + return result +} + +async function sha256Files(directory) { + const files = (await readdir(directory)).filter((file) => file !== "SHA256SUMS").sort() + const checksums = [] + for (const file of files) { + const buffer = await readFile(path.join(directory, file)) + checksums.push({ + file, + sha256: createHash("sha256").update(buffer).digest("hex"), + }) + } + return checksums +} + +async function buildCycloneDxSbom() { + const lockfile = YAML.parse(await readFile(path.join(repoRoot, "pnpm-lock.yaml"), "utf8")) + const components = Object.keys(lockfile.packages ?? {}) + .map(parsePnpmPackageKey) + .filter((component) => component !== null) + .sort((a, b) => `${a.name}@${a.version}`.localeCompare(`${b.name}@${b.version}`)) + + return { + $schema: "http://cyclonedx.org/schema/bom-1.5.schema.json", + bomFormat: "CycloneDX", + specVersion: "1.5", + serialNumber: `urn:uuid:${randomUUID()}`, + version: 1, + metadata: { + timestamp: new Date().toISOString(), + tools: [ + { vendor: "JCode Labs", name: "Mimir release artifacts", version: packageJson.version }, + ], + component: { + type: "library", + name: packageJson.name, + version: packageJson.version, + licenses: [{ license: { id: packageJson.license } }], + purl: `pkg:npm/${packageJson.name}@${packageJson.version}`, + }, + }, + components, + } +} + +function parsePnpmPackageKey(key) { + const withoutPeers = key.replace(/\(.+\)$/, "") + const packageRef = withoutPeers.includes("@npm:") + ? withoutPeers.slice(withoutPeers.indexOf("@npm:") + 5) + : withoutPeers + const versionSeparator = packageRef.startsWith("@") + ? packageRef.indexOf("@", 1) + : packageRef.lastIndexOf("@") + + if (versionSeparator <= 0) { + return null + } + + const name = packageRef.slice(0, versionSeparator) + const version = packageRef.slice(versionSeparator + 1) + if (!name || !version) { + return null + } + + return { + type: "library", + name, + version, + scope: "required", + purl: `pkg:npm/${name}@${version}`, + } +} diff --git a/scripts/smoke.mjs b/scripts/smoke.mjs index be64fc1..215cb14 100644 --- a/scripts/smoke.mjs +++ b/scripts/smoke.mjs @@ -17,6 +17,7 @@ try { const ingest = await runKb(["ingest"], tempRoot) assertIncludes(ingest.stdout, "errors=0", "ingest should complete without parse errors") + assertIncludes(ingest.stdout, "redactions=", "ingest should report DLP redactions") const search = await runKb(["search", "French tax residency", "--top-k", "1"], tempRoot) assertIncludes(search.stdout, "tax.md", "search should retrieve the tax document") @@ -36,6 +37,18 @@ try { assertIncludes(audit.stdout, "missingFromIndex=0", "audit should find no missing files") assertIncludes(audit.stdout, "staleInIndex=0", "audit should find no stale files") + const security = await runKb(["security-audit", "--json"], tempRoot) + assertIncludes( + security.stdout, + '"zeroTelemetry": true', + "security audit should report no telemetry", + ) + assertIncludes( + security.stdout, + '"classification": "loopback"', + "security audit should classify local Ollama", + ) + await runKb(["install-skill"], tempRoot) const skill = await readFile(path.join(tempRoot, ".mimir", "skills", "mimir", "SKILL.md"), "utf8") assertIncludes(skill, "name: mimir", "install-skill should copy the bundled skill") @@ -44,6 +57,9 @@ try { assertIncludes(gitignore, ".mimir/", "install-skill should ignore generated agent kit files") await smokeMcp(tempRoot) + + const destroy = await runKb(["destroy-index", "--yes"], tempRoot) + assertIncludes(destroy.stdout, "removed=true", "destroy-index should remove generated storage") console.log("Smoke test passed.") } finally { await fakeOllama.close() @@ -78,6 +94,7 @@ async function writeFixtureDocuments(cwd) { "", "French tax residency risk is tied to French clients, a French company, and French invoicing.", "The document should be retrieved when the user asks about French tax residency.", + "Sensitive maintainer email: maintainer@example.com.", ].join("\n"), "utf8", ) diff --git a/skills/mimir/SKILL.md b/skills/mimir/SKILL.md index b1b09d1..6868fcc 100644 --- a/skills/mimir/SKILL.md +++ b/skills/mimir/SKILL.md @@ -20,12 +20,15 @@ private/ # raw documents to ingest .kb/config.json # local Mimir config .kb/sources.txt # optional extra source paths .kb/storage/ # generated local index +.kb/access.log # metadata-only access log ``` ## Data Safety - Do not commit raw documents, secrets, tax IDs, scans, bank documents, tokens, or generated vector stores. -- Keep `private/**` and `.kb/storage/**` ignored by Git. +- Keep `private/**`, `.kb/`, and `.mimir/` ignored by Git. +- Treat `kb search`, `kb ask`, and MCP results as sensitive because they can contain private + source passages even when redaction is enabled. - Prefer summaries and citations over dumping long private passages into the chat. - If the user asks for a high-stakes answer, identify which facts came from Mimir and which still require professional or official verification. @@ -35,6 +38,7 @@ From the repository root: ```bash pnpm exec kb status +pnpm exec kb security-audit ``` If Mimir is not installed: @@ -58,10 +62,12 @@ After documents are added or changed: ```bash pnpm exec kb ingest pnpm exec kb audit +pnpm exec kb security-audit pnpm exec kb status ``` -The audit must show no missing or stale supported files before relying on the index. +The audit must show no missing or stale supported files before relying on the index. The security +audit should not show warnings before relying on Mimir for sensitive work. ## Query Workflow @@ -101,9 +107,13 @@ Available MCP tools: - `mimir_search`: retrieve source passages. - `mimir_ask`: synthesize an answer with local citations. - `mimir_audit`: compare source files with the current index. +- `mimir_security_audit`: inspect local privacy, network, redaction, MCP, and gitignore posture. Prefer MCP tools over shell commands when the agent runtime provides them. Use shell commands when MCP is unavailable. +MCP is read-focused and intentionally does not expose index deletion. Use `pnpm exec kb +destroy-index --yes` from the shell when the user explicitly wants to remove the generated index. + ## Installing This Skill Into A Repository Run: diff --git a/src/access-log.ts b/src/access-log.ts new file mode 100644 index 0000000..6f296a0 --- /dev/null +++ b/src/access-log.ts @@ -0,0 +1,40 @@ +import { createHash } from "node:crypto" +import { appendFile, mkdir } from "node:fs/promises" +import path from "node:path" +import type { Config } from "./types.js" + +export interface AccessLogEvent { + action: "ingest" | "search" | "ask" | "destroy-index" + query?: string + topK?: number + resultCount?: number + redactions?: number +} + +export async function recordAccess(config: Config, event: AccessLogEvent): Promise { + if (!config.accessLog) { + return + } + + try { + await mkdir(path.dirname(config.accessLogPath), { recursive: true }) + await appendFile(config.accessLogPath, `${JSON.stringify(toLogLine(event))}\n`, "utf8") + } catch { + // Access logging is best-effort so read-only workspaces do not block local use. + } +} + +function toLogLine(event: AccessLogEvent): Record { + return { + timestamp: new Date().toISOString(), + action: event.action, + queryHash: event.query ? hashQuery(event.query) : undefined, + topK: event.topK, + resultCount: event.resultCount, + redactions: event.redactions, + } +} + +function hashQuery(query: string): string { + return createHash("sha256").update(query).digest("hex") +} diff --git a/src/cli.ts b/src/cli.ts index 1864c83..23720a7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -2,10 +2,12 @@ import { Command } from "commander" import pc from "picocolors" import { loadConfig } from "./config.js" +import { destroyIndex } from "./destroy.js" import { audit, ingest } from "./ingest.js" import { initProject } from "./init.js" import { serveMcp } from "./mcp.js" import { ask, search } from "./query.js" +import { securityAudit } from "./security.js" import { bundledSkillPath, installSkill } from "./skill.js" import { countRows } from "./store.js" import { VERSION } from "./version.js" @@ -40,7 +42,7 @@ program const result = await ingest({ cwd: process.cwd(), rebuild: true }) console.log( pc.green( - `Done. indexedFiles=${result.indexedFiles} chunks=${result.chunks} skippedFiles=${result.skippedFiles} errors=${result.errors.length}`, + `Done. indexedFiles=${result.indexedFiles} chunks=${result.chunks} skippedFiles=${result.skippedFiles} redactions=${result.redactions} errors=${result.errors.length}`, ), ) for (const error of result.errors) { @@ -122,11 +124,63 @@ program console.log(`rawDir=${config.rawDir}`) console.log(`storageDir=${config.storageDir}`) console.log(`sourcesFile=${config.sourcesFile}`) + console.log(`accessLogPath=${config.accessLogPath}`) + console.log(`networkPolicy=${config.networkPolicy}`) console.log(`embedModel=${config.embedModel}`) console.log(`llmModel=${config.llmModel}`) + console.log(`redactionEnabled=${config.redaction.enabled}`) + console.log(`accessLog=${config.accessLog}`) + console.log(`mcpMaxTopK=${config.mcpMaxTopK}`) console.log(`chunksIndexed=${rows}`) }) +program + .command("security-audit") + .description("Show local privacy, network, redaction, MCP, and gitignore posture.") + .option("--json", "Print machine-readable JSON.") + .option("--strict", "Exit with code 1 when warnings are present.") + .action(async (options: { json?: boolean; strict?: boolean }) => { + const report = await securityAudit(process.cwd()) + if (options.json) { + console.log(JSON.stringify(report, null, 2)) + } else { + console.log(`zeroTelemetry=${report.zeroTelemetry}`) + console.log(`networkPolicy=${report.network.policy}`) + console.log(`ollamaHost=${report.network.ollamaHost}`) + console.log(`ollamaHostClassification=${report.network.classification}`) + console.log(`redactionEnabled=${report.redaction.enabled}`) + console.log(`redactionBuiltIn=${report.redaction.builtIn}`) + console.log(`accessLog=${report.accessLog.enabled}`) + console.log(`accessLogStoresRawQueries=${report.accessLog.storesRawQueries}`) + console.log(`storageGitIgnored=${report.storage.gitIgnored}`) + console.log(`mcpMaxTopK=${report.mcp.maxTopK}`) + console.log(`mcpDestructiveToolsExposed=${report.mcp.destructiveToolsExposed}`) + for (const warning of report.warnings) { + console.log(pc.yellow(`warning: ${warning}`)) + } + } + if (options.strict && report.warnings.length > 0) { + process.exitCode = 1 + } + }) + +program + .command("destroy-index") + .description("Remove the generated local vector index from .kb/storage.") + .option("--yes", "Confirm deletion without an interactive prompt.") + .action(async (options: { yes?: boolean }) => { + if (!options.yes) { + console.error(pc.red("Refusing to delete the index without --yes.")) + process.exitCode = 1 + return + } + + const result = await destroyIndex(process.cwd()) + console.log(`storageDir=${result.storageDir}`) + console.log(`removed=${result.removed}`) + console.log(result.note) + }) + program .command("serve-mcp") .description( diff --git a/src/config.test.ts b/src/config.test.ts index 4095bc6..4ccd2dc 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -31,5 +31,10 @@ describe("loadConfig", () => { const config = await loadConfig(nested) expect(config.rawDir).toBe(path.join(root, "docs")) expect(config.storageDir).toBe(path.join(root, ".kb/index")) + expect(config.accessLogPath).toBe(path.join(root, ".kb/access.log")) + expect(config.networkPolicy).toBe("local-only") + expect(config.redaction.enabled).toBe(true) + expect(config.accessLog).toBe(true) + expect(config.mcpMaxTopK).toBe(10) }) }) diff --git a/src/config.ts b/src/config.ts index 7c76bdc..0af5f4d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,10 +8,30 @@ const rawConfigSchema = z.object({ rawDir: z.string().default("private"), storageDir: z.string().default(".kb/storage"), sourcesFile: z.string().default(".kb/sources.txt"), + accessLogPath: z.string().default(".kb/access.log"), tableName: z.string().default("chunks"), ollamaHost: z.string().default("http://localhost:11434"), + networkPolicy: z.enum(["local-only", "allow-private", "allow-any"]).default("local-only"), embedModel: z.string().default("nomic-embed-text"), llmModel: z.string().default("gemma4:latest"), + redaction: z + .object({ + enabled: z.boolean().default(true), + builtIn: z.boolean().default(true), + patterns: z + .array( + z.object({ + name: z.string().min(1), + pattern: z.string().min(1), + flags: z.string().optional(), + replacement: z.string().optional(), + }), + ) + .default([]), + }) + .default({ enabled: true, builtIn: true, patterns: [] }), + accessLog: z.boolean().default(true), + mcpMaxTopK: z.number().int().positive().default(10), topK: z.number().int().positive().default(5), chunkSize: z.number().int().positive().default(1200), chunkOverlap: z.number().int().nonnegative().default(150), @@ -56,10 +76,15 @@ export async function loadConfig(start = process.cwd()): Promise { rawDir: resolveFromRoot(projectRoot, withEnv.rawDir), storageDir: resolveFromRoot(projectRoot, withEnv.storageDir), sourcesFile: resolveFromRoot(projectRoot, withEnv.sourcesFile), + accessLogPath: resolveFromRoot(projectRoot, withEnv.accessLogPath), tableName: withEnv.tableName, ollamaHost: withEnv.ollamaHost, + networkPolicy: withEnv.networkPolicy, embedModel: withEnv.embedModel, llmModel: withEnv.llmModel, + redaction: withEnv.redaction, + accessLog: withEnv.accessLog, + mcpMaxTopK: withEnv.mcpMaxTopK, topK: withEnv.topK, chunkSize: withEnv.chunkSize, chunkOverlap: withEnv.chunkOverlap, @@ -76,15 +101,46 @@ function applyEnv(config: RawConfig): RawConfig { rawDir: process.env.KB_RAW_DIR ?? config.rawDir, storageDir: process.env.KB_STORAGE_DIR ?? config.storageDir, sourcesFile: process.env.KB_SOURCES_FILE ?? config.sourcesFile, + accessLogPath: process.env.KB_ACCESS_LOG_PATH ?? config.accessLogPath, ollamaHost: process.env.KB_OLLAMA_HOST ?? config.ollamaHost, + networkPolicy: readNetworkPolicyEnv("KB_NETWORK_POLICY", config.networkPolicy), embedModel: process.env.KB_EMBED_MODEL ?? config.embedModel, llmModel: process.env.KB_LLM_MODEL ?? config.llmModel, + redaction: { + ...config.redaction, + enabled: readBooleanEnv("KB_REDACTION_ENABLED", config.redaction.enabled), + builtIn: readBooleanEnv("KB_REDACTION_BUILT_IN", config.redaction.builtIn), + }, + accessLog: readBooleanEnv("KB_ACCESS_LOG", config.accessLog), + mcpMaxTopK: readPositiveIntEnv("KB_MCP_MAX_TOP_K", config.mcpMaxTopK), topK: readPositiveIntEnv("KB_TOP_K", config.topK), chunkSize: readPositiveIntEnv("KB_CHUNK_SIZE", config.chunkSize), chunkOverlap: readNonNegativeIntEnv("KB_CHUNK_OVERLAP", config.chunkOverlap), } } +function readNetworkPolicyEnv( + name: string, + fallback: RawConfig["networkPolicy"], +): RawConfig["networkPolicy"] { + const raw = process.env[name] + if (raw === "local-only" || raw === "allow-private" || raw === "allow-any") { + return raw + } + return fallback +} + +function readBooleanEnv(name: string, fallback: boolean): boolean { + const raw = process.env[name]?.toLowerCase() + if (raw === "1" || raw === "true" || raw === "yes") { + return true + } + if (raw === "0" || raw === "false" || raw === "no") { + return false + } + return fallback +} + function readPositiveIntEnv(name: string, fallback: number): number { const raw = process.env[name] if (!raw) { diff --git a/src/destroy.ts b/src/destroy.ts new file mode 100644 index 0000000..66f94c1 --- /dev/null +++ b/src/destroy.ts @@ -0,0 +1,19 @@ +import { existsSync } from "node:fs" +import { rm } from "node:fs/promises" +import { recordAccess } from "./access-log.js" +import { loadConfig } from "./config.js" +import type { DestroyIndexResult } from "./types.js" + +export async function destroyIndex(cwd = process.cwd()): Promise { + const config = await loadConfig(cwd) + const existed = existsSync(config.storageDir) + + await recordAccess(config, { action: "destroy-index" }) + await rm(config.storageDir, { recursive: true, force: true }) + + return { + storageDir: config.storageDir, + removed: existed, + note: "Generated index removed. For forensic deletion guarantees, keep .kb/ on an encrypted volume and rotate/destroy the volume key.", + } +} diff --git a/src/embeddings.ts b/src/embeddings.ts index 99c83be..1b631d3 100644 --- a/src/embeddings.ts +++ b/src/embeddings.ts @@ -1,4 +1,5 @@ import { Ollama } from "ollama" +import { assertNetworkPolicy } from "./network.js" import type { Config } from "./types.js" export async function embedTexts(texts: string[], config: Config): Promise { @@ -6,6 +7,7 @@ export async function embedTexts(texts: string[], config: Config): Promise { onlyFiles: true, dot: false, followSymbolicLinks: false, - ignore: ["**/.git/**", "**/node_modules/**", "**/.kb/storage/**"], + ignore: ["**/.git/**", "**/node_modules/**", "**/.kb/**", "**/.mimir/**"], }) for (const absolutePath of entries) { diff --git a/src/index.ts b/src/index.ts index c3454d7..0041801 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,19 @@ export { loadConfig } from "./config.js" +export { destroyIndex } from "./destroy.js" export { audit, ingest } from "./ingest.js" export { initProject } from "./init.js" export { serveMcp } from "./mcp.js" export { ask, search } from "./query.js" +export { redactText } from "./redaction.js" +export { securityAudit } from "./security.js" export { bundledSkillPath, installSkill } from "./skill.js" export type { AskResult, AuditReport, Config, + DestroyIndexResult, IngestResult, SearchResult, + SecurityAuditReport, } from "./types.js" export { VERSION } from "./version.js" diff --git a/src/ingest.ts b/src/ingest.ts index 8694d46..e936a4b 100644 --- a/src/ingest.ts +++ b/src/ingest.ts @@ -1,10 +1,19 @@ +import { recordAccess } from "./access-log.js" import { chunkDocument } from "./chunking.js" import { loadConfig } from "./config.js" import { embedTexts } from "./embeddings.js" import { listSourceFiles } from "./files.js" import { parseFile } from "./parsing.js" +import { redactText, totalRedactions } from "./redaction.js" import { openRowsTable, writeRows } from "./store.js" -import type { AuditReport, IngestOptions, IngestResult, TextChunk, VectorRow } from "./types.js" +import type { + AuditReport, + IngestOptions, + IngestResult, + RedactionCount, + TextChunk, + VectorRow, +} from "./types.js" const EMBED_BATCH_SIZE = 32 @@ -13,12 +22,19 @@ export async function ingest(options: IngestOptions = {}): Promise const files = await listSourceFiles(config) const allChunks: TextChunk[] = [] const errors: IngestResult["errors"] = [] + const redactionCounts: RedactionCount[] = [] let skippedFiles = 0 for (const file of files) { try { const parsed = await parseFile(file) - const chunks = chunkDocument(parsed, config.chunkSize, config.chunkOverlap) + const redacted = redactText(parsed.text, config) + redactionCounts.push(...redacted.counts) + const chunks = chunkDocument( + { ...parsed, text: redacted.text }, + config.chunkSize, + config.chunkOverlap, + ) if (chunks.length === 0) { skippedFiles += 1 } @@ -48,11 +64,17 @@ export async function ingest(options: IngestOptions = {}): Promise } await writeRows(rows, config) + await recordAccess(config, { + action: "ingest", + resultCount: rows.length, + redactions: totalRedactions(redactionCounts), + }) return { indexedFiles: new Set(rows.map((row) => row.relativePath)).size, chunks: rows.length, skippedFiles, + redactions: totalRedactions(redactionCounts), errors, } } diff --git a/src/init.ts b/src/init.ts index 159e5da..7d76c81 100644 --- a/src/init.ts +++ b/src/init.ts @@ -7,10 +7,19 @@ const DEFAULT_CONFIG = { rawDir: "private", storageDir: ".kb/storage", sourcesFile: ".kb/sources.txt", + accessLogPath: ".kb/access.log", tableName: "chunks", ollamaHost: "http://localhost:11434", + networkPolicy: "local-only", embedModel: "nomic-embed-text", llmModel: "gemma4:latest", + redaction: { + enabled: true, + builtIn: true, + patterns: [], + }, + accessLog: true, + mcpMaxTopK: 10, topK: 5, chunkSize: 1200, chunkOverlap: 150, diff --git a/src/mcp.ts b/src/mcp.ts index 83fa5e5..3bf67b8 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -4,6 +4,7 @@ import { z } from "zod" import { loadConfig } from "./config.js" import { audit } from "./ingest.js" import { ask, search } from "./query.js" +import { securityAudit } from "./security.js" import { countRows } from "./store.js" import { VERSION } from "./version.js" @@ -30,6 +31,9 @@ export async function serveMcp(cwd = process.cwd()): Promise { sourcesFile: config.sourcesFile, embedModel: config.embedModel, llmModel: config.llmModel, + networkPolicy: config.networkPolicy, + redactionEnabled: config.redaction.enabled, + mcpMaxTopK: config.mcpMaxTopK, chunksIndexed, } @@ -47,7 +51,7 @@ export async function serveMcp(cwd = process.cwd()): Promise { topK: z.number().int().positive().optional(), }), }, - async ({ query, topK }) => textResult(await search(query, searchOptions(cwd, topK))), + async ({ query, topK }) => textResult(await search(query, await searchOptions(cwd, topK))), ) server.registerTool( @@ -61,7 +65,7 @@ export async function serveMcp(cwd = process.cwd()): Promise { topK: z.number().int().positive().optional(), }), }, - async ({ query, topK }) => textResult(await ask(query, searchOptions(cwd, topK))), + async ({ query, topK }) => textResult(await ask(query, await searchOptions(cwd, topK))), ) server.registerTool( @@ -74,6 +78,16 @@ export async function serveMcp(cwd = process.cwd()): Promise { async () => textResult(await audit(cwd)), ) + server.registerTool( + "mimir_security_audit", + { + title: "Mimir Security Audit", + description: "Show local privacy, network, redaction, MCP, and gitignore posture.", + inputSchema: z.object({}), + }, + async () => textResult(await securityAudit(cwd)), + ) + await server.connect(new StdioServerTransport()) } @@ -88,6 +102,11 @@ function textResult(value: unknown): { content: Array<{ type: "text"; text: stri } } -function searchOptions(cwd: string, topK: number | undefined): { cwd: string; topK?: number } { - return topK === undefined ? { cwd } : { cwd, topK } +async function searchOptions( + cwd: string, + topK: number | undefined, +): Promise<{ cwd: string; topK?: number }> { + const config = await loadConfig(cwd) + const boundedTopK = Math.min(topK ?? config.topK, config.mcpMaxTopK) + return { cwd, topK: boundedTopK } } diff --git a/src/network.test.ts b/src/network.test.ts new file mode 100644 index 0000000..858ecce --- /dev/null +++ b/src/network.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "vitest" +import { assertNetworkPolicy, classifyHost } from "./network.js" +import type { Config } from "./types.js" + +describe("network policy", () => { + it("classifies loopback and private hosts", () => { + expect(classifyHost("http://localhost:11434").kind).toBe("loopback") + expect(classifyHost("http://127.0.0.1:11434").kind).toBe("loopback") + expect(classifyHost("http://192.168.1.10:11434").kind).toBe("private") + expect(classifyHost("https://example.com").kind).toBe("remote") + }) + + it("blocks remote Ollama hosts by default", () => { + expect(() => assertNetworkPolicy(testConfig({ ollamaHost: "https://example.com" }))).toThrow( + /Refusing to send document text/, + ) + }) + + it("allows private hosts only when explicitly configured", () => { + expect(() => + assertNetworkPolicy( + testConfig({ ollamaHost: "http://192.168.1.10:11434", networkPolicy: "allow-private" }), + ), + ).not.toThrow() + }) +}) + +function testConfig(overrides: Partial = {}): Config { + return { + projectRoot: "/tmp/project", + rawDir: "/tmp/project/private", + storageDir: "/tmp/project/.kb/storage", + sourcesFile: "/tmp/project/.kb/sources.txt", + accessLogPath: "/tmp/project/.kb/access.log", + tableName: "chunks", + ollamaHost: "http://localhost:11434", + networkPolicy: "local-only", + embedModel: "nomic-embed-text", + llmModel: "gemma4:latest", + redaction: { enabled: true, builtIn: true, patterns: [] }, + accessLog: true, + mcpMaxTopK: 10, + topK: 5, + chunkSize: 1200, + chunkOverlap: 150, + ...overrides, + } +} diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000..bf885cc --- /dev/null +++ b/src/network.ts @@ -0,0 +1,81 @@ +import { isIP } from "node:net" +import type { Config, HostClassification } from "./types.js" + +export function assertNetworkPolicy(config: Config): void { + const classification = classifyHost(config.ollamaHost) + + if (config.networkPolicy === "allow-any") { + return + } + + if (config.networkPolicy === "local-only" && classification.kind !== "loopback") { + throw new Error( + `Refusing to send document text to non-local Ollama host "${config.ollamaHost}". Set networkPolicy to "allow-private" or "allow-any" only if this is intentional.`, + ) + } + + if ( + config.networkPolicy === "allow-private" && + classification.kind !== "loopback" && + classification.kind !== "private" + ) { + throw new Error( + `Refusing to send document text to remote Ollama host "${config.ollamaHost}". Set networkPolicy to "allow-any" only if this is intentional.`, + ) + } +} + +export function classifyHost(input: string): HostClassification { + let url: URL + try { + url = new URL(input) + } catch { + return { kind: "invalid", host: input } + } + + const host = url.hostname.replace(/^\[(.*)\]$/, "$1").toLowerCase() + if (isLoopbackHost(host)) { + return { kind: "loopback", host } + } + + if (isPrivateHost(host)) { + return { kind: "private", host } + } + + return { kind: "remote", host } +} + +function isLoopbackHost(host: string): boolean { + if (host === "localhost" || host === "::1") { + return true + } + + if (isIP(host) === 4) { + return host.startsWith("127.") + } + + return false +} + +function isPrivateHost(host: string): boolean { + if (host === "host.docker.internal" || host.endsWith(".local")) { + return true + } + + if (isIP(host) === 4) { + const parts = host.split(".").map((part) => Number.parseInt(part, 10)) + const [first = 0, second = 0] = parts + return ( + first === 10 || + (first === 172 && second >= 16 && second <= 31) || + (first === 192 && second === 168) || + (first === 169 && second === 254) + ) + } + + if (isIP(host) === 6) { + return host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80") + } + + return false +} diff --git a/src/query.ts b/src/query.ts index fff15fe..93fb9ce 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,6 +1,8 @@ import { Ollama } from "ollama" +import { recordAccess } from "./access-log.js" import { loadConfig } from "./config.js" import { embedText } from "./embeddings.js" +import { assertNetworkPolicy } from "./network.js" import { openRowsTable } from "./store.js" import type { AskResult, SearchOptions, SearchResult } from "./types.js" @@ -25,13 +27,20 @@ export async function search(query: string, options: SearchOptions = {}): Promis .limit(options.topK ?? config.topK) .toArray()) as SearchRow[] - return rows.map((row) => ({ + const results = rows.map((row) => ({ source: row.source, relativePath: row.relativePath, chunkIndex: row.chunkIndex, text: row.text, distance: typeof row._distance === "number" ? row._distance : null, })) + await recordAccess(config, { + action: "search", + query, + topK: options.topK ?? config.topK, + resultCount: results.length, + }) + return results } export async function ask(query: string, options: SearchOptions = {}): Promise { @@ -52,6 +61,7 @@ export async function ask(query: string, options: SearchOptions = {}): Promise { + it("redacts built-in sensitive identifiers before indexing", () => { + const config = testConfig() + const result = redactText("Contact me at user@example.com.", config) + + expect(result.text).toContain("[REDACTED_EMAIL]") + expect(result.text).not.toContain("user@example.com") + expect(result.counts).toEqual([{ name: "email", count: 1 }]) + }) + + it("supports custom repository patterns", () => { + const config = testConfig({ + redaction: { + enabled: true, + builtIn: false, + patterns: [{ name: "client_id", pattern: "CLIENT-[0-9]+", replacement: "[CLIENT]" }], + }, + }) + + expect(redactText("CLIENT-12345", config).text).toBe("[CLIENT]") + }) +}) + +function testConfig(overrides: Partial = {}): Config { + return { + projectRoot: "/tmp/project", + rawDir: "/tmp/project/private", + storageDir: "/tmp/project/.kb/storage", + sourcesFile: "/tmp/project/.kb/sources.txt", + accessLogPath: "/tmp/project/.kb/access.log", + tableName: "chunks", + ollamaHost: "http://localhost:11434", + networkPolicy: "local-only", + embedModel: "nomic-embed-text", + llmModel: "gemma4:latest", + redaction: { enabled: true, builtIn: true, patterns: [] }, + accessLog: true, + mcpMaxTopK: 10, + topK: 5, + chunkSize: 1200, + chunkOverlap: 150, + ...overrides, + } +} diff --git a/src/redaction.ts b/src/redaction.ts new file mode 100644 index 0000000..b230c18 --- /dev/null +++ b/src/redaction.ts @@ -0,0 +1,74 @@ +import type { Config, RedactionCount, RedactionPattern } from "./types.js" + +const BUILT_IN_PATTERNS: RedactionPattern[] = [ + { + name: "private_key", + pattern: "-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----", + flags: "g", + }, + { + name: "jwt", + pattern: "\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b", + flags: "g", + }, + { + name: "api_token", + pattern: + "\\b(?:sk|pk|ghp|gho|github_pat|npm)_[A-Za-z0-9_=-]{20,}\\b|\\b[A-Za-z0-9_-]{32,}\\.[A-Za-z0-9_-]{16,}\\.[A-Za-z0-9_-]{16,}\\b", + flags: "g", + }, + { + name: "email", + pattern: "\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b", + flags: "gi", + }, + { + name: "iban", + pattern: "\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b", + flags: "g", + }, + { + name: "credit_card", + pattern: "\\b(?:\\d[ -]*?){13,19}\\b", + flags: "g", + }, +] + +export function redactText( + input: string, + config: Config, +): { text: string; counts: RedactionCount[] } { + if (!config.redaction.enabled) { + return { text: input, counts: [] } + } + + let text = input + const counts: RedactionCount[] = [] + const patterns = [ + ...(config.redaction.builtIn ? BUILT_IN_PATTERNS : []), + ...config.redaction.patterns, + ] + + for (const pattern of patterns) { + const regexp = compilePattern(pattern) + let count = 0 + text = text.replace(regexp, () => { + count += 1 + return pattern.replacement ?? `[REDACTED_${pattern.name.toUpperCase()}]` + }) + if (count > 0) { + counts.push({ name: pattern.name, count }) + } + } + + return { text, counts } +} + +export function totalRedactions(counts: RedactionCount[]): number { + return counts.reduce((total, entry) => total + entry.count, 0) +} + +function compilePattern(pattern: RedactionPattern): RegExp { + const flags = pattern.flags?.includes("g") ? pattern.flags : `${pattern.flags ?? ""}g` + return new RegExp(pattern.pattern, flags) +} diff --git a/src/security.ts b/src/security.ts new file mode 100644 index 0000000..7346d9e --- /dev/null +++ b/src/security.ts @@ -0,0 +1,95 @@ +import { existsSync } from "node:fs" +import { readFile } from "node:fs/promises" +import path from "node:path" +import { loadConfig } from "./config.js" +import { classifyHost } from "./network.js" +import type { SecurityAuditReport } from "./types.js" + +export async function securityAudit(cwd = process.cwd()): Promise { + const config = await loadConfig(cwd) + const gitignore = await readGitignore(config.projectRoot) + const network = classifyHost(config.ollamaHost) + const warnings: string[] = [] + + const kbIgnored = hasGitignoreEntry(gitignore, ".kb/") + const mimirIgnored = hasGitignoreEntry(gitignore, ".mimir/") + const privateIgnored = hasGitignoreEntry(gitignore, "private/**") + + if (config.networkPolicy === "allow-any") { + warnings.push("networkPolicy is allow-any; document text can be sent to a remote Ollama host.") + } + if (config.networkPolicy === "local-only" && network.kind !== "loopback") { + warnings.push("networkPolicy is local-only but ollamaHost is not loopback.") + } + if (!config.redaction.enabled) { + warnings.push("Redaction is disabled; secrets and identifiers may be embedded in the index.") + } + if (!kbIgnored) { + warnings.push(".kb/ is not ignored by Git.") + } + if (!mimirIgnored) { + warnings.push(".mimir/ is not ignored by Git.") + } + if (!privateIgnored) { + warnings.push("private/** is not ignored by Git.") + } + + return { + projectRoot: config.projectRoot, + zeroTelemetry: true, + network: { + policy: config.networkPolicy, + ollamaHost: config.ollamaHost, + host: network.host, + classification: network.kind, + }, + redaction: { + enabled: config.redaction.enabled, + builtIn: config.redaction.builtIn, + customPatterns: config.redaction.patterns.map((pattern) => pattern.name), + }, + accessLog: { + enabled: config.accessLog, + path: config.accessLogPath, + storesRawQueries: false, + }, + storage: { + path: config.storageDir, + gitIgnored: kbIgnored, + encryptedAtRest: "external-required", + }, + mcp: { + maxTopK: config.mcpMaxTopK, + destructiveToolsExposed: false, + }, + gitignore: { + kbIgnored, + mimirIgnored, + privateIgnored, + }, + recommendations: [ + "Run Mimir inside an encrypted disk, VM, or container volume for at-rest encryption.", + "Use npm provenance, release checksums, and the generated SBOM for release verification.", + "Use one repository checkout per trust boundary; Mimir does not implement multi-user RBAC.", + ], + warnings, + } +} + +async function readGitignore(projectRoot: string): Promise> { + const gitignorePath = path.join(projectRoot, ".gitignore") + if (!existsSync(gitignorePath)) { + return new Set() + } + + return new Set( + (await readFile(gitignorePath, "utf8")) + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean), + ) +} + +function hasGitignoreEntry(lines: Set, entry: string): boolean { + return lines.has(entry) +} diff --git a/src/types.ts b/src/types.ts index 5800acd..27cd55a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,15 +5,45 @@ export interface Config { rawDir: string storageDir: string sourcesFile: string + accessLogPath: string tableName: string ollamaHost: string + networkPolicy: NetworkPolicy embedModel: string llmModel: string + redaction: RedactionConfig + accessLog: boolean + mcpMaxTopK: number topK: number chunkSize: number chunkOverlap: number } +export type NetworkPolicy = "local-only" | "allow-private" | "allow-any" + +export interface RedactionConfig { + enabled: boolean + builtIn: boolean + patterns: RedactionPattern[] +} + +export interface RedactionPattern { + name: string + pattern: string + flags?: string | undefined + replacement?: string | undefined +} + +export interface RedactionCount { + name: string + count: number +} + +export interface HostClassification { + kind: "loopback" | "private" | "remote" | "invalid" + host: string +} + export interface SourceFile { absolutePath: string relativePath: string @@ -53,6 +83,7 @@ export interface IngestResult { indexedFiles: number chunks: number skippedFiles: number + redactions: number errors: Array<{ path: string; message: string }> } @@ -81,3 +112,46 @@ export interface AuditReport { staleInIndex: string[] totalChunks: number } + +export interface DestroyIndexResult { + storageDir: string + removed: boolean + note: string +} + +export interface SecurityAuditReport { + projectRoot: string + zeroTelemetry: true + network: { + policy: NetworkPolicy + ollamaHost: string + host: string + classification: HostClassification["kind"] + } + redaction: { + enabled: boolean + builtIn: boolean + customPatterns: string[] + } + accessLog: { + enabled: boolean + path: string + storesRawQueries: false + } + storage: { + path: string + gitIgnored: boolean + encryptedAtRest: "external-required" + } + mcp: { + maxTopK: number + destructiveToolsExposed: false + } + gitignore: { + kbIgnored: boolean + mimirIgnored: boolean + privateIgnored: boolean + } + recommendations: string[] + warnings: string[] +} diff --git a/src/version.ts b/src/version.ts index 988e4eb..d91ac72 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = "0.2.1" +export const VERSION = "0.3.0"