atproto user agency toolkit for individuals and groups
1/**
2 * Type definitions and constants for P2P replication.
3 */
4
5/** Lexicon NSIDs */
6export const PEER_NSID = "org.p2pds.peer";
7export const MANIFEST_NSID = "org.p2pds.manifest";
8export const OFFER_NSID = "org.p2pds.replication.offer";
9export const CONSENT_NSID = "org.p2pds.replication.consent";
10
11/** Open consent record — declares that anyone may replicate this account's data. */
12export interface ConsentRecord {
13 $type: typeof CONSENT_NSID;
14 /** Scope of consent. Currently only "any" is supported. */
15 scope: "any";
16 /** ISO 8601 timestamp. */
17 createdAt: string;
18}
19
20/** Peer identity record — binds a DID to an IPFS PeerID. */
21export interface PeerIdentityRecord {
22 $type: typeof PEER_NSID;
23 peerId: string;
24 multiaddrs: string[];
25 createdAt: string;
26}
27
28/** Replication manifest — declares that this node serves a given DID's data. */
29export interface ManifestRecord {
30 $type: typeof MANIFEST_NSID;
31 subject: string;
32 status: "active" | "paused";
33 lastSyncRev: string | null;
34 lastSyncAt: string | null;
35 createdAt: string;
36}
37
38/** Replication offer — declares willingness to replicate a given DID's data. */
39export interface OfferRecord {
40 $type: typeof OFFER_NSID;
41 subject: string; // DID whose data I will replicate
42 minCopies: number; // Desired redundancy (default 2)
43 intervalSec: number; // Sync frequency in seconds (default 600)
44 priority: number; // 0-100 (default 50)
45 createdAt: string; // ISO 8601
46}
47
48/** Operational sync state tracked in SQLite (not in repo). */
49export interface SyncState {
50 did: string;
51 pdsEndpoint: string;
52 peerId: string | null;
53 peerMultiaddrs: string[];
54 peerInfoFetchedAt: string | null;
55 lastSyncRev: string | null;
56 rootCid: string | null;
57 lastSyncAt: string | null;
58 lastVerifiedAt: string | null;
59 status: "pending" | "syncing" | "synced" | "error" | "tombstoned";
60 errorMessage: string | null;
61 needsGc: boolean;
62}
63
64/**
65 * Convert a DID to a valid rkey by replacing colons with hyphens.
66 * e.g. "did:plc:abc123" → "did-plc-abc123"
67 */
68export function didToRkey(did: string): string {
69 return did.replace(/:/g, "-");
70}
71
72/**
73 * Convert an rkey back to a DID-like string by replacing hyphens with colons.
74 * Note: only works for simple DIDs without hyphens in method-specific-id.
75 */
76export function rkeyToDid(rkey: string): string {
77 return rkey.replace(/-/g, ":");
78}
79
80/** Configuration for layered remote verification. */
81export interface VerificationConfig {
82 /** Layer 1: how many blocks to sample via RASL (default 50). */
83 raslSampleSize: number;
84 /** Layer 2: how many blocks to fetch via bitswap (default 5). Future use. */
85 bitswapSampleSize: number;
86 /** Layer 3: how many record paths to verify (default 2). Future use. */
87 mstProofCount: number;
88 /** How often to run verification in ms (default 30 minutes). */
89 verificationIntervalMs: number;
90 /** Challenge epoch duration in ms (default 1 hour). */
91 challengeEpochDurationMs: number;
92 /** How many records to include in challenges (default 3). */
93 challengeRecordCount: number;
94 /** How many blocks to sample in challenges (default 5). */
95 challengeBlockSampleSize: number;
96 /** Challenge expiration in ms (default 5 minutes). */
97 challengeExpirationMs: number;
98 /** Minimum reliability score for peer trust, 0-1 (default 0.8). */
99 reliabilityThreshold: number;
100}
101
102export const DEFAULT_VERIFICATION_CONFIG: VerificationConfig = {
103 raslSampleSize: 50,
104 bitswapSampleSize: 5,
105 mstProofCount: 2,
106 verificationIntervalMs: 30 * 60 * 1000,
107 challengeEpochDurationMs: 60 * 60 * 1000,
108 challengeRecordCount: 3,
109 challengeBlockSampleSize: 5,
110 challengeExpirationMs: 5 * 60 * 1000,
111 reliabilityThreshold: 0.8,
112};
113
114/** What initiated a sync event. */
115export type SyncTrigger =
116 | "firehose"
117 | "firehose-resync"
118 | "gossipsub"
119 | "periodic"
120 | "manual"
121 | "tombstone-recovery"
122 | "gc"
123 | "unknown";
124
125/** A row from the sync_history table. */
126export interface SyncHistoryRow {
127 id: number;
128 did: string;
129 sourceType: string;
130 trigger: SyncTrigger;
131 startedAt: string;
132 completedAt: string | null;
133 status: string;
134 errorMessage: string | null;
135 blocksAdded: number;
136 blobsAdded: number;
137 carBytes: number;
138 blobBytes: number;
139 durationMs: number | null;
140 rev: string | null;
141 rootCid: string | null;
142 incremental: boolean;
143}
144
145/** Aggregate metrics across all replicated DIDs. */
146export interface AggregateMetrics {
147 totalDids: number;
148 totalBlocks: number;
149 totalBlobs: number;
150 totalRecords: number;
151 totalBytesHeld: number;
152 totalSyncs: number;
153 recentTransferredBytes: number;
154}
155
156/** Per-DID metrics summary. */
157export interface DidMetrics {
158 blocks: number;
159 blobs: number;
160 records: number;
161 bytesHeld: number;
162 recentSyncs: SyncHistoryRow[];
163}
164
165/** Result of a single verification layer. */
166export interface LayerResult {
167 layer: number;
168 name: string;
169 passed: boolean;
170 checked: number;
171 available: number;
172 missing: string[];
173 error?: string;
174 durationMs: number;
175}
176
177/** Combined result of all verification layers for a peer. */
178export interface LayeredVerificationResult {
179 did: string;
180 pdsEndpoint: string;
181 timestamp: string;
182 layers: LayerResult[];
183 overallPassed: boolean;
184}