Skip to content
Open
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
92 changes: 92 additions & 0 deletions components/CopyRawButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useState } from 'react';
import { useRouter } from 'next/router';

function getRawUrl(currentPath) {
if (currentPath === '/') return '/raw/index.md';
return `/raw${currentPath}.md`;
}

export function CopyRawButton() {
const [copied, setCopied] = useState(false);
const [copyError, setCopyError] = useState(false);
const [isCopying, setIsCopying] = useState(false);
const router = useRouter();
const currentPath = router.asPath.split('#')[0].split('?')[0] || '/';
const rawUrl = getRawUrl(currentPath);

const handleCopy = async () => {
setIsCopying(true);

try {
const response = await fetch(rawUrl);
if (!response.ok) throw new Error('Failed to load raw markdown');

const raw = await response.text();

try {
await navigator.clipboard.writeText(raw);
} catch {
const textarea = document.createElement('textarea');
textarea.value = raw;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}

setCopyError(false);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
setCopyError(true);
setCopied(false);
setTimeout(() => setCopyError(false), 2000);
} finally {
setIsCopying(false);
}
};

return (
<div className="page-tools" aria-label="Page tools">
<button
className="page-tools-button"
onClick={handleCopy}
type="button"
disabled={isCopying}
>
<svg
className="page-tools-icon"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<rect x="9" y="9" width="10" height="10" rx="2" stroke="currentColor" strokeWidth="1.8" />
<path d="M7 15H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v1" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
</svg>
<span>{isCopying ? 'Copying...' : copied ? 'Copied' : copyError ? 'Copy failed' : 'Copy raw'}</span>
</button>
<a
className="page-tools-link"
href={rawUrl}
target="_blank"
rel="noopener noreferrer"
>
<svg
className="page-tools-icon"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<path d="M14 5h5v5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
<path d="M10 14 19 5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
<path d="M19 13v5a1 1 0 0 1-1 1h-12a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<span>View raw</span>
</a>
</div>
);
}
2 changes: 2 additions & 0 deletions components/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LastUpdated } from './LastUpdated';
import { Breadcrumbs } from './Breadcrumbs';
import { ThemeToggle } from './ThemeToggle';
import { SearchDialog } from './SearchDialog';
import { CopyRawButton } from './CopyRawButton';

const SITE_URL = 'https://docs.hypercerts.org';
const SITE_NAME = 'Hypercerts Documentation';
Expand Down Expand Up @@ -192,6 +193,7 @@ export default function Layout({ children, frontmatter }) {

<main className="layout-content" id="main-content">
<Breadcrumbs />
{frontmatter && <CopyRawButton />}
<LastUpdated />
<article>{children}</article>

Expand Down
47 changes: 47 additions & 0 deletions lib/generate-raw-pages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const {
readdirSync,
statSync,
readFileSync,
writeFileSync,
rmSync,
mkdirSync,
} = require('fs');
const { dirname, join, relative } = require('path');

const PAGES_DIR = join(__dirname, '..', 'pages');
const OUTPUT_DIR = join(__dirname, '..', 'public', 'raw');

function walkDir(dir) {
const results = [];
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) {
results.push(...walkDir(full));
} else if (full.endsWith('.md')) {
results.push(full);
}
}
return results;
}

function getRawOutputPath(filePath) {
const rel = relative(PAGES_DIR, filePath)
.replace(/\.md$/, '')
.replace(/\\/g, '/');
const route = rel === 'index' ? '/index' : `/${rel.replace(/\/index$/, '')}`;
const outputRel = route === '/index' ? 'index.md' : `${route.slice(1)}.md`;
return join(OUTPUT_DIR, outputRel);
}

rmSync(OUTPUT_DIR, { recursive: true, force: true });
mkdirSync(OUTPUT_DIR, { recursive: true });

const files = walkDir(PAGES_DIR);

for (const file of files) {
const outputPath = getRawOutputPath(file);
mkdirSync(dirname(outputPath), { recursive: true });
writeFileSync(outputPath, readFileSync(file, 'utf-8'));
}

