librings: concentric gates for data that earns its way in

Imagine you are standing outside a series of concentric walls. Each wall has a single gate. The gate will not open unless you can prove you are carrying exactly what the wall expects — not a password, not a token issued by some authority, but a mathematical proof derived from the thing you are carrying. The proof changes every time. You cannot reuse yesterday’s proof. You cannot forge tomorrow’s.

That is what librings does.


The problem it solves

Most security systems work like a nightclub bouncer. You show a credential at the door — a password, a certificate, an API key — and if it matches, you are in. Everything behind the door trusts you equally. The credential was checked once, at the perimeter, and then it is done.

This is a remarkably poor model for anything that matters. A single check at a single boundary means that anything which gets past that check has unrestricted access to everything behind it. It is why a stolen API key is catastrophic. It is why a compromised session token lets an attacker do anything the user could do. The bouncer checked once and went home.

What if instead of checking once at the perimeter, you checked at every layer? And what if each check was not just “do you have a valid credential” but “does your credential prove that you actually did the work the previous layer required”?

That is the ring model.


How it works

librings concentric gated pipeline diagram

A ring pipeline is a sequence of processing stages — rings — each with three parts:

  1. A work function that takes data and produces output
  2. A test that evaluates whether that output is correct
  3. A gate that computes a cryptographic nonce from the test result and compares it against the expected value

The nonce is the key insight. It is not a random number. It is not a pre-shared secret. It is derived from the actual output of the work, combined with a chain of prior nonces stretching back to the first ring. You cannot produce the correct nonce without having done the work. And you cannot produce the correct nonce for ring three without having produced the correct nonce for ring two, which required ring one, which required the initial commitment.

Think of it like a chain of consequences. Each ring’s proof depends on every ring before it. Tamper with one and every subsequent proof is wrong.

Input → [Ring 0] → gate₀(nonce₀) → [Ring 1] → gate₁(nonce₁) → [Ring 2] → gate₂(nonce₂) → Output
         work       test+verify      work       test+verify      work       test+verify

The chain starts with a seed and a counter. The counter is different every execution — it mixes a random value with the system clock. Same pipeline, same input, run a hundred times: a hundred different nonce chains, a hundred different execution fingerprints. The output is the same. The proof of how you got there is unique.

This is the moving target property. There is no “correct nonce” to steal. The correct nonce for this execution will never be correct again.


The nonce chain

The cryptographic spine of the system is an HKDF-SHA256 chain. At the start of every execution, a commitment is derived from the pipeline seed and a fresh counter:

commitment = HKDF(seed || counter, info="librings-commitment-v1")

Each ring advances the chain by mixing the prior nonce, the ring’s counter offset, and B — a 32-byte value that only exists if the ring’s test passed:

nonce_n = HKDF(nonce_{n-1} || counter_n || B_n, info="librings-advance-v1")

B is the test’s verdict expressed as entropy. For an exact-match test, B is the SHA-256 hash of the output. For a structure check, B is derived from the schema hash and the field count. For a composite test that runs multiple sub-tests, B is an HKDF reduction of all sub-Bs. The point is that B is deterministic from the output and unknowable without it. You cannot guess B. You have to compute it from real data.

Nonce comparison uses constant-time equality everywhere. No timing side channels. The library uses the subtle crate, which compiles to fixed-time comparison operations — the comparison takes the same number of CPU cycles whether the nonces match on the first byte or the last.


What happens when a ring fails

The pipeline halts. It does not skip to the next ring. It does not retry. It returns a typed failure naming the ring that failed and why — test failed, or test passed but the nonce did not match (which would mean the chain is broken, which would mean something very wrong happened). The halted state is a first-class result, not an exception.

This is the nested isolation property. A failure at ring two means the data never reaches ring three. It does not matter what ring three would have done with it. The data was not qualified to reach ring three because it could not satisfy ring two. The gate is closed.


The audit log

Every gate evaluation — pass or fail — is recorded. The audit log stores the execution ID, the ring ID, the counter, the nonce, the B value, and the outcome. The log is append-only: SQLite triggers physically prevent UPDATE and DELETE operations. Even root cannot silently modify an entry.

The beautiful consequence: given the audit log and the original seed, a third party can reconstruct the entire nonce chain and verify that every gate was valid. They do not need the original data. They do not need to re-run the pipeline. The log IS the receipt. The nonces are a mathematical proof that the work happened in the correct order with the correct results.

ring 0  [SigilValidation]      VALID  nonce=abcd1234...
ring 1  [HandshakeComplete]    VALID  nonce=ef567890...
ring 2  [KeyDerivation]        VALID  nonce=...
ring 3  [TransferComplete]     VALID  nonce=...
ring 4  [ReceiptAcknowledged]  VALID  nonce=...

All gates verified. Transfer receipt is authentic.

Capability tokens

Here is where the ring model becomes something more than a validation pipeline.

The nonce produced by a passing gate is not just a checksum. It is a credential. A Capability is issued only from a GateResult::Pass. It cannot be constructed any other way — the constructor is private to the module. The type system enforces this at compile time.

A capability carries a scope, a ring ID, and an expiry. A resource protected by a CapabilityGuard will not grant access unless the capability matches the required ring, the required scope, and a valid nonce — verified constant-time. No IAM system. No JWT. No session management. The proof of correct traversal IS the credential.

