rcan
A Rust crate for capability-based authorization through signed, chainable ed25519 delegations.
Repository Health
Technical Analysis
rcan (Really simple user Controlled Authorization Networks) is a Rust crate from n0-computer implementing a UCAN-style capability system: an issuer grants a Capability to an audience by signing a Delegation, and delegations can be re-delegated in a chain so a receiver can pass along an attenuated grant it was itself given. An Authorizer holding a principal’s public key verifies that an invocation is backed by a valid, unexpired chain of delegations rooted at that principal — no central authorization server or database lookup is required to check a grant.
The crate is deliberately narrow in scope: it defines the Capability and CapabilityEncoding traits, the Delegation<C> type (generic over the capability payload, defaulting to opaque bytes via OpaqueCapability), expiry handling (Expires), and the encode/decode/verify machinery, using ed25519-dalek for signing and postcard/serde for the wire format. Application authors implement Capability::permits for their own capability enum to define which grants imply which others, then use Delegation::issue/delegate to mint tokens and Authorizer::check_invocation_from to verify them.
What You Get
- A
Capabilitytrait applications implement to define their own grantable permissions and thepermitsrelation between broader and narrower capabilities - A generic
Delegation<C>type that signs and encodes a capability grant, defaulting toOpaqueCapability(raw bytes) until typed viatry_with_capability_type - Chainable delegation via
Delegation::delegate, so a capability holder can re-delegate an attenuated grant to another audience, tracked throughCapabilityOrigin - Expiry support via
Expires::valid_for, checked at verification time withis_valid_at - An
Authorizertype that verifies an invocation against a signed delegation chain rooted at a known issuer’s public key, viacheck_invocation_from/check_invocation_from_at - Compact binary encoding via
postcard, plus base32 string encode/decode for human-readable transport of delegations
Common Use Cases
- Issuing scoped, expiring access tokens between peers in a distributed or peer-to-peer Rust application without a central auth server
- Letting a service delegate a narrower slice of its own permissions to a downstream client or agent, which can itself re-delegate further
- Verifying that an incoming request is backed by an unbroken, unexpired chain of signed grants back to a trusted root key
- Building capability-secure APIs where authorization is proven by possession of a valid delegation rather than looked up from a database
Under The Hood
Architecture
The crate is a single flat module (src/lib.rs) built around one core type, Delegation<C>, generic over a capability payload type that defaults to OpaqueCapability (raw bytes) and is upgraded to a concrete type via try_with_capability_type. A delegation bundles issuer and audience VerifyingKeys, a CapabilityOrigin (issuer-owned or chained from a prior delegation), an Expires window, the encoded capability bytes, and an ed25519 Signature over all of it. Chaining is expressed structurally through CapabilityOrigin::Delegation, letting Authorizer::check_invocation_from walk a chain back to a trusted root key with no external state. This keeps the trust boundary entirely local: verification is a pure function of the chain plus a known public key, with no lookup service in the loop.
Tech Stack
Pure Rust, edition 2021, minimum Rust 1.91. Signing/verification is delegated to ed25519-dalek (with a pinned pre-release range for v3), capability payloads are (de)serialized generically through serde plus a postcard binary codec chosen specifically for its compact varint encoding and blanket CapabilityEncoding impl over any Serialize/Deserialize type, data-encoding/data-encoding-macro provide base32 string transport, smallvec avoids heap allocation for small capability payloads, and n0-error supplies structured, stack_error-derived error enums (DecodeError, InvocationError). No async runtime or I/O dependency — the crate is computation-only.
Code Quality
Tests live in a dedicated src/tests.rs module (14 #[test] functions) covering issuance, delegation chaining, expiry, and decode/verify failure paths, using assert_matches, ron, and minicbor-serde as dev-dependencies to check cross-format behavior. Errors are typed and structured via n0_error::stack_error rather than stringly-typed, public API surfaces are documented with doc comments including a worked impl Capability example, and the repo runs cargo-deny (license/advisory/ban checks) plus a multi-workflow CI (ci.yaml, tests.yaml, wasm.yaml, zizmor.yaml for workflow security linting) on every change, indicating a maintained, security-conscious release process.
API Design
The public API is small and centered on a handful of well-named entry points — Delegation::issue, Delegation::delegate, Authorizer::check_invocation_from — with the generic C parameter defaulting to OpaqueCapability so callers can start with raw bytes and opt into a typed capability later via try_with_capability_type. Any Serialize + DeserializeOwned type automatically satisfies CapabilityEncoding, so integrating a custom capability enum requires implementing only the single permits method of the Capability trait rather than hand-rolling an encoding. Documentation is thorough at the type and method level, though the crate currently ships no top-level usage example or examples/ directory, so a newcomer has to piece together a full flow from doc comments and tests.