Hack Hydra 2026 · Track 01 · built on HydraDB
A fact inferred from three documents is not a document. It has no ACL of its own — so document-level filtering, which is what every enterprise AI assistant does, has no answer for it. We measured the hole, and closed it.
“What changes did the Engineering Lead suggest for the AnomalyForce product vision?”
01 · the measurement
1,514 questions from Salesforce’s HERB enterprise benchmark, 12 principals across the access spectrum, 18,168 trials per system. Identical retrieval, identical answer assembly. The only difference is what each is willing to disclose.
| Measured over 18,168 trials | Document-level ACL | Cordon |
|---|---|---|
| Leak rate | 17.4% | 0.0% |
| Answer quality (set-F1) | 0.099 | 0.099 |
| False denials | 0 | 0 |
| Disclosure decided by ingest order | 1,008 of 7,280 | never |
| Verified against | its own model of access | GitHub’s own 404 |
Zero leaked at derivation depth 0. That is not a compliment we are paying the baseline — it is the finding. Document filtering does not have a bug. It has a ceiling, and the ceiling is the first inference.
Facts leaked by document-level filtering, by derivation depth. Cordon: zero on every rung.
02 · worse than wrong
A derived fact carries one space, assigned by whichever source the writer reached first. That assignment is what a document gate reads. So we asked: would it have answered differently had the node been filed under a different one of its own sources?
Same graph, same permissions, same person asking — opposite answer. A security decision settled by ingest order is not a security decision. docs/RESULTS.md · regenerate with npm run audit
03 · not our opinion
The strongest objection to any of this is that we invented the access control we then enforced. So the whole pipeline runs a second time against a system that already has permissions: 8 real GitHub repositories — 5 private, 3 public — inside a real organisation with 11 nested teams as principals. Only the loader differs. Extraction, resolution, derivation and the admissibility rule are the same code.
For every source underneath a withheld fact, the audit issues an unauthenticated request and asserts the refusal:
The check deliberately uses github.com, not the API.
The unauthenticated API allows 60 requests an hour and then answers 403
to everything — and counting a rate-limit 403 as a pass would fabricate the exact
result this section exists to establish. Rate limiting is detected and reported separately
from pass and fail.
04 · the attack nobody else can express
Two facts a principal is entitled to can together determine a third they are not. Document-level filtering cannot defend against this even in principle, because the thing being aggregated is not a document. We formalised it, proved what is impossible, and mined the rest from the graph rather than inventing examples.
An attacker cannot climb the derivation edges. required(f)
is the union of its supports’ requirements, so holding every part of a conclusion
already entitles you to the conclusion.
0 counterexamples over every case in the graph.
Cordon is closed under aggregation exactly when every claim about a space is only asserted by facts resting on that space — claim locality. So the exposure is a property of the corpus, not of the rule.
HERB: 0 of 38,600 artifacts name another space. Locality holds exactly.
So we seeded a fixture with the cross-repository references real issues are full of — and Cordon leaked 16 times out of 130 denied pairs. We are reporting that because it locates the mechanism exactly: a public issue naming a private repository.
“Ingrid Holm notes the SDK release depends on cordon-demo-borealis integration work.” public repository · asserting a claim about a private one
The fix follows from the diagnosis: widen the requirement to include the spaces a fact names, not only where its evidence sits. Leaks go to zero, and it costs 54 additional withholdings. Both rules ship with their numbers; the operator picks.
Open, measured. A denied fact’s claims are partly reachable from permitted ones. Recovery falls as depth rises — Cordon degrades most gracefully where the explicit channel is most dangerous.
Our own defence opens it. Naming the space you lack is useful to a colleague and a perfect oracle to an attacker. Measured in bits, with an indistinguishable-abstention mode that closes it and the cost stated.
Whether you perceive a contradiction at all depends on your clearance. Two colleagues ask the same question and receive confidently opposed answers, and neither is told the other side exists.
05 · why this needs HydraDB
The requirement is discovered by traversal, per asker, at query time — never read from a field. It cannot be materialised: with n principals there are 2n visibility subsets, and precomputing per principal is 30M rows that one membership change invalidates.
A level-3 fact sits four hops from any evidence and has no edge to a space at all. There is no field to read. The answer only exists as a walk.
And a vector index cannot express this even in principle. An embedding records what a fact resembles; it carries no record of what it was derived from, and a constraint that propagates along derivation has nothing to travel down. Similarity is not provenance.
Building this, we probed what the OpenCypher subset actually supports and found queries silently truncating at 1,024 rows with no error — which for a security system means a quarter of an authorization table disappearing quietly. Our client fails closed on it. All three findings are filed upstream with reproductions: #115, #116, #117. We also publish what does not run in the engine: the transitive org closure is application-side, because variable-length traversal requires a fixed source id. A reviewer who finds that themselves reads it as a hole; measured and published, it is a capability map.
06 · how to disbelieve us
Cordon’s whole thesis is auditability, so nothing here is estimated, projected or illustrative. Raw artifacts carrying the git SHA and timestamp are committed alongside the code.
The invariant compares requirements obtained by graph traversal against requirements recomputed independently from the corpus. That distinction is not pedantry: our first version compared a value against itself, could not have failed, and passed for hours. When it was fixed, 47 facts disagreed — and that is how a real access-control bug was found.
A security property only ever checked against the field that produced it is not being checked at all. the thing we would most want a reviewer to take away