DEV Community

Dosunmu Hassan
Dosunmu Hassan

Posted on

An agent should be able to prove it was allowed to act

An agent should be able to prove it was allowed to act

Agents now hold credentials that move money, deploy software and delete infrastructure. The part that keeps failing is not the acting. It is the accounting. Ask who authorised a specific action, under what ceiling, in which scope, and whether that authority still holds, and there is usually no answer. Logs record that something happened. They rarely record why it was permitted.

I built Mandate for the Graph Hacks hackathon to answer those questions with a graph, because every one of them is relational. "Is this agent allowed to do this, now, for this amount?" is a question about a chain: the agent was authorised by a team, which acted under a grant from a principal, which carried a ceiling, a scope and an expiry, and one link in that chain may have been withdrawn an hour ago. That is a traversal, not a row lookup, and pretending otherwise is how authority systems end up with a nightly job that tries to invalidate caches correctly.

The model

FalkorDB holds principals, teams, agents, grants, resources, tools, actions and attempts. A grant carries a ceiling, a scope, an expiry and a redelegation flag, and a sub delegation points at its parent grant. Two rules do most of the work: authority may only narrow along a chain, and liveness is computed by traversal rather than stored.

That second rule is the design decision I would defend hardest. When a grant is withdrawn, nothing has to be cascaded, because nothing was cached. Every descendant simply stops finding a live path to a principal, at the same instant, and a test asserts exactly that (test_revocation_is_derived_not_a_stored_cascade).

Before an agent acts, the engine traverses for a live chain. If one exists, the action proceeds and the path that authorised it is returned with the answer. If none exists, the refusal carries the query that returned nothing as its own evidence, plus the narrowest person who could unblock it. A refusal is therefore checkable by whoever receives it, instead of being a bare denial. A zero ceiling is never reported as unlimited, and a zero TTL means expired immediately rather than never, both of which are now tests because both were ways to be confidently wrong.

One graph per tenant. Isolation becomes a storage boundary rather than a filter that somebody can forget to apply, which is the kind of mistake that reads as a normal code path until the day it does not.

The rule that shaped the build

The hackathon's rules include a sentence worth quoting: if the product still works once the graph database is taken out, the graph is decoration rather than the thing the product runs on. That is a fair test and an easy one to fail, so I wrote a probe that asks it directly. It removes the graph engine and puts the same questions to the product: who may act, within what ceiling, on whose authority, and what happens to everything derived when a grant is withdrawn. Twelve checks, all of which fail without the graph, and the probe ships in the repository so a reader does not have to take my word for it.

What the tests are for

The suite is 34 tests and runs with no network and no model. Names carry the reason they exist, so a failure reads as a statement about authority rather than a line number: the ceiling is the minimum along the chain and not the nearest grant, a sub delegation from a non redelegable parent is refused, a redelegation restriction breaks an existing chain, an expired grant falls back to a live alternative rather than failing outright, escalation is never offered through a withdrawn chain, one tool is never both offered and withheld.

The last two are the ones I would have got wrong without writing them down. Offering a user an escalation route that itself runs through withdrawn authority is a security bug that looks like a helpful feature, and it is invisible until someone enumerates what the system offers rather than what it allows.

Honest limits

It runs embedded in one process, which is right for a demo and wrong for production. It is not an identity provider, it does not do key management, and the browser interface exists to show decisions being made rather than to be an operator console. The fixture is synthetic. If you need an authority system today, use something older and better funded than a hackathon project. What this is, is a working argument that the graph is the correct shape for the problem, with the checks to show it.

Where it is

Code and tests: https://codeberg.org/Zaygalx1/Mandate (public, no account needed). Built solo for Graph Hacks, Track 01, Agents That Act on Connected Data. The run instructions are in the README: a virtual environment, one dependency that is the embedded graph engine, a seed command, a guided demonstration in the terminal, and optionally a browser view.

What I learned

Two things, both from getting them wrong first. Authority is path shaped, so any system that stores "this agent may do this" as a boolean has already lost the information needed to explain itself later. And a graph earns its place by being load bearing, which is a claim you should be able to test by removal rather than assert in a README.

Top comments (0)