Ranex
A Signed Record Is Not a Fact

A Signed Record Is Not a Fact

A signature can prove who made a record, not whether the policy deciding it came from a trusted committed state.

8 min read
Share:X

TL;DR: Ed25519 proves who signed specific bytes, not whether the claim is true; verdict policy must come from the evaluated commit. This slice log started with the question that matters: what exactly did that green light prove?

Your build says PASS, the record is signed, and everyone wants to move on. Before you do, what exactly did that green light prove?

In this note

A signed record can be real and still be used to tell you a false story. The failure is in the trust root, not the cryptography.

I learned this by closing the same slice, reopening it when audits showed the tests were narrower than reality, closing it again, then reopening it a second time. That cost belongs in the record because it gives you something useful to check in your own pipeline.

A signature proves origin, not truth

An Ed25519 signature proves that the holder of a private key signed specific bytes. It does not prove that the claim inside those bytes is true.

That distinction is the whole post. Your CI record can tell you who produced an observation. It cannot, by signature alone, tell you whether the rule that admitted it was trustworthy, whether the command meant what the claim says, or whether an approver was independent.

In Ranex, evidence is signed and bound to a producer in a committed public keyring. The verifier has public keys, not the private keys used to sign. That is useful when evidence is produced on one machine and verified on another.

But the first version still had a dangerous opening: the keyring and gate catalog could be read from the working tree. An uncommitted edit could decide a verdict. The project said review of the committed keyring was the control. The code had not made that true.

Review cannot control bytes that were never committed for review.

That is a shape worth noticing. Security language can make a weak boundary sound solid: signed, verified, trusted, approved. Ask one plain question instead: which exact bytes decided this result, and who was allowed to choose them?

The second reopening was the real scar

The second reopening showed that checking a committed file is not enough. You must also refuse a trust-root path the commit does not carry.

SLICE-002 was reopened a second time on 2026-08-02 because the trust-root check skipped itself when the evaluated commit had no such path. The path came from a flag. The party being gated could name a catalog or keyring that the commit did not carry, and Ranex would read it unchecked.

This is the attacker selecting the rulebook after the game, not an edge case.

The slice record reproduced an attacker-named gate catalog that could rewrite the gate after the work. It also reproduced a keyring at a gitignored path, where a producer could register itself while git status stayed clean. Another route used a committed symlink at a reviewed name: resolution followed the link before Git was asked, so the reviewed name was never the thing being checked.

All of those routes landed on the same mistake: absence got a pass. The code returned without comparing anything.

ADR-002 closed that opening by refusing any trust-root path the evaluated ref does not carry. It compares the bytes Git records for the path as named with the bytes that would decide the verdict, then returns committed bytes for the loaders to parse. The loader does not reopen a mutable path afterward.

That last part matters. A compare followed by a second read is two chances for different bytes to appear. The record measured the change with strace: one open per trust-root file after the fix, where there had been three and two.

Read the policy from the committed state, not from wherever a flag points. The kernel model only helps when the inputs to its decision are pinned too.

What to hunt for in your own pipeline

Look for the files and paths that decide what a PASS means. If a worker, build, or command under test can choose them, your verifier has an opening.

  • Configuration that declares required checks, trusted identities, allowed commands, or approval rules.
  • CLI flags or environment variables that select policy files, keyrings, manifests, or catalogs.
  • Loaders that check one path and then reopen that path later.
  • Symlinks, normalized paths, and ignored files near a policy lookup.
  • Code that treats a missing policy file as an empty policy, a default, or an innocent absence.
  • Tests that edit a committed policy file but never name a policy file the commit does not carry.
  • Green controls that prove ordinary work can still proceed after a refusal is added.

Do not stop at “the file is in the repository.” Ask whether the evaluated commit carries that exact path. A working tree is a place people work. It is not automatically a trustworthy source of policy.

And do not mistake a clean status output for evidence that nothing changed. Gitignored inputs can be invisible to the habitual check while still deciding the result.

Why four audits found what the tests missed

Independent audits exist because your first test suite is narrower than reality. SLICE-002 closed after 17 defects across four independent audits, not because the first green run had been sufficient.

The initial close was premature. Two independent auditors found defects and showed that two completed criteria were false. One test compared the same string to itself. Another used a single-claim gate even though the real repository gate required two claims.

They are the lesson, embarrassing or not. A test can be green because it tested an easier world than the one your tool inhabits.

The second reopen sharpened it further. The tests had caught an edit to a committed trust root. Nobody had asked what happened when the path was absent from the commit. The test proved the answer to one question. The code needed to survive another.

There is a simple review prompt hiding here: what input shape did this test not ask about? Missing path. Ignored path. Redirected path. File swap after check. A policy that is syntactically valid but weak. These are not exotic when the input selects the control.

Ranex is pre-release. The closed slice does not mean the whole system has become a source of truth. Its own record states limits plainly: a signature does not stop a same-user attacker who can read the signing key, and the approver identity is an unauthenticated string. I do not have evidence that either is solved by signing evidence.

Stating limits that plainly is part of the control. A signed record that looks stronger than it is can cause more damage than an unsigned one, because people stop asking questions.

Questions people actually ask

These questions separate signature origin from committed trust-root policy.

What does an Ed25519 signature prove in an evidence record?

An Ed25519 signature proves the record was signed by the holder of a registered producer key over the signed fields. An Ed25519 signature does not prove the claim is true or that the approval is authentic.

Why must a keyring and gate catalog come from the commit?

The keyring and gate catalog files decide which evidence counts. If the party being judged can point the verifier at unchecked bytes, it can choose the policy that judges it.

What should happen when a trust-root path is absent from the evaluated commit?

The verifier should refuse. A file no commit carries was reviewed by nobody, whatever its contents say.

Make one trust decision visible this week

Start with one green check you already trust. Identify its policy file, identity list, manifest, or rule catalog. Then answer: does the verifier read committed bytes, or does it read whatever the current process can reach?

If the answer is the second one, make absence block. Test an uncarried path. Test an ignored one. Test a symlink. Test a swap between validation and load. Keep the ordinary committed path as a green control so “refuse everything” cannot impersonate safety.

The slice records are in the Ranex repository, under docs/slices/done/. Read the failure before you borrow the fix.

Try it. Break it. Tell me what broke. If this helped, star the repository and send an honest critique. The useful reply is the one that finds the next unchecked input.

Disclosure: this post was drafted with AI assistance. Every factual claim traces to the repository’s README or slice records — the same fact gate the product enforces on code. It ships only after Anthony’s own review.

About the author

Anthony Garces

Anthony Ryan M. Garces is a Senior Principal Lead Architect with 17+ years in IT, including four years at Pantheon on mission-critical platform work. He is building Ranex in public.