Building Evidence Systems That Know What They Cannot Prove
When we started building verifiable Knowledge in MyZubster, hashing seemed like the easy part.
Take some data.
Canonicalize it.
Hash it.
Optionally anchor the digest somewhere external.
Done.
Except that almost every interesting problem begins after that.
What exactly did we hash?
Can somebody reproduce those bytes?
Does a blockchain transaction prove the underlying claim?
Does a merged GitHub pull request prove that a contributor was paid?
Does a payment record need to expose the recipient's wallet publicly?
And what should the system return when evidence simply does not exist?
Those questions pushed us from a collection of hashes toward an Evidence Graph.
More importantly, they changed our definition of a successful verification system.
A good evidence system shouldn't just tell you what it can prove.
It should preserve the boundary around what it cannot prove.
The first mistake: treating a hash as a conclusion
A cryptographic digest can establish something very useful:
These exact bytes produce this digest.
But that statement is much narrower than:
The information represented by these bytes is true.
Consider a Knowledge Card describing a technical contribution.
We can serialize it and calculate SHA-256.
Knowledge Card
↓
Canonical Payload
↓
SHA-256
↓
Digest
That gives us integrity.
It does not independently establish authorship, correctness, ownership, payment, or truth.
Once we started making those distinctions explicit, a single proof object stopped being sufficient.
We needed relationships.
Moving from hashes to an Evidence Graph
Our current Evidence Graph separates concepts that are often collapsed together:
Person
↓ CLAIMS
Claim
↓ DESCRIBED_BY
Knowledge Card
↓ SUPPORTED_BY
Evidence
↓ PRODUCED
Artifact
↓ CANONICALIZED_AS
Canonical Payload
↓ HASHED_AS
Digest
↓ ATTESTED_BY
Attestation
Each relation has deliberately limited semantics.
SUPPORTED_BY doesn't mean “proven true.”
HASHED_AS doesn't mean “verified.”
ATTESTED_BY doesn't mean “certified.”
And the hash of the Evidence Graph itself only protects the integrity of that graph representation.
This may sound overly cautious.
In practice, it makes the system much easier to reason about.
Zero attestations can be the correct answer
One of our tests produced a graph containing seven nodes, seven edges and:
attestation nodes: 0
That became one of our favorite results.
There was no confirmed blockchain attestation associated with that graph.
So the system didn't create one.
It still produced a deterministic graphHash.
It still represented the evidence chain we actually had.
But it stopped there.
That gave us an architectural rule:
Missing evidence should remain visibly missing.
A verification system becomes dangerous when absence gets silently upgraded into inference.
Then someone asked the right question
After we wrote publicly about the Evidence Graph, Mike Dabydeen pointed out another reproducibility problem.
Our Proof v2 experiment contains an exact canonical payload stored in Git.
It is 3,168 bytes.
Re-hashing those stored bytes produces:
6097e05866bafceec24663d2638cb1dae5742ac78284abbfd45cc9c3b0bfb845
That's reproducible.
But Mike's question, paraphrased, was:
Can someone reproduce those same bytes starting from the Knowledge Card?
That is a different test.
And it exposed an important missing piece.
Canonicalization is part of the proof
“Hash the thing you mean” isn't enough.
We also need:
Record how you produced the thing you hashed.
If two serializers represent the same logical JSON differently, they can produce different byte sequences and therefore different hashes.
So this:
Knowledge
↓
Canonical Payload
needs to become closer to:
Knowledge
↓
CANONICALIZED_AS
│
├── scheme
└── version
↓
Canonical Payload
RFC 8785, the JSON Canonicalization Scheme, is an obvious candidate for JSON data.
But there's an important migration rule here.
We cannot simply decide that our historical payload “was RFC 8785.”
First we have to test it.
If our existing 3,168-byte payload can be reproduced byte-for-byte using that scheme, great.
If it cannot, history should remain history.
We preserve Proof v2 with its actual serialization semantics and introduce a new explicitly labelled canonicalization version.
No retroactive renaming.
We've now turned this into a tracked engineering requirement.
What does a blockchain attestation actually add?
This led to another useful boundary.
Suppose Git already contains the canonical payload.
Why anchor its digest to a blockchain?
One possible answer is ordering outside our own infrastructure.
A Git commit timestamp isn't equivalent to an ordering event produced by an external blockchain.
So we're narrowing the intended meaning of ATTESTED_BY to something approximately like:
The committed digest existed no later than the referenced block on the identified chain.
That's useful.
But notice everything it doesn't say.
It doesn't say:
the claim is true
It doesn't say:
the contributor owns the work
It doesn't say:
MyZubster certified the person
And it doesn't say:
the blockchain verified the real-world event
Therefore an attestation needs explicit metadata sufficient to check the narrow statement.
Conceptually:
{
"chainId": "...",
"blockNumber": "...",
"transactionHash": "...",
"digest": "..."
}
The blockchain becomes an evidence source with defined semantics rather than a generic “verified” badge.
Testnets taught us another lesson
We currently have an experimental Sepolia proof.
That's useful for development.
It is not the same thing as permanent evidence infrastructure.
Testnets can disappear.
So the graph shouldn't assume one attestation is the final attestation.
Instead:
┌─ ATTESTED_BY → testnet
│
Digest ──────────┼─ ATTESTED_BY → L2
│
└─ ATTESTED_BY → another anchor
That gives us an interesting property.
A later, more durable attestation doesn't need to rewrite or erase the earlier experiment.
Evidence accumulates.
Then we applied the same model to GitHub contributors
Once the Knowledge model became stricter, we asked whether it could represent software contribution history.
That sounds straightforward:
Contributor
↓
Pull Request
↓
Merged
↓
Bounty
↓
Paid
But this representation hides several independent state transitions.
A merged pull request is not a payment receipt.
A reward record is not necessarily a blockchain settlement.
And a transaction ID alone doesn't establish that the expected settlement was independently confirmed.
So we introduced additional Evidence Graph concepts:
contribution
bounty
settlement
and relationships:
CONTRIBUTED_TO
ACCEPTED_AS
SETTLED_AS
A real merged contribution became our regression test
We're using actual GitHub contribution history rather than relying entirely on synthetic fixtures.
For a merged contribution, the graph can establish:
Person
↓ CONTRIBUTED_TO
Contribution
↓ ACCEPTED_AS
GitHub Artifact
But if we don't have confirmed settlement evidence, the graph stops there.
There is no:
SETTLED_AS
edge.
This produces another important invariant:
Merged does not mean paid.
And similarly:
A reward state does not automatically mean independently confirmed external settlement.
Our targeted Evidence Graph suite currently contains 23 passing tests around these semantics.
The contributor extension is still going through the pull-request lifecycle, so we distinguish tested implementation from merged production state.
Making payment evidence deliberately difficult to invent
For SETTLED_AS, we're enforcing stronger conditions.
Conceptually:
Bounty
↓ SETTLED_AS
Settlement
├── status: paid
├── txId
├── independent sourceReference
└── verification: confirmed
The source must actually be a bounty.
The target must actually be a settlement.
The settlement must be paid.
The relationship must represent confirmed verification.
And transaction evidence needs an independent source reference.
Why be this strict?
Because:
transaction submitted != transaction confirmed
and:
transaction ID present != bounty settlement proven
Evidence graphs are useful precisely when those distinctions survive serialization, APIs and UI layers.
Privacy changes what “verifiable” should mean
There's another trap.
If a system wants stronger evidence, it's tempting to publish more data.
Wallet addresses.
Identity information.
Full documents.
Payment metadata.
That isn't necessary.
Our evidence model distinguishes visibility classes including:
PUBLIC
RESTRICTED
PARTICIPANT_ONLY
EPHEMERAL
Unknown classification fails closed.
A settlement can internally contain information required to execute a payment without automatically exposing that information in the public Evidence Graph.
This leads to another principle:
Verification should disclose the minimum evidence required for the claim being checked.
More public data doesn't automatically mean stronger verification.
Sometimes it only means weaker privacy.
We found the same pattern while building private AI
Interestingly, we encountered almost the same problem in Zorgax's AI privacy architecture.
Calling a provider “local” wasn't enough.
If its endpoint was configurable, then “local” was only a convention.
So the boundary had to become testable:
LOCAL_ONLY
↓
must resolve to loopback
The same happened with web search.
Protecting the model request was insufficient if sensitive information had already left through a search provider.
So the decision had to move earlier:
classify
↓
policy
↓
egress decision
↓
search / model / provider
Om Yaduvanshi summarized the loopback lesson particularly well in feedback on our article:
“turns a vibe into something testable.”
That phrase describes much of what we're trying to do with Evidence Graphs too.
Turn assumptions into invariants.
Then test them.
The next experiment has to happen through the product
The next step isn't another manually assembled graph.
We want someone to create Knowledge through MyZubster and Zorgax and have the complete pipeline execute automatically.
Our target is:
Human
↓
MyZubster / Zorgax
↓
Knowledge candidate
↓
Human confirmation
↓
Independent review
↓
Verified Knowledge
↓
Versioned canonicalization
↓
SHA-256
↓
Evidence Graph
↓
GitHub provenance
↓
optional external attestations
GitHub writes need to be genuinely automated.
Paths need to be deterministic.
Retries need to be idempotent.
The resulting commit SHA needs to return to the Knowledge provenance.
And privacy classification has to survive the process.
If we manually create the final GitHub file ourselves, we haven't tested that system.
We've only tested GitHub.
Eventually: Knowledge → development → contribution
The larger architecture goes further.
Verified Knowledge can reveal a missing capability.
That can become a neutral DevelopmentRequest.
Verified Knowledge
↓
DevelopmentRequest
↓
DRAFT
↓
OPEN
↓
IN_PROGRESS
↓
SUBMITTED
↓
VERIFIED / REJECTED
GitHub materialization is optional.
Bounty attachment is optional.
Payment is separate.
That separation is deliberate.
Creating work doesn't promise money.
Verifying work doesn't prove payment.
Paying someone doesn't prove every claim associated with their profile.
What exists, and what doesn't
Today we have working foundations for verified Knowledge, provenance, deterministic hashing, Evidence Graph construction, privacy boundaries and contributor-evidence projection.
We have real regression cases.
We have a 23-test targeted Evidence Graph suite.
And we have learned to consider “zero attestations” a perfectly legitimate output.
But several things remain unfinished.
Canonicalization scheme/version metadata is our next hardening step.
Our existing payload still needs to be tested before we can claim RFC 8785 compatibility.
Blockchain attestation metadata needs explicit chain/block semantics.
The contributor Evidence Graph work still needs to complete its PR lifecycle.
The MyZubster/Zorgax → GitHub Knowledge pipeline still needs a genuine product-level end-to-end test.
And we still need a real, independently confirmed contributor settlement before we can show a real SETTLED_AS example.
Sepolia remains experimental.
Those aren't embarrassing gaps.
They are the difference between a roadmap and a claim.
The architecture we're aiming for
Eventually, we want this:
Human / Contributor
↓
MyZubster + Zorgax
↓
Knowledge
↓
confirmation + review
↓
Canonical Payload
(scheme + version)
↓
Digest
↓
Evidence Graph
↓
GitHub provenance
↓
optional attestations
↓
DevelopmentRequest
↓
Contribution
↓
Acceptance
↓
optional Bounty
↓
independently confirmed Settlement
↓
Verifiable Contributor History
Not a résumé on-chain.
Not a blockchain declaring truth.
Not an AI deciding reputation.
An evidence structure that applications can inspect without silently changing the meaning of the underlying evidence.
The rule we're converging on
After several iterations, three principles keep surviving:
Hash the thing you mean.
Then:
Record how you produced the bytes.
And finally:
Model only the relationship you actually have evidence for.
Perhaps there's a fourth:
If you can't prove something yet, preserve that uncertainty.
That's less dramatic than putting a green “VERIFIED” badge on everything.
But it's much more useful when somebody eventually asks:
“Verified by what?”
Want to contribute?
MyZubster is being developed in public, and small, reviewable contributions are useful.
You don't need to implement a blockchain protocol.
Useful contributions can be much narrower:
- reproducible tests for privacy or evidence boundaries;
- canonicalization test vectors;
- Evidence Graph edge cases;
- GitHub integration tests;
- documentation;
- accessibility improvements;
- small focused fixes.
A particularly interesting challenge right now is trying to break one of our assumptions: find a case where the graph appears to claim more than its evidence actually supports.
That's exactly the kind of bug we want to find.
Suggested Coder Legion topics/tags: Software Architecture, Blockchain, AI, Open Source, Privacy, Node.js
Suggested subtitle:
What canonicalization, zero attestations, GitHub contributors and private AI taught us about building systems that preserve the limits of their own evidence.