menu_bookHow Aletheia works

From a git push to a proof no author can rewrite.

Aletheia (ἀλήθεια, un-concealment) anchors every push to Monad and hands anyone — human or judging agent — the means to re-verify the whole record from a fresh clone. This page is the full mechanism: the architecture, the contract, and the threat model it was designed against.

The claim, stated honestly

What it proves — and what it doesn't

Git history is author-controlled. git commit --date and GIT_COMMITTER_DATE fabricate any timeline after the fact; a force-push rewrites it silently. A block timestamp is produced by consensus at a moment in time and cannot be retroactively manufactured.

check_circle

It proves

  • Existence by time T — the attested tree of source existed at or before its block timestamp.
  • Continuity — the sequence of attestations shows incremental evolution, not a single late dump.
cancel

It does not prove

  • Authorship or originality — someone can attest code they copied. Aletheia proves timing, not who wrote it.
  • We say this plainly in the UI and README rather than overstate the guarantee.
End to end

System architecture

A push flows through an ingestion layer that verifies and forwards it, into a single event-sourced contract, and back out to two independent readers — the proof page and the verification CLI.

commit
git push
GitHub repo
arrow_forward
webhook
Ingest
Bridge · HMAC / Action
arrow_forward
deployed_code
attest()
AletheiaRegistry
arrow_forward
account_tree
Read
Proof page + CLI
verified_user

Verify & forward

The bridge checks the X-Hub-Signature-256 HMAC over the raw body, dedupes the delivery ID, resolves the repo to a projectId, and submits attest / attestBatch through a serialized nonce-managed queue.

bolt

Seal on chain

The registry emits one Attested event per commit carrying the commit hash, tree hash, and block.timestamp. Events, not storage — every consumer reads them via eth_getLogs.

fact_check

Read two ways

The proof page renders the timeline from chain events (GitHub metadata is optional garnish). The CLI re-derives the same hashes from a raw clone and compares.

The threat-model core

Why the tree hash, not just the commit

Attesting only commit hashes admits a cheat: push empty or junk commits on time, then later force-push real content and claim the old hashes. The commit hash does commit to its content — but verifying that requires the original objects, which a force-push can vanish.

So Aletheia attests the tree hash alongside the commit. Verification recomputes both from the public repo:

$ git rev-parse <commit>^{tree}
# must reproduce the attested tree hash, byte for byte

If the repo's current objects don't reproduce the attested pair, verification fails visibly. A cheater would need a pre-image of the attested tree hash that matches their later-written code — computationally infeasible. Git SHA-1 ids are 20 bytes, left-aligned and zero-padded into bytes32; SHA-256 repos fill all 32, and the CLI detects which encoding a repo uses.

Trust, minimized

Two attestation paths

dns

Hosted bridge

A GitHub webhook hits a Fastify service that verifies the HMAC, dedupes replays, and submits with a low-privilege attestor key. Fastest to set up — one webhook URL and secret pasted into repo settings.

Convenience · you trust the bridge's uptime
shield_lock

Trustless GitHub Action

A published workflow lets a project attest its own pushes with its own key, writing to the same contract with the same semantics. No one has to trust Aletheia's server at all.

Zero third-party trust

Both paths still attest a force-push (the new head is real content at a real time) but mark it as a rewrite on the timeline — honesty includes showing rewrites.

AletheiaRegistry.sol

Contract reference

A single, event-sourced contract. Storage holds only per-project control state; all commit data lives in events, which are far cheaper and are what every reader consumes.

State-changing functions

registerProject(bytes32 repoHash, string repoUrl, address attestor)

One project per repo — reverts RepoAlreadyRegistered. repoUrl travels only in the event.

attest(uint256 projectId, bytes32 commitHash, bytes32 treeHash)

Attestor-only, unsealed-only. Emits one Attested with block.timestamp.

attestBatch(uint256 projectId, bytes32[] commitHashes, bytes32[] treeHashes)

Multi-commit pushes. Array lengths must match and be non-empty (BadInput).

linkContract(uint256 projectId, address deployed, string label)

Owner-only. Binds a deployed contract into the build timeline.

setAttestor(uint256 projectId, address newAttestor)

Owner-only. Rotates the attestor key if the bridge key is compromised.

seal(uint256 projectId)

Owner-only, idempotence-guarded. After sealing, every mutating call reverts forever.

Events

ProjectRegistered(id, owner, attestor, repoHash, repoUrl, ts)
Attested(id, commitHash, treeHash, ts)
ContractLinked(id, deployed, label, ts)
AttestorChanged(id, newAttestor)
Sealed(id, ts)

Errors

RepoAlreadyRegistered()UnknownProject()NotOwner()NotAttestor()ProjectSealed()BadInput()

Deployed on Monad Testnet · chain 10143

What it defends against

Threat model

Covered

  • shieldForged commit dateschain timestamp wins.
  • shieldPost-hoc history rewritemissing commits flagged by the CLI.
  • shieldContent substitutiontree-hash mismatch.
  • shieldJunk-commit-then-replacetree-hash pre-image infeasibility.
  • shieldPost-submission additionssealing closes the record.
  • shieldBridge key theftlow-privilege, rotatable; worst case is spurious attestations, never fund loss.
  • shieldWebhook forgery / replayHMAC + delivery-ID dedupe.

Out of scope, stated honestly

  • radio_button_uncheckedProving authorshipsomeone can attest code they copied — timing, not originality.
  • radio_button_uncheckedPre-writing code before registeringmitigated socially; the timeline's shape during the event is the signal judges read.
  • radio_button_uncheckedGitHub lying about tree contentsdefeated by independent CLI recomputation from a raw clone.
The judge's tool

Independent verification

Anyone re-runs the whole proof with nothing but Node ≥ 20 and a system git. No keys, no writes, no trust in Aletheia's servers.

$ npx aletheia-verify <projectId>
# reads ProjectRegistered + all Attested events
# blobless-clones the repo, recomputes commit + tree pairs
# compares against chain, prints a per-commit verdict, exits non-zero on mismatch

Green — verified

Commit present and its recomputed tree matches the attested hash. An honest repo that never rewrites published history stays all-green and exits 0.

Yellow — missing

An attested commit is gone from history — rewritten after attestation. Fails: a tool that passed these would let a rewritten repo look clean.

Red — mismatch

A present commit whose recorded tree never matched it. The residual case; also fails and exits non-zero.

Read it straight from the chain

Every proof on this site is reproducible. Verify a repository, browse the registry, or read the source — the record answers to the chain, not to us. Hackathon clock starts 2026-07-13.