Configuration
All settings can be persisted via imprint config instead of setting environment variables. Settings are stored in data/config.json (gitignored).
Precedence: env var > config.json > hardcoded default. Environment variables always win, so you can override config.json for one-off runs.
Project association settings (also in Settings → Project associations):
| Key | Default | Meaning |
|---|---|---|
associations.enabled |
true |
Suggest links after a full snapshot is published; existing manual links remain active when disabled. |
associations.auto_accept_paths |
false |
Accept exact registered-project-path matches automatically. Name and local-model matches still need review. |
associations.unassigned_workspace |
empty / Off | Explicit known workspace for client conversations without cwd evidence. Never overrides conflicting directory ownership. |
Environment overrides are IMPRINT_ASSOCIATIONS_ENABLED,
IMPRINT_ASSOCIATIONS_AUTO_PATHS and IMPRINT_CONVERSATION_INBOX_WORKSPACE.
See project organization.
# Select the supported Qwen default (then rebuild; see embeddings.md)imprint config set model.name Qwen/Qwen3-Embedding-0.6B-GGUFimprint config set model.dim 1024imprint config set model.seq_length 2048
# Use local Ollama for LLM taggingimprint config set tagger.llm trueimprint config set tagger.llm_provider ollamaimprint config set tagger.llm_model llama3.2
# Or fully in-process via llama-cpp (no server to run)imprint config set tagger.llm trueimprint config set tagger.llm_provider local
# Custom Qdrant serverimprint config set qdrant.host 192.168.1.50imprint config set qdrant.no_spawn true
# See what's changedimprint config
# Show one setting + where the current value came fromimprint config get model.device
# One-off env override (doesn't persist)IMPRINT_DEVICE=cpu imprint ingest ~/code
# Remove one overrideimprint config reset model.device
# Wipe all overridesimprint config reset --allimprint config groups settings visually by prefix (model.*, huggingface.*, qdrant.*, chunker.*, tagger.*, tagger.local.*, ingest.*, chat.*, summarizer.*, recall.*). Non-default values are highlighted by source (config.json cyan, env yellow).
The dashboard’s Settings page edits the same values. Sections follow the key’s group, except GPU, which gathers model.gpu_device (the GPUs llama.cpp finds on this device), model.device and the local models’ n_gpu_layers. A setting with known values, such as model.device, a provider or model.gpu_device, is a dropdown. Custom value… accepts anything else the setting allows. A value set by an environment variable is shown read-only. The reset button (remove, for a secret) deletes the config.json override, like imprint config reset <key>. Both the CLI and the dashboard refuse a value outside a fixed set.
Settings also holds Workspaces and, for a Windows installation, WSL profiles: enroll or remove the client profiles (whichever clients the CLI supports there) of each WSL distribution’s default user. See installation.
Hugging Face token
Section titled “Hugging Face token”Imprint downloads the embedding model and the optional local helper GGUF from the Hugging Face Hub. Without a token those downloads are anonymous: they work for the default public models but are rate-limited, and huggingface_hub warns about unauthenticated requests. Gated or private repositories need a token. Create a read token at https://huggingface.co/settings/tokens, then store it in one of these ways:
- Dashboard: Settings → Hugging Face → Token. Click Not set (or the masked value), paste the token and save. The field is a password input and starts empty.
- CLI:
imprint config set huggingface.token hf_.... The token appears on the command line and in shell history, so prefer the dashboard or the environment variable on shared machines. - Environment: export the standard
HF_TOKENvariable. It takes precedence over the stored token.
Every Imprint process reads the token when it downloads, so a token saved in the dashboard applies to the running dashboard server and to the ingest, refresh and learn processes it starts. It is also used by MCP and hook processes, with no restart needed. Embedding downloads pass it to huggingface_hub, and GGUF downloads send it as an Authorization header to huggingface.co. httpx drops that header if a redirect leaves the host. With neither HF_TOKEN nor a stored token set, downloads use huggingface_hub’s own login (hf auth login). One case is not covered: Chonkie’s own model2vec fallback, which runs only when Imprint’s embedding model cannot load, sees HF_TOKEN and hf auth login but not the stored token.
huggingface.token is a secret setting. imprint config, imprint config get/set, imprint status and GET /api/config show only **** plus its last four characters, and only for tokens of 12 characters or more. The raw value is stored only in data/config.json. Imprint writes that file through a temporary file created with owner-only permissions (0600 on macOS/Linux). On Windows it inherits the per-user install directory’s ACL. Uninstalling without keeping data deletes it. See ADR 0033.
All Settings
Section titled “All Settings”| Key | Default | Description |
|---|---|---|
| Embedding model | ||
model.name |
Qwen/Qwen3-Embedding-0.6B-GGUF |
HuggingFace embedding model repo (GGUF) |
model.revision |
auto |
Pinned default export revision; custom repos use main unless pinned |
model.file |
auto |
GGUF file in the model repo (auto = the default model’s file; a custom repo must name its file) |
model.device |
auto |
Compute device: auto / cpu / gpu. auto embeds on a GPU when llama.cpp finds one, else on the CPU; cpu forces the CPU; gpu asks for the GPU and warns, then uses the CPU, when there is none. Any GPU llama.cpp’s build supports: Vulkan on Windows and Linux x64, Metal on macOS. Changing it installs nothing; running processes use it once they restart. Settings marks gpu unavailable where llama.cpp finds no GPU (ADR 0086) |
model.dim |
1024 |
Embedding vector dimension |
model.seq_length |
2048 |
Token cap per embed call |
model.threads |
0 |
CPU threads for CPU embedding and the privacy filter (0 = half the logical CPUs) |
model.gpu_device |
auto |
GPU that runs embeddings and the local LLM, each model whole on it: auto = the discrete GPU with the most memory (an integrated GPU only when there is no discrete one), else an index into the GPUs Settings lists, integrated ones after the discrete (ADR 0087, ADR 0125) |
model.batch_size |
0 |
Embedding batch size (0 = auto: 8 on a GPU, 1 on the CPU) |
model.pooling |
auto |
Pooling strategy: auto / cls / mean / last. auto follows the model family (Qwen3 last token, BGE-M3 CLS), else the GGUF’s own metadata |
model.query_instruction |
auto |
Model-specific query task; Qwen wraps Instruct/Query |
| Hugging Face Hub | ||
huggingface.token |
— | Access token for model downloads. Secret: displayed masked. Its env var is the standard HF_TOKEN, not an IMPRINT_* name |
| Qdrant | ||
qdrant.host |
127.0.0.1 |
Qdrant host; auto-spawn requires a literal loopback IP or localhost |
qdrant.port |
6333 |
Qdrant HTTP port |
qdrant.grpc_port |
6334 |
Qdrant gRPC port |
qdrant.version |
v1.17.1 |
Pinned Qdrant release for auto-download |
qdrant.no_spawn |
false |
Skip auto-spawn (BYO server) |
| Chunker | ||
chunker.overlap |
400 |
Sliding overlap chars between chunks |
chunker.size_code |
4000 |
Soft target chunk size for code |
chunker.size_prose |
6000 |
Soft target chunk size for prose |
chunker.hard_max |
8000 |
Absolute max chunk size |
| Tagger | ||
tagger.zero_shot |
true |
Enable zero-shot topic tagging |
tagger.llm |
false |
Enable LLM tagging: memories stored by learn/ingest/refresh are classified by the dashboard’s background tagging job (tagging) |
tagger.peer |
true |
With tagger.llm off here, leave LLM tagging to a paired device that runs it: new memories wait for its tags, which sync brings back (ADR 0138) |
tagger.llm_provider |
local |
LLM provider: anthropic / openai / ollama / vllm / gemini / local |
tagger.llm_model |
— | Server model override (default per provider); local uses GGUF settings |
tagger.llm_base_url |
— | LLM tagger API base URL override |
Tagger — local model (llama-cpp, provider=local) |
||
tagger.local.model_repo |
unsloth/Qwen3.5-4B-GGUF |
HF repo for GGUF auto-download |
tagger.local.model_file |
Qwen3.5-4B-Q4_K_M.gguf |
GGUF filename within the repo |
tagger.local.model_path |
— | Absolute path to a local GGUF (overrides repo/file) |
tagger.local.n_ctx |
8192 |
Tagger context window in tokens |
tagger.local.n_gpu_layers |
-1 |
GPU layers to offload (-1 = all) |
| Redaction | ||
redact.privacy_filter |
false |
Also redact the secrets and account numbers OpenAI Privacy Filter finds, after the always-on rules. ~945 MB download on first use; seconds per chunk on a CPU, on a GPU where setup installed ONNX Runtime’s GPU build and the model’s first-use check passes (ADR 0122); a write fails rather than store less redacted text when the model cannot run. See secret redaction |
| Ingest (docs + URLs) | ||
ingest.doc_formats |
pdf,docx,pptx,xlsx,csv,epub,rtf,html,eml,json |
Comma-separated doc formats enabled for the file walker |
ingest.ocr_enabled |
true |
Local OCR for scanned PDFs and images. Setup installs RapidOCR (including its small Chinese/English models), Pillow and the PDFium renderer; no system tools are required. Explicit saved false stays off. See ADR 0076. |
ingest.ocr_lang |
eng |
eng or auto uses the bundled Chinese/English recognizer. Other Tesseract codes (e.g. eng+fra) use the compatibility backend and require system Tesseract plus the selected language data. |
ingest.max_doc_size_mb |
25 |
Largest document in MiB (1–4096) read for extraction: PDFs, Office files, EPUB, RTF, HTML, email, JSON and images on disk (checked by file size before opening), and every URL download (Content-Length, then a streamed byte cap). A refused document is an error, keeps its prior chunks and marks the run incomplete. Text files use ingest.max_file_bytes; audio uses audio.max_file_size_mb. |
| Audio ingestion | ||
audio.enabled |
true |
Include audio files in ingestion/refresh; the selected model downloads only on first uncached use |
audio.model |
large-v3 |
Multilingual CPU model: large-v3 (~2.88 GiB, most accurate, about as slow as real time), large-v3-turbo (~1.51 GiB, about twice as fast) or small (~464 MiB) (ADR 0178) |
audio.diarize |
true |
Tell voices apart in meeting recordings: who said what within the microphone and within the computer’s audio. The two models (about 32 MB) download on the first transcription; adds about 0.07–0.11× each channel’s duration (ADR 0180) |
audio.language |
auto |
Detect each window’s language, or pin ro / en; output is transcribed, not translated |
audio.max_file_size_mb |
1024 |
Positive per-audio-file size limit in MiB, independent of document limits |
audio.max_duration_minutes |
120 |
Positive decoded-audio duration limit in minutes |
| URL ingestion | ||
ingest.url_timeout_sec |
30 |
HTTP connect timeout for URL fetch |
ingest.url_read_timeout_sec |
300 |
Per-chunk read timeout for URL fetch (raise for very large files) |
ingest.url_user_agent |
imprint/1.0 |
HTTP User-Agent header |
ingest.url_respect_robots |
true |
Check the site’s robots.txt against ingest.url_user_agent before ingesting, following links (disallowed links are not queued) or refreshing a URL; read once per site per hour (retried after five minutes when it could not be read). 4xx (e.g. 404) = no rules, allowed; 5xx or unreachable = site refused for now (RFC 9309). Refused URLs are reported and keep prior content. false never fetches robots.txt. |
ingest.use_kreuzberg |
false |
Use kreuzberg (ELv2) for doc extraction — replaces PDF/DOCX/image extractors, adds archives (.zip, .7z, .tar, Apple iWork, ODF, .eml, images via OCR). Turning it on installs kreuzberg and accepts its license (ADR 0121); the built-in extractor handles a file kreuzberg cannot. ELv2 — not permitted for SaaS/hosted use. |
ingest.use_obscura |
false |
Use obscura headless browser for HTML URL fetch (JS rendering). Turning it on downloads the pinned obscura release into data/obscura-bin/ (an obscura on PATH also works). Enables full-fidelity ingestion of JS-heavy SPAs and docs sites. |
ingest.url_follow |
false |
Follow same-domain links found on ingested pages (BFS crawl) |
ingest.url_follow_depth |
1 |
Max link-follow depth (1 = links on seed pages only) |
ingest.url_follow_max |
20 |
Max discovered URLs to queue per ingest run |
| Chat (dashboard panel) | ||
voice.avatar |
echo |
Echo’s look and voice: echo (a synaptic core whose neurons fire as it thinks, around an eye that follows the pointer; a robotic voice), iris (indigo straight cut, pale lavender eyes; a female voice) or kai (spiky orange hair, a determined frown; a male voice). Also chosen on the Voice page and in Companion settings. See Echo voice. |
voice.runtime_dir |
— | Explicit external cache containing the built native live speech worker for Windows x64, Apple silicon macOS or Linux x64; IMPRINT_VOICE_RUNTIME_DIR overrides it. See Echo voice. |
voice.wake.idle_seconds |
15 |
Seconds a conversation that “Hey Echo” started waits for you after Echo’s reply before it ends and Echo goes back where it was (5–120). Turning “Hey Echo” on or off is a choice in Echo’s settings in the desktop app, kept on that device, not a setting. See Echo voice. |
voice.save_conversations |
true |
Save Echo’s conversations in Chat history, as text on this device: your questions as Echo heard them and its answers. Audio is never saved. Turning it off stops saving the conversation in progress at once; turning it on saves the next one. Also in Companion settings. See Echo voice. |
chat.provider |
local |
Chat provider: local / vllm / openai / ollama / gemini / anthropic |
chat.model |
— | Model name for remote providers (default per provider) |
chat.base_url |
— | Base URL override for OpenAI-compat providers |
chat.local_model |
default |
Local chat model: default (Qwen3.5 4B through chat.model_repo/chat.model_file/chat.model_path), qwen3.5-35b-a3b (pinned 22 GB MoE, offered with 32 GB of memory), or a custom GGUF: a path, <owner>/<repo>/<file>.gguf or a Hugging Face file URL (ADR 0153) |
chat.model_repo |
unsloth/Qwen3.5-4B-GGUF |
HF repo for GGUF auto-download (local provider) |
chat.model_file |
Qwen3.5-4B-Q4_K_M.gguf |
GGUF filename within the repo |
chat.model_path |
— | Absolute path to a local GGUF (overrides repo/file) |
chat.n_ctx |
8192 |
Chat context window tokens |
chat.n_gpu_layers |
-1 |
GPU layers to offload (-1 = all) |
chat.max_tokens |
1024 |
Max tokens per chat response |
chat.temperature |
0.3 |
Chat sampling temperature |
chat.research_timeout_sec |
300 |
Research time budget in seconds (10–3600), excluding setup, the reserved final answer and time spent waiting for your answer to a question (ADR 0159). Env: IMPRINT_CHAT_RESEARCH_TIMEOUT. |
chat.research_token_budget |
65536 |
Tokens research may use across its coordinator and worker requests (1024–2000000): each request is charged for its prompt past the prefix the previous request already processed plus the output it generates; its whole output allowance must fit before it starts (ADR 0157). Env: IMPRINT_CHAT_RESEARCH_TOKENS. |
chat.research_no_progress_rounds |
4 |
Consecutive read rounds without new evidence before synthesis (2–20). Env: IMPRINT_CHAT_RESEARCH_PATIENCE. |
chat.analysis_workers |
2 |
Logical workers for oversized chat questions/evidence, executed sequentially. 1–8 keep separate rolling notes; 0 disables section analysis. Settings → Chat → Sequential analysis workers. Env: IMPRINT_CHAT_ANALYSIS_WORKERS. See local helper models. |
chat.tool_format |
auto |
Tool-call format of the local model: auto (detected from the GGUF’s chat template), qwen-xml, hermes, llama3, mistral, or none (chat without tools). Remote providers ignore it. See local helper models. |
| Session summarizer (opt-in, Stop hook) | ||
summarizer.enabled |
false |
Enable the LLM-based session summarizer |
summarizer.provider |
local |
Provider: local / ollama / vllm / anthropic / openai / gemini |
summarizer.model |
qwen3.5:4b |
Model name for the chosen provider |
summarizer.base_url |
— | API base URL override |
summarizer.min_messages |
5 |
Skip sessions with fewer messages |
summarizer.max_input_tokens |
5000 |
Truncate transcript before summarizing |
| Recall (MCP tool) | ||
recall.llm |
false |
Let the local model plan recall reads and answer from the evidence |
recall.llm_timeout_sec |
30 |
Seconds one recall waits for the local model (planning and answering together) |
organize.background |
false |
Organize new and changed memories when the queue is idle, after background tagging (ADR 0132) |
organize.auto_move |
true |
Move catch-all sources with strong evidence without review; weaker proposals still wait (ADR 0133) |
| Workspace selection | ||
workspace.unassigned |
— (the active workspace) | Workspace for agent sessions outside an assigned project, such as desktop app chats. Choices are this device’s workspaces; deleting the chosen one resets it. Env IMPRINT_UNASSIGNED_WORKSPACE. See workspace per project |
| Device sync | ||
sync.recordings |
false |
Exchange meeting recordings, audio included, with paired devices that also turned it on; finished recordings travel by themselves in resumable 1 MiB pieces, and deleting a recording or its audio does the same on the other device. Env IMPRINT_SYNC_RECORDINGS (ADR 0177) |
| Desktop app | ||
desktop.floating_orb |
false |
Show the floating Imprint button while the dashboard is hidden; minimizing then hides to the tray. Off, minimizing keeps the dashboard in the taskbar, closing hides it to the tray, and the button appears only during a recording. Read each time the dashboard is hidden. Env IMPRINT_DESKTOP_FLOATING_ORB (ADR 0174) |
| Dashboard | ||
ui.mode |
simple |
The dashboard this device shows: simple (the everyday pages, a short list of settings, plain words) or advanced (every page and setting). Presentation only: both use the same memory and settings. The dashboard API writes it once at its first start with this setting: advanced for an installation that had run a job or served an agent session, simple otherwise. Env IMPRINT_UI_MODE (ADR 0182) |
ui.tutorials |
true |
Start a page’s short tutorial the first time it opens; off, the top bar’s help menu still shows it. Written false at that first start for an installation already in use. Which tutorials were seen is kept per browser. Env IMPRINT_UI_TUTORIALS (ADR 0183) |
| Updates | ||
update.channel |
stable |
Releases this installation updates to: stable, or dev for dev builds as well. The dashboard’s update notice and imprint update follow it; --stable or --dev overrides it for one run. Env IMPRINT_UPDATE_CHANNEL. See updating an existing install |
update.auto |
true |
Install new releases by itself, while the dashboard runs, when nothing would be interrupted: no job, recording, chat reply or Echo conversation, no agent call for 15 minutes, no keyboard or mouse input for 10 minutes (Windows, macOS). macOS package installs still ask. Env IMPRINT_UPDATE_AUTO (ADR 0175) |
| Other | ||
collection |
memories |
Default Qdrant collection name (workspace suffix is appended automatically) |
Research has no fixed tool-round cutoff. It continues while new evidence appears within the time and token budgets, then reserves a tools-free final answer. Repeated evidence across different queries, alternating cycles, empty reads and recurring errors can stop research earlier. Writes are excluded from the stagnation heuristic. Settings → Chat exposes Research time budget (seconds), Research token budget and Rounds without new evidence.
Token accounting conservatively reserves each formatted prompt plus its maximum
output, including section workers and repeated prompt processing. Local prompt
counts use the model tokenizer; remote prompts use an estimate. Unused output
reservations are not refunded; this is not a provider billing or monetary cap.
Time checks are cooperative: an already-running blocking tool or native prompt
prefill may finish after the deadline. Pending calls are then skipped explicitly.
The final answer has separate chat.max_tokens space and a 120-second deadline.
It is written from a deduplicated digest of what research found, not from the
raw research turns, and lists the sources it cites. An answer that cannot finish
ends with a short list of the strongest findings
(ADR 0127).
The old chat.max_tool_iters key and IMPRINT_CHAT_MAX_TOOL_ITERS are ignored;
old config entries stay on disk but do not appear in Settings or cut research
off at six or twelve rounds. See ADR 0102.
Each setting also has an env var, IMPRINT_* except HF_TOKEN (shown via imprint config get <key>). Env vars still work and take priority over config.json.
Recall
Section titled “Recall”The recall MCP tool (MCP tools) works without any model. recall.llm lets a model plan its reads and condense the evidence into a cited answer, but only a model on this computer:
tagger.llm_provider=local: the GGUF configured undertagger.local.*, loaded by llama.cpp inside each MCP server process on its first recall. Recall does not download it; until the file exists (LLM tagging or the dashboard chat downloads the default one), recall notes that the model is missing and runs without it.tagger.llm_provider=ollamaorvllm, whentagger.llm_base_url(or the provider’s defaultlocalhostURL) points to a loopback address. A server elsewhere on the network does not count.- Any other provider (
anthropic,openai,gemini) leaves recall without a model, with a one-line note. Recall questions and evidence are never sent to a remote API.
recall.llm is independent of tagger.llm: recall can use the model while ingest tagging stays off. Both use the same provider settings.
recall.llm_timeout_sec bounds the time one recall waits for the model, planning and answering together. At the limit recall returns what it returns without the model (the search, or the evidence of the steps that ran) and notes why. llama.cpp stops generating at its next token, but a prompt it is already reading finishes in the background first. On a CPU the default usually lets the model plan but not answer (measured: 1 of 16 answers within 30 s, about 25–45 s per answer with more time); see benchmarks. Some clients (Codex, for one) default to a 60 s MCP tool timeout, so keep the limit below the client’s. Each Claude Code or Codex session runs its own MCP server, so with the local provider every session that recalls holds its own copy of the model in memory (about 3 GB for the default Qwen3.5-4B); ollama or vllm share one.
imprint config set recall.llm trueimprint config set recall.llm_timeout_sec 60 # e.g. CPU-only machinesLocal service boundaries
Section titled “Local service boundaries”Managed Qdrant binds both HTTP and gRPC to loopback, disables cluster mode and browser CORS, and refuses nonlocal qdrant.host values before connecting or spawning. localhost is bound as 127.0.0.1. Using an external Qdrant requires explicitly setting qdrant.no_spawn=true; its network isolation, authentication and TLS are then the operator’s responsibility. Imprint cannot establish the bind policy of an already-running or independently managed server.
The dashboard and its API share a loopback listener on port 8420. Requests must have a loopback connection address and local Host/Origin headers. The supported launcher ignores forwarded client headers. Container mode is an explicit exception to the connection-address check because Docker NAT changes the peer address: publish only 127.0.0.1:8420:8420 and use a trusted container network. Host/Origin checks do not authenticate a remote client that forges those headers. See Docker and ADR 0024.
Ollama is independently managed. Imprint’s Ollama endpoints default to http://localhost:11434/v1; configure the Ollama process itself with OLLAMA_HOST=127.0.0.1:11434, then restart it. A local client URL does not constrain the server’s listening interfaces. See Ollama’s server configuration.
Env-only settings
Section titled “Env-only settings”These don’t live in config.json because they’re path-shaped infra knobs the config system is not designed to manage. Set them at process start:
| Env var | Default | Purpose |
|---|---|---|
IMPRINT_DATA_DIR |
<repo>/data or ~/.local/share/imprint/data |
Root for workspaces, Qdrant storage, SQLite graphs, config.json. Override for multi-install setups or when running out of a read-only tree. |
IMPRINT_QDRANT_BIN |
auto (downloaded into data/qdrant-bin/) |
Path to a system-installed qdrant binary, bypassing the pinned auto-download. |
IMPRINT_MCP_IDLE_S |
Retired | Ignored compatibility setting. HTTP Qdrant clients retain their connection pool until explicit cleanup or process exit; there is no embedded lock to release. See ADR 0022. |
Source limits and operation workspace
Section titled “Source limits and operation workspace”ingest.max_file_bytes (IMPRINT_INGEST_MAX_FILE_BYTES) defaults to 2,000,000 for source-text indexing. Oversize/undecodable sources retain their old chunks and report an incomplete run; there is no silent 50,000-character cutoff. Documents (local and downloaded) are capped separately by ingest.max_doc_size_mb (MiB) and fail the same way. LLM tagging remains opt-in even with a local provider; tagger.zero_shot=false also disables batch backfill.
IMPRINT_DATA_DIR selects persistent storage consistently in Go and Python. IMPRINT_WORKSPACE is an operation override used by queued subprocesses; it must name an existing valid workspace. It outranks a directory’s own workspace, so set in a client’s MCP server environment it pins every session of that client. New jobs capture it at submission so a later dashboard switch cannot redirect them. imprint sync --workspace NAME selects the workspace for one session the same way, without changing the active workspace. MCP calls resolve their workspace at entry. Per-project binding is a project file this device approved (workspace per project, ADR 0072).
See embedding rebuild instructions and local helper setup. Explicit persisted values override new defaults. Model configuration changes require restarting long-running processes. Local summaries share tagger.local.*; local non-thinking and grammar controls do not configure external servers.