Skip to content

A fact's evidence is read only inside its own base - #1261

Merged
WaylandYang merged 1 commit into
deeplethe:devfrom
gedaiu:fix/evidence-stays-inside-its-base
Oct 16, 2026
Merged

WaylandYang merged 1 commit into
deeplethe:devfrom
gedaiu:fix/evidence-stays-inside-its-base

Conversation

@gedaiu

@gedaiu gedaiu commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Closes #1260.

What happened

graph::fact_evidence(pool, fact_id) loaded a fact's evidence by id alone. The route checked the base in the path and nothing else, so a role in base A was enough to read the quotes of a fact in base B through /kbs/A/facts/<B's fact>/evidence. Proof steps (reasoning::steps_for) called it the same way.

What changed

graph::fact_evidence(pool, kb_id, fact_id) first checks that the fact is in kb_id and answers NotFound otherwise, as entity_detail does for an entity outside its base. A fact of the right base with no evidence still returns an empty list. The route passes the base from its path; proof and blocked_proof already had the base and now hand it down through premise_steps to steps_for. The cost is one primary-key lookup per evidence read.

Tests

evidence_stays_inside_its_base (utopia-store): evidence of a fact in base B, asked from A, is NotFound; asked from B it returns the row. It fails without the check.

Run in rust:1-slim-bookworm against Postgres with UTOPIA_TEST_REQUIRE_DB=1: cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D warnings are clean; cargo test -p utopia-store --test store evidence 9 passed, proof 3 passed, blocked 1 passed; cargo test -p utopia-server evidence 12 passed.

…base no longer reads another base's quotes

Signed-off-by: Bogdan Szabo <[email protected]>

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, this is the right fix in the right place. fact_evidence now takes the base and answers NotFound for a fact outside it, the same rule entity_detail follows, and passing the base through premise_steps and steps_for means proofs can't be used to read another base's quotes either. Checking only the fact is enough because the fact_evidence_stays_inside_its_kb trigger already keeps a fact's evidence in the fact's base. The store test fails without the check and runs in CI against Postgres. This has to land before #1263, which opens this route to kb_ids-limited tokens. Merging.

@WaylandYang
WaylandYang merged commit 15fd7d5 into deeplethe:dev Oct 16, 2026
6 checks passed
gedaiu added a commit to re-cinq/lore that referenced this pull request Oct 16, 2026
…y external_id) (#2789)

Bumps Utopia from `re.6` to `0.1.0-re.7` (digest `sha256:9fa04033…`). It
puts the reads Lore's Utopia side needs on the running Utopia, so the
trace reader can be built and measured against real data (dual-write
experiment, #2787) while upstream reviews them.

`re.7` = `re.6` + current upstream `dev` (which now has
deeplethe/utopia#1261, the cross-base evidence fix, and #1270, members
of a class lacking a relation) + our open PRs:

- #1263, #1264, #1265, #1266: a personal token reads the graph of the
bases it reaches (entities, history, evidence, overview, neighbourhood,
ontology, instances, extractions, export; export audited with the token)
- #1268: a push that names its `external_id` keeps that id's document;
content clashes answer `duplicate`; tombstone-then-push renames
- #1272: reads return a statement's role-word qualifiers (e.g. a test's
line range)
- #1273: search takes a token and returns scores, ranks, source,
`external_id` and pages
- #1274: a write token creates `api` sources
- #1280: read a pushed document back by its `external_id`

Left out on purpose: the list-by-`external_id` change (held until #1280
merges), because it adds a migration whose number upstream has not
assigned yet. **No new migration in `re.7`** (still 0113).

Checked before pushing: on the merged `re-dev` (`8c477cd`), `cargo fmt
--check`, `clippy -D warnings` and `cargo test --workspace` on a freshly
migrated database (1612 passed, 6 ignored, 0 failed). The image booted
on a fresh database: health 200, migrations at 113, and a bogus token on
the new entities route answers 401 (route present, token checked).

After merge: `terraform apply
-target='kubernetes_deployment_v1.utopia[0]'` from main.

Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants