Hessra Logo
BACK TO DOCS
#capability-security#overview#documentation#biscuit#designation

Capability Security Overview

How Hessra implements the capability security model and how it compares to OAuth and centralized policy engines.

by The Hessra Team

Hessra is built on the capability security model, a formal security model with a specific set of properties. Here is what that means and how it compares to what you might already be using.

Capability security background

Capability security is a security model for how objects perform actions on other objects. An object (i.e. subject) must present a capability to their target object (i.e. resource) with their request. This capability is all the proof that is needed for the request to proceed.

A capability is made up of two things: the authority to perform the action and the designation — the complete, unambiguous name of what it is acting on. While not strictly necessary in the model, Hessra capabilities also include the operation (e.g. read/write). The tight coupling of authority and designation is a major distinction that separates capability security from other forms of access control.

A strict access control list (ACL) is a list kept by the resource that states who is allowed to access it. In that model, both authority and designation are determined at the final point of the request. OAuth and JWT access tokens blur this line. They confer authority (the token is signed by an authorization server) but they do not contain a full designation of what is being accessed. Instead they provide something more like a hint that the verifier has to figure out. This is where confused deputy problems live.

Capabilities are fully constructed before the request is made and then included with it. The verifier confirms the context matches. If it does not, the request fails closed.

Hessra's implementation

Hessra's primitives are built on Biscuit, an asymmetrically signed token that carries a small logic language called Datalog rather than JSON. Biscuit tokens support attenuation natively: any holder can append signed blocks that add restrictions, and those blocks can never be removed or forged. Attenuation only narrows, never expands. The token primitives are in the open source hessra-tokens repository.

On top of those primitives, the hessra-cap repository provides a capability engine that handles minting identity, context, and capability tokens, along with policy evaluation. A capability-list (CList) policy backend is included, configured via TOML. You can implement the PolicyBackend trait for custom policy evaluation if needed.

The shape of the API is straightforward:

use hessra_cap::{CapabilityEngine, CListPolicy, ObjectId, Operation};

let policy = CListPolicy::from_toml(r#"
    [[objects]]
    id = "service:orders-api"
    capabilities = [
        { target = "db:orders", operations = ["read", "write"] },
    ]
"#)?;

let engine = CapabilityEngine::with_generated_keys(policy);

// Mint a capability token. fully constructed before the request
let result = engine.mint_capability(
    &ObjectId::new("service:orders-api"),
    &ObjectId::new("db:orders"),
    &Operation::new("read"),
    None,
)?;

// Verify locally. public key, token, and request facts only
engine.verify_capability(
    &result.token,
    &ObjectId::new("db:orders"),
    &Operation::new("read"),
)?;

The verifier needs only the engine's public key. There is no service call, no policy engine to query, no network dependency. This holds whether the verifier is a microservice, a Postgres extension, an edge function, or an embedded device.

General usage pattern

Running Hessra in your system involves four moving parts:

Your root authority is a running instance of the hessra-cap engine, initialized with your keypair and a policy file that maps objects to their allowed capabilities. This is the source of all authority in your system. It mints capability tokens for the objects beneath it according to policy.

Your objects such as services, agents, jobs, and users need identities. The recommended approach is to generate root identities using hessra-cap and your signing key, then delegate sub-identities from there. This creates a natural hierarchy: a CI runner delegates to its jobs, a webapp delegates to its users, an agent delegates to its sub-agents. The Hessra hosted root authority also supports TLS client certificates as identity.

Objects that need to perform actions request capabilities from the root authority using their identity. The root authority evaluates the request against policy and mints a capability token for that specific subject, resource, and operation.

Objects receiving requests verify the capability token locally using the root authority's public key. They gather the full context of the request including resource name, operation, and any designators that fully name the resource and confirm the token matches.

Designation

The best way to think about capability construction is that the work happens up front. For a small flat set of microservices, the root authority might be able to fully name every resource and that is all you need. For anything that has dynamic naming, things like tenants, users, projects, and regions, designation is where capability security handles complexity that other models struggle with.

An example: a cat webapp has two resources. cat_of_the_day is the same for every user. favorite_cat is per-user. User1's favorite cat lives at user1/favorite_cat, and user2's at user2/favorite_cat.

The root authority grants the webapp access to both cat_of_the_day and favorite_cat. The webapp owns all the cats; it can access any of them. When user1 authenticates and requests their favorite_cat, the webapp requests a capability from the root authority for favorite_cat. Once it has that capability, it attenuates the token before handing it to user1, adding user1 as a designation. The token now names the resource completely: favorite_cat designated to user1. If user2 tries to use user1's capability token, the designation check fails. There are no confused deputies.

Now say the webapp is deployed to US and EU regions, and US users are not allowed to see EU cats. Add region:us as a designation alongside the user designation. The token now encodes both constraints. No coordination between services is needed. The token carries the complete authorization proof.

If the cat pictures live in S3 buckets organized by user name, neither the webapp nor the S3 retrieval service needs to know the access rules for the other. Both receive the request with its capability token, extract the context relevant to them, and verify against it. The policy is in the token.

This is where capability security scales past ACLs cleanly. As systems grow organically with more regions, more tenants, more resource types, you add designations. The root authority stays simple. The objects that own a namespace control the naming in it. This means when you update the cat webapp with tenants to sell to enterprise customers, you only need to update designation in the cat webapp. Authority and policy stay the same while the naming changes when the app changes it.

The quickstart guide shows this in practice for a multi-service setup. The webapp auth guide covers the full designation and delegation flow in depth.

Using the Hessra managed service

Running your own root authority means managing a keypair, a running service, and a policy file. If you would rather not, the Hessra managed service handles all of that: key management, the running engine, and policy configuration through an API. The tokens, verification, and designation model are identical. The open core and the managed offering use the same Biscuit primitives and the same hessra-tokens verification crates. Swapping between them requires changing how you connect to the authority service, not what tokens are minted or how you verify tokens.

The quickstart covers both paths.