The verdict layer for event logs

Your logs don't say what happened. They say what the clocks believed.

Every double charge, every reconciliation break, every "impossible" balance ends the same way: engineers sort a million events by timestamp and argue about one millisecond. Orderproof replaces the argument with a verdict — SAFE, VIOLATED, or an honest AMBIGUOUS — and hands you the smallest set of events that forces it.

How a verdict is computed
01 — Live engine

Three traces. One of them is a lie.

Each scenario is a real structure: services, clocks, message ids, a declared skew bound. Nothing is hard-coded — the verdict on the right is computed in this request by searching admissible orderings.

Trace

—
VERDICT —
computing…

wall-clock sort —
events
—
causal edges
—
downsets searched
—
compute
—
±250 ms

The only assumption in the model. Change it and watch the truth change — that is the point.

what this shows


02 — Why this is not a dashboard

Observability shows you spans. This decides a question.

The question finance actually asks — "did we take money we were not owed?" — is a question about orderings. Databases of spans cannot answer it, because they treat timestamps as truth.

/ 01

It quantifies over orderings

A trace with n concurrent events has exponentially many orders consistent with causality. Orderproof searches the space, not a sample, and keeps the two facts that matter: is there a safe order, and is there any order that breaks.

/ 02

It answers in three values

Dashboards force a binary. Real logs often cannot decide: the message id was dropped, the clock bound was a guess. Saying AMBIGUOUS, then telling you exactly what to instrument, is worth more than a confident wrong number.

/ 03

It ships a witness

A VIOLATED verdict comes with a deletion-minimized core: the smallest subset of events that still forces the failure. That is the difference between "somewhere in 1M events" and a three-line incident report you can hand to an auditor.

03 — Method

Four deterministic steps, no model in the loop.

This is arithmetic and search. No LLM, no scoring, no "AI-powered" anything. That is deliberate: a verdict that cannot be audited is not a verdict.

01

Recover causality

Program order within a service, plus send/receive pairs on message ids. This yields a happens-before DAG that stays correct while the wall clocks are wrong.

02

Bound real time

Each clock reading c is only known to lie in [c−B, c+B]. If the latest possible time of A is before the earliest possible time of B, then A precedes B in every admissible order.

03

Search downsets

Every prefix of an admissible order is a downset of the DAG. The balance at a downset is fixed — independent of the order taken to reach it. So the whole question collapses to reachability over downsets.

04

Classify, then minimize

If no negative downset is reachable: SAFE. If no safe path exists: VIOLATED. Otherwise AMBIGUOUS. For violations, delete events greedily while the violation persists.

// the whole product, essentially causal = program_order ∪ message_edges forced(a,b) = hi(a) < lo(b) must = transclosure(causal ∪ forced) // balance of a prefix depends only on its event set bal(S) = initial + Σ delta(e) for e ∈ S // three-valued decision SAFE ¬∃ reachable S : bal(S) < 0 VIOLATED ¬∃ path ∅→all : ∀S on path bal(S) ≥ 0 AMBIGUOUS otherwise // witness W = argmin |W| : restricted(W) still VIOLATED

Exact search is bounded at 24 events and 400k downsets per request; beyond that the engine samples orders and marks the answer approximate. The demo traces are all exact.

04 — API

Two endpoints. One decision.

Send the events you already emit. Get a verdict, a witness, and the wall-clock counterfactual for the incident review.

# ask the engine
curl -s https://orderproof.toyou.gr/api/audit \
  -H 'content-type: application/json' \
  -d '{
    "initial": 0,
    "skew": 250,
    "events": [
      {"id":"led-1","svc":"ledger","type":"credit",
       "clock":1009,"seq":0,"delta":120},
      {"id":"led-2","svc":"ledger","type":"debit",
       "clock":1002,"seq":1,"delta":-120}
    ]
  }'
// what comes back
{
  "verdict": "SAFE",
  "reason": "Every admissible ordering keeps
     the balance at or above 0...",
  "wallClock": {
    "verdict": "VIOLATED",
    "minBalance": -120
  },
  "stats": { "states": 3, "ms": 0.6 }
}
05 — Limits, stated up front

What this cannot do.

A product that hides its assumptions is selling confidence, not truth. These are the three that matter.

It needs message ids

Without send/receive pairs there is no partial order, and the honest answer collapses to AMBIGUOUS. Instrumenting those ids is the actual adoption work — the engine is the easy part.

Aggregate invariants are hard

Exact downset search is exponential in the worst case. Incident-sized windows are fast; "audit every event forever" is not, and the engine says so instead of pretending.

The skew bound is an assumption

Every verdict is relative to a declared bound B. Set B to zero and you get confident nonsense. The engine detects when B is provably too small and refuses the trace rather than lying.