console.log(`Generated raw markdown files for ${files.length} pages`);
22 changes: 11 additions & 11 deletions lib/lastUpdated.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"/architecture/account-and-identity": "2026-04-01T13:47:13+06:00",
"/architecture/data-flow-and-lifecycle": "2026-03-05T20:17:36+06:00",
"/architecture/data-flow-and-lifecycle": "2026-04-01T19:28:36+06:00",
"/architecture/epds": "2026-04-01T13:47:13+06:00",
"/architecture/indexers-and-discovery": "2026-03-05T04:01:13+08:00",
"/architecture/overview": "2026-03-05T20:46:24+08:00",
"/architecture/portability-and-scaling": "2026-03-05T13:01:42+08:00",
"/core-concepts/cel-work-scopes": "2026-02-26T16:26:35+01:00",
"/core-concepts/certified-identity": "2026-03-14T20:43:33-07:00",
"/core-concepts/cel-work-scopes": "2026-04-01T19:28:36+06:00",
"/core-concepts/certified-identity": "2026-04-01T19:28:36+06:00",
"/core-concepts/common-use-cases": "2026-03-05T12:52:26+08:00",
"/core-concepts/funding-and-value-flow": "2026-03-05T17:11:22+06:00",
"/core-concepts/hypercerts-core-data-model": "2026-03-05T20:17:43+06:00",
"/core-concepts/funding-and-value-flow": "2026-04-01T19:28:36+06:00",
"/core-concepts/hypercerts-core-data-model": "2026-04-01T19:28:36+06:00",
"/core-concepts/what-is-hypercerts": "2026-03-05T03:40:14+08:00",
"/core-concepts/why-at-protocol": "2026-03-05T21:06:04+08:00",
"/ecosystem/why-we-need-hypercerts": "2026-02-20T12:49:43+01:00",
"/getting-started/building-on-hypercerts": "2026-03-06T22:56:46+08:00",
"/getting-started/quickstart": "2026-03-14T20:43:33-07:00",
"/getting-started/testing-and-deployment": "2026-03-05T14:36:55+06:00",
"/getting-started/working-with-evaluations": "2026-03-06T22:56:46+08:00",
"/getting-started/working-with-evaluations": "2026-04-01T19:28:36+06:00",
"/": "2026-03-24T12:56:03-07:00",
"/lexicons/certified-lexicons/badge-award": "2026-03-24T12:56:03-07:00",
"/lexicons/certified-lexicons/badge-definition": "2026-03-24T12:56:03-07:00",
Expand All @@ -36,11 +36,11 @@
"/lexicons/hypercerts-lexicons/measurement": "2026-03-24T12:56:03-07:00",
"/lexicons/hypercerts-lexicons/rights": "2026-03-24T12:56:03-07:00",
"/lexicons/introduction-to-lexicons": "2026-03-24T12:56:03-07:00",
"/reference/faq": "2026-03-14T20:43:33-07:00",
"/reference/faq": "2026-04-01T19:28:36+06:00",
"/reference/glossary": "2026-03-14T20:43:33-07:00",
"/roadmap": "2026-03-05T20:17:36+06:00",
"/tools/hyperboards": "2026-03-09T15:56:42+08:00",
"/roadmap": "2026-04-01T19:28:36+06:00",
"/tools/hyperboards": "2026-04-01T19:28:36+06:00",
"/tools/hypercerts-cli": "2026-03-05T12:30:00+06:00",
"/tools/hyperindex": "2026-02-20T19:42:03+08:00",
"/tools/scaffold": "2026-03-05T15:58:36+06:00"
"/tools/hyperindex": "2026-04-03T00:18:36+06:00",
"/tools/scaffold": "2026-04-01T19:28:36+06:00"
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"private": true,
"description": "Hypercerts Protocol Documentation",
"scripts": {
"dev": "node lib/generate-search-index.js && node lib/generate-last-updated.js && node lib/generate-sitemap.js && next dev --webpack",
"build": "node lib/generate-search-index.js && node lib/generate-last-updated.js && node lib/generate-sitemap.js && next build --webpack",
"dev": "node lib/generate-search-index.js && node lib/generate-last-updated.js && node lib/generate-sitemap.js && node lib/generate-raw-pages.js && next dev --webpack",
"build": "node lib/generate-search-index.js && node lib/generate-last-updated.js && node lib/generate-sitemap.js && node lib/generate-raw-pages.js && next build --webpack",
"start": "next start"
},
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions pages/architecture/data-flow-and-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ Contributors are embedded in the activity claim's `contributors` array. For rich

#### Attachment Records

`org.hypercerts.claim.attachment` records attach supporting documentation. Attachments can be URLs, file uploads, or structured data. Each attachment record includes a strong reference to the claim it supports.
`org.hypercerts.context.attachment` records attach supporting documentation. Attachments can be URLs, file uploads, or structured data. Each attachment record includes a strong reference to the claim it supports.

