Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const eslintConfig = defineConfig([
"build/**",
"typechain-types/**",
"next-env.d.ts",
// Android native project — not source code
"android/**",
]),
]);

Expand Down
68 changes: 67 additions & 1 deletion src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
export const metadata = {
title: "About",
description:
"What OpenProof is and why it exists — a privacy-first, open-source proof-of-existence infrastructure tool.",
"What OpenProof is, who it is for, and when to use it — a privacy-first, open-source proof-of-existence tool for file fingerprinting, blockchain timestamping, and document integrity verification.",
};

export default function AboutPage() {
Expand All @@ -17,7 +17,7 @@
{/* ──────────── Hero ──────────── */}
<section className="mx-auto max-w-3xl px-6 pt-28 pb-20 sm:pt-40 sm:pb-28">
<div className="flex flex-col items-center text-center">
<img

Check warning on line 20 in src/app/about/page.tsx

View workflow job for this annotation

GitHub Actions / Validate

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
alt=""
className="size-24 sm:size-32"
src="/icon.svg"
Expand All @@ -37,6 +37,72 @@
</p>
</section>

{/* ──────────── Who uses OpenProof ──────────── */}
<section className="mx-auto max-w-3xl px-6 pb-20 sm:pb-28">
<h2 className="text-2xl font-bold sm:text-3xl">Who uses OpenProof</h2>
<p className="mt-4 text-sm leading-relaxed text-text-secondary sm:text-base">
OpenProof is for anyone who needs a permanent, independently verifiable
timestamp for a file — without uploading, storing, or exposing the file
itself.
</p>
<div className="mt-8 grid gap-x-12 gap-y-6 sm:grid-cols-2">
{[
["Developers", "Timestamp release builds, dependency manifests, and deployment artifacts. Verify that a deployed binary matches its source."],
["Creators & artists", "Prove original work existed before sharing with collaborators, submitting to galleries, or publishing online."],
["Researchers", "Establish priority for findings, datasets, and preprints before peer review. Protect against scooping."],
["Legal professionals", "Create independently verifiable timestamps for contracts, disclosures, records of counsel, and evidence submissions."],
["Journalists", "Anchoring source material, internal communications, and unpublished drafts — before publication or legal request."],
["Businesses & compliance", "Document integrity verification for regulatory records, audit trails, and internal policy compliance."],
["Archivists", "Long-term integrity checks for digital collections. Verify that archived files haven't drifted from their registered originals."],
["Individuals", "Personal records — wills, letters, receipts, photos, certificates. Anyone can timestamp anything."],
].map(([title, desc]) => (
<div key={title as string}>
<h3 className="text-sm font-bold sm:text-base">{title as string}</h3>
<p className="mt-1 text-sm leading-relaxed text-text-secondary">{desc as string}</p>
</div>
))}
</div>
</section>

{/* ──────────── Typical use cases ──────────── */}
<section className="mx-auto max-w-3xl px-6 pb-20 sm:pb-28">
<h2 className="text-2xl font-bold sm:text-3xl">When to use OpenProof</h2>
<p className="mt-4 text-sm leading-relaxed text-text-secondary sm:text-base">
Timestamp a file before any event where you might later need to prove
what you had, and when.
</p>
<ol className="mt-8 space-y-6">
{[
[
"Before publishing or releasing",
"Timestamp a draft, dataset, or release artifact before making it public. If someone later claims priority or originality, the onchain fingerprint proves your file existed first.",
],
[
"Before sharing with others",
"Send a file to a collaborator, client, or regulator? Timestamp it first. The registry preserves your version at that moment, independent of any later dispute.",
],
[
"Before archiving or migrating",
"Timestamp a file before moving it to cold storage or converting formats. Years later, re-hash and verify the content is unchanged from the original.",
],
[
"Before regulatory or legal submission",
"Records, logs, contracts, and disclosures benefit from an independent timestamp before submission. The onchain record is immutable and verifiable by anyone.",
],
].map(([title, desc], i) => (
<li className="flex gap-4" key={title as string}>
<span className="mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full bg-accent/10 text-sm font-bold text-accent">
{i + 1}
</span>
<div>
<h3 className="text-base font-bold sm:text-lg">{title as string}</h3>
<p className="mt-1 text-sm leading-relaxed text-text-secondary sm:text-base">{desc as string}</p>
</div>
</li>
))}
</ol>
</section>

{/* ──────────── Architecture diagram ──────────── */}
<ArchitectureDiagram />

Expand Down
5 changes: 3 additions & 2 deletions src/app/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ export default function CreateProofPage() {
<br /><span className="text-text-secondary">on Base Sepolia.</span>
</h1>
<p className="mt-5 max-w-xl text-base leading-relaxed text-text-secondary sm:text-lg">
Timestamp a SHA-256 fingerprint onchain. The file never leaves your browser.
Timestamp a SHA-256 fingerprint onchain. Prove a file existed at this moment.
The file never leaves your browser.
</p>
</section>

Expand Down Expand Up @@ -174,7 +175,7 @@ export default function CreateProofPage() {
</ActionPill>
)}
</div>
<p className="mt-4 text-xs text-text-muted">Base Sepolia testnet &middot; No real funds required</p>
<p className="mt-4 text-xs text-text-muted">Base Sepolia testnet &middot; No real funds required &middot; Only the hash is written onchain</p>
</div>

{/* Status line */}
Expand Down
26 changes: 18 additions & 8 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,28 @@ const geistMono = Geist_Mono({
export const metadata: Metadata = {
metadataBase: new URL("https://proof.kovina.org"),
title: {
default: "OpenProof",
default: "OpenProof — Proof-of-Existence · File Fingerprinting · Blockchain Timestamping",
template: "%s | OpenProof",
},
description:
"Open-source cryptographic proof infrastructure built on Base Sepolia. Timestamp file fingerprints onchain without uploading files anywhere.",
"Prove a file existed at a specific time — without uploading it. OpenProof is a privacy-first, open-source proof-of-existence tool. Hash a file locally with SHA-256, register the fingerprint on Base Sepolia, and verify independently. No uploads, no accounts, no backend. Used by developers, creators, researchers, and legal professionals for document integrity, digital evidence, and cryptographic timestamping.",
keywords: [
"OpenProof",
"proof of existence",
"file fingerprinting",
"blockchain timestamping",
"SHA-256 verification",
"cryptographic proof",
"document integrity",
"digital evidence",
"privacy-first",
"Base Sepolia",
"SHA-256",
"cryptographic proof",
"privacy-first",
"privacy-preserving verification",
"file integrity verification",
"timestamping service",
"open source proof of existence",
"web3",
"Solidity",
"wagmi",
Expand All @@ -36,26 +46,26 @@ export const metadata: Metadata = {
canonical: "https://proof.kovina.org",
},
openGraph: {
title: "OpenProof",
title: "OpenProof — Privacy-First Proof-of-Existence",
description:
"Open-source cryptographic proof infrastructure built on Base Sepolia. Timestamp file fingerprints onchain without uploading files anywhere.",
"Prove a file existed without uploading it. Hash locally with SHA-256, timestamp the fingerprint on Base Sepolia, verify independently. No accounts, no backend, no tracking.",
url: "https://proof.kovina.org",
siteName: "OpenProof",
images: [
{
url: "/og.png",
width: 1200,
height: 630,
alt: "OpenProof cryptographic proof infrastructure built on Base",
alt: "OpenProof — privacy-first cryptographic proof-of-existence on Base Sepolia",
},
],
type: "website",
},
twitter: {
card: "summary_large_image",
title: "OpenProof",
title: "OpenProof — Proof-of-Existence · File Fingerprinting · Blockchain Timestamping",
description:
"Open-source cryptographic proof infrastructure built on Base Sepolia. Timestamp file fingerprints onchain without uploading files anywhere.",
"Prove a file existed without uploading it. Hash locally with SHA-256, timestamp the fingerprint onchain, verify independently.",
images: ["/og.png"],
},
appleWebApp: {
Expand Down
139 changes: 137 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,36 @@ export default function Home() {
</div>
</section>

{/* ── Who it's for — lean, readable ──────────────── */}
<section className="mx-auto max-w-5xl px-6 pt-36 pb-24 sm:pt-48 sm:pb-32">
<h2 className="text-3xl font-black sm:text-4xl">Who uses OpenProof</h2>
<p className="mt-6 max-w-2xl text-lg leading-relaxed text-text-secondary">
Anyone who needs to prove a file existed at a specific time — without
revealing or uploading the file itself.
</p>
<div className="mt-12 grid gap-x-16 gap-y-8 sm:grid-cols-2 sm:gap-y-10">
{[
["Developers", "Timestamp release artifacts, build outputs, and dependency manifests to verify integrity later."],
["Creators & artists", "Prove your original work existed before publishing, sharing, or sending it to a collaborator."],
["Researchers & academics", "Establish priority of findings, datasets, and preprints before peer review or publication."],
["Legal professionals", "Create independently verifiable timestamps for contracts, disclosures, and records of counsel."],
["Journalists", "Anchoring source material, internal communications, and unpublished drafts to a permanent timestamp."],
["Businesses & compliance", "Document integrity checks for regulatory records, audit trails, and internal policy adherence."],
["Archivists & preservationists", "Long-term integrity verification for digital collections, cultural records, and historical documents."],
["Individuals", "Personal records — wills, letters, receipts, photos, certificates — anyone can timestamp anything."],
].map(([title, desc]) => (
<div key={title as string}>
<h3 className="text-base font-bold sm:text-lg">{title as string}</h3>
<p className="mt-1.5 text-sm leading-relaxed text-text-secondary">{desc as string}</p>
</div>
))}
</div>
</section>

{/* ── Editorial: How it works ──────────────────────
Large numbers. Horizontal rhythm. No cards.
───────────────────────────────────────────── */}
<section className="mx-auto max-w-5xl px-6 pt-36 pb-24 sm:pt-48 sm:pb-32">
<section className="mx-auto max-w-5xl px-6 pb-24 sm:pb-32">
<h2 className="text-3xl font-black sm:text-4xl">How it works</h2>
<div className="mt-16 space-y-20">
{[
Expand All @@ -57,6 +83,85 @@ export default function Home() {
</div>
</section>

{/* ── Real-world scenarios ───────────────────────── */}
<section className="border-t border-border-default">
<div className="mx-auto max-w-5xl px-6 py-24 sm:py-32">
<h2 className="text-3xl font-black sm:text-4xl">When to timestamp a file</h2>
<p className="mt-6 max-w-2xl text-lg leading-relaxed text-text-secondary">
A blockchain timestamp is useful any time you want to prove a file
existed before an event — publication, collaboration, disclosure, or
loss. The hash captures the file&apos;s identity at that moment.
</p>
<div className="mt-12 grid gap-x-16 gap-y-10 sm:grid-cols-2">
{[
{
t: "Before publishing",
d: "Timestamp a draft or dataset before you publish. If someone later claims you copied them, the onchain fingerprint shows your file existed first.",
},
{
t: "Before sharing with others",
d: "Send a file to a collaborator? Timestamp it first. If the relationship sours, you can prove you held the original at a known time.",
},
{
t: "Before archiving",
d: "Timestamp a file before moving it to cold storage or a new format. Years later, verify the content hasn't drifted from the original.",
},
{
t: "Before regulatory submission",
d: "Records, logs, and compliance documents can be timestamped before submission. The onchain record serves as an independent integrity checkpoint.",
},
].map(({ t, d }) => (
<div key={t}>
<h3 className="text-base font-bold sm:text-lg">{t}</h3>
<p className="mt-1.5 text-sm leading-relaxed text-text-secondary">{d}</p>
</div>
))}
</div>
</div>
</section>

{/* ── What it proves vs doesn't ──────────────────── */}
<section className="mx-auto max-w-5xl px-6 py-24 sm:py-32">
<h2 className="text-3xl font-black sm:text-4xl">What a proof means</h2>
<div className="mt-12 grid gap-x-16 gap-y-10 sm:grid-cols-2">
<div>
<h3 className="text-base font-bold text-accent sm:text-lg">OpenProof proves</h3>
<ul className="mt-4 space-y-3">
{[
"A specific SHA-256 fingerprint existed in the registry at a recorded block time.",
"A specific wallet address submitted that hash for registration.",
"A file you hold today produces the same hash (it is unchanged since registration).",
].map((item) => (
<li className="flex gap-3 text-sm leading-relaxed text-text-secondary" key={item}>
<span className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent/10">
<span className="text-xs text-accent font-bold">✓</span>
</span>
{item}
</li>
))}
</ul>
</div>
<div>
<h3 className="text-base font-bold text-text-secondary sm:text-lg">OpenProof does not prove</h3>
<ul className="mt-4 space-y-3">
{[
"Who created, owns, or has rights to the file.",
"That the file contents are true, accurate, or correct.",
"That the file is legally valid or compliant with any regulation.",
"Recovery of the original file if it is lost.",
].map((item) => (
<li className="flex gap-3 text-sm leading-relaxed text-text-secondary" key={item}>
<span className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full border border-error/40">
<span className="text-xs text-error font-bold">✗</span>
</span>
{item}
</li>
))}
</ul>
</div>
</div>
</section>

{/* ── Registry — sleek horizontal strip ──────────
Like a stock ticker. No box.
───────────────────────────────────────────── */}
Expand All @@ -72,10 +177,40 @@ export default function Home() {
</div>
</section>

{/* ── Privacy-first messaging ───────────────────── */}
<section className="mx-auto max-w-5xl px-6 py-24 sm:py-32">
<h2 className="text-3xl font-black sm:text-4xl">Privacy by design</h2>
<div className="mt-12 grid gap-x-16 gap-y-10 sm:grid-cols-2">
{[
{
t: "No uploads",
d: "Files are hashed locally in your browser using the native Web Crypto API. The bytes never leave your device. Only the 32-byte SHA-256 hash leaves your browser — and only when you choose to register it.",
},
{
t: "No accounts",
d: "There is no sign-up, no profile, no database of users. OpenProof has no backend. Your proof history lives in your browser's local storage, not on a server.",
},
{
t: "Permanent verification",
d: "Once a hash is registered on Base Sepolia, it is immutable. Anyone can verify the proof using the onchain record alone — no website, no account, no special software required.",
},
{
t: "No tracking",
d: "OpenProof contains no analytics scripts, no tracking pixels, no cookies, and no telemetry. There is nothing to opt out of because nothing is collected.",
},
].map(({ t, d }) => (
<div key={t}>
<h3 className="text-base font-bold sm:text-lg">{t}</h3>
<p className="mt-1.5 text-sm leading-relaxed text-text-secondary">{d}</p>
</div>
))}
</div>
</section>

{/* ── Brand statement ─────────────────────────────
Bold. Simple. No card, no border.
───────────────────────────────────────────── */}
<section className="mx-auto max-w-5xl px-6 py-28 sm:py-40">
<section className="mx-auto max-w-5xl px-6 pb-28 sm:pb-40">
<p className="text-2xl font-bold leading-relaxed sm:text-3xl lg:text-4xl">
OpenProof is proof-of-existence infrastructure. A SHA-256 fingerprint
on Base Sepolia. No backend, no database, no tracking. Just a hash
Expand Down
4 changes: 3 additions & 1 deletion src/app/verify/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ export default function VerifyProofPage() {
Check a fingerprint<br /><span className="text-text-secondary">on Base Sepolia.</span>
</h1>
<p className="mt-5 max-w-xl text-base leading-relaxed text-text-secondary sm:text-lg">
Public read. No wallet required. Hash a file locally and check the onchain registry.
Public read. No wallet required. Hash a file locally and check whether
its fingerprint exists in the onchain registry. Prove a file is unchanged
since it was registered.
</p>
</section>

Expand Down
2 changes: 1 addition & 1 deletion src/components/file-drop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function FileDrop({
const selectedCount = files.length || (file ? 1 : 0);
const defaultHelperText = file
? `${formatBytes(file.size)}`
: `The file stays in your browser. Max: ${formatBytes(maxFileSizeBytes)}.`;
: `Your file stays in your browser. Only its SHA-256 fingerprint can leave. Max: ${formatBytes(maxFileSizeBytes)}.`;

function handleFiles(selectedFiles: File[]) {
if (!selectedFiles.length || disabled) return;
Expand Down
Loading