The closed/hosted/schema-rich extreme: everything is a block (UUID id, type, properties, ordered child content, single parent), pages are blocks and database rows are pages with a schema, Postgres-backed hosted SaaS. Databases add typed properties + relations + rollups + formulas across many views = the apex of wiki-page-as-structured-record. Extension model has no in-app plugin runtime; the only extensibility is the external REST API (+ webhooks 2026) inside a tight envelope (~3 rps, eventual consistency, recursive child fetch, scoped/revocable per-page grants). Adds the third attachment mode (external-API-only) alongside file-store (Obsidian/TWiki) and in-engine host (Roam/XWiki); Notion enforces no silent remote mutation via scoped grants. Added UC-57 (attach closed external-API-only shard w/ operational envelope + scoped grant), UC-58 (typed database w/ schema+relations+views, no flattening), UC-59 (lossy-aware translation w/ fidelity report); enriched UC-31/34/36/39/50/51/52/54/56. Boundary: one external-API candidate shard, best as projection/mirror/overlay/backup, not a substrate and not the federation layer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
18 KiB
Findings — Notion: a closed block-database SaaS, attached only through its API
Date: 2026-06-14 Source kind: modern shipped product — a hosted block-database SaaS; a candidate shard of a distinct family (closed, external-REST-API-only, schema-rich, heaviest translation cost); has no in-app plugin runtime Lens: shard-wiki — the external-API attachment mode, database-as-pages, lossy translation/fidelity, scoped consent, and graceful degradation against a sovereign closed backend
Where Notion sits in the set. Roam and Notion are both block databases with per-block UUIDs, but they are opposite on access: Roam is a client-side DataScript DB reachable only through an in-app JS API (write-through needs an adapter hosted inside Roam); Notion is a server-side Postgres store reachable through a real external REST API — attachable from outside with no in-engine hosting, but rate-limited, eventually consistent, and scoped by explicit per-integration page grants. And against Obsidian's "file over app," Notion is the mirror image: app over file — a closed hosted store with no portable files at all. So Notion is the dive that stress-tests shard-wiki's hardest constraints: graceful degradation, no silent remote mutation, union without erasure, and Markdown-first that must degrade against a proprietary, schema-rich, non-Markdown backend.
Pairs with — and contrasts against — the Roam dive (block-DB, in-app vs external API), the Obsidian dive (closed-hosted vs file-over-app), and the XWiki dive (both are structured wiki-as-app-platforms; Notion is the apex of database-as-pages but closed and REST-only).
1. Core data model — everything is a block
Notion's data model is uniform: "Everything you see in Notion is a block. Text, images, lists, a row in a database, even pages themselves — these are all blocks."
Each block record holds:
| Field | Meaning |
|---|---|
| id | a randomly generated UUID v4, visible in the page URL — the block's stable address |
| type | determines rendering + which properties apply (text, to-do, heading, page, child_database, …) |
| properties | type-specific attributes (e.g. title text; for database rows, the typed property values) |
| content | an ordered array of child block IDs — nesting |
| parent | a single upward pointer — used for permission inheritance |
Blocks form a render tree (content + parent pointers); indentation is structural, not cosmetic — it changes block relationships. Pages are blocks; databases are blocks whose children are pages; a database row is a page with typed properties. Stored in Postgres on Notion's servers (hosted SaaS — not local, not files).
The shard-wiki-relevant facts: (a) per-block UUIDs give native sub-page addressing (UC-51); (b) the page = block, database-row = page-with-schema identity is a real page-model impedance (§3); (c) the store is closed and server-side — reachable only through the API (§4).
2. Databases — schema, typed properties, relations, views
A Notion database is a collection of pages sharing a schema of typed
properties: title, rich_text, select / multi_select, number, date,
checkbox, person, files, relation (a typed link to rows in another
database — shown on both sides, i.e. bidirectional), rollup (an aggregate over a
relation), and formula (computed). The same row-set is shown through multiple
views — table, board (kanban), calendar, gallery, list, timeline — each a
filtered/sorted/grouped projection of the same data.
For shard-wiki this is the apex of "wiki page as structured record" (stronger than XWiki XObjects, UC-39): a page can be bodiless typed data, pages are joined by typed inter-database relations (a graph of typed links), and "the page" is routinely a row in a schema rather than prose. Notion databases also ship the ZigZag insight commercially: one row-set, many views/dimensions (UC-47/48) and filtered/linked databases = query-defined pages (UC-54).
3. Page-model impedance
Mapping Notion to a Markdown-first page model is the heaviest translation case in the research set:
- Block/rich-text ≠ Markdown. Notion's rich text is an annotated-span model; many block types (synced blocks, columns, callouts, embeds, database views) have no clean Markdown equivalent. Notion's own export to Markdown/CSV is lossy (databases → CSV, relations/rollups/formulas flatten or drop).
- Database-row-as-page + schema must map onto pages + sidecar metadata without discarding the schema or the relations (extends UC-34/UC-39).
- Therefore translation must be lossy-aware with a fidelity report — surface what did not round-trip rather than silently flattening (UC-59). This is different from UC-42 (Foswiki TML↔HTML lossless round-trip); Notion is fundamentally lossy.
4. Extension model — there is no plugin runtime; only the API
This is the defining architectural fact for shard-wiki: Notion has no third-party in-app plugin/extension system. There is no marketplace of code that runs inside Notion (the only "in-app modding" is the unofficial Notion Enhancer desktop patcher, out of scope). Extension = external integration via the public REST API (plus embeds and, recently, webhooks).
The public REST API (api.notion.com/v1):
- Resources:
pages,blocks(retrieve / append / update / delete children),databases(retrieve, query with filters+sorts, create),users,search,comments. - Authorization: an internal integration token (workspace-owned) or OAuth 2.0 (public integrations). Critically, an integration only sees pages a user has explicitly connected to it — the user "approves the app and connects specific pages" via Add connections. Access is a scoped, revocable, per-page grant (§6).
- Operational constraints the adapter must encode:
- Rate limit ~3 requests/second average (429
rate_limited); no paid increase. - Eventual consistency / no live read model — the API is not real-time; reads can lag.
- Recursive retrieval — a block returns only first-level children; full-page
reconstruction requires walking
has_childrenrecursively. - Payload caps — 1000 blocks / 500 KB per request; child arrays ≤ 100.
- Webhooks (added 2026) deliver page/database change events — a push transport for UC-31, replacing pure polling.
- Rate limit ~3 requests/second average (429
- Version history: Notion keeps internal page history (retention bounded by plan); it is not portable and not exposed as git — a UC-36 supplementation case (like Confluence/MediaWiki), not an import case.
- Publish: pages can be published to the web as read-only public pages — an outbound publish surface (UC-56).
Consequence: Notion is attachable only as an external-API shard — but, unlike Roam, no in-engine adapter hosting is needed (the REST API is external). The cost is the operational envelope (rate-limit, eventual consistency, scoped grant) that the capability profile must model (UC-57).
5. Notion as a shard — capability profile
| Capability | Notion | Notes for the adapter contract |
|---|---|---|
| Read | yes (external REST) | rate-limited (~3 rps), eventually consistent, recursive child fetch |
| Write | yes (external REST) | append/update/delete blocks, create pages — no in-engine host needed (vs Roam) |
| Write granularity | block-level (fine) | like Roam; per-block ops |
| Identity / addressing | block UUID v4 | native sub-page addressing (UC-51), store-minted (like Roam, not in-file) |
| Structured data | yes (apex) | databases: schema + typed properties + relations + rollups + formulas (UC-34/39/58) |
| Native query | yes | database query API (filters/sorts) → delegate views (UC-52) |
| Views / dimensions | yes | table/board/calendar/gallery = many views of one row-set (UC-47/48/54) |
| Subscribe | webhooks (2026) | push events; else poll (UC-31) |
| Version history | internal, not portable | supplement via coordination journal (UC-36) |
| Diff / merge | no native | — |
| Lock | no | — |
| Publish | publish-to-web | outbound read-only (UC-56) |
| Access model | scoped per-page grant (OAuth/token) | explicit consent; revocable (UC-57, authz) |
| Syntax / content | proprietary block + rich text | lossy to Markdown; needs fidelity-aware translation (UC-59) |
Verdict: Notion is a legitimate but demanding shard — external-API-attached, schema-rich, fine-grained, closed. It behaves best as a projected / mirrored / overlay / backup participant; full write-through is possible but bounded by rate limits and eventual consistency. The strongest reasons to attach it: structured databases (UC-58) and block-UUID addressing (UC-51); the strongest cautions: lossy translation (UC-59) and no portable history (UC-36).
6. Scoped consent and "no silent remote mutation"
Notion enforces one of shard-wiki's INTENT constraints at the platform level: an integration can touch only the pages a user has explicitly connected to it, and the grant is revocable. This is a clean, real-world model of no silent remote mutation and of a shard granting the orchestrator scoped, consented access — and it ties directly to the settled authz-in-core / authn-delegated decision (shard-wiki-auth-in-core-decision): authentication to Notion is delegated (OAuth / integration token), while shard-wiki's own authorization decides what to do with the granted scope. The adapter contract should treat "scoped, revocable grant" as a first-class attachment property (UC-57), not an afterthought.
7. Mapping to shard-wiki INTENT (compare, do not equate)
7.1 Reinforcements
- Graceful degradation has its sharpest test here: a closed SaaS with no files, rate limits, eventual consistency, and lossy export must still be usable as read/projection/overlay/backup. If the adapter contract handles Notion, it handles most things.
- No silent remote mutation is modeled by the platform (scoped grants) — Notion validates the principle (§6).
- Database-as-pages validates that "wiki page" must stretch to typed records with relations, not just prose (UC-34/39/58).
- Block UUIDs reconfirm (with Roam) that native sub-page addressing is real and adoptable (UC-51).
7.2 Deliberate divergences (design bugs if conflated)
- Closed hosted store; no sovereignty over bytes. shard-wiki cannot make Notion git-native or local. It can mirror/project/overlay/back-up and supply a git-addressable history (UC-36) — never claim to own Notion's store.
- Lossy, proprietary content. Do not pretend Notion round-trips to Markdown. Translate lossily with a fidelity report and preserve non-mappable elements as provenance/sidecar (UC-59) — union without erasure includes erasure of fidelity being made visible.
- External-API-only, rate-limited, eventually consistent. Projection must be cache/poll/webhook, not a live read model; sync is bounded — encode this in the capability profile (UC-57). Do not design flows that assume cheap, instant, unlimited reads.
- One workspace (or its granted page set) = one shard, never the federation layer.
7.3 What Notion teaches that shard-wiki should keep
- Model operational envelope (rate limit, consistency class, payload caps, pagination) as explicit capability-profile fields — Notion makes them unignorable.
- Model scoped, revocable consent as a first-class attachment property (UC-57, §6).
- Treat translation fidelity as data: a per-shard, per-page report of what projects cleanly vs. degrades (UC-59) — applies beyond Notion.
- Recognize external-REST attach as a distinct, preferred-where-available mode: full write-through without in-engine hosting (contrast Roam) — but pay the operational envelope.
8. Use-case seeds → catalog (promoted 2026-06-14)
Last existing UC is UC-56. New UCs UC-57–UC-59 added; existing UCs enriched.
| Seed | Catalog action |
|---|---|
| Attach a closed hosted shard via its external REST API only — no file store, no in-app runtime — honoring rate limits, eventual consistency, payload caps, and a scoped/revocable access grant | UC-57 (new) |
| Attach a typed database (schema + relations + rollups + multiple views) as a shard without flattening the schema or the inter-record relations | UC-58 (new) |
| Translate a proprietary block/rich-text model to/from Markdown with an explicit fidelity report, preserving non-mappable elements rather than silently dropping them | UC-59 (new) |
| Block UUIDs = store-minted native span addresses (external-API variant) | enriches UC-51 |
| External-API block-DB attach (no in-engine host) — contrast Roam's in-app-only | enriches UC-50 |
| Database query API + filtered/linked DBs | enriches UC-52, UC-54 |
| Database-as-pages apex; typed records + relations | enriches UC-34, UC-39 |
| Webhooks (2026) as a push transport | enriches UC-31 |
| Internal-only page history, not portable | enriches UC-36 |
| Publish-to-web outbound | enriches UC-56 |
| Scoped, revocable per-integration grant; no silent mutation | links UC-57 + shard-wiki-auth-in-core-decision |
9. Architecture notes for SHARD-WP-0002 (no UC)
- Add an operational-envelope section to the adapter capability profile: rate limit, consistency class (live / eventually-consistent / snapshot), payload/pagination caps, recursive-fetch requirement, push-vs-poll transport. Notion is the forcing example.
- Add access-grant semantics: scope (which pages), revocability, auth mode (delegated token/OAuth) — ties the authz-in-core decision and "no silent mutation".
- Add a translation-fidelity capability: adapters declare and report what content round-trips vs. degrades (UC-59); generalizes UC-42 (lossless) to the lossy case.
- Attachment-mode taxonomy now spans: file-store direct (Obsidian/TWiki, UC-40), in-engine hosted adapter (Roam/XWiki, UC-38/50), and external-API-only (Notion, UC-57). T14 binding should enumerate all three.
- Database/schema/relations as a unit (UC-58) presses the page-model spec: collection
- schema + typed relations, not just a page.
10. Open questions (for spec / workplans)
- Is external-API-only with a tight rate limit (Notion) viable for write-through at wiki scale, or do we cap Notion at read/projection/overlay/backup by default?
- How are inter-database relations (UC-58) represented in the union — as typed links in the link graph, as a separate relation index (cf. ZigZag many-to-many), or both?
- What is the fidelity report format (UC-59), and where does it surface — provenance panel, projection metadata, reconciliation review?
- For scoped grants (§6), how does shard-wiki represent partial visibility (only some of a workspace's pages granted) without misrepresenting the shard as complete?
- Do we consume Notion webhooks (push) or poll, given eventual consistency and the rate limit (UC-31)?
11. Sources
| Source | Used for |
|---|---|
| Notion — "The data model behind Notion's flexibility" (https://www.notion.com/blog/data-model-behind-notion) | Everything-is-a-block; block record (id/type/properties/content/parent); render tree; pages/databases as blocks; Postgres |
| Notion Docs — Request limits (https://developers.notion.com/reference/request-limits) | ~3 rps rate limit, 429, payload caps, recursive first-level children |
| Notion Docs — Authorization (https://developers.notion.com/docs/authorization) | Internal token vs OAuth; integration connected to specific pages; scoped grant |
| Hookdeck / ClickUp — Notion webhooks guides (https://hookdeck.com/webhooks/platforms/guide-to-notion-webhooks-features-and-best-practices) | Webhook support (2026), page/database change events |
| Truto / Rollout — Notion API architecture & essentials (https://truto.one/blog/how-to-integrate-with-the-notion-api-architecture-guide-for-b2b-saas/) | REST endpoints (pages/blocks/databases/search), integration patterns, no in-app plugin model |
| General API knowledge — database property types, views, relations/rollups/formulas, export-to-Markdown lossiness | §2, §3 |
Cross-references: research/260614-roam-deep-dive/findings.md (block-DB/UUID, in-app vs
external API), research/260614-obsidian-deep-dive/findings.md (closed-hosted vs
file-over-app), research/260613-xwiki-deep-dive/findings.md (structured wiki-app-
platform), spec/UseCaseCatalog.md (UC-31, UC-34, UC-36, UC-39, UC-50/51/52, UC-54,
UC-56), workplans/SHARD-WP-0002-federation-architecture.md (T14), and the authz
decision shard-wiki-auth-in-core-decision.
12. Traceability
- New UCs: UC-57, UC-58, UC-59 →
spec/UseCaseCatalog.md. - Enriched UCs: UC-31, UC-34, UC-36, UC-39, UC-50, UC-51, UC-52, UC-54, UC-56.
- Architecture (no UC): operational-envelope + access-grant + translation-fidelity
capability fields; three-way attachment-mode taxonomy; database/schema/relations in the
page model →
SHARD-WP-0002(T14). - Decision link: scoped/revocable grant + no-silent-mutation → shard-wiki-auth-in-core-decision.
- Boundary recorded: Notion is one external-API candidate shard — closed, hosted, schema-rich, lossy-to-Markdown — best as projection/mirror/overlay/backup; not a substrate, not the federation layer (INTENT graceful-degradation + no-silent-mutation).