#### Measurement Records

`org.hypercerts.claim.measurement` records provide quantitative data. A measurement specifies what was measured, the value, the unit, and the methodology. Multiple measurements can track different metrics.
`org.hypercerts.context.measurement` records provide quantitative data. A measurement specifies what was measured, the value, the unit, and the methodology. Multiple measurements can track different metrics.

#### Rights Records

Expand Down Expand Up @@ -98,7 +98,7 @@ at://did:alice/... at://did:bob/... at://did:carol/

Third parties assess the work by creating evaluation records on their own servers.

An evaluator creates an `org.hypercerts.claim.evaluation` record on their PDS. The evaluation includes a `subject` field with a strong reference to the activity claim. Strong references include both the AT-URI and the CID (content hash), ensuring the evaluation references a specific version of the claim.
An evaluator creates an `org.hypercerts.context.evaluation` record on their PDS. The evaluation includes a `subject` field with a strong reference to the activity claim. Strong references include both the AT-URI and the CID (content hash), ensuring the evaluation references a specific version of the claim.

The evaluation record includes the evaluator's assessment. This can be a score, a category, structured feedback, or a link to a detailed report. The lexicon allows flexible evaluation formats.

Expand Down
2 changes: 1 addition & 1 deletion pages/core-concepts/cel-work-scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ Every CEL expression evaluates against a typed context. Each variable maps to da

## Starter tag vocabulary

