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
4 changes: 2 additions & 2 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions documentation/lib/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ export const navigation = [
{ title: 'Hypercerts Lexicons', path: '/lexicons/hypercerts-lexicons' },
],
},
{ title: 'Error Handling & Constraints', path: '/reference/error-handling' },
{ title: 'Testing & Security', path: '/reference/testing-and-security' },
{ title: 'Testing & Deployment', path: '/reference/testing-and-deployment' },
{ title: 'Glossary', path: '/reference/glossary' },
{ title: 'FAQ', path: '/reference/faq' },
],
Expand Down
26 changes: 25 additions & 1 deletion documentation/pages/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ A typical data flow: a user writes a record to their PDS → the PDS signs it an

![Data flow through ATProto](/images/architecture-dataflow.svg)

## Security Model

ATProto's cryptographic properties make hypercerts tamper-evident and auditable without a central authority.

#### Signed repositories

Every PDS repository is a Merkle tree signed by the user's DID signing key. Any modification to a record changes the Merkle root, invalidating the signature. This means you can verify a record came from a specific DID, detect if a record was altered after creation, and audit the entire history of a repository.

#### Strong references

When one record references another — for example, an evaluation referencing an activity claim — the reference includes both the AT-URI and the CID (content hash). If the referenced record is modified, its CID changes and the mismatch is detectable. This makes cross-record references tamper-evident.

#### Identity verification

