atproto user agency toolkit for individuals and groups
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 216 lines 6.5 kB view raw
1/** 2 * Type definitions for the declarative policy engine. 3 * 4 * Policies are plain data (JSON-serializable), deterministic, and 5 * transport-agnostic — they operate on atproto accounts (DIDs), not raw blocks. 6 */ 7 8// ============================================ 9// Target: which accounts a policy applies to 10// ============================================ 11 12/** Match all serviced accounts. */ 13export interface TargetAll { 14 type: "all"; 15} 16 17/** Match an explicit list of DIDs. */ 18export interface TargetList { 19 type: "list"; 20 dids: string[]; 21} 22 23/** Match DIDs whose string representation matches a pattern (substring or prefix). */ 24export interface TargetPattern { 25 type: "pattern"; 26 /** A prefix string to match against DIDs, e.g. "did:plc:" or "did:web:example.com". */ 27 prefix: string; 28} 29 30export type PolicyTarget = TargetAll | TargetList | TargetPattern; 31 32// ============================================ 33// Replication goals 34// ============================================ 35 36export interface ReplicationGoals { 37 /** Minimum number of copies across peers (including this node). Default: 1. */ 38 minCopies: number; 39 /** Preferred peer DIDs to replicate with. Optional. */ 40 preferredPeers?: string[]; 41} 42 43// ============================================ 44// Sync configuration 45// ============================================ 46 47export interface SyncConfig { 48 /** How often to sync, in seconds. Default: 300 (5 minutes). */ 49 intervalSec: number; 50} 51 52// ============================================ 53// Retention configuration 54// ============================================ 55 56export interface RetentionConfig { 57 /** How long to keep data, in seconds. 0 = forever. Default: 0. */ 58 maxAgeSec: number; 59 /** Whether to keep historical revisions. Default: false. */ 60 keepHistory: boolean; 61} 62 63// ============================================ 64// Policy definition 65// ============================================ 66 67export interface Policy { 68 /** Unique identifier for this policy. */ 69 id: string; 70 /** Human-readable name. */ 71 name: string; 72 /** Which accounts this policy applies to. */ 73 target: PolicyTarget; 74 /** Replication goals. */ 75 replication: ReplicationGoals; 76 /** Sync frequency. */ 77 sync: SyncConfig; 78 /** Data retention. */ 79 retention: RetentionConfig; 80 /** Priority (higher = more important). Range: 0-100. Default: 50. */ 81 priority: number; 82 /** Whether this policy is currently active. */ 83 enabled: boolean; 84} 85 86// ============================================ 87// Effective policy: the resolved result for a single DID 88// ============================================ 89 90/** 91 * The effective (merged) policy for a single DID after evaluating 92 * all matching policies. 93 */ 94export interface EffectivePolicy { 95 /** The DID this effective policy is for. */ 96 did: string; 97 /** IDs of all policies that contributed to this result. */ 98 sourcePolicyIds: string[]; 99 /** Merged replication goals. */ 100 replication: ReplicationGoals; 101 /** Merged sync config. */ 102 sync: SyncConfig; 103 /** Merged retention config. */ 104 retention: RetentionConfig; 105 /** Highest priority among matching policies. */ 106 priority: number; 107 /** Whether replication is enabled (at least one enabled policy matches). */ 108 shouldReplicate: boolean; 109} 110 111// ============================================ 112// Policy set: what gets loaded from config / stored on disk 113// ============================================ 114 115export interface PolicySet { 116 /** Schema version for forward compatibility. */ 117 version: 1; 118 /** All defined policies. */ 119 policies: Policy[]; 120} 121 122// ============================================ 123// Defaults 124// ============================================ 125 126export const DEFAULT_REPLICATION: ReplicationGoals = { 127 minCopies: 1, 128}; 129 130export const DEFAULT_SYNC: SyncConfig = { 131 intervalSec: 300, 132}; 133 134export const DEFAULT_RETENTION: RetentionConfig = { 135 maxAgeSec: 0, 136 keepHistory: false, 137}; 138 139export const DEFAULT_PRIORITY = 50; 140 141// ============================================ 142// Lifecycle types for persistent policies 143// ============================================ 144 145/** Policy lifecycle state. */ 146export type PolicyState = "proposed" | "active" | "suspended" | "terminated" | "purged"; 147 148/** What kind of policy this is. */ 149export type PolicyType = "reciprocal" | "archive" | "config" | "saas" | "group"; 150 151/** How this policy was created. */ 152export type PolicySource = "config" | "file" | "offer" | "manual" | "api"; 153 154/** Consent status for the replication relationship. */ 155export type ConsentStatus = "reciprocal" | "consented" | "unconsented" | "revoked"; 156 157/** 158 * A policy with lifecycle metadata for persistence. 159 * Extends the base Policy with type, state, source, consent, and timestamps. 160 */ 161export interface StoredPolicy extends Policy { 162 /** What kind of policy this is. */ 163 type: PolicyType; 164 /** Current lifecycle state. */ 165 state: PolicyState; 166 /** How this policy was created. */ 167 source: PolicySource; 168 /** Consent status. */ 169 consent: ConsentStatus; 170 /** Who/what created this policy. */ 171 createdBy: string; 172 /** The counterparty DID (for reciprocal/P2P policies). */ 173 counterpartyDid?: string; 174 /** URI of the local offer record (for P2P policies). */ 175 localOfferUri?: string; 176 /** URI of the remote offer record (for P2P policies). */ 177 remoteOfferUri?: string; 178 /** When this policy was created. */ 179 createdAt: string; 180 /** When this policy was activated. */ 181 activatedAt: string | null; 182 /** When this policy was suspended. */ 183 suspendedAt: string | null; 184 /** When this policy was terminated. */ 185 terminatedAt: string | null; 186 /** When this policy expires (null = never). */ 187 expiresAt: string | null; 188} 189 190/** 191 * Create a StoredPolicy from a base Policy with lifecycle metadata. 192 * `type` is required (no inference from ID prefix). 193 */ 194export function toStoredPolicy( 195 policy: Policy, 196 type: PolicyType, 197 overrides?: Partial<Omit<StoredPolicy, keyof Policy | "type">>, 198): StoredPolicy { 199 const now = new Date().toISOString(); 200 return { 201 ...policy, 202 type, 203 state: overrides?.state ?? "active", 204 source: overrides?.source ?? "manual", 205 consent: overrides?.consent ?? "unconsented", 206 createdBy: overrides?.createdBy ?? "system", 207 counterpartyDid: overrides?.counterpartyDid, 208 localOfferUri: overrides?.localOfferUri, 209 remoteOfferUri: overrides?.remoteOfferUri, 210 createdAt: overrides?.createdAt ?? now, 211 activatedAt: overrides?.activatedAt ?? (overrides?.state === "active" || !overrides?.state ? now : null), 212 suspendedAt: overrides?.suspendedAt ?? null, 213 terminatedAt: overrides?.terminatedAt ?? null, 214 expiresAt: overrides?.expiresAt ?? null, 215 }; 216}