Skip to content

fix(context): keep daemon-internal namespaces out of undirected recall - #650

Merged
CueCrux-Myles merged 1 commit into
mainfrom
investigate/bootstrap-recall
Aug 7, 2026
Merged

CueCrux-Myles merged 1 commit into
mainfrom
investigate/bootstrap-recall

Conversation

@CueCrux-Myles

Copy link
Copy Markdown
Contributor

On a fresh node, GET /v1/context answers a user's question with the daemon's own manual.

Measured against a node holding exactly one user fact, authenticated with real scopes (CORECRUXD_AUTH_MODE=dev_scopes):

Call Facts returned __bootstrap__:: The user's
no arguments 20 19 1
entity=project:atlas 1 0 1
query="what database does atlas use" 50 49 1

So "what database do we use" returns 50 results, 49 of them Crux documentation. /v1/context is the injection surface an agent gets at session start — this is the first thing a new user sees.

Not an auth artifact

It was first spotted with auth off, so I checked with auth on before claiming anything; the table above is the auth-on run. The mechanism:

__bootstrap__:: is forced private at ingest by enforce_global. But the seeder writes actor: None, and the bundle maps private && actor.is_some() — so the assembler's private-owner check never engages. That check is explicitly documented as defence-in-depth, with fetch-time visibility as the real gate, and fetch-time admits these to any authenticated caller.

The fix

Undirected recall — keyword, and the zero-hint default bundle — skips daemon-internal namespaces. Addressed recall does not, so nothing becomes unreachable:

  • entity=__bootstrap__::doc:architecture still returns it (test).
  • get_bootstrap is a separate path, untouched, and remains the intended door to that content.

After: no-args 20 → 1, query= 50 → 1, addressed still 1.

The predicate lives in corecrux-memory::fact_privacy, next to the prefix list that defines the convention, rather than being invented locally in one HTTP handler — otherwise the next recall surface invents its own. It keys on the __ marker: 37 of the 39 DEFAULT_PRIVATE_PREFIXES use it, and the two that don't (decisions::, github::) are private user content rather than daemon bookkeeping, which is exactly the line being drawn. The doc comment says plainly that storage, export and audit paths must not use it — they need the complete set.

Checks

2 regression tests on the context surface (undirected excludes; addressed still returns) + 2 on the predicate (covers every __ prefix; excludes user content). cargo test --workspace 7750 passed, 0 failed. fmt, clippy --workspace -D warnings, licence headers and the unwrap ratchet all clean.

Why it surfaced now

Found while building the M6 framework adapters (#641, #643). The adapters deliberately do not filter this client-side — trimming results in an adapter is precisely what their conformance suite forbids. So it had to be fixed here or not at all.

Heads-up: #630 also touches context_surface.rs. My hunk is in gather_facts; happy to rebase behind it.

🤖 Generated with Claude Code

On a fresh node, `GET /v1/context` answered a user's question with the
daemon's own manual. Measured against a node holding exactly ONE user
fact, authenticated with real scopes:

    no arguments   20 facts, 19 __bootstrap__::, 1 the user's
    entity=         1 fact,   0                  1
    query=         50 facts, 49 __bootstrap__::, 1 the user's

So "what database do we use" returned 50 results, 49 of them Crux
documentation. This is the injection surface an agent gets at session
start, so it is the first thing a new user sees.

Not an auth artifact -- it was first spotted with auth off, and the
numbers above are with CORECRUXD_AUTH_MODE=dev_scopes and a scoped
caller. `__bootstrap__::` IS forced private at ingest by enforce_global,
but the seeder writes actor: None, and the bundle maps
`private && actor.is_some()`, so the assembler's owner check never
engages. The real gate is fetch-time visibility, which admits these to
any authenticated caller.

Fix: undirected recall (keyword, and the zero-hint default bundle) skips
daemon-internal namespaces. Addressed recall does not, so nothing becomes
unreachable -- `entity=__bootstrap__::doc:architecture` still returns it,
and `get_bootstrap` (a separate path, untouched) remains the intended door
to that content.

The predicate lives in corecrux-memory::fact_privacy next to the prefix
list that defines the convention, rather than being invented locally in
one HTTP handler. It keys on the `__` marker: 37 of the 39
DEFAULT_PRIVATE_PREFIXES use it, and the two that do not (decisions::,
github::) are private USER content rather than daemon bookkeeping -- which
is exactly the line being drawn. Storage, export and audit paths must not
use it; they need the complete set, and the doc comment says so.

After: no-args 20 -> 1, query= 50 -> 1, addressed still 1.

Tests: 2 regression tests on the context surface (undirected excludes,
addressed still returns) + 2 on the predicate (covers every __ prefix;
excludes user content). cargo test --workspace 7750 passed, 0 failed.
fmt, clippy --workspace -D warnings, licence headers, unwrap ratchet clean.

Found while building the M6 framework adapters: the adapters deliberately
do NOT filter this client-side, because trimming results in an adapter is
what their conformance suite forbids. It had to be fixed here or not at all.

agent:claude-opus-5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CueCrux-Myles
CueCrux-Myles added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 8518402 Aug 7, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant