Skip to content

Non-Functional Requirements

Quality attributes — performance, reliability, compatibility, security, privacy.

Status: Draft v0.1 · 2026-05-03 Source documents: PRD.md, SPEC.md, API.md, CLI.md, MCP.md, AI.md Companion: FR.md

This document captures the quality attributes of indx: how well the system must do what FR.md says it must do. Every NFR is testable; every NFR has a measurable target or a verification method.


  • ID: stable identifier (NFR-<area>-<n>).
  • Target: the measurable bar. A number where possible.
  • Measure: how compliance is verified.
  • Priority: MUST (release-blocking), SHOULD (target for v1, may slip to v1.1), MAY (post-v1).

IDPriorityTargetMeasureTrace
NFR-PERF-1MUSTCold start to ready: < 2 sContainer docker run to /v1/health returning 200 on a 1 k-note vault.PRD §8
NFR-PERF-2MUSTp50 single-note read: ≤ 5 ms; p95: ≤ 50 msk6/autocannon against /v1/notes/{path} on a warm 10 k-note vault.PRD §8
NFR-PERF-3MUSTp50 search (10 k notes): ≤ 30 ms; p95: ≤ 200 msk6 against /v1/search?mode=lexical with realistic queries.PRD §8
NFR-PERF-4MUSTFull reindex of 10 k notes: < 30 s on a developer laptop (M-series or equivalent).vault_reindex from cold cache; wall clock.SPEC §5.3
NFR-PERF-5MUSTIncremental reindex on a typical edit: < 5 schokidar event → index updated → note.updated emitted.SPEC §5.3
NFR-PERF-6SHOULDNDJSON list streaming: first byte < 50 ms; sustained ≥ 5 000 rows/s.Accept: application/x-ndjson against /v1/notes.API §11
NFR-PERF-7MUSTPatch op application: ≤ 20 ms for a single op on a 50 KB note.Microbenchmark of core.md.patch.SPEC §6.2
IDPriorityTargetMeasureTrace
NFR-SCALE-1MUSTThe system SHALL operate on vaults up to 100 k notes with the sizing in SPEC.md §10.4.Boot, reindex, sample CRUD on a synthetic 100 k-note vault.SPEC §10.4
NFR-SCALE-2MUSTSustained sustained write throughput: ≥ 50 writes/s/token without backpressure under default rate limit.k6 PUT /v1/notes; observe latency stable.API §13
NFR-SCALE-3SHOULDConcurrent SSE subscribers per server: ≥ 100 without affecting p95 read latency by more than 10 %.Load harness with 100 SSE clients + read traffic.API §9
NFR-SCALE-4MAYHorizontal scaling out of one container is (post-v1). v1 is single-container, single-process.PRD §4
IDPriorityTargetMeasureTrace
NFR-RES-1MUSTDocker image: < 100 MB compressed.docker manifest inspect of the published tag.PRD §8
NFR-RES-2MUSTIdle RAM: < 256 MB on a 1 k-note vault.docker stats after 60 s idle post-boot.PRD §8, SPEC §10.4
NFR-RES-3SHOULDPeak RAM during reindex on a 10 k-note vault: ≤ 500 MB.docker stats during vault_reindex.SPEC §10.4
NFR-RES-4SHOULDIndex disk size: ~5 KB / note average.Measure .indx/index.db after reindex on representative corpora.SPEC §10.4
IDPriorityTargetMeasureTrace
NFR-REL-1MUSTEvery write SHALL be atomic (write-to-temp + rename). A crash mid-write SHALL leave the vault in either the old or new state — never partial.Fault-injection tests: SIGKILL the process between write and rename.SPEC §6.1
NFR-REL-2MUSTConcurrent writes to the same path SHALL be serialized; the second writer SHALL receive 409 etag_mismatch if If-Match is set.Concurrency test harness.SPEC §6.1, API §3
NFR-REL-3MUSTIndex drift after external file edits SHALL be self-healing. The watcher + content hashing SHALL converge within one debounce window (default 200 ms).Modify a file with sed; observe event + index update.SPEC §5.3
NFR-REL-4MUSTDeleting .indx/ and restarting SHALL restore full functionality after one reindex.Integration test: rm -rf .indx/; restart; vault_status reports indexed_notes matches file count.PRD §9, SPEC §3
NFR-REL-5MUSTIdempotent replays SHALL return the cached response within 24 h; replays with a different body SHALL fail with 409 idempotency_key_reused.Integration test against /v1/notes/{p} with repeated Idempotency-Key.API §12
IDPriorityTargetMeasureTrace
NFR-COMPAT-1MUSTRound-trip “open → write-back unchanged” on a corpus of 1 000+ public sample vaults SHALL produce byte-identical files when no edit is intended.Property-based test in CI; corpus checked into tests/fixtures/vaults/.PRD §8, SPEC §13
NFR-COMPAT-2MUSTRound-trip with intended edits SHALL produce semantic-identical files (same AST modulo the requested change), preserving unknown frontmatter keys.Same harness as NFR-COMPAT-1; assertions on AST diff.SPEC §6.2, FR-F-2
NFR-COMPAT-3MUSTIndx SHALL never modify .obsidian/. After indx writes, opening the same folder in Obsidian SHALL show no surprises.Manual smoke test in CI; .obsidian/ checksum unchanged.PRD §6 (J6), SPEC §3
NFR-COMPAT-4MUSTThe on-disk format SHALL be Obsidian’s existing format plus a single .indx/ directory; indx-internal files SHALL NOT pollute user folders.Filesystem audit after a session.PRD §4
IDPriorityTargetMeasureTrace
NFR-USE-AGENT-1MUSTAgent task success rate on the internal 50-task benchmark (CRUD, search, link-graph, canvas) SHALL be ≥ 95 %.Benchmark run on each release candidate using Claude/GPT/local models.PRD §8, SPEC §13
NFR-USE-AGENT-2MUSTEvery UI capability SHALL be reachable via at least one tool call on each of API, CLI, MCP — no screen-scraping required.API/CLI/MCP coverage matrix in CI.PRD §1, MCP §7
NFR-USE-AGENT-3MUSTOutput across surfaces SHALL be deterministic given the same inputs (no spinners, ANSI, timestamps, or non-deterministic ordering on stdout).Snapshot tests on CLI/MCP output.CLI §1
NFR-USE-AGENT-4MUSTErrors SHALL be machine-readable: stable code, structured details, predictable HTTP status / exit code / isError.Cross-surface error code tests.API §4, CLI §2.2, MCP §3
NFR-USE-AGENT-5MUSTA new agent connecting cold SHALL be productive in one round trip by calling tools/list + resources/list (MCP), GET /openapi.json (API), or indx schema * (CLI).Onboarding test: agent that has never seen indx completes a CRUD task using only discovery output.MCP §1, API §14, CLI §3.10
IDPriorityTargetMeasureTrace
NFR-USE-HUMAN-1MUSTThe web UI SHALL meet WCAG 2.1 AA on the editor, file tree, search, and activity panel.Lighthouse + axe-core in CI.PRD §3
NFR-USE-HUMAN-2MUSTUI SHALL be responsive at viewports ≥ 320 px (mobile-class) without functionality loss.Storybook viewport stories; Playwright responsive checks.PRD §5
NFR-USE-HUMAN-3SHOULDTime-to-first-edit on an empty vault: ≤ 60 s from docker run per PRD.md §6 J5.Manual stopwatch test in onboarding script.PRD §6 (J5)
NFR-USE-HUMAN-4MUSTThe UI SHALL never block on a remote AI call; semantic search degradation SHALL be silent (warning surfaced, not modal).Disconnect embeddings provider; UI remains usable.FR-S-4
IDPriorityTargetMeasureTrace
NFR-SEC-1MUSTAll paths supplied via API/CLI/MCP SHALL be normalized; .. escapes and absolute paths SHALL be rejected with 400 bad_request.Property test: 1 k random path strings; none escape INDX_VAULT.SPEC §6.4, §12
NFR-SEC-2MUSTTokens SHALL be stored hashed (or kept only in-memory from env). The .indx/config.json SHALL persist token IDs + scopes, not the secret itself.Inspect config.json after token issuance.SPEC §8.1, §9.2
NFR-SEC-3MUSTThe system SHALL make zero outbound calls unless an embeddings provider is explicitly configured (no telemetry, no analytics, no update pings).Network-namespace test: deny egress; system boots and serves with embeddings disabled.PRD §9, SPEC §12
NFR-SEC-4MUSTPer-token rate limit SHALL default to 100 rps with 1 000 burst; configurable. Limits SHALL apply uniformly across surfaces sharing a token.Load test verifying 429 + headers.API §13, SPEC §12
NFR-SEC-5MUSTWeb UI sessions SHALL set SameSite=Strict cookies; CSRF tokens SHALL be required for any state-changing request originating from the UI.OWASP ZAP CSRF probe.SPEC §8.2
NFR-SEC-6SHOULDThe Docker image SHALL be built from gcr.io/distroless/nodejs24-debian12 (no shell, minimal CVE surface).docker history audit.SPEC §10.1
NFR-SEC-7MUSTThe threat model SHALL be documented: trust boundary = host filesystem; defenses against unauthenticated callers, scope escalation, path traversal; explicitly out of scope: malicious agent with valid vault:write, host root, embeddings provider attacks.docs/SECURITY.md published before GA.SPEC §12
NFR-SEC-8SHOULDDependency CVEs of severity ≥ HIGH SHALL be patched within 14 days.pnpm audit + GitHub advisories cron.
IDPriorityTargetMeasureTrace
NFR-PRIV-1MUSTNo vault content SHALL leave the host except (a) embedding requests to a configured provider, and (b) responses to authenticated callers.Network capture during a browse + edit session with embeddings off.PRD §9, SPEC §12
NFR-PRIV-2MUSTLogs SHALL NOT include note bodies or frontmatter values; paths and sizes only.Log-redaction test on a vault containing canary strings.SPEC §11
NFR-PRIV-3SHOULDWhen embeddings are enabled, the user SHALL be able to scope which paths are eligible for embedding (allow/deny globs).Integration test: gated paths produce no provider calls.SPEC §9.2
IDPriorityTargetMeasureTrace
NFR-DEPLOY-1MUSTA user SHALL be able to launch indx with one docker run plus a vault volume mount and a token env var.README quickstart followed verbatim by a fresh user.PRD §4, SPEC §10.3
NFR-DEPLOY-2MUSTThe image SHALL ship a healthcheck endpoint (GET /v1/health) suitable for Docker, Kubernetes, and Vercel-style probes.docker inspect shows healthcheck.SPEC §10.1, §11
NFR-DEPLOY-3MUSTThe container SHALL run as a non-root user with the vault volume writable to that user.docker exec id returns non-root.SPEC §10
NFR-DEPLOY-4SHOULDThe image SHALL run on linux/amd64 and linux/arm64.Multi-arch buildx in CI.
NFR-DEPLOY-5MUSTThe CLI SHALL be installable both via npm i -g @indx/cli and via the bundled binary inside the Docker image.npm publish smoke test; docker exec ... indx --version.SPEC §1
NFR-DEPLOY-6SHOULDHonoring X-Forwarded-* SHALL be opt-in via INDX_TRUST_PROXY=true so the system is safe behind a reverse proxy.Proxy integration test.SPEC §9.1
IDPriorityTargetMeasureTrace
NFR-MAINT-1MUSTTypeScript strict mode SHALL be on across the monorepo; no any in public APIs.tsc --noEmit in CI.SPEC §2
NFR-MAINT-2MUSTA single Zod tree SHALL be the source of truth; TS types, JSON Schema, OpenAPI 3.1 SHALL be derived.Build script generates all three; CI fails on drift.SPEC §14, FR-PKG-1
NFR-MAINT-3MUSTUnit-test coverage on @indx/core SHALL be ≥ 85 % lines.vitest --coverage.SPEC §13
NFR-MAINT-4MUSTProperty-based tests SHALL run on the AST patch ops and the round-trip serializer on every PR.CI required check.SPEC §13
NFR-MAINT-5MUSTAdding a capability SHALL be one PR touching: (1) Zod schema, (2) @indx/core op, (3) API route, (4) CLI verb, (5) MCP tool. PR template SHALL enforce.PR template + CI lint.SPEC §14
NFR-MAINT-6SHOULDNew dependencies SHALL require a “why not the existing stack” note in docs/DECISIONS.md.PR review checklist.IMPLEMENTATION §0
IDPriorityTargetMeasureTrace
NFR-COMPAT-API-1MUST/v1/* SHALL be additive-only; no breaking changes within a major.OpenAPI diff in CI fails on breaking changes under /v1.API §15
NFR-COMPAT-API-2MUSTBreaking changes SHALL ship under /v2; /v1 SHALL remain available for at least one minor release after /v2 GA.Release process documented.API §15
NFR-COMPAT-API-3MUSTDeprecated paths SHALL include a Deprecation: <date> header and a Link: <docs>; rel="deprecation".Integration test on deprecated routes.API §15
NFR-COMPAT-API-4MUSTThe OpenAPI document SHALL be the contract; clients generated from it SHALL be the supported client path.Codegen smoke test.API §14
IDPriorityTargetMeasureTrace
NFR-OBS-1MUSTLogs SHALL be structured JSON on stdout (pino); every log line SHALL include level, time, msg, and an actor field where applicable.Log schema test.SPEC §11
NFR-OBS-2MUST/v1/health SHALL respond in < 50 ms under normal load.k6 probe.SPEC §11
NFR-OBS-3SHOULDOTLP traces SHALL include spans for: route handler, core op, SQLite query, embedding call.Trace inspection in a Tempo/Jaeger backend.SPEC §11
NFR-OBS-4MUSTThe .indx/events.log SHALL roll at 10 MB and retain at least 5 historical files.File rotation test.SPEC §7
IDPriorityTargetMeasureTrace
NFR-PORT-1MUSTThe runtime SHALL target Node.js 24 LTS. No platform-specific binaries beyond better-sqlite3 (and sqlite-vss if enabled).Build matrix: linux/amd64, linux/arm64, macOS dev.SPEC §2
NFR-PORT-2MUSTPath handling SHALL be POSIX. Vault files containing characters legal on Linux SHALL not require escaping in API/CLI/MCP beyond a single percent-encoding.Edge-case path test corpus.SPEC §6.4, API §5.1
NFR-PORT-3SHOULDThe CLI SHALL run on Linux, macOS, and Windows (WSL acceptable).npm install + smoke test on each.
IDPriorityTargetMeasureTrace
NFR-LIC-1MUSTThe codebase SHALL be MIT-licensed; the OpenAPI document and JSON Schemas SHALL be CC0 or MIT.LICENSE file; spec licensing notice.PRD §4
NFR-LIC-2MUSTAll dependencies SHALL have permissive licenses (MIT/BSD/Apache-2.0); copyleft (GPL/AGPL) is forbidden in the runtime image.License-check CI step.
NFR-LIC-3SHOULDA reproducible-build attestation SHALL be published for each release.SLSA-style provenance.
IDPriorityTargetMeasureTrace
NFR-AI-1MUSTWhen no AI provider is configured, indx SHALL make zero outbound calls and SHALL serve the vault with full functionality minus AI ops.Egress-deny network namespace test; /v1/ai/* returns 503 ai_unavailable.AI §2, NFR-PRIV-1
NFR-AI-2MUSTai_summarize of a single 50 KB note SHALL complete in ≤ 6 s p95 against a streaming-capable provider; ai_ask with top_k: 8 SHALL stream a first byte in ≤ 1.5 s p95.k6 + provider stub matching real provider latencies.AI §5.1, §5.2
NFR-AI-3MUSTRepeating an AI op with identical inputs against an unchanged vault SHALL be served from cache in ≤ 50 ms with usage.cost_usd: 0.Integration test: warm cache hit on /v1/ai/summarize.AI §8.1
NFR-AI-4MUSTAI cache invalidation SHALL be incremental — a single note edit SHALL only invalidate cache entries whose scope cited that note.Unit test on cache key + content fingerprint.AI §8.1
NFR-AI-5MUSTCitation verification post-hoc SHALL be 100 %: every citation in a returned citations[] SHALL resolve to an existing path/etag/anchor at the moment of return; broken citations SHALL be dropped or fail the op (ai_grounding_failed).Property test: 1 k random AI runs, zero unverified citations leak.AI §6
NFR-AI-6MUSTDeterminism: with temperature: 0, seed: 0, and a recording-mode provider stub, AI ops SHALL produce byte-identical structured payloads across runs.Snapshot tests in tests/ai/.AI §13
NFR-AI-7MUSTAI prompts and model outputs SHALL NOT appear in pino logs or .indx/events.log. Only paths, counts, durations, costs, and error codes are persisted.Log-redaction test on a vault containing canary prompt/output strings.AI §12, NFR-PRIV-2
NFR-AI-8SHOULDWhen INDX_AI_DAILY_COST_USD is set, the runtime SHALL refuse calls projected to exceed the cap; over-cap calls SHALL respond within ≤ 100 ms without contacting the provider.Quota integration test.AI §8.3
NFR-AI-9MUSTINDX_AI_DENY_GLOBS SHALL exclude matching paths from any AI op even when an agent specifies them in scope; excluded items SHALL be reported via globs_excluded warning, never silently included.Integration test against a deny-listed Private/** path.AI §14, NFR-PRIV-3
NFR-AI-10MUSTAI ops SHALL NOT auto-mutate the vault: ai_relate returns draft patches only; ai_toc writes only when write: { path } is explicit and the caller carries vault:write.Authorization + behavior tests.AI §5.4, FR-A-2
NFR-AI-11SHOULDThe 50-task agent benchmark SHALL include ≥ 14 AI tasks covering each op (summarize, ask, toc, relate, tag, metadata, extract); pass rate SHALL meet NFR-USE-AGENT-1.Benchmark run; per-task pass/fail recorded.NFR-USE-AGENT-1, AI §1
NFR-AI-12MUSTai_tag and ai_metadata suggest mode SHALL be deterministic in tag/value choice given identical inputs and temperature: 0, seed: 0 (see NFR-AI-6); only rationale text MAY vary across runs.Snapshot tests on a 50-note fixture.AI §13
NFR-AI-13MUSTApply ops (ai_tag / ai_metadata / ai_extract with apply: true) SHALL be per-note atomic: a SIGKILL between writes SHALL never leave a note partially modified, and SHALL never leave the index inconsistent with the on-disk content of any note.Fault-injection test on a 100-note batch.AI §5.8, NFR-REL-1
NFR-AI-14MUSTai_metadata writes for special keys (tags, aliases, cssclasses) SHALL produce frontmatter that round-trips byte-identically through Obsidian (NFR-COMPAT-1).Round-trip property test on a 200-note fixture.NFR-COMPAT-1, FR-F-3
IDPriorityTargetMeasureTrace
NFR-MIG-1MUSTSchema migrations on .indx/index.db SHALL run automatically on startup if the schema version differs; failures SHALL roll back and surface in /v1/health.Boot against an out-of-date DB; verify migration.SPEC §3
NFR-MIG-2MUSTThe vault directory format SHALL never require migration to be opened by a newer indx version (forward-compatible by construction; the on-disk format is Obsidian’s).Sample-vault tests across releases.PRD §4

A release candidate SHALL pass the following gates before being tagged latest:

  1. Performance gate: k6 scripts in tests/perf/ meet NFR-PERF-1 through NFR-PERF-5 on the reference 10 k-note vault.
  2. Compatibility gate: Round-trip property tests (NFR-COMPAT-1, -2) pass on the 1 k-vault corpus.
  3. Agent gate: 50-task benchmark passes at ≥ 95 % (NFR-USE-AGENT-1) on at least two model families.
  4. Security gate: Dependency audit clean (NFR-SEC-8); ZAP probe clean (NFR-SEC-5); penetration-test path-traversal corpus passes (NFR-SEC-1).
  5. Resource gate: docker stats confirms NFR-RES-1, -2, -3 on the reference vault.
  6. API gate: OpenAPI breaking-change diff is empty against the previous /v1 release (NFR-COMPAT-API-1).

Failures on any MUST gate block the release. SHOULD gates produce a release-note advisory but do not block.