Crux Daemon · 18. Runtime code intelligence
**The daemon knows two things about your code: what it is, and what it did when it ran. This chapter is the join between them.** Reference-counting alone cannot see dynamic dispatch; execution alone cannot see code that never ran. Neither half answers "is this actually dead?" on its own.
Capture is off by default. If your runtime answers come back empty, that is why — see §18.2 before anything else.
The static half comes from the AST workspace scan (chapter 10). The runtime half comes from tracing spans captured by the daemon's own span layer, joined to symbols at flush time.
18.1 The five tools
All read-only, over MCP and HTTP, and unlimited on every tier including Free. They run against whichever daemon the client is already connected to, reading that daemon's own scan and trace store — no hosted service is involved, so there is nothing to meter.
| Tool | Answers |
|---|---|
code_path | the ordered path a request actually took through the code |
code_blast_radius | who breaks if this symbol changes |
code_liveness | did this symbol execute, and how often |
code_trace_diff | what differs between two executions, or two releases |
code_dead_code | an evidence ladder per candidate, not a bare verdict |
code_dead_code returns a ladder rather than a yes/no because the honest answer has tiers: never referenced, referenced only from tests, referenced but never executed in the observed window. Collapsing those into "dead" is how a function in use gets deleted.
18.2 Turning capture on
With CORECRUXD_TRACE_CAPTURE unset the span layer is not installed at all — not installed and early-returning, but absent. The hot path is unchanged.
| Variable | Effect |
|---|---|
CORECRUXD_TRACE_CAPTURE | install the span layer |
CORECRUXD_TRACE_PERSIST | write spans to disk as well as the in-memory ring |
CORECRUXD_TRACE_SAMPLE_RATE | capture one span in N |
CORECRUXD_TRACE_RELEASE | label spans with a release; defaults to the daemon version |
CORECRUXD_TRACE_RETENTION_DAYS | retention window; defaults to 90 |
CORECRUXD_TRACE_TENANT_CEILING | per-tenant retained-span ceiling |
Capture on with persistence off is a valid configuration, not an oversight: you get live inspection of the in-memory ring and nothing written to disk.
Overhead when capture is on is roughly +2.6% on a realistic handler; below 1% only for handlers taking longer than about 340µs. That is measured, not estimated, and it is why the flag defaults off.
18.3 Which tenant an answer came from
Spans carry the tenant that captured them, and the only public read is tenant-filtered. There is no unfiltered read to call — the unscoped variant is private to the module, so a future caller cannot reach one by accident.
That design exists to prevent one specific failure: an authorization check with no matching data filter, where a request is correctly authenticated and then answered from everyone's data.
Two consequences, both failing closed:
- Spans written before tenant labelling resolve to the daemon's own capture tenant and to no other. They were captured by that process under that configuration, so attributing them there is accurate. Treating them as wildcards would rebuild the defect.
- Trace lookup and listing are scoped too.
trace_idis drawn from one space shared by every tenant, so an unscoped lookup would be a guessing oracle — and an unscoped listing discloses the existence and volume of another tenant's activity even without returning a span body.
Naming a tenant, and what happens if you do not
/v1/traces, /v1/traces/{trace_id}, and the dossier and storybook generators take an optional tenant_id.
| Behaviour | |
|---|---|
| Supplied | authorised against that tenant, answered only from its spans |
| Omitted | answers for the daemon's own capture tenant |
The fallback is reported, not assumed: tenant_scope in the body for /v1/traces*, and the x-crux-runtime-tenant-scope header for dossier and storybook. A value of daemon-capture-tenant means no tenant was named.
One daemon per customer: omitting tenant_id is fine. One daemon serving several: name the tenant, or they all resolve to the same one.
18.4 The two limits
Two limits on two different axes. They are not interchangeable, and the difference is what you need to reason about what you might lose.
The retained-span ceiling — a volume limit, per tenant. On breach the daemon refuses new spans and never deletes stored ones. You lose new capture, not the history your answers have been coming from. It warns at 80%, readable at GET /v1/code-intel/volume, because a limit whose first symptom is missing data is a support ticket rather than a limit.
Retention — an age limit. This one does delete: spans past the window are pruned on flush. GET /v1/code-intel/releases reports the active window alongside the releases actually retained, so what you can still diff against is visible rather than discovered by trial.
Pro retains 90 days, Governance 365. Ninety is the deliberate middle: thirty makes release-over-release comparison useless to anyone shipping quarterly, and a year makes retention the largest storage line for a capability most teams query over weeks. Governance takes the year because for compliance evidence the long tail is the product.
18.5 Across repositories
code_blast_radius accepts all_repos=true, answering across every enabled repository registered to the tenant. This is the question a single checkout structurally cannot answer, because the caller lives in a repo it has never seen.
Paths in the aggregated answer are prefixed with their repository, so a result names which repo a caller is in. Repositories you have disabled are excluded — they are not being aggregated, so they do not appear as callers.
One caveat, and it is load-bearing if you are about to delete something. Cross-repo references resolve by symbol name. That is the capability — a shared symbol called from two services is exactly the edge a single repo cannot see — and it is equally the limit: two unrelated functions sharing a name merge into one radius.
The aggregated answer is a sound superset. It is right for "what might break". It is not precise enough to delete from without reading. The response says so in a precision field rather than leaving it to this page.
Repositories are not metered and there is no repo limit to buy — see pricing and credits.
18.6 Related
- Chapter 9 — the tracing substrate these spans come from
- Chapter 10 — the workspace scan providing the static half
- Chapter 7 — scopes and tenant authorisation generally
- Chapter 16 — verified defects, including anything open here

