Local copy at docs/reference/outline-openapi-spec3.yml (from outline/openapi) so exact request/response shapes are checkable without network access before adding new OutlineAPIClient methods — already caught two real mismatches (documents.search's sort field rejecting "relevance" as an explicit value, documents.search_titles not working against this instance at all).
4.4 KiB
4.4 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
/developerspage (same docs as getoutline.com/developers, versioned per Outline release — check against your self-hosted version, not just the public docs). - Full OpenAPI spec, vendored:
docs/reference/outline-openapi-spec3.yml— pulled fromoutline/openapi. Check this before adding any newOutlineAPIClientmethod — read the exact request/response shape here rather than guessing from prose docs. It's already caught real mismatches twice:documents.search'ssortfield rejects"relevance"as an explicit value even though the field is documented as accepting it (server only treats it as the implicit default when omitted), anddocuments.search_titlesdoesn't work against this self-hosted instance at all despite being in the spec (seeDocumentTitleSearchViewModel's doc comment — likely server version drift, matching the risk already called out below). Re-pull from the source repo periodically since it can drift from what's actually deployed.
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/Connectionheaders 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 overyrs(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.Docyou 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.