Skip to the repair story

Cut the dangerous path. Keep the work alive.

CUTLINE is a permission-repair agent built on FalkorDB. Name the access that must disappear and the workflows that must survive. It traces every hidden route, rejects unsafe fixes with counterexamples, and proves the repair against a running app before anything changes.

Live · before CUTLINEPOST /demo/customers/export as maya2001,000 customer recordsexported by a support agent

01 / 04Trace

Two hidden routes to a capability nobody granted.

Maya handles orders and reports. FalkorDB finds she can bulk-export customer data anyway: through Shared Ops, and through Export Helper. Felix needs Shared Ops for Finance close; the DSAR Bot needs Export Helper for legal data requests.

maya → support → shared_ops → export_customers
maya → support → export_helper → export_customers

4 risky paths · 2 people × 2 routes · [:INHERITS*0..6]

02 / 04Reject

The obvious fixes break something.

Every candidate is applied to its own copy of the graph and re-queried. Watch two of them fail.

Attempt ADetach Shared Ops from Supportstill open: maya → support → export_helper → export_customers
Attempt BRevoke export_customers from Shared Opsfelix loses export: finance close breaks. export_helper is still open.

9 of 10 candidates rejected with a counterexample

03 / 04Repair

Cut two assignments. Add one narrow role.

Support (safe) grants exactly view_orders and generate_reports. Maya and Noah move onto it; nobody else changes. Seven edits, applied as one atomic Cypher write after a human approves.

  1. CREATE_ROLEsupport_safe
  2. ADD_GRANTsupport_safe → generate_reports
  3. ADD_GRANTsupport_safe → view_orders
  4. ASSIGN_ROLEmaya → support_safe
  5. ASSIGN_ROLEnoah → support_safe
  6. UNASSIGN_ROLEmaya ✕ support
  7. UNASSIGN_ROLEnoah ✕ support

Cost 8 · the cheapest candidate that passes all 8 constraints

04 / 04Verify

Then prove it against a running app.

Real HTTP requests, decided by an independent enforcement point that must agree with FalkorDB on every single check.

  • maya200→403denied, as required
  • felix200→200finance close
  • dsar_bot200→200dsar fulfilment
12 / 12 checks pass · see all twelve
02Isolation

Every candidate gets its own graph.

CUTLINE never judges a fix in its head. Each candidate is applied to a GRAPH.COPY of the policy and re-queried in FalkorDB. A rejection always carries a counterexample: the route still open, or the workflow that would break.

cutline_dev → GRAPH.COPY × 10

0 struck · 0 survives

  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
  10. 10
VerdictCandidate fixCounterexample, found on the copy
RejectedRevoke export_customers from Shared OpsFelix loses export, so Finance close breaks. Maya still exports through Export Helper.
RejectedDetach Shared Ops from SupportA forbidden route is still open:maya → support → export_helper → export_customers
RejectedDetach Export Helper from SupportA forbidden route is still open:maya → support → shared_ops → export_customers
RejectedDetach both bundles from SupportThe DSAR Bot loses export, so legally required DSAR fulfilment breaks.
RejectedRemove Maya and Noah from SupportThey lose view_orders and generate_reports, so Order support breaks.
PassCreate Support (safe) and move Maya and Noah onto itAll 8 constraints hold. Nobody gains access they did not already have.

Bounded on purpose. At most 24 candidates, cuts of at most 3 edges, inheritance depth 6. CUTLINE returns the cheapest candidate that passes, not a claim of global optimality.

Reported, not hidden. The old Support role still reaches export, now held only by the DSAR Bot. Anyone newly assigned to it would inherit export, so CUTLINE flags it for review.

03Proof

Not a green tick. Twelve real requests.

Before anything is applied, CUTLINE calls a running Customer Ops app, against the current policy and against the candidate. Every principal and capability is checked, including the ones nobody asked to change.

