Files
Outpost/docs/Architecture.md
T

3.5 KiB

Architecture Notes

Deeper technical reference backing CLAUDE.md. Update this as decisions are made — treat it as a living design doc, not a one-time spec.

1. Outline API surface

  • Base: https://<your-instance>/api/*, RPC-style (POST for nearly everything, not strict REST verbs).
  • Auth: Authorization: Bearer <API_KEY> header. Keys are created per-user under Settings → API Keys on the Outline instance. Store in Keychain, scope a dedicated key to this app rather than reusing a personal one.
  • Core endpoints to wrap first: documents.info, documents.list, documents.search, documents.create, documents.update, collections.list, collections.info, users.info.
  • Full reference: your instance's /developers page (same docs as getoutline.com/developers, versioned per Outline release — check against your self-hosted version, not just the public docs).

2. Realtime collaboration transport

  • Endpoint: wss://<your-instance>/collaboration/document.<document-id>.
  • Protocol: Hocuspocus (a Yjs-CRDT-over-WebSocket server implementation). This is not the same socket as any notification/presence channel — it's the actual document edit transport.
  • Payload: binary Yjs update messages (Yjs update format v1/v2) plus the Yjs awareness protocol for cursor/selection presence.
  • Your reverse proxy (Caddy, in your homelab) must forward Upgrade/Connection headers correctly or collaboration silently falls back to "no live sync" while REST edits still work — worth a smoke test against your own Caddy config early.

3. CRDT engine — YSwift

  • Repo: y-crdt/yswift. Swift bindings over yrs (Rust) via UniFFI, distributed with a prebuilt XCFramework.
  • Status as of mid-2026: WIP, pre-1.0, small but active contributor base. Not all Yrs/Yjs features are exposed yet.
  • Integration point: YSwift gives you a local Y.Doc you mutate; a Hocuspocus-compatible WebSocket provider (you'll likely need to write this provider yourself in Swift — no off-the-shelf Swift Hocuspocus client exists yet) syncs that doc's binary updates with the server.
  • Fallback if YSwift blocks progress: vendor the C FFI (yffi) directly and write a thinner Swift wrapper for just the operations you need (XML fragment read/write, update encode/decode, state vector diffing).

4. Document schema mapping

Outline's editor is ProseMirror-based; the Yjs doc stores document structure as a Y.XmlFragment mirroring the ProseMirror schema (paragraphs, headings, bullet/ordered/checklist lists, tables, code blocks, blockquotes, embeds, comments-as-marks). Native editor work breaks into:

  • Read path: Y.XmlFragment → your own document model → AttributedString/TextKit 2 layout.
  • Write path: local edit → diff against your document model → Yjs transaction on the XmlFragment → binary update sent over the Hocuspocus socket.
  • Build the node-type mapping table (ProseMirror node/mark name → native representation) as an explicit, tested module — this is the piece most likely to silently drift from Outline's actual schema as Outline ships updates.

5. Known risk areas (revisit as the project progresses)

  • YSwift feature gaps vs. full Yjs (undo/redo manager, relative positions) — check current state before Phase 2 starts, this library moves fast.
  • Table and embed support are the least standardized part of ProseMirror schemas generally — budget them last.
  • Outline server version drift: self-hosted instance upgrades could change API shapes or the collaboration payload; pin against your actual running version, not assumptions from docs.