/** * aidimag sync server — `dim serve` (Phase 6, self-hostable team mode). * * Zero extra dependencies: node:http + better-sqlite3. The hosted SaaS later * wraps this same protocol with OAuth/billing; teams can always self-host. * * Protocol (all JSON, Bearer token auth): * POST /v1/push?brain= { items: SyncItem[] } → { accepted, seq } * GET /v1/pull?brain=&since= → { items, seq } * POST /v1/events?brain= { events: EventItem[] } → { accepted } * GET /v1/consensus?brain=[&memory=] → { consensus } * GET /v1/snapshot?brain=[&id=][&list=0][&limit=] → { seq, counts, items? } * GET /v1/health → { ok, brains } * * Device auth (SaaS groundwork — RFC 8628-style device flow, no external IdP): * POST /v1/auth/device → { device_code, user_code, verification_uri, interval, expires_in } * GET /v1/auth/approve → browser approval page * POST /v1/auth/approve (form) → approve a user code with an existing credential * POST /v1/auth/token { device_code } → pending | { token, brain } * The minted account token (aidimag_at_…) inherits the scope of the approving * credential: admin token → all brains; member key → that key's brain. The * hosted SaaS swaps the approval page for GitHub OAuth — same endpoints. * * SyncItem = { tbl: "memories"|"proposals", id, updatedAt, deleted, payload } * The server is a dumb ordered log + latest-state index per brain. All merge * logic (LWW, verification arbitration) lives in clients. Events are an * append-only ingest used for cross-machine verification consensus. */ export type Role = "admin" | "member" | "viewer"; export type Permission = "read" | "write" | "admin"; export interface UserInfo { id: string; username: string; role: Role; oidcSub: string | null; createdAt: string; revokedAt: string | null; } export interface SyncItem { tbl: "memories" | "proposals"; id: string; updatedAt: string; deleted: boolean; /** full row snapshot (MemoryEntry / Proposal JSON); null when deleted */ payload: unknown | null; } export interface EventItem { id: string; type: string; memoryId: string | null; payload: Record; machine: string; schemaVersion: number; createdAt: string; } /** Current remote state for a brain (GET /v1/snapshot). */ export interface RemoteSnapshotItem { id: string; tbl: "memories" | "proposals"; updatedAt: string; deleted: boolean; claim?: string; status?: string; kind?: string; payload?: unknown; } export interface RemoteSnapshot { brain: string; seq: number; counts: { memories: number; proposals: number; tombstones: number; }; items?: RemoteSnapshotItem[]; item?: RemoteSnapshotItem; } /** Escape untrusted text before interpolating into the HTML approval page (XSS guard). */ export declare function escapeHtml(s: string): string; export interface SyncServerHandle { url: string; close: () => Promise; } export declare function startSyncServer(opts: { dbPath: string; token: string; port?: number; host?: string; }): Promise;