Planner · predicts
Reads the full policy graph with variable-length Cypher on FalkorDB.
Enforcement point · decides
Reads only one-hop relations and computes reachability itself, with a reference closure or OPA and Rego. Separate code, so a bug in one shows up as a disagreement, never as a false pass.

X-Cutline-Policy: candidate:<repair_id> tests a repair before it exists.

Runtime verification

00 / 12 passed

Must be denied
MayaPOST /demo/customers/export200403
NoahPOST /demo/customers/export200403
Must keep access
FelixPOST /demo/customers/export200200
DSAR BotPOST /demo/customers/export200200
MayaGET /demo/orders200200
MayaGET /demo/reports200200
NoahGET /demo/orders200200
NoahGET /demo/reports200200
Not in the contract · must not change
FelixGET /demo/orders403403
FelixGET /demo/reports403403
DSAR BotGET /demo/orders200200
DSAR BotGET /demo/reports200200
04Protocol

Six steps. The AI decides none of them.

  • aThe AI interprets and explains.
  • bFalkorDB holds and reasons over the permission state.
  • cA deterministic planner and verifier make every security decision.
  • dAn independent engine confirms predictions against real enforcement.
  1. Step 1: Discover

    FalkorDB returns every bounded route from a person to a forbidden capability, not just direct grants.

    [:INHERITS*0..6]
  2. Step 2: Contract

    Guardrails (must deny) and workflows (must keep working) are graph nodes. Plain-English additions are parsed, then confirmed by a human.

    2 must-deny · 6 must-allow
  3. Step 3: Plan

    Bounded, deterministic candidates: single cuts, minimal hitting sets, scoped-role migration. Each one runs on its own graph copy.

    GRAPH.COPY per candidate
  4. Step 4: Verify

    Real HTTP requests hit a running app. An independent enforcement point decides; it must agree with FalkorDB on every check.

    12 real requests
  5. Step 5: Approve & apply

    A human signs off. All edits land in one Cypher statement guarded by a revision compare-and-set, then the checks re-run live.

    1 atomic write
  6. Step 6: Stay honest

    Evidence is bound to a revision and a content hash. When the policy moves, the proof goes stale, apply is refused, and CUTLINE re-plans.

    revision + sha256

The model is never the security proof.

05Live challenge

Change the graph underneath it. The proof expires.

Evidence is never trusted forever. Every plan records the policy revision and a sha256 hash of the policy's edges it was built on.

Stale is a live check, not a status
Recomputed on every read. Approve and apply refuse a stale plan with 409 STALE_PROPOSAL.
The hash catches quiet edits
Editing the graph directly in FalkorDB Browser, without bumping the revision, still invalidates the proof.
The write checks again
The atomic apply compares the revision inside the statement. If it moved, zero rows match and nothing is written.

Repair Studio · live challenge

policy r4 · sha256:eb8b…

Verified on r4 · 12 / 12 checks

Support (safe) applied. Evidence is bound to r4 and its content hash.

Applied
06Why FalkorDB

Not the visualisation layer. The state space.

Every access path, business dependency, candidate repair and counterexample lives in the graph. Pull the plug and CUTLINE cannot plan, prove, or even let the demo app serve a request.

What FalkorDB does in CUTLINE

  • 503

    Effective-access reachability and multi-path witnesses

    Variable-length Cypher traversal

  • 503

    Business intent: workflows, guardrails, the derived contract

    Graph nodes and relationships

  • 503

    Candidate isolation

    GRAPH.COPY: one throw-away graph per candidate

  • 503

    Counterexamples: path still open, workflow lost, new privilege

    Re-querying the candidate graph

  • 503

    Atomic, revision-guarded application

    One parameterised Cypher statement

  • 503

    Evidence ledger and cross-session agent memory

    The cutline_dev__ledger graph

  • 503

    What the enforcement point reads at runtime

    One-hop relations from the selected graph