A capability scoped to ring two does not grant access to a resource guarded by ring three. A capability for object ID 1 does not work for object ID 2. The permissions are as narrow as the ring that issued them.


Wormhole integration

The first real application maps a Wormhole file transfer onto five rings:

RingPhaseWhat the test checks
0Sigil parsedInput matches word AAAA BBBB format
1SPAKE2 handshakeKey material is 32 bytes, non-zero
2Key derivationDerived key is exactly 32 bytes
3File transferDecrypted output has valid framing
4ReceiptResponse contains ACK, 3-64 bytes

The pipeline seed is derived from the SPAKE2 shared secret via HKDF. It is never transmitted. Both sides derive it independently from the key exchange output. If you did not participate in the handshake, you cannot derive the seed, which means you cannot produce valid nonces, which means you cannot forge a receipt.

A companion CLI tool, wormhole-verify, reads an audit log and reconstructs the chain. If the nonces check out, the transfer receipt is authentic. If someone tampered with a single nonce in the log, the chain breaks at that ring and every ring after it shows a mismatch.


Seeing it run

Here is a proof-of-concept: three rings, three gates, a Sagan quote as input. Each gate that opens reveals a line of a haiku. (source)

  librings — RINGS Isolating Nested Gated Security
  ─────────────────────────────────────────────────
  Input  (131 bytes):
  "We are made of starstuff -- the nitrogen in our DNA, the cal…"

  Executing 3-ring cryptographic gate pipeline…

  ┌─ Ring 0 gate opens ──────────────────────────────────
  │  "Carbon in our pies"
  │  nonce  b1201862d59060dd…
  │  B      a3647fd38b9c894a…
  └──────────────────────────────────────────────────────

  ┌─ Ring 1 gate opens ──────────────────────────────────
  │  "iron, calcium, all forged"
  │  nonce  950caa01bad87726…
  │  B      bcc5fa0d57dfb856…
  └──────────────────────────────────────────────────────

  ┌─ Ring 2 gate opens ──────────────────────────────────
  │  "in the death of stars"
  │  nonce  11c4be520a7c032d…
  │  B      6c7baf60e4861c3e…
  └──────────────────────────────────────────────────────

  ══════════════════════════════════════════════════════
  PIPELINE SUCCESS
  ══════════════════════════════════════════════════════

  Output (168 bytes):
  "We are made of starstuff -- the nitrogen in our DNA,
   the calcium in our teeth, the iron in our blood,
   the carbon in our apple pies.
   [ring:0] starstuff [ring:1] [ring:2]"

  Execution counter : 3174205049486631756
  Final nonce       : 11c4be520a7c032d19fe12076f0f7186…

  Audit trail (3 entries):
    ring=0 outcome=Pass nonce=b1201862d59060dd…
    ring=1 outcome=Pass nonce=950caa01bad87726…
    ring=2 outcome=Pass nonce=11c4be520a7c032d…

  Capabilities issued (3):
    ring=0 scope=Named { name: "ring_0" } no expiry
    ring=1 scope=Named { name: "ring_1" } no expiry
    ring=2 scope=Named { name: "ring_2" } no expiry

  ─────────────────────────────────────────────────────
  The poem, complete:
    "Carbon in our pies"
    "iron, calcium, all forged"
    "in the death of stars"
  ─────────────────────────────────────────────────────

Ring 0 checks that input exists. Ring 1 checks that the payload contains “starstuff.” Ring 2 checks that the data passed through both prior rings. Each gate computes a nonce from the actual output — and each nonce depends on the one before it. The B values are identical across runs because the test outputs are deterministic. The nonces are different every time because the execution counter changes.

Run it yourself: cargo run --example haiku_poc


What this is really about

Richard Feynman once described how he checked the safety of the plutonium storage at Oak Ridge during the Manhattan Project. The facility had an impressive vault door at the front. Feynman walked around back and found the fence had a hole in it. The expensive door was theater. The actual security boundary was the cheapest part of the perimeter.

Most software security works this way. Enormous effort at the login screen. A bearer token behind it that works everywhere. The vault door is TLS and OAuth2. The hole in the fence is that once you have a session cookie, every API endpoint trusts you equally.

The ring model is different. There is no single perimeter. There are concentric perimeters, each with its own proof requirement derived from actual computation. The credential for each layer is manufactured by passing through the previous layer. You cannot skip ahead. You cannot present a credential from a different execution. You cannot replay a credential from last time because the counter has changed.

It is not a new idea. It is the old idea — defense in depth — taken to its mathematical conclusion. Every ring is a checkpoint. Every checkpoint produces a unique, unforgeable, unreplayable proof. The proof is the permission.

The nonce chain uses HKDF-SHA256 (RFC 5869) for key derivation.
Constant-time comparison uses the subtle crate (Rust, docs.rs/subtle),
which implements the ConstantTimeEq trait from the crypto-common
ecosystem. The append-only audit log enforcement uses SQLite triggers
(sqlite.org/lang_createtrigger.html). The Wormhole handshake uses
SPAKE2 (RFC 9382). Capability tokens follow the object-capability
model (Miller, "Robust Composition: Towards a Unified Approach to
Access Control and Concurrency Control," 2006), where possession of
an unforgeable reference IS the authority — no ambient authority,
no access control lists.