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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
[]
= "roteiro"
= "Roteiro: a provenance-tagged knowledge graph for your codebase — structure, intent, and context in one queryable store"
= "README.md"
= true
= true
= true
= true
= true
= true
= true
[]
= { = true }
= { = true }
# The analyzer contract and the ingest path behind it (`roteiro security …`).
# Optional so the seam has a name that Stages 22/24 can hang their
# `exec-subprocess` / `exec-boxlite` backends off — but enabled by default,
# because ingest is the zero-install path (ADR-0014) and pulls no third-party
# dependency the workspace does not already have.
= { = true, = true }
= { = true }
= { = true, = true }
# `remote` only: the remote model tier's consent gate, payload allow-list and
# egress record (ADR-0019). Optional and **off by default** — see the `remote`
# feature below, and do not "complete the job" by adding it to `default`.
= { = true, = true }
= { = true }
= { = true, = true }
= { = true }
= { = true }
= { = true }
# Shared by `serve`/`mcp` (a SIGHUP handler that reloads the workspace registry
# without a restart, ADR-0008) and `explorer` (the standalone axum server binds a
# `tokio::net::TcpListener` and runs on a small current-thread runtime).
# `unsafe_code = "forbid"` rules out a raw signal handler, so use tokio's signal
# support; `net` pulls tokio's I/O driver, which both the signal handling and the
# TCP listener require. Declared here so this crate is self-contained, not reliant
# on another crate's features. Pure Rust — no C/C++ toolchain.
= { = "1", = ["rt", "signal", "net", "time"], = true }
# `explorer` only: the read-only `/v1/graph/*` JSON API router (axum 0.8). Pure
# Rust — no llama.cpp, no `rto-serve` — so the explorer builds and tests without a
# C/C++ toolchain.
= { = "0.8", = true }
# Layered project/user configuration (ADR-0007). TOML only — `serde_yaml` is
# unmaintained. Permissive, `cargo deny`-clean.
= "1.1"
# Structured logging/telemetry groundwork (ADR-0011). `tracing` is the event
# facade; `tracing-subscriber` builds the layered subscriber (the human-text
# stdout layer, unchanged and default, plus the opt-in structured file layer);
# `tracing-appender` provides the time-based rolling file appender and the
# non-blocking writer (whose worker guard is held for the process lifetime so a
# slow disk can never stall the app). No OTLP/network exporter yet — the file
# layer emits OpenTelemetry-shaped JSON lines a future collector can ingest.
= "0.1"
= { = "0.3", = ["env-filter", "fmt", "registry"] }
= "0.2"
# `inference-local-models` only: consent-gated model download (rustls, no
# platform TLS). Blocking + pure-Rust.
= { = "3", = true }
# Restore the default SIGPIPE disposition (`SIG_DFL`) at startup so the CLI dies
# quietly on a closed stdout pipe (`roteiro query … | head`) instead of panicking
# with a broken-pipe backtrace — the classic Rust-CLI footgun (Rust sets SIGPIPE
# to `SIG_IGN`, so `println!` hits EPIPE and panics). We forbid `unsafe_code`
# workspace-wide, which rules out a raw `libc::signal` call here, so we lean on
# this tiny, contained wrapper (one `libc::signal(SIGPIPE, SIG_DFL)`, a no-op off
# Unix). libc is already in the tree; MIT-licensed. See `main`'s first line.
= "0.1.3"
[]
# Write a codegraph-schema SQLite fixture in the `import --from codegraph` test.
= { = true }
# `explorer` route tests: drive the axum router in-memory (no TCP bind) with
# `tower::ServiceExt::oneshot`, mirroring `rto-serve`'s server tests.
= { = "1", = ["rt", "macros"] }
= { = "0.5", = ["util"] }
= "0.1"
# `tests/ci_release_pr_parity.rs` reads `.github/workflows/ci.yml` structurally —
# job by job, step by step — rather than grepping it, because the thing it
# asserts is the *shape* of a job and a `contains` over the text would be
# satisfied by the comments that discuss that shape at length. Adds no crate to
# the tree: `rto-graph` already depends on `yaml-rust2` non-optionally (for
# `config_keys`), so it is in the lockfile and in every build of this binary
# already, and neither `cargo deny` nor `cargo audit` gains a new surface. Keep
# the version in step with `crates/rto-graph/Cargo.toml`.
= "0.11.0"
[]
# Ingest is available in a stock install — ADR-0014 lists it as "always, no
# feature" — so `execution` is on by default. It stays a *named* feature because
# the subprocess and sandboxed backends land behind subfeatures of it, and so
# `--no-default-features` still yields a binary with no analyzer surface at all.
#
# `models` is on by default because `roteiro model pull` is the **prerequisite
# for working offline**, and without it that command does not exist: the clap
# variant is `#[cfg(feature = "models")]`, so a stock install answered
# `unrecognized subcommand` rather than degrading gracefully. "Prepare once, then
# work offline" — the story `docs/OFFLINE_SETUP.md` tells — was unreachable from
# the shipped default. It is cheap to carry: measured on an unloaded 18-core
# machine, a cold release build is within noise of the old default (28.0–30.5 s
# before, 29.2–31.1 s after) and the binary grows 2.17 MiB over 20 extra crates
# (209 → 229). It adds **no new host-toolchain class**: `ureq`'s rustls stack
# does reach C and pregenerated assembly via `ring`, but the default build
# already compiles C for bundled SQLite and 18 tree-sitter grammars, so this
# needs the same `cc` and no more — never C++, cmake or libclang. Do not restate
# this as "pure Rust"; it is one `cc` away from that and the distinction is the
# whole point. `cargo deny check` is clean at this set; every licence it
# moves into the default binary (`ISC`, `BSD-3-Clause`, and
# `CDLA-Permissive-2.0` via `webpki-roots`) was already allow-listed with a
# recorded rationale — see `deny.toml` and ADR-0017.
#
# **`serve` deliberately stays off**, and this is the record so that nobody
# "completes" the job later:
# * it transitively builds llama.cpp from source — measured +24 s cold on 18
# cores, realistically **3–12 minutes on 2–4 cores**;
# * without `cmake` and `libclang` it does not degrade, it **panics in a build
# script** and `cargo install` fails. On Debian/Ubuntu `build-essential`
# supplies neither, so it turns zero extra packages into two;
# * `docs/VENDORED_DEPENDENCIES.md` records that the vendored llama.cpp has
# **13 published upstream advisories invisible to `cargo audit`**, which
# reads crate metadata and cannot see C++ vendored by a build script.
# Defaulting it on would ship that unmonitored surface to everyone rather
# than to the people who asked for inference.
# `exec-subprocess` is on by default so that the *other* half of "prepare once,
# then work offline" ships too. Enabling it compiles the capability to run an
# analyzer as a child process; it does not exercise it, and three things keep
# that true:
# * `--allow-unsandboxed` is still required **per run**. See below — with the
# build-time gate gone from the default path, that flag is now the only thing
# between a stock install and an unsandboxed execution, so it got *more*
# load-bearing here, not less.
# * Roteiro never installs an analyzer. `semgrep` and `osv-scanner` must
# already be on `PATH`, so the host owner has already opted into having them;
# a missing one is a named error, not a fetch.
# * A run records `isolation=none` in its evidence either way.
# `serve` is the one thing that stays off, for the reasons above.
= ["execution", "models", "exec-subprocess"]
# The `AnalyzerRunner` seam, `roteiro security ingest|list`, and — since the
# provisioning/execution split — `security prefetch|status` (ADR-0012, ADR-0014).
# It also carries `roteiro lint`'s command surface, because that reads types from
# `rto-exec`; the linter itself needs `exec-subprocess` below, and in a build
# without it the command parses and refuses by name rather than vanishing.
#
# Provisioning lives here rather than behind a backend feature because it
# executes nothing: it downloads, digests, pins and reports. Every `Command::new`
# in `rto-exec` is in `subprocess.rs` or `boxlite.rs`, both still gated. Gating it
# on a backend was also circular — `exec-boxlite`'s build script demands a
# verified runtime archive at compile time, and `prefetch` is what obtains it, so
# provisioning had to be reachable from a build with no backend at all.
#
# It takes `ureq` for that reason and no other: `prefetch --allow-download`
# fetches `osv-scanner`'s per-ecosystem OSV databases. `rto-exec` deliberately
# does **not** gain a network dependency — it takes the fetcher as a function
# argument, so the code that can open a socket lives in the CLI and is reachable
# only from `prefetch`. In the default set `ureq` is already present for
# `roteiro model pull`, so this costs nothing there; in a
# `--no-default-features --features execution` build it is the one crate the
# split adds.
# It also forwards `rto-render/execution`, which is what puts `security list` and
# `security status` on the **MCP** tool surface (issue #435). Forwarded from here
# rather than from `mcp` because this is the feature that decides whether this
# binary has an analyzer surface at all: the served-chat registry's two
# `security_*` tools are gated on `all(serve, execution)` and MCP's on
# `all(mcp, execution)`, so both surfaces gain and lose the pair together and
# `both_tool_surfaces_offer_the_same_tools` cannot be satisfied by only one of
# them. Cargo cannot express "only when `mcp` is also on", so a non-`mcp` default
# build carries the dependency edge and none of the code — and `rto-exec` is
# already in that build, so the edge costs nothing.
= ["dep:rto-exec", "dep:ureq", "rto-render/execution"]
# The subprocess analyzer *backend*: `roteiro security run` (ADR-0014). **On by
# default**, which is a change — it used to have to be asked for at build time as
# well as consented to per run.
#
# Rewriting rather than deleting that caution, because only half of it stopped
# being true. What the build-time default grants is the *presence* of the runner;
# what it does not grant is a run. `security run` still refuses without
# `--allow-unsandboxed` on every single invocation, still records
# `isolation=none` in the run's evidence, and still cannot fetch an asset — a
# cold cache fails and names the `prefetch` command. And it cannot install an
# analyzer: if `semgrep` is not on `PATH` the run stops with a message saying so
# and pointing at `roteiro security ingest`.
#
# So `--allow-unsandboxed` is now the *only* gate between a stock install and
# executing a third-party binary on this host with no isolation boundary. Do not
# weaken it "for consistency" because the build-time gate is gone; it is carrying
# the load the build-time gate used to share. ADR-0014 v1.1 records this.
#
# This feature also compiles **`roteiro lint`**, which under ADR-0020 v1.3 is
# sandboxed by default and — since no sandboxed builder exists yet — **refuses**
# unless host execution has been granted. An earlier revision of this note said
# `lint` ran here with no equivalent gate, on the reasoning that it is your own
# toolchain over the tree you are standing in. The owner overruled that, and
# correctly: that the *toolchain* is yours does not make the *code* yours when
# you are linting a branch you are reviewing, and `cargo clippy` has `cargo
# check` semantics, so it runs that branch's build scripts here.
#
# So both commands now gate host execution, and they gate it differently on
# purpose. `security run --allow-unsandboxed` is invocation-only: there is no
# config key, and a run files its verdict into the store as a fact about the
# repository. `roteiro lint` also accepts a standing grant — `[lint]
# allow_unsandboxed` in the *user's own* config, never a committed `roteiro.toml`
# — because it stores nothing and a person may reasonably say once that builds on
# their own machine are fine. Do not collapse the two into one key without
# deciding, in an ADR, whether a standing grant should also reach `security run`;
# that would make an invocation-only gate reachable without the invocation.
#
# Keeping it a named feature is still worth it: `--no-default-features --features
# execution` builds a Roteiro that provisions and ingests but **cannot execute**,
# which is the shape a locked-down environment wants.
= ["execution", "rto-exec/exec-subprocess"]
# The sandboxed analyzer backend: `roteiro security run` inside a digest-pinned
# OCI image in a microVM (ADR-0014). **Off by default.**
#
# Build requirements, which are real and not optional: `protoc >= 3.12` on the
# host, and a sandbox runtime provisioned and verified beforehand —
# `roteiro security prefetch --analyzer sandbox --allow-download`, then build
# with `BOXLITE_RUNTIME_URL` pointing at the verified copy. `rto-exec`'s build
# script refuses to proceed otherwise, and prints exactly what to run. See
# `crates/rto-exec/NOTICE-boxlite-runtime.md` for the third-party binaries this
# embeds and the licence duties they carry.
#
# It pulls in `exec-subprocess` for a reason that is CLI plumbing, not policy:
# `security run` is gated on that feature, and the sandboxed backend needs the
# same command. (`prefetch` and `status` are **not** — they moved to `execution`
# in the provisioning/execution split, and this comment kept saying all three
# afterwards. That gating is load-bearing now: `rto-exec/build.rs` tells a
# first-time installer that `cargo install roteiro` alone can provision the
# runtime *because* `prefetch` sits behind a default feature, so the two must not
# drift apart again.) Enabling it does **not** enable unsandboxed runs — those
# still require `--allow-unsandboxed` per invocation, and still record
# `isolation=none`. Worth untangling when the `security` command's feature gates
# are next revisited.
= ["exec-subprocess", "rto-exec/exec-boxlite"]
= ["rto-render/mcp", "dep:tokio"]
# The interactive workspace explorer's read-only `/v1/graph/*` JSON API, off by
# default. Pure Rust: adds only axum + tokio, never `rto-serve`/llama.cpp, so it
# builds, serves (`roteiro explorer`), and tests without a C/C++ toolchain. Also
# merged onto `/v1` when combined with `serve` (see `serve_v1_tail`).
= ["dep:axum", "dep:tokio"]
# Offline similarity inference (`roteiro infer`) — the `inferred` provenance
# class. Adds no dependencies (pure-Rust hashing embedding; ADR-0003).
= ["rto-graph/inference"]
# The shared model machinery: the `roteiro model` list/pull commands and the
# consent-gated downloader (`ureq`), over rto-graph's candle-free registry.
# Enabled by every model tier so `roteiro model pull` works without candle, and
# **on by default** — see the `default` entry above for why. Being present is not
# being active: `pull` still fetches nothing without an explicit `[y/N]` yes (or
# `--yes`), and no other command opens a socket, so the default build remains
# offline-by-default in behaviour as well as in name.
= ["rto-graph/models", "dep:ureq"]
# Pluggable local embedding/generative models (`roteiro infer --model`, `spec
# draft`) via the shared **llama.cpp** engine — GGUF embedding + generative
# models, no candle (ADR-0003 v1.1). Pulls `rto-llama` (a C/C++ toolchain).
= [
"inference",
"models",
"dep:rto-llama",
"rto-llama/llama",
]
# The remote model tier (ADR-0019) — Roteiro's **one explicitly-consented egress
# path**, and the first capability in the project that can send repository
# content off the machine.
#
# **Off by default, and it stays off.** No release may flip it. That is not a
# preference about defaults: ADR-0019 exempts this capability from Principle 10
# (a remote call is fetching by definition and can be neither digest-pinned nor
# prefetched), and the exemption is only tolerable because the capability is
# absent unless someone asked for it at build time *and* again in their own user
# config *and* again per invocation. `remote_is_not_a_default_feature` in
# `tests/remote_consent_cli.rs` reads this file and fails if `default` grows it.
#
# Enabling it adds **no third-party dependency**: `rto-remote` is a workspace
# crate over `rto-graph`, `serde` and `thiserror`, and it deliberately contains
# no HTTP client. `ureq` is already in the tree for `roteiro model pull` and
# `security prefetch`, and `rto-exec` is a workspace crate — so `cargo deny` and
# ADR-0017 have nothing new to look at, which is what ADR-0019 anticipated when
# it recorded that `ureq` "is already in the tree, so this need not add a new
# dependency closure".
#
# **Part 2 is what made a build with this feature able to send.** Part 1
# compiled the policy and no backend at all — the consent gate, the payload
# allow-list, the dry-run and the egress ledger — so that build could not send
# anything and the guard was reviewable on its own. `src/remote_transport.rs` is
# the socket, and it is still handed to `rto_remote::call_with` as a closure:
# the crate holding the policy has no HTTP client and must never acquire one,
# which stays checkable from `crates/rto-remote/Cargo.toml` rather than promised
# in prose.
#
# Three dependencies, each for one named reason:
# * `rto-remote` — the policy: who consented, what may be sent, what came
# back, what was recorded.
# * `ureq` — the transport, in `remote_transport::call` and nowhere else.
# * `rto-exec` — for `rfc3339_utc` alone, which stamps the ledger's entries
# through `rto_remote::Clock`. Part 1 deliberately left this undeclared
# because nothing then called `call_with`; that call site now exists. It
# does **not** enable `execution`, so `--features remote` still yields a
# binary with no analyzer surface.
#
# Part 2b adds a fourth, and it is a **resolver** dependency rather than a model
# one. The tier does not choose itself: `ModelSource::Remote` is a variant of the
# *shared* `rto_graph::model_choice` resolver, which is gated on
# `rto-graph/models`, and `spec draft` / Ask ask that resolver which backend
# serves them before either can send. Without it a `--features remote` build
# would hold a consent gate and a transport and nothing that decides when to use
# them.
# * `models` — the shared resolver (and, incidentally, the registry and the
# `roteiro model` commands, both already in `default`). It pulls no
# llama.cpp, and it does **not** enable the tier: the user layer and the
# invocation still have to grant, every run.
#
# Part 2c adds a fifth, and it adds **no toolchain**: `rto-llama` without its
# `llama` feature is the pure-Rust `Engine` trait and its request/result types,
# with no `build.rs` and no vendored C++. What this tier needs from it is
# `rto_llama::thinking` — the rule for reading a reasoning model's `<think>`
# block, which moved there in #582 because `rto-serve` needs it too and cannot
# depend on `roteiro`. A hosted reasoning model emits the same block as a local
# one, so `spec_draft_remote` reads it with the same function every other
# consumer does, rather than keeping a copy for the one build that could not see
# the original. Do **not** write this as `rto-llama/llama`: that would put
# cmake/libclang between a user and the remote tier, which is one of the costs
# this tier exists to avoid.
# * `rto-llama` — the `<think>` rule and `FinishReason`. No C/C++ toolchain.
= ["dep:rto-remote", "dep:rto-exec", "dep:ureq", "dep:rto-llama", "models"]
# Ingest text from PDF documents during `roteiro sync` (off by default; pulls a
# large PDF/font-parsing tree). See [`rto_graph::extract`].
= ["rto-graph/pdf-text"]
# OCR text from images during `roteiro sync` (ADR-0005 Tier A; off by default;
# pulls the pure-Rust ocrs/rten tree). Implies `models` so `roteiro model pull
# ocrs-text` can fetch the OCR models with consent.
= ["models", "rto-graph/image-ocr"]
# Describe images with a local GGUF vision-language model during `roteiro sync`
# (ADR-0005 Tier B; off by default). Uses the shared llama.cpp engine via
# `rto-graph/image-vision` (smolvlm + mmproj) — no candle.
= ["models", "rto-graph/image-vision"]
# Transcribe spoken-word audio (wav/mp3/flac) during `roteiro sync` (Stage 18;
# off by default). Uses the shared llama.cpp engine via
# `rto-graph/audio-transcribe` (voxtral + audio mmproj) — no candle. Implies
# `models` so `roteiro model pull voxtral-mini-3b` can fetch it.
= ["models", "rto-graph/audio-transcribe"]
# Extract audio metadata — codec, sample rate, bit depth, channels, duration, tags
# — as `derived` facts during `roteiro sync` (ADR-0016; off by default). Reads the
# container with `symphonia`; **no model, no network and no C/C++ toolchain**, so
# unlike `audio-transcribe` this one costs a compile and nothing else. Implies
# neither `models` nor `audio-transcribe`: extraction and generation are separate
# stories (ADR-0015 / ADR-0016).
= ["rto-graph/audio-metadata"]
# Local model serving (`roteiro serve --models`): the opt-in, loopback,
# OpenAI-compatible `/v1` endpoint over installed models (ADR-0006). Pulls
# `rto-serve` with its llama.cpp engine; also enables the internal llama.cpp uses
# (`infer --model`, `spec draft`) via `inference-local-models`.
= ["dep:rto-serve", "rto-serve/llama", "rto-serve/tls", "inference-local-models", "dep:tokio"]
[]
= true