cqs is a local code search tool for developers. It runs on your machine, indexes your code, and answers semantic queries.
| Boundary | Trust Level | Notes |
|---|---|---|
| Local user | Trusted | You run cqs, you control it |
| Project files | Trusted | Your code, indexed by your choice |
| External documents | Semi-trusted | PDF/HTML/CHM files converted via cqs convert — parsed but not executed |
| Reference sources | Semi-trusted | Indexed via cqs ref add — search results blended with project code |
cqs serve HTTP clients |
Untrusted by default | Per-launch 256-bit auth token gates every request (#1118 / SEC-7). Three credential channels: Authorization: Bearer, cqs_token_<port> cookie (port-scoped per RFC 6265, #1135 — concurrent instances don't collide in the browser jar), ?token= query param. Cookie handoff is HttpOnly; SameSite=Strict; Path=/; compare is constant-time on every channel. Disabling auth requires --no-auth plus an internal NoAuthAcknowledgement proof token (#1136), so no internal caller can ship a fully-open server by accident; the disabled branch logs a structured tracing::error! regardless of quiet. Loud-warn banner on non-loopback binds with --no-auth. |
| Indexed content (in AI agent context) | Untrusted | cqs relays code, comments, summaries, and developer notes verbatim. Injection payloads in any of those surfaces survive the relay. See Indirect Prompt Injection below. |
- Path traversal: Commands cannot read files outside project root
- FTS injection: Search queries sanitized before SQLite FTS5 MATCH operations
- Database corruption:
PRAGMA quick_check(1)on write-mode opens (opt-out viaCQS_SKIP_INTEGRITY_CHECK=1). Read-only opens skip the check entirely — reads cannot introduce corruption and the index is rebuildable viacqs index --force - Reference config trust: Warnings logged when reference configs override project settings
- Malicious code in your project: If your code contains exploits, indexing won't stop them
- Local privilege escalation: cqs runs with your permissions
- Side-channel attacks: Beyond timing, not in scope for a local tool
- Indirect prompt injection from indexed content: cqs relays code, comments, summaries, and notes verbatim to AI consumers; injection payloads inside those surfaces survive the relay. See Indirect Prompt Injection below.
cqs's primary consumer is AI agents. By design, cqs faithfully relays the content it has indexed — code, comments, doc strings, LLM-generated summaries, and developer notes — into the agent's context window. Any of those surfaces can carry indirect prompt injection payloads: instructions disguised as content that try to redirect the consuming agent ("Ignore prior instructions and...", "This function is safe to call with sudo", etc.).
cqs cannot reliably distinguish a legitimate doc comment from a malicious one. Defence has to live partly in the agent (treat retrieved code as untrusted input) and partly in cqs's protocol (make the trust boundary loud and visible).
| Surface | Vector | Persistent? |
|---|---|---|
| Project source code | Comments, strings, doc blocks containing injection payloads (committed by a contributor or embedded by an upstream dependency) | Yes — survives until removed from source |
Reference content (cqs ref add) |
Third-party code indexed for cross-project search; less curated than the user's own code, blended into search results without an explicit trust signal | Yes — survives until ref is removed |
Shared notes (docs/notes.toml) |
A cloned repo can ship committed notes that bias rankings and surface in agent context. audit-mode mitigates ranking influence at runtime, but not the first-encounter case |
Yes — survives in the indexed repo |
LLM-generated summaries (cqs index --llm-summaries) |
Claude is prompted with chunk content; a poisoned chunk can produce a summary that contains injection text. The summary text is cached in the llm_summaries table keyed by (content_hash, purpose) (search for CREATE TABLE IF NOT EXISTS llm_summaries in src/schema.sql); the post-summary embedding flows through the normal embeddings_cache.db (purpose embedding, the same purpose served to search) and is replayed to downstream agents |
Yes — cached in llm_summaries table + embeddings_cache.db |
Doc-comment generation (cqs index --llm-summaries --improve-docs) |
LLM output is staged as patch files under .cqs/proposed-docs/ for review (default since v1.30.1); only --apply writes back to source files in place. A poisoned chunk can still land a doc comment in the user's repo on commit if --apply is used or the patch is accepted unread |
Patch-only by default — see Mitigation #3 below; --apply reverts to in-place writes |
| Search result blending | RRF merges chunks across project + references; the consuming agent sees a single ranked list with no in-protocol trust signal distinguishing user code from third-party content | Yes — every query |
-
audit-mode:cqs audit-mode onexcludes notes from rankings and forces direct code examination. Mitigates the runtime side of shared-notes injection. -
No automatic execution: cqs never executes indexed code; the threat is purely textual relay into agent context.
-
--improve-docsreview gate (since v1.30.1): by default,cqs index --improve-docswrites proposed doc comments as unified-diff patches to.cqs/proposed-docs/<rel>.patchinstead of mutating source files in place. Review withgit diffand apply withgit apply .cqs/proposed-docs/**/*.patch. Pass--applyto opt back into direct write-back; the run prints a warning when it does. -
First-encounter shared-notes gate (since v1.30.1): on the first
cqs indexagainst a repo containingdocs/notes.toml, cqs prompts to confirm before indexing the notes — committed notes affect search rankings and surface in agent context. Acceptance is persisted to.cqs/.accepted-shared-notesso the prompt doesn't repeat. Pass--accept-shared-notesto bypass for CI / scripted use; non-TTY stdin auto-skips the notes pass with a warning so CI never hangs. (#1168) -
trust_level+reference_nameon chunk JSON (since v1.30.1, three-tier as of #1221): every chunk-returning JSON output (search,gather,task,scout,onboard,read,read --focus,context,similar) carries one of:"user-code"— chunk lives in the user's project store and did not match any vendored-path prefix at index time."vendored-code"— chunk lives in the user's project store but its origin passed through a configured vendored-path segment (default list:vendor,third_party,node_modules,.cargo,target,dist,build; override via[index].vendored_pathsin.cqs.toml). Treat as third-party content for indirect-injection purposes — same threat profile asreference-code. (#1221, schema v24)"reference-code"(withreference_name) — chunk lives in acqs refreference index. Wins overvendored-codewhen both apply: the per-reference name is the more useful agent-facing signal.
Scope of
user-codeafter #1221.trust_level: "user-code"now means "from the user's project store AND not under a configured vendored-path prefix". Vendored upstream content (vendor/,third_party/,node_modules/, etc.) is structurally distinguished as"vendored-code"at index time — the structural fix the SEC-V1.30.1-5 doc-only stop-gap acknowledged was needed. Caveats that still apply: (1)user-codedoes not mean "authored by the user" — generated code undersrc/is stilluser-code. (2) Developer notes (docs/notes.tomlcontent surfaced viacqs notes mention) still ride the user-code label since they're stored separately and don't pass through the chunk pipeline. (3) Reindex required to retroactively flag pre-v24 chunks: the v23→v24 migration adds the column with default0; only the nextcqs index(orcqs watchre-emit) populates it. -
CQS_TRUST_DELIMITERSis on by default (since v1.30.2): every chunk'scontentis wrapped in<<<chunk:{id}>>> ... <<</chunk:{id}>>>markers so prompt-injection guards downstream of cqs can detect content boundaries even when the agent inlines the rendered string into a larger prompt. SetCQS_TRUST_DELIMITERS=0to opt out (raw text). Was opt-in in v1.30.1. (#1167, #1181) -
LLM summary validation (since v1.30.1): every summary headed for the
llm_summariescache passes throughcqs::llm::validation::validate_summarybefore insertion. Catches lazy injections (leading "Ignore prior" / "Disregard"; embedded code fences; embedded URLs) and enforces a 1500-char hard length cap. Configurable viaCQS_SUMMARY_VALIDATION=strict|loose|off(defaultloose: log + keep on pattern match, truncate over-long; strict drops pattern-matched summaries entirely). Doc-comment generation is intentionally exempt — its prose is imperative by design and would false-positive; it has its own review gate (#1166). (#1170) -
_meta.handling_adviceis opt-in viaCQS_ULTRASECURITY=1(was always-on v1.30.2 → v1.39.x; opt-in since v1.40.0): every JSON-emitting command surface (emit_json, batchwrite_json_line, daemon socket) accepts an_meta.handling_advicestring framing the response as untrusted-by-default for any consuming agent.trust_levelsignals origin, not safety;injection_flagslists which heuristics fired but cqs never refuses to relay. Default-on through v1.39.x added ~80 bytes to every response — telemetry showed agents reduced theircqs searchrate from 79% → 6% mid-April → early May, attributable to the always-on advisory plus per-result advisory fields (#1181 → #1593). v1.40.0 (#1593, #1601-#1613) inverted the default: underPosture::Friendly(no env var orCQS_ULTRASECURITY=0),_meta.handling_adviceis omitted; underPosture::Adversarial(CQS_ULTRASECURITY=1, set explicitly for adversarial deployments like cqs-as-MCP-server), it's emitted on every envelope as before. The advisory is unchanged when present; only the default emission posture flipped. (#1181 → #1593 → #1613) -
_meta.worktree_stale+_meta.worktree_name(#1254): when a cqs command runs from inside agit worktreethat has no.cqs/of its own, cqs auto-discovers the main project's index via the worktree's.git/commondirlink and serves queries against main's.cqs/. Every JSON envelope from that process carriesworktree_stale: trueplus the worktree's directory name inworktree_name. Consuming agents should treat the served snapshot as reflecting main's branch, not uncommitted edits in the worktree — fall back to reading absolute worktree paths for any chunk that's about to be edited. The fields are absent (skipped) for the non-worktree happy path so the wire shape only grows when the redirect actually fires. Closes the worktree-leakage class of bugs documented infeedback_agent_worktrees.md. -
Per-chunk
injection_flagsarray is opt-in/skip-when-default since v1.40.0 (was always-on v1.30.2 → v1.39.x): every chunk-returning JSON output accepts aninjection_flagsfield listing which injection heuristics fired on the chunk's raw content (e.g.["leading-directive", "code-fence", "embedded-url"]). UnderPosture::Friendly(default v1.40.0+), the field is skipped when empty (the typical case) to keep the wire shape lean; emitted as a populated array when at least one heuristic fires. UnderPosture::Adversarial(CQS_ULTRASECURITY=1), the field is force-emitted on every chunk including the empty-array case, preserving the v1.30.2 schema-stability contract for adversarial deployments. cqs labels — never refuses to relay. Agents that want a stricter posture can refuse to act on chunks with non-emptyinjection_flags. (#1181 → #1602 → #1613)
These are defence in depth, not absolute protection. Subtle injections (a summary that is superficially correct but biased) will still get through. The agent-side defence — treat retrieved code as untrusted input, sandbox tool calls, never execute payload-shaped output — remains the load-bearing layer.
cqs runs locally by default. No network telemetry. Optional local command logging to .cqs/telemetry.jsonl — active when CQS_TELEMETRY=1 is set OR when the telemetry file already exists (persists across shells/subprocesses). Never transmitted. Delete the file to opt out. The optional --llm-summaries flag sends function code to the Anthropic API (see below).
The only network activity is:
-
Model download (
cqs init): Downloads embedding model from HuggingFace Hub- Default since v1.35.0:
huggingface.co/onnx-community/embeddinggemma-300m-ONNX(~1.2GB FP32 ONNX bundle + ~20MB tokenizer) - Preset:
bge-large(BAAI/bge-large-en-v1.5, ~1.2GB) — former default; opt-in viaCQS_EMBEDDING_MODEL=bge-large - Preset:
e5-base(intfloat/e5-base-v2, ~438MB) - Preset:
nomic-coderank(jamie8johnson/CodeRankEmbed-onnx, ~547MB) — code-specialised, opt-in viaCQS_EMBEDDING_MODEL=nomic-coderank(#1110) - Custom: any HuggingFace repo via
[embedding]config orCQS_EMBEDDING_MODELenv var. Custom model configs download ONNX files from the specified repo — only configure repos you trust. - One-time download per model, cached in
~/.cache/huggingface/
- Default since v1.35.0:
-
Reranker model download (first
--rerankuse): Downloads cross-encoder model from HuggingFace Hub- Model:
ms-marco-MiniLM-L-6-v2(cross-encoder) - One-time download, cached in
~/.cache/huggingface/
- Model:
-
LLM summaries (
cqs index --llm-summaries): Sends function code to the Anthropic API -
HyDE queries (
cqs index --llm-summaries --hyde-queries): Sends function descriptions to the Anthropic API for synthetic query generation
| Flag | Endpoint | Data Sent | Notes |
|---|---|---|---|
--llm-summaries |
api.anthropic.com | Function bodies (up to 8000 chars), chunk type, language | Requires ANTHROPIC_API_KEY. Opt-in via cqs index --llm-summaries |
--hyde-queries |
api.anthropic.com | Function NL descriptions, signatures | Requires --llm-summaries. Generates synthetic search queries per function |
--improve-docs |
api.anthropic.com | Function bodies (for doc generation) | Requires --llm-summaries. Stages doc comments as patches under .cqs/proposed-docs/ for review (default); --apply writes them in place |
- Model export (
cqs export-model): Spawns Pythonoptimum.exporters.onnxwhich downloads the specified HuggingFace model and converts to ONNX format
No other network requests are made. Without --llm-summaries or export-model, all operations are offline.
| Path | Purpose | When |
|---|---|---|
| Project source files | Parsing and embedding | cqs index, cqs watch |
.cqs/slots/<name>/index.db |
SQLite database (per-slot, PR #1105). Pre-migration projects may still see the legacy .cqs/index.db. |
All operations |
.cqs/slots/<name>/index.hnsw.* |
HNSW vector index files (per-slot) | Search operations |
.cqs/slots/<name>/index_base.hnsw.* |
Base (non-enriched) HNSW index (per-slot) | Search operations (Phase 5 dual routing) |
.cqs/splade.index.bin |
SPLADE sparse inverted index | Search operations (--splade or routed cross-language) |
docs/notes.toml |
Developer notes | Search, cqs read |
~/.cache/huggingface/ |
ML model cache | Embedding operations |
<project>/.cqs/embeddings_cache.db |
Per-project embedding cache (PR #1105, primary; legacy global cache at ~/.cache/cqs/embeddings.db is fallback) |
cqs index, search |
~/.cache/cqs/embeddings.db |
Global embedding cache (content-addressed, capped at 1 GB). Linux path; on macOS resolves to ~/Library/Caches/cqs/embeddings.db, on Windows to %LOCALAPPDATA%\cqs\embeddings.db |
Index and search |
~/.cache/cqs/query_cache.db |
Recent query embedding cache (size-capped at CQS_QUERY_CACHE_MAX_SIZE, 100 MiB default). Linux path; macOS ~/Library/Caches/cqs/query_cache.db; Windows %LOCALAPPDATA%\cqs\query_cache.db |
Search |
~/.config/cqs/ |
Config file (user-level defaults) | All operations |
$CQS_ONNX_DIR/ |
Local ONNX model directory | When CQS_ONNX_DIR is set |
~/.local/share/cqs/refs/*/ |
Reference indexes (read-only copies) | Search operations |
| Path | Purpose | When |
|---|---|---|
.cqs/ directory |
Index storage | cqs init |
.cqs/slots/<name>/index.db |
SQLite database (per-slot, PR #1105). Pre-migration projects may still see the legacy .cqs/index.db. |
cqs index, note operations |
.cqs/slots/<name>/index.hnsw.* |
HNSW vector index + checksums (per-slot) | cqs index |
.cqs/slots/<name>/index_base.hnsw.* |
Base HNSW index + checksums (per-slot) | cqs index |
.cqs/splade.index.bin |
SPLADE sparse inverted index | cqs index (with CQS_SPLADE_MODEL set), lazy rebuild on first --splade query |
.cqs/index.lock |
Process lock file | cqs watch |
.cqs/audit-mode.json |
Audit mode state (on/off, expiry) | cqs audit-mode on, cqs audit-mode off |
.cqs/telemetry.jsonl |
Command usage logs (opt-in, persists via file presence; single file, no rotation) | CQS_TELEMETRY=1 or file exists, delete to opt out |
docs/notes.toml |
Developer notes | cqs notes add, cqs notes update, cqs notes remove |
.cqs.toml |
Reference configuration | cqs ref add, cqs ref remove |
~/.config/cqs/projects.toml |
Project registry | cqs project register, cqs project remove |
~/.local/share/cqs/refs/*/ |
Reference index creation and updates (write) | cqs ref add, cqs ref update |
<project>/.cqs/embeddings_cache.db |
Per-project embedding cache writes (primary; PR #1105) | cqs index, search |
~/.cache/cqs/embeddings.db |
Global embedding cache writes (legacy fallback). Linux path; macOS ~/Library/Caches/cqs/embeddings.db; Windows %LOCALAPPDATA%\cqs\embeddings.db |
cqs index |
~/.cache/cqs/query_cache.db |
Recent query embedding cache writes. Linux path; macOS ~/Library/Caches/cqs/query_cache.db; Windows %LOCALAPPDATA%\cqs\query_cache.db |
Search (cache miss) |
~/.cache/cqs/query_log.jsonl |
Local query log (append-only). Linux path; macOS ~/Library/Caches/cqs/query_log.jsonl; Windows %LOCALAPPDATA%\cqs\query_log.jsonl |
cqs chat / cqs batch (search, gather, onboard, scout, where, task) |
| Project source files | Doc comment insertion | cqs index --llm-summaries --improve-docs |
<output>/ directory |
ONNX model files + model.toml | cqs export-model |
| Operation | Purpose |
|---|---|
libc::kill(pid, 0) |
Check if watch process is running (signal 0 = existence check only) |
cmd /C start "" <url> (Windows / WSL) / xdg-open <url> (Linux) / open <url> (macOS) |
Browser launch for cqs serve --open. Token-bearing URLs are suppressed from argv (see below). |
cqs serve --open historically spawned the OS browser launcher with the full http://<bind>/?token=<token> URL on argv. That places the token in /proc/<pid>/cmdline (Linux) / wmic process get CommandLine (Windows) for any local user to read until the launcher exits, and audit subsystems (auditd, ETW) typically capture exec command lines independently of process lifetime — same surface the per-launch banner already avoids by routing the token to stderr instead of journald.
Mitigation (#1337 / SEC-V1.33-1): when cqs serve runs with auth enabled (the default), --open no longer spawns a browser. The CLI prints a notice instructing the user to paste the banner URL manually; the token is never written to a subprocess argv. With --no-auth there is no token to leak and --open continues to launch the browser normally.
The convert module spawns external processes for format conversion:
| Subprocess | Purpose | When |
|---|---|---|
python3 / python |
PDF-to-Markdown via pymupdf4llm | cqs convert *.pdf |
7z |
CHM archive extraction | cqs convert *.chm |
Attack surface:
CQS_PDF_SCRIPTenv var: If set, the convert module executes the specified script instead of the default PDF conversion logic. This allows arbitrary script execution under the user's permissions.- Output directory: Generated Markdown files are written to the
--outputdirectory. The output path is not sandboxed beyond normal filesystem permissions.
Mitigations:
- Symlink filtering: Symlinks are skipped during directory walks and archive extraction
- Zip-slip containment: Extracted paths are validated to stay within the output directory
- Page count limits: PDF conversion enforces a maximum page count to bound processing time
The export-model command spawns Python to convert HuggingFace models to ONNX format:
| Subprocess | Purpose | When |
|---|---|---|
python3 / python / py |
ONNX export via optimum.exporters.onnx |
cqs export-model --repo org/model |
Attack surface:
- Repo ID: Passed to
python -m optimum.exporters.onnx --model <repo>. Validated to contain/and reject",\n,\characters (SEC-18). - Output directory: Model files and
model.tomlwritten to--outputpath. Not sandboxed beyond filesystem permissions. - Python execution: Spawns Python with user permissions to run optimum library code.
Mitigations:
- Repo ID format validation prevents injection (SEC-18)
- Output path canonicalized via
dunce::canonicalize(PB-30) model.tomlrestricted to 0o600 permissions on Unix (SEC-19)
The cqs read command validates paths:
let canonical = dunce::canonicalize(&file_path)?;
let project_canonical = dunce::canonicalize(root)?;
if !canonical.starts_with(&project_canonical) {
bail!("Path traversal not allowed: {}", path);
}This blocks:
../../../etc/passwd- resolved and rejected- Absolute paths outside project - rejected
- Symlinks pointing outside - resolved then rejected
cqs has two symlink-handling regimes, depending on the entry point.
Symlinks are skipped entirely — enumerate_files / enumerate_files_iter in src/lib.rs (search for WalkBuilder::follow_links(false)) and cqs convert's archive extraction skip them in extract paths. The walker never opens the link's target.
| Scenario | Behavior |
|---|---|
project/link → project/src/file.rs |
Skipped (symlink, regardless of target) |
project/link → /etc/passwd |
Skipped |
project/link → ../sibling/file |
Skipped |
This is conservative: a monorepo workspace that uses in-tree symlinks to share common code will silently miss those files. Workaround: replace the symlinks with the actual files (or use a [references] config block to index the shared tree as a separate slot).
When the user passes a path on the command line, cqs canonicalizes it (dunce::canonicalize), then validates the resolved path against the project root.
| Scenario | Behavior |
|---|---|
cqs read link where link → project/src/file.rs |
Allowed (target inside project, canonicalised path reads project/src/file.rs) |
cqs read link where link → /etc/passwd |
Blocked (target outside project) |
cqs read link where link → ../sibling/file |
Blocked (target outside project) |
cqs ref add --source <path> redirect surfacing (since v1.30.2, #1222): when the user-supplied --source path resolves through a symlink to a different filesystem location, cmd_ref_add emits a tracing::warn! (and a WARN: line on stderr in non---quiet text mode) naming both the user-supplied and resolved paths. JSON output gains a warnings: ["source path '<input>' resolved via symlink to '<target>'"] field. The reference is still indexed at the resolved path — the warning exists so an operator who ran cqs ref add foo vendored-monorepo-pull/ against a symlink to ~/work/customer-A-private/ can see what they actually pulled in. Lexical normalization (.., ., repeated separators) is applied before comparison so purely syntactic differences in the input don't trigger false positives.
TOCTOU consideration: A symlink could theoretically be changed between canonicalization and read. This is a standard filesystem race condition that affects all programs. Mitigation would require O_NOFOLLOW or similar, which would break legitimate symlink use cases on cqs read.
Recommendation: If you don't trust symlinks in your project, remove them. The directory-walk path is already conservative.
Several cfg(unix) paths apply file-mode hardening that the cfg(not(unix)) arms can't replicate without per-file ACL programming. Documented here so the SEC promises don't read as universal when they're actually Linux/macOS-specific.
- Linux/macOS: file is created with
mode(0o600)so it's never world-readable; the audit token can't be read by other accounts on a shared host. - Windows: file is created via
std::fs::writeand inherits the parent directory's DACL. The default%LOCALAPPDATA%\cqs\location inherits user-only ACLs from the user profile, which is fine — but operators with roaming profiles, or who've relocated%LOCALAPPDATA%to a shared mount (some enterprise deployments), can end up with the audit-mode file readable by other authenticated users on the same machine. - Recommendation: on Windows, verify the parent directory's ACL doesn't grant read to other users before treating the audit-mode marker as a confidentiality boundary. Audit-mode is a debugging aid, not a credential store; the file contains a timestamp + opt-in flag, not a token.
- Linux/macOS:
apply_db_file_permssetsmode(0o600)after creation. - Windows:
apply_db_file_perms(_path: &Path) {}is a no-op. Same DACL inheritance story as above. The cache contains chunk content_hashes + embedding vectors — not directly sensitive, but the same operator-environment caveat applies.
- Linux/macOS: identity is
(dev, inode)— durable acrossmtimechanges from--forcereindexes. - Windows / non-Unix: identity falls back to
mtime, which can collide if two--forceoperations run in the same second. The legacy backup discovery path (used only for the v18→v19 migration on existing indexes) may misidentify a fresh DB as the pre-migration one. Mitigated in practice because the v18→v19 migration shipped over a year ago and operators on current schemas don't hit this code path.
- The
cqs hook installscript body assumescqsis on PATH from a POSIX-style shell. On Windows-native Git (cmd.exe / PowerShell), the inherited PATH from MSYS shells doesn't always carry over. Operators on native-Windows git should use Git for Windows' bundled bash for the hook scripts to fire reliably.
These limitations are tracked as Windows-specific issues in the v1.33.0 audit batch (#1353, #1354, #1355).
- Stored in
.cqs/slots/<name>/index.db(SQLite with WAL mode; PR #1105 introduced per-slot layout, pre-migration projects may still see the legacy.cqs/index.db) - Contains: code chunks, embeddings (768-dim vectors for default embeddinggemma-300m since v1.35.0; 1024-dim for bge-large preset), file metadata
- Add
.cqs/to.gitignoreto avoid committing - Database is not encrypted - it contains your code
- Schema v27 (#1497) adds
chunks.needs_embedding; chunks created during a--llm-summariesreindex skip the initial cold embed and get embedded once the LLM-enriched description is available, then cleared byenrichment_pass. This is behavior-visible to anyone tracing a "why isn't this chunk in HNSW yet?" path mid-index — seedocs/audit-triage.mdCluster A for the gap closures (DS-V1.38-1/2/3/8, #1514).
- Dependabot: Automated weekly checks for crate updates
- CI workflow: Runs clippy with
-D warningsto catch issues - cargo audit: Runs in CI, allowed warnings documented in
audit.toml - No secrets in CI: Build and test only, no publish credentials exposed
The main branch is protected by a GitHub ruleset:
- Pull requests required: All changes go through PR
- Status checks required:
test,clippy,fmtmust pass - Force push blocked: History cannot be rewritten
Known advisories and mitigations:
| Crate | Advisory | Status |
|---|---|---|
bincode |
RUSTSEC-2025-0141 | Mitigated: checksums validate data before deserialization |
paste |
RUSTSEC-2024-0436 | Accepted: proc-macro, no runtime impact, transitive via tokenizers |
Run cargo audit to check current status.
Report security issues to: https://github.com/jamie8johnson/cqs/issues
Use a private security advisory for sensitive issues.