atproto user agency toolkit for individuals and groups
1/**
2 * Built-in policy presets for common replication scenarios.
3 *
4 * Each preset is a factory that accepts minimal configuration and returns
5 * a full Policy object. Presets are convenience helpers — users can always
6 * write policies from scratch.
7 */
8
9import type { Policy, StoredPolicy } from "./types.js";
10import {
11 DEFAULT_REPLICATION,
12 DEFAULT_SYNC,
13 DEFAULT_RETENTION,
14 DEFAULT_PRIORITY,
15 toStoredPolicy,
16} from "./types.js";
17
18// ============================================
19// mutual-aid: N-of-M redundancy among a peer group
20// ============================================
21
22export interface MutualAidOptions {
23 /** Unique policy ID. Defaults to "mutual-aid". */
24 id?: string;
25 /** Human-readable name. */
26 name?: string;
27 /** The DIDs of all peers in the mutual-aid group. */
28 peerDids: string[];
29 /** Minimum copies across the group. Default: 2. */
30 minCopies?: number;
31 /** Sync interval in seconds. Default: 600 (10 minutes). */
32 intervalSec?: number;
33 /** Priority. Default: 50. */
34 priority?: number;
35}
36
37/**
38 * Create a mutual-aid policy. Each peer in the group stores copies of
39 * the others' data for collective resilience.
40 */
41export function mutualAid(options: MutualAidOptions): Policy {
42 return {
43 id: options.id ?? "mutual-aid",
44 name: options.name ?? "Mutual Aid",
45 target: {
46 type: "list",
47 dids: options.peerDids,
48 },
49 replication: {
50 minCopies: options.minCopies ?? 2,
51 preferredPeers: options.peerDids,
52 },
53 sync: {
54 intervalSec: options.intervalSec ?? 600,
55 },
56 retention: {
57 maxAgeSec: 0, // keep forever
58 keepHistory: false,
59 },
60 priority: options.priority ?? DEFAULT_PRIORITY,
61 enabled: true,
62 };
63}
64
65// ============================================
66// saas: SLA compliance
67// ============================================
68
69export interface SaasOptions {
70 /** Unique policy ID. Defaults to "saas". */
71 id?: string;
72 /** Human-readable name. */
73 name?: string;
74 /** The DIDs of serviced accounts. */
75 accountDids: string[];
76 /** Minimum copies for SLA guarantees. Default: 3. */
77 minCopies?: number;
78 /** Sync interval in seconds. Default: 60 (1 minute). */
79 intervalSec?: number;
80 /** Retention max age in seconds. Default: 0 (forever). */
81 maxAgeSec?: number;
82 /** Whether to keep history. Default: true. */
83 keepHistory?: boolean;
84 /** Priority. Default: 80 (high). */
85 priority?: number;
86}
87
88/**
89 * Create a SaaS SLA policy. Guarantees fast sync, high redundancy,
90 * and defined retention for paying accounts.
91 */
92export function saas(options: SaasOptions): Policy {
93 return {
94 id: options.id ?? "saas",
95 name: options.name ?? "SaaS SLA",
96 target: {
97 type: "list",
98 dids: options.accountDids,
99 },
100 replication: {
101 minCopies: options.minCopies ?? 3,
102 },
103 sync: {
104 intervalSec: options.intervalSec ?? 60,
105 },
106 retention: {
107 maxAgeSec: options.maxAgeSec ?? 0,
108 keepHistory: options.keepHistory ?? true,
109 },
110 priority: options.priority ?? 80,
111 enabled: true,
112 };
113}
114
115// ============================================
116// group-governance: quorum-based replication decisions
117// ============================================
118
119export interface GroupGovernanceOptions {
120 /** Unique policy ID. Defaults to "group-governance". */
121 id?: string;
122 /** Human-readable name. */
123 name?: string;
124 /** The DIDs that the group has decided to replicate. */
125 approvedDids: string[];
126 /** Members of the governance group (for preferred peers). */
127 memberDids?: string[];
128 /** Minimum copies. Default: 2. */
129 minCopies?: number;
130 /** Sync interval in seconds. Default: 300 (5 minutes). */
131 intervalSec?: number;
132 /** Priority. Default: 60. */
133 priority?: number;
134}
135
136/**
137 * Create a group-governance policy. The group collectively decides
138 * which accounts to replicate. The approved list is the outcome of
139 * that governance process (quorum, vote, etc.) — the policy engine
140 * simply enforces the result.
141 */
142export function groupGovernance(options: GroupGovernanceOptions): Policy {
143 return {
144 id: options.id ?? "group-governance",
145 name: options.name ?? "Group Governance",
146 target: {
147 type: "list",
148 dids: options.approvedDids,
149 },
150 replication: {
151 minCopies: options.minCopies ?? 2,
152 preferredPeers: options.memberDids,
153 },
154 sync: {
155 intervalSec: options.intervalSec ?? 300,
156 },
157 retention: {
158 maxAgeSec: 0,
159 keepHistory: false,
160 },
161 priority: options.priority ?? 60,
162 enabled: true,
163 };
164}
165
166// ============================================
167// configArchive: policy for a config-origin DID
168// ============================================
169
170/**
171 * Create a StoredPolicy for a DID originating from REPLICATE_DIDS config.
172 * ID: `config:{did}`, type: config, priority 30.
173 */
174export function configArchive(did: string): StoredPolicy {
175 const base: Policy = {
176 id: `config:${did}`,
177 name: `Config: ${did}`,
178 target: { type: "list", dids: [did] },
179 replication: { ...DEFAULT_REPLICATION },
180 sync: { ...DEFAULT_SYNC },
181 retention: { ...DEFAULT_RETENTION },
182 priority: 30,
183 enabled: true,
184 };
185 return toStoredPolicy(base, "config", { source: "config", consent: "unconsented" });
186}
187
188// ============================================
189// archive: policy for a user-added DID
190// ============================================
191
192export interface ArchiveOptions {
193 /** Priority. Default: 40. */
194 priority?: number;
195 /** Sync interval in seconds. Default: 300. */
196 intervalSec?: number;
197 /** Who created the policy. Default: "user". */
198 createdBy?: string;
199}
200
201/**
202 * Create a StoredPolicy for a user-added (archive) DID.
203 * ID: `archive:{did}`, type: archive, priority 40.
204 */
205export function archive(did: string, opts?: ArchiveOptions): StoredPolicy {
206 const base: Policy = {
207 id: `archive:${did}`,
208 name: `Archive: ${did}`,
209 target: { type: "list", dids: [did] },
210 replication: { ...DEFAULT_REPLICATION },
211 sync: { intervalSec: opts?.intervalSec ?? DEFAULT_SYNC.intervalSec },
212 retention: { ...DEFAULT_RETENTION },
213 priority: opts?.priority ?? 40,
214 enabled: true,
215 };
216 return toStoredPolicy(base, "archive", {
217 source: "manual",
218 consent: "unconsented",
219 createdBy: opts?.createdBy ?? "user",
220 });
221}