-- every bounded route from one person to one capability
MATCH p = (u:User {id: $principal})
      -[:ASSIGNED_TO]->(:Role)
      -[:INHERITS*0..6]->(:Role)
      -[:GRANTS]->(c:Capability {id: $capability})
RETURN DISTINCT [n IN nodes(p) | n.id] AS node_ids

One query returns every witness path the UI draws. A direct grant is the zero-hop case.

backend/app/db/queries.py · values are always parameters

07Track 01 · Agents That Act on Connected Data

An agent that uses the graph to decide and act.

The model chooses which investigation to run. It never writes Cypher, never sees graph names, and can never approve. With an Anthropic API key, Claude picks the tools in a tool-use loop; without one, the same tools run in a fixed order and the run is labelled deterministic.

  1. 01

    Multi-hop reasoning

    Person → role → inherited roles → capability, up to 6 inheritance hops, every independent route.

  2. 02

    Graph queries as agent tools

    11 typed tools, each running fixed, parameterised Cypher on FalkorDB.

  3. 03

    Tool selection through MCP

    The same tools ship as an MCP server (Python SDK v2); the model picks which to call.

  4. 04

    Actions based on the graph

    Repairs are generated from the discovered paths and applied after human approval.

  5. 05

    Explainable results

    Every rejection carries a witness path or the named workflow it breaks.

POST /api/agent/investigatedeterministic

    11 typed tools · also served over MCP

    app/mcp_server.py

    • inspect_policy
    • find_risks
    • trace_access
    • check_access
    • recall_history
    • get_contract
    • get_evidence
    • export_regression_tests
    • propose_repair
    • verify_repair
    • apply_repair
    readisolated writehuman-approved only
    08Results

    Benchmarked on real FalkorDB Cloud.

    Ten synthetic scenarios with known correct answers, against two deliberately simple baselines. Every number below comes from that run.

    10/10

    Correct outcome: repair, refuse, or flag, as expected.

    7/7

    Safe repairs on the repairable scenarios.

    CUTLINE7/7
    Greedy revoke-at-source2/7

    11/12

    Forbidden access detected through multi-hop routes.

    CUTLINE11/12
    Direct-grant checker7/12

    1.46 s

    Median planning time, candidates evaluated in parallel.

    72/72 backend tests pass with real FalkorDB Cloud and OPA enabled.

    Why 11 of 12, not 12. The twelfth is an 8-hop chain, beyond the planner's 6-hop bound. The unbounded independent engine catches it, and CUTLINE reports the disagreement instead of hiding it.

    #ScenarioOutcome, as expected
    01The demo: two inherited paths; Finance and DSAR share the rolesScoped role · 7 edits
    02Needed and forbidden capability bundled in one roleScoped role · 4 edits
    03Forbidden capability three hops down a private chainSingle cut · 1 edit
    04Roles inherit each other in a cycleSingle cut · 1 edit
    05Dedicated export role shared with a legitimate userSingle cut · 1 edit
    06One subject, two forbidden capabilitiesScoped role · 5 edits
    07Two subjects keep different capabilitiesScoped role · 8 edits
    08A required capability the subject never hadInfeasible: refuses to invent a grant
    09Guardrail already holdsNo violations: nothing to do
    10An 8-hop chain beyond the 6-hop planner boundDisagreement flagged by the independent engine

    Supported

    Allow-only RBAC with role inheritance, named principals and capabilities, bounded depth (6 inheritance hops by default).

    Not modelled, on purpose

    Deny rules, conditions and ABAC, time-bound grants, resource-scoped grants, cloud IAM policy languages.

    Not silently approximated. CUTLINE claims only what its declared model can prove, and never invents a grant to make a constraint pass.

    One dangerous capability. Two hidden graph paths. The obvious fixes rejected with proof. One structural migration that keeps every workflow alive, twelve real requests proving it, and a live policy change it catches and repairs.