1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
[]
# The crate is published as `mindfork`. The GitHub repository stays
# `mindfork-rs`, and so do the Linux package, `/usr/lib/mindfork-rs/`, the
# release assets and the data directory: there the `-rs` says "written in
# Rust" and keeps the project apart from an unrelated company of the same
# name (docs/research/binary-rename.md §10).
= "mindfork"
= "0.11.0"
= "2024"
# The one sentence that describes the app. The same words are in site/zola.toml,
# packaging/nfpm.yaml and the README masthead — one description, four places that
# have to agree (docs/research/public-documents.md N4).
= "A terminal AI chat written in Rust: local models via llama.cpp or OpenAI, Anthropic, Gemini and Grok in the cloud, with persistent memory, notes, RAG and tools."
= "MIT"
= "README.md"
= "https://mindfork.io"
= "https://github.com/vshylov/mindfork-rs"
= "https://github.com/vshylov/mindfork-rs/blob/main/docs/manual.md"
= ["llm", "chat", "tui", "llama-cpp", "ai"]
= ["command-line-utilities", "text-processing"]
# The toolchain this is developed and gated on (rust-toolchain.toml pins the same
# version): a pin, not a floor discovered by bisecting older compilers.
= "1.96"
# What a build actually needs, measured: without this the package is 700 files
# and 26.8 MiB (9.0 MiB compressed) and carries docs/, assets/ and the website —
# none of which takes part in compiling anything.
= [
"src/**/*",
"build.rs",
"locales/*.json",
"syntaxes/**/*",
"dictionaries/**/*",
# The Windows icon `build.rs` embeds into the .exe. Caught by the
# verification build, which warned "icon not found, .exe will ship without
# it" — a crate that installs a binary without its icon.
"assets/mindfork.ico",
"README.md",
"CHANGELOG.md",
# Compiled into the binary, not shipped beside it: the `F1` legal tabs read
# these through `include_str!` (`shared/credits.rs`), so leaving any of them
# out is a build failure rather than a missing document.
"LICENSE",
"DISCLAIMER.md",
"PRIVACY.md",
"docs/legal/*",
]
# The user-facing command is the brand name (`shared/credits.rs::APP_NAME`, held
# by a gate test there); the package keeps the project name. See
# docs/research/binary-rename.md.
[[]]
= "mindfork"
= "src/main.rs"
[]
= "8.0.1"
= "1.0.103"
# Clipboard (Windows + Linux). `image-data` is on for pasting an image into a message
# (spec §9.10): it pulls the `image` crate, which this project already depends on for
# preparing attached images, so the feature costs a couple of extra decoders rather than a
# new dependency tree. No other default feature is wanted.
= { = "3.4.1", = false, = ["image-data"] }
= "0.3.6"
= "0.1.89"
# Decoding audio from TTS-provider responses (Gemini returns PCM as base64
# inside `inlineData.data`). Already in the graph transitively; the version is pinned to the one
# in Cargo.lock, so as not to spawn a duplicate (precedent — quick-xml). See shared/tts.
= "0.23.1"
= "1.25.0"
# AEAD encryption of stored API keys on Linux (the `machine-key-v1` scheme: the key
# is derived from /etc/machine-id via HKDF). RustCrypto, pure Rust. On Windows
# the same task is solved by the system's DPAPI (windows-sys below). See shared/secrets.rs,
# docs/research/api-key-storage.md.
= "0.10.1"
# Guessing the encoding of a fetched page that declares none usable (`fetch_url`,
# `web_search`'s result pages): the detector Firefox runs on unlabelled pages, by the
# encoding_rs author. Pure Rust. See shared/http_text.rs, docs/research/page-charset.md.
= "1.0.0"
= { = "0.4.45", = ["serde"] }
= "0.29.0"
# Standard OS folders for non-portable data-storage mode
# (Windows %APPDATA%\mindfork-rs, Linux ~/.local/share/mindfork-rs). See shared/paths.rs.
= "6.0.0"
# Decoding a fetched page in the encoding it declares (windows-1251, KOI8-R, Shift_JIS,
# GBK, ...). reqwest's own `charset` feature left with its default features, and it only
# ever read the header — a page declaring itself in `<meta>` would stay unreadable.
# Already in the graph transitively (via pdf-extract); the version is pinned to the one in
# Cargo.lock, so as not to spawn a duplicate. See shared/http_text.rs.
= "0.8.35"
= "0.2.3"
# Unpacking the `wasmer` archive (tar.gz) in `mindfork sandbox setup` (Phase 2 of the Python
# sandbox). flate2 — pure Rust (miniz_oxide, no C dependencies), tar — pure Rust.
= "1.0"
= "0.3.32"
# Deriving the API-key encryption key from machine-id (HKDF-SHA256; `sha2` is already present).
# 0.13 is the generation built on `digest` 0.11 — the one `sha2` 0.11 speaks; 0.12 is
# `digest` 0.10 and will not type-check against it.
# The pattern is prescribed by systemd's documentation — machine-id isn't used raw.
= "0.13.0"
# Decoding, downscaling and re-encoding images attached with `/image attach`
# (spec §9.10). `default-features = false` keeps only the decoders users actually
# drop into a chat; the encoders we emit are png and jpeg alone, because xAI accepts
# nothing else and normalizing once at attach time keeps the provider matrix uniform.
# Without it a phone photo would ride **every** turn at full size, and a webp would be
# attachable on three providers out of four. See docs/research/multimodal-images.md §5 F3.
# The unified diff the changes screen shows: baseline bytes against the file as
# it stands now, per file the assistant touched (spec §9.12). Diffing is one of
# those problems that looks like an afternoon and is not - Myers with the usual
# heuristics, grapheme-aware, and a `unified_diff` writer that already groups
# hunks. Pure Rust, no build script, and the only crate in the graph that needs
# it. See features/workspace_diff.rs.
= "3.2.0"
# Walking an attached project the way a developer expects: `.gitignore` (nested,
# plus the repo's exclude file), hidden entries and `.git/` itself are skipped by
# `code_list`/`code_grep`. A hand-rolled skip list gets the common cases and then
# lies about every project-specific ignore - and on a Rust checkout the difference
# is `target/`, which is most of the bytes on disk. ripgrep's own crate; pure Rust,
# and `walkdir`/`regex-automata` were already in the graph. See features/tools/code.rs.
= "0.4.23"
= { = "0.25.9", = false, = [
"png",
"jpeg",
"webp",
"gif",
"bmp",
] }
# Rendering ```mermaid blocks in the feed as Unicode/ASCII graphics (flowchart/sequence,
# a whitelist + a hard fallback to the source). Pure Rust (only ascii-dag is net-new);
# ≥0.56.1 is mandatory — 0.56.0 panicked/corrupted labels on Cyrillic (our upstream fix,
# leboiko/markdown-reader#29/#30). 0.57.0 closed our feature request #32 (max_width as a
# hard budget: RenderOptions::max_width_strict + Error::TooWide) — we do NOT use it,
# our post-check via display_width is more accurate (see mermaid.rs). See docs/research/mermaid-ascii-rendering.md.
= "0.57.0"
# Extracting text from PDFs for indexing into RAG (`/rag add`, stage B1b). Pure Rust
# (lopdf + font/CFF/CMap parsers, no C dependencies); extraction quality is
# "best effort" — a scanned/image-only PDF yields empty text, which is acceptable.
# See features/doc_extract.rs, docs/rag-sources-retrieval.md §B1b.
= "0.12.0"
# Decoding `%XX` in the last path segment of an image URL into the display name
# (`/image attach <url>`). Already in the graph transitively (via `url`/`reqwest`); the
# version is pinned to the one in Cargo.lock, so as not to spawn a duplicate.
# See features/image_fetch.rs.
= "2.3.2"
= { = "0.13.4", = false }
# Parsing `word/document.xml` inside a DOCX for indexing into RAG (`/rag add`, stage B1b).
# Ours alone now: syntect is taken with `default-features = false`, so the plist
# edge that once pinned this version is gone (`cargo tree --invert quick-xml`
# prints one edge). Kept at the version that carries the fixes for
# RUSTSEC-2026-0194 and -0195, which is why `deny.toml` no longer ignores them.
# See features/doc_extract.rs.
= "0.42.0"
# `unstable-rendered-line-info` is on for one call: `Paragraph::line_count`, which the
# tool-confirmation popup sizes itself by. The alternative is a second implementation of
# word wrapping whose only job is to agree with ratatui's own — and where it disagreed,
# the user approved code the popup had clipped out of view. The version is pinned, so a
# change to the unstable API can only arrive with a deliberate upgrade, and the compiler
# catches it. See src/screens/chat/popups.rs.
= { = "0.30.1", = ["unstable-rendered-line-info"] }
# `code_grep`'s pattern (spec 9.12). Already in the graph transitively; the version
# is pinned to the one in Cargo.lock, so as not to spawn a duplicate (precedent -
# quick-xml, base64). Deliberately *not* `grep-searcher`/`grep-regex`: matching line
# by line over files this walker already opened is a dozen lines, and the two extra
# crates would buy throughput this app has no use for. See features/tools/code.rs.
= "1.12.4"
# Playing synthesized speech (`/tts`) within the application process: a source
# queue (`Player::append`) gives a "synthesize chunk N+1 while chunk N
# plays" pipeline and instant cancellation. `default-features = false` — only the needed
# decoders (cloud providers' raw PCM bypasses the decoder via `SamplesBuffer`). The only
# new system dependency — ALSA on Linux (`libasound2-dev` at build time).
# See docs/research/tts.md §6.
= { = "0.22.0", = false, = ["playback", "wav", "mp3"] }
= { = "0.13.4", = false, = ["json", "stream", "rustls", "form"] }
# `serialize` — `Connection::deserialize_read_exact`: `mindfork stats <archive>` reads a
# backup's `data.db` from the zip entry straight into memory, so no decrypted copy of an
# encrypted backup's database ever lands on disk (docs/history/data-stats.md F5). A module of
# rusqlite itself; it adds no crate.
= { = "0.40.1", = ["bundled", "serialize"] }
= "0.27.0"
= { = "1.0.228", = ["derive"] }
= "1.0.150"
# Integrity check (sha256) of downloaded sandbox assets (Phase 2). Pure Rust.
= "0.11"
= "0.3.3"
= "0.4.2"
= "0.1.9"
# Detecting the language from the OS locale on a fresh install (no defaults.json/settings.json).
# Cross-platform (Windows/Linux), pure Rust. See shared/i18n::detect_os_language.
= "0.3"
# `default-syntaxes` is off: the app loads its own dump (syntect's bundled
# syntaxes **plus** the vendored grammars, assembled by build.rs), so syntect's
# own packdump would be dead weight. `html`/`metadata` are unused, and the theme
# is built from our palette (ADR 0003), so `default-themes`/`plist-load` are not
# needed either; `yaml-load` belongs to the build script alone. Measured on the
# release binary: 20 397 056 -> 20 582 400 bytes — **+181 KiB** for 19 grammars.
# (An earlier guess that trimming these would make the binary *smaller* was
# wrong: under LTO the linker already drops syntect's unused dumps.)
= { = "5.3.0", = false, = [
"parsing",
"regex-onig",
"dump-load",
] }
# Unpacking the `wasmer` tar archive (see flate2 above). Pure Rust.
= "0.4"
= "2.0.18"
= { = "1.52.3", = ["rt-multi-thread", "macros", "sync", "process", "time", "io-util", "rt"] }
= "0.7.18"
= "0.1.44"
= "0.2.5"
= { = "0.3.23", = ["env-filter"] }
= "0.6.5"
= "1.13.3"
= "0.2.2"
= { = "1.23.3", = ["v4", "v5", "serde"] }
# Backup/restore of user data (a zip archive with a
# configurable deflate compression level). default-features=false disables bzip2/zstd
# (native C dependencies) — only deflate + store are needed. `aes-crypto` adds
# password-protected backups (WinZip AES-256, spec §12.3): it pulls RustCrypto
# crates only (pbkdf2/sha1/constant_time_eq on top of aes/hmac, already in the
# graph) — still no C dependency. See features/backup.rs, docs/backup-password.md.
= { = "2.2.2", = false, = ["deflate", "aes-crypto"] }
# Windows specifics:
# * `Win32_System_JobObjects` — an OS-level memory limit for the Python sandbox via a Job
# Object (`JOB_OBJECT_LIMIT_PROCESS_MEMORY`), protecting the host from OOM during a runtime
# script. Windows only: on Unix, rlimit is unreliable with the V8 backend (it reserves a
# large virtual address space). See ADR 0005 / features/tools/python.rs.
# * `Win32_Security_Cryptography` — DPAPI (`CryptProtectData`/`CryptUnprotectData`)
# for machine-bound encryption of stored API keys. See shared/secrets.rs,
# docs/research/api-key-storage.md.
# * `Win32_UI_Input_KeyboardAndMouse` + `Win32_UI_WindowsAndMessaging` — resolving a
# character back to its physical key through the active/installed keyboard layouts
# (`VkKeyScanExW`/`MapVirtualKeyExW`), so `Ctrl+<letter>` shortcuts work under any
# layout without a per-language table. See shared/keys.rs,
# docs/research/layout-independent-hotkeys.md.
[]
= { = "0.61", = ["Win32_System_JobObjects", "Win32_Foundation", "Win32_Security", "Win32_Security_Cryptography", "Win32_System_Console", "Win32_System_Threading", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
# `killpg` — ending a spawned process **tree** on unix (the code workspace's
# build/run/test commands: `cargo build` outlives its own kill through the
# `rustc` children it spawned). The Windows half of the same job is a Job Object
# from `windows-sys` above; unix needs one libc call, so the dependency is one
# call wide and unix-only. See shared/proc.rs, docs/code-workspace.md §3.3.
[]
= "0.2"
[]
# Assembling the vendored syntax grammars into a dump (see build.rs and
# syntaxes/SOURCES.md). The regex engine must be the **same as the runtime's**
# (`regex-onig`): loading a grammar compiles its regexes, so the build is also
# what validates them — and the two engines disagree. Measured: with
# `regex-fancy` the Swift grammar is rejected over a subroutine call (`\g<1>`),
# which oniguruma accepts and the app therefore highlights fine. Validating with
# a different engine than the one that runs would reject working grammars and,
# worse, could accept broken ones.
= { = "5.3.0", = false, = [
"parsing",
"default-syntaxes",
"regex-onig",
"yaml-load",
"dump-create",
"dump-load",
] }
[]
= "0.1.31"
[]
= "3.27.0"
# `test-util` enables pausing/fast-forwarding tokio's virtual time in tests
# (`start_paused`) — deterministic tests of the server-restart debounce without
# real waiting. Test builds only (feature unification dev-deps).
= { = "1.52.3", = ["test-util"] }
# The release profile (Windows + Linux): size/speed optimization.
# panic=unwind is kept deliberately — drop guards/a panic hook are needed to restore
# the terminal on a panic (ratatui::init installs the hook; M0).
[]
= 3
= true
= 1
= true