`workScopeTag` records are published as ATProto records that anyone can reference via strong references. [Certified](https://certified.ink) publishes curated tag sets for specific domains. Here's an example for regenerative work:
`workScopeTag` records are published as ATProto records that anyone can reference via strong references. [Certified](https://certified.app) publishes curated tag sets for specific domains. Here's an example for regenerative work:

| Domain | Example tags |
|--------|-------------|
Expand Down
4 changes: 2 additions & 2 deletions pages/core-concepts/certified-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Your DID resolves via the [PLC directory](https://plc.directory) to a DID docume
|-------|---------------------|
| **Data** | Every record (activity claims, evaluations, measurements) carries the author's DID. The PDS signs records into a Merkle tree, making authorship tamper-evident. |
| **Trust** | Evaluators build reputation tied to their DID. Applications can weight evaluations based on the evaluator's history and credentials. |
| **Funding** | Funding receipts link funder DIDs to the work they support. Wallet linkage (work-in-progress) connects DIDs to onchain addresses for payment flows and tokenization. |
| **Funding** | Funding receipts link funder DIDs to the work they support. Wallet linkage (work-in-progress) connects DIDs to on-chain addresses for payment flows and tokenization. |
| **Portability** | Switching PDS providers doesn't change your DID. Your entire history — claims, evaluations, contributions — migrates with you. |

## Certified: the reference identity provider
Expand All @@ -52,7 +52,7 @@ Hypercerts is fully interoperable with the AT Protocol ecosystem. If you already

## Wallet linkage

To receive onchain funding, a DID needs to be linked to an onchain wallet address. This is handled by [**IdentityLink**](https://identitylink.vercel.app/) — a cryptographic attestation system that binds a DID to one or more onchain addresses via a signed proof stored in your PDS. For the Ethereum ecosystem this looks like:
To receive on-chain funding, a DID needs to be linked to an on-chain wallet address. This is handled by [**IdentityLink**](https://identitylink.vercel.app/) — a cryptographic attestation system that binds a DID to one or more on-chain addresses via a signed proof stored in your PDS. For the Ethereum ecosystem this looks like:

1. Authenticates the user via ATProto OAuth
2. Connects an EVM wallet (EOA, Smart Wallet, or Safe)
Expand Down
5 changes: 2 additions & 3 deletions pages/core-concepts/funding-and-value-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Funding receipts are typically created by a **facilitator** — a payment proces

| Scenario | Facilitator | Verification |
|----------|-------------|--------------|
| **Onchain funding** | A funding app verifies the transaction and creates the receipt, linking the transaction hash and chain ID | Verifiable onchain |
| **On-chain funding** | A funding app verifies the transaction and creates the receipt, linking the transaction hash and chain ID | Verifiable on-chain |
| **Card / bank transfer** | A payment processor settles the payment and creates the receipt | Trust in the processor |
| **Grant platform** | The grant platform records the award and creates the receipt on behalf of the funder | Trust in the platform |

Expand All @@ -56,7 +56,7 @@ The protocol does not enforce that projects or funders disclose their funding
Tokenization is under active development. This section describes the planned architecture.
{% /callout %}

A hypercert can optionally be wrapped in an onchain token. This gives funders a programmable proof of their contribution. Tokenization is an optional wrapper around a claim snapshot; the canonical record remains the AT Protocol data.
A hypercert can optionally be wrapped in an on-chain token. This gives funders a programmable proof of their contribution. Tokenization is an optional wrapper around a claim snapshot; the canonical record remains the AT Protocol data.

When locking is available, a claim can be frozen before tokenization. This gives funders a stronger guarantee — the claim they reviewed is exactly the claim they funded, and it cannot change after the fact.

Expand Down Expand Up @@ -105,4 +105,3 @@ Two years later, Eve assesses the health of Alice's trees and publishes a positi

- [Architecture Overview](/architecture/overview) — how the full protocol stack fits together
- [Data Flow & Lifecycle](/architecture/data-flow-and-lifecycle) — how a hypercert moves through the system

8 changes: 4 additions & 4 deletions pages/core-concepts/hypercerts-core-data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ The following diagram shows record types and how they reference the activity cla

{% figure src="/images/hypercert-erd.svg" alt="Hypercert record relationships" /%}

The diagram includes a **token** entity — tokenization (anchoring a hypercert onchain) is not yet implemented.
The diagram includes a **token** entity — tokenization (anchoring a hypercert on-chain) is not yet implemented.

| Record type | What it adds | Who creates it | Lexicon |
|-------------|-------------|----------------|---------|
| **Attachment** | Supporting documentation — URLs, uploaded files, IPFS links. Can link to any record type, not only activity claims. | Anyone with additional data | `org.hypercerts.claim.attachment` |
| **Measurement** | Quantitative data — "12 pages written", "50 tons CO₂ reduced" | E.g. a third-party measurer or the project (self-reported) | `org.hypercerts.claim.measurement` |
| **Evaluation** | An (independent) assessment of the work | E.g. a third-party evaluator, community members, beneficiaries | `org.hypercerts.claim.evaluation` |
| **Attachment** | Supporting documentation — URLs, uploaded files, IPFS links. Can link to any record type, not only activity claims. | Anyone with additional data | `org.hypercerts.context.attachment` |
| **Measurement** | Quantitative data — "12 pages written", "50 tons CO₂ reduced" | E.g. a third-party measurer or the project (self-reported) | `org.hypercerts.context.measurement` |
| **Evaluation** | An (independent) assessment of the work | E.g. a third-party evaluator, community members, beneficiaries | `org.hypercerts.context.evaluation` |

### Additional notes

Expand Down
8 changes: 4 additions & 4 deletions pages/getting-started/working-with-evaluations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ await agent.login({
// Create an evaluation of an activity claim
const evaluation = await agent.com.atproto.repo.createRecord({
repo: agent.session.did,
collection: "org.hypercerts.claim.evaluation",
collection: "org.hypercerts.context.evaluation",
record: {
subject: {
uri: "at://did:plc:xyz789/org.hypercerts.claim.activity/3k2j4h5g6f7d8s9a",
cid: "bafyreiabc123...",
},
evaluators: ["did:plc:evaluator123"],
summary: "Verified documentation updates. All 15 examples tested and working. High quality contribution with clear impact on developer experience.",
$type: "org.hypercerts.claim.evaluation",
$type: "org.hypercerts.context.evaluation",
createdAt: new Date().toISOString(),
},
});
Expand All @@ -46,7 +46,7 @@ Measurements provide quantitative data that supports your evaluation:
```typescript
const measurement = await agent.com.atproto.repo.createRecord({
repo: agent.session.did,
collection: "org.hypercerts.claim.measurement",
collection: "org.hypercerts.context.measurement",
record: {
subject: {
uri: "at://did:plc:xyz789/org.hypercerts.claim.activity/3k2j4h5g6f7d8s9a",
Expand All @@ -60,7 +60,7 @@ const measurement = await agent.com.atproto.repo.createRecord({
methodURI: "https://example.com/analytics-methodology",
evidenceURI: ["https://example.com/analytics-report.pdf"],
comment: "Page view data collected over the first 30 days after publication.",
$type: "org.hypercerts.claim.measurement",
$type: "org.hypercerts.context.measurement",
createdAt: new Date().toISOString(),
},
});
Expand Down
Loading