DIDs are cryptographically verifiable. A `did:plc` identifier resolves via the [PLC directory](https://plc.directory) to a DID document containing the public key for signature verification, the user's current PDS, and their handle. Always verify the DID matches the expected identity before trusting a record.

#### Data integrity chain

These properties combine into an auditable chain:

1. Alice creates an activity claim. Her PDS signs the repository, producing CID `bafyA`.
2. Bob evaluates Alice's claim, referencing `{ uri: "at://alice/...", cid: "bafyA" }`.
3. Anyone can verify: Alice's signature proves she created the claim, Bob's signature proves he created the evaluation, and the CID proves Bob evaluated the exact version Alice published.

## Ownership Layer (Planned)

The ownership layer is not yet implemented. The planned design freezes ATProto records and anchors them on-chain before funding, ensuring funders know exactly what they are paying for. For the full planned design — including anchoring, tokenization, and funding mechanisms — see [Planned: Funding & Tokenization](/architecture/planned-funding-and-tokenization).
Expand All @@ -69,7 +93,7 @@ Storing rich data on-chain is expensive. A single activity claim with evidence a

#### Why Not Fully Off-Chain?

Mutable records are fine for collaboration, but funding requires immutability. Without on-chain anchoring, there's no way to freeze a hypercert's state and guarantee that the claim a funder evaluates is the same claim they end up funding. Additionally, funding mechanisms like retroactive public goods funding require smart contracts to distribute funds according to rules. See [Planned: Funding & Tokenization](/architecture/planned-funding-and-tokenization) for the planned freeze-then-fund design.
Mutable records are fine for collaboration, but funding requires immutability. Without on-chain anchoring, there's no way to freeze a hypercert's state and guarantee that the claim a funder evaluates is the same claim they end up funding. Additionally, funding mechanisms like retroactive public goods funding require on-chain logic to distribute funds according to rules. See [Planned: Funding & Tokenization](/architecture/planned-funding-and-tokenization) for the planned freeze-then-fund design.

#### Why ATProto Over IPFS, Ceramic, or Other Alternatives?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ ATProto records are public by default. Anyone can read your activity claims, eva

ATProto is adding support for encrypted records. In the future, you'll be able to create private claims visible only to specific DIDs. For example, a contributor might share sensitive evidence with an evaluator without making it public.

#### Access control via smart contracts (planned)
#### Access control via on-chain tokens (planned)

In the planned design, on-chain tokens could have access control logic — for example, granting read access to private ATProto records only to token holders. This is a potential future feature. See [Planned: Funding & Tokenization](/architecture/planned-funding-and-tokenization) for details.
In the planned design, on-chain tokens could enforce access control — for example, granting read access to private ATProto records only to token holders. This is a potential future feature. See [Planned: Funding & Tokenization](/architecture/planned-funding-and-tokenization) for details.
267 changes: 2 additions & 265 deletions documentation/pages/reference/error-handling.md
Original file line number Diff line number Diff line change
@@ -1,270 +1,7 @@
---
title: Error Handling & Constraints
description: Common errors, validation rules, and how to handle them.
---

## Overview
# Error Handling & Constraints

When creating or updating records on a PDS, various errors can occur. This page documents common error scenarios and how to handle them.

---

## Record Validation Errors

Errors occur when a record does not match its lexicon schema.

| Error Scenario | Cause | Fix |
|----------------|-------|-----|
| Missing required field | Creating an activity claim without `title` or `startDate` | Include all required fields in the record |
| Invalid field type | Passing a number where a string is expected | Ensure field types match the lexicon definition |
| String length violation | String exceeds `maxLength` (in bytes) or `maxGraphemes` (in Unicode grapheme clusters) | Truncate or validate string length before submission |
| Array length violation | Array exceeds `maxLength` on array fields | Reduce array size to meet constraints |
| Invalid datetime format | Datetime not in ISO 8601 format | Use ISO 8601 format: `2024-01-15T10:30:00Z` |
| Invalid strong reference | Missing `uri` or `cid` in strong reference | Include both `uri` and `cid` fields |

**Example: Missing required field**

```typescript
// ❌ Error: missing required fields
const claim = {
description: "Built a community garden",
// Missing: title, startDate
};

// ✅ Correct
const claim = {
title: "Community Garden Project",
description: "Built a community garden",
startDate: "2024-01-01T00:00:00Z",
};
```

**Example: Invalid datetime format**

```typescript
// ❌ Error: invalid format
const claim = {
title: "Project",
startDate: "01/15/2024", // Wrong format
};

// ✅ Correct
const claim = {
title: "Project",
startDate: "2024-01-15T00:00:00Z", // ISO 8601
};
```

**Example: Invalid strong reference**

```typescript
// ❌ Error: missing cid
const evidence = {
claim: {
uri: "at://did:plc:abc123/org.hypercerts.activityClaim/xyz",
// Missing: cid
},
};

// ✅ Correct
const evidence = {
claim: {
uri: "at://did:plc:abc123/org.hypercerts.activityClaim/xyz",
cid: "bafyreiabc123...",
},
};
```

---

## Blob Constraints

Size limits for uploaded blobs vary by type.

| Blob Type | Maximum Size | Used For |
|-----------|--------------|----------|
| `smallBlob` | 10 MB | General attachments |
| `largeBlob` | 100 MB | Large documents, datasets |
| `smallImage` | 5 MB | Thumbnails, icons |
| `largeImage` | 10 MB | High-resolution images |

**What happens when limits are exceeded:**
- The PDS rejects the blob upload
- You receive a validation error indicating the size limit
- The record creation/update fails

**Solution:** Compress images, split large files, or use external storage with links.

---

## Authentication Errors

Common authentication issues and resolutions.

| Error | Cause | Resolution |
|-------|-------|------------|
| Invalid credentials | Wrong username or password | Verify credentials and retry |
| Expired session | Session token has expired | Refresh the session or re-authenticate |
| Insufficient permissions | User lacks permission for the operation | Ensure user has appropriate access rights |
| Missing authentication | Request made without credentials | Include authentication token in request |

**Refreshing sessions:**

```typescript
try {
await agent.createRecord({ /* ... */ });
} catch (error) {
if (error.message.includes('expired')) {
await agent.resumeSession(sessionData);
await agent.createRecord({ /* ... */ }); // Retry
}
}
```

---

## Reference Errors

Issues with strong references between records.

| Error | Cause | Resolution |
|-------|-------|------------|
| Referenced record does not exist | URI points to non-existent record | Verify the record exists before referencing |
| CID mismatch | Record was updated since reference was created | Fetch the latest CID and update the reference |
| Cross-PDS resolution failure | Referenced record is on an unreachable PDS | Ensure PDS is accessible or use local references |

**Example: Handling CID mismatch**

```typescript
// Fetch the latest version of the record
const latestRecord = await agent.getRecord({
repo: 'did:plc:abc123',
collection: 'org.hypercerts.activityClaim',
rkey: 'xyz',
});

// Use the current CID
const evidence = {
claim: {
uri: latestRecord.uri,
cid: latestRecord.cid, // Current CID
},
};
```

---

## Rate Limits & PDS Constraints

Operational limits imposed by PDS implementations.

{% callout type="warning" %}
Specific limits depend on the PDS implementation. Always check your PDS documentation for exact values.
{% /callout %}

| Constraint | Description | Handling |
|------------|-------------|----------|
| Rate limits | Maximum requests per time window | Implement exponential backoff and retry logic |
| Repository size | Total storage per user repository | Monitor usage, archive old records |
| Concurrent operations | Maximum simultaneous requests | Queue operations, process sequentially |

**Retry with exponential backoff:**

```typescript
async function createWithRetry(agent, record, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await agent.createRecord(record);
} catch (error) {
if (error.message.includes('rate limit') && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
}
```

---

## Best Practices

General error handling recommendations.

**1. Validate client-side before submission**

```typescript
function validateClaim(claim) {
if (!claim.title || claim.title.length === 0) {
throw new Error('Title is required');
}
if (!claim.startDate) {
throw new Error('Start date is required');
}
// Validate ISO 8601 format
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(claim.startDate)) {
throw new Error('Start date must be in ISO 8601 format');
}
}
```

**2. Use try/catch around all API calls**

```typescript
try {
const result = await agent.createRecord({
repo: agent.session.did,
collection: 'org.hypercerts.activityClaim',
record: claim,
});
console.log('Created:', result.uri);
} catch (error) {
console.error('Failed to create claim:', error.message);
// Handle specific error types
if (error.message.includes('validation')) {
// Show validation errors to user
} else if (error.message.includes('auth')) {
// Prompt re-authentication
}
}
```

**3. Log errors with context**

```typescript
function logError(operation, record, error) {
console.error({
operation,
collection: record.collection,
timestamp: new Date().toISOString(),
error: error.message,
stack: error.stack,
});
}

try {
await agent.createRecord(params);
} catch (error) {
logError('createRecord', params, error);
throw error;
}
```

**4. Provide user-friendly error messages**

```typescript
function getUserMessage(error) {
if (error.message.includes('validation')) {
return 'Please check that all required fields are filled correctly.';
}
if (error.message.includes('rate limit')) {
return 'Too many requests. Please wait a moment and try again.';
}
if (error.message.includes('auth')) {
return 'Your session has expired. Please sign in again.';
}
return 'An unexpected error occurred. Please try again.';
}
```
This content has moved to [Testing & Deployment](/reference/testing-and-deployment).
Loading