ADR-0205: Navin Enterprise AI Notes & Knowledge Workspace, notes and knowledge workspace above the Control Plane
- Status: proposed (2026-11-05)
- Deciders: Navin Enterprise AI platform team, brief 03 mission (
agent_uses_cases/tel_note_meeting/03_NAVIN_NOTES_IA_EQUIVALENT_NOTION.md) - References: ADR-0176 (UC-00 model whose generic modules are copied), ADR-0180 (shared frontend shell, not used
here, see decision 1), ADR-0174 (memory provider), product conventions
navin-enterprise-notes/CONVENTIONS.md, OPA packageeai.notes(deploy/policies/opa/notes.rego, testsnotes_test.rego, keynotesofdeploy/policies/opa/data.json), OpenFGA fragmentdeploy/policies/openfga/products/notes.fga(+notes.tests.fga.yaml,notes.tuples.yaml), Manager templatenavin-notes-agent(navin-manager/agent-templates/navin-notes-agent.yaml,published: false,navin-enterprise-notes/navin-integration/template/manifest.yaml).
Context
Brief 03 asks for a notes and knowledge workspace for the teams of one tenant: block pages with sub pages, databases with typed properties (relations, rollups, formulas), views, forms, verified wikis, tasks, real time coedition with history, permissioned search, automations, import/export, sharing (guests, public links, sites) and an AI assistant that proposes and never decides. The functional target is parity with the usages of a Notion like tool; nothing proprietary is copied: no code, no internal format, no brand, only the documented export format is read by the import. Permissions must be checked by the server on every read, search result, WebSocket sync, export, embed, rollup and background job; an agent never holds more rights than the user it serves; content under legal hold is neither exported nor deleted.
Decision
- Separate product
navin-enterprise-notes/(product idnotes, sectorcross_industry) with its own Next.js 16 + React 19 + MUI 9 frontend instead of the shared shell of ADR-0180: a CRDT block editor, database views and a knowledge graph cannot be described by a shell drivenproduct.manifest.json; the product therefore has no root product manifest and theagent-product-manifestcontract stays reserved to the sector agents. The BFF (OIDC, Redis sessions, CSRF,/api/notesproxy) is copied from UC-00 and adapted. - FastAPI backend
navin_notes(hatchling, Python 3.12, same pins as UC-00) with PostgreSQL as the source of truth: schemanotes, tables prefixednotes_,tenant_idandproject_idon every table, RLS enabled and forced throughnotes.current_tenant()/notes.current_project(), rolesnavin_notes_migrate(DDL) andnavin_notes_app(no BYPASSRLS), Alembic with named revisions. Files stay in the object storage of the Control Plane, the semantic index in Qdrant/Cognee through the Control Plane: never a second index of the content. - Coedition through Yjs CRDT: the live document of a page is a Yjs document served by a dedicated
realtimeprocess (pycrdt-websocket,y-websocketprotocol), persisted innotes_crdt_documentsand materialized intonotes_blocks. The server issues a short room token (60 s) after an OpenFGA check, closes any update sent by aview/commentclient (code 4403), bounds update and document sizes, and revokes sessions when a right is lost. Identity never comes from the browser. - Database formulas: an own DSL (lexer, parser, typing, pure evaluation, depth and cost limits) compiled and tested
before save; never
eval, never free code execution; CSV outputs neutralize spreadsheet formula injection. - Two layer authorization through the Control Plane: OpenFGA for relations (
notes_instance,notes_workspace,notes_teamspace,notes_page,notes_database,notes_form,notes_automation,notes_file,can_*inherited from parent to child,public: [user:*]written only by the sharing service) and OPAeai.notesfor the decisions above the relations: public sharing (classification, instance setting), guests (allowed domain), export (legal hold, guest, format), sites (administrator, approved domain), automations (webhook host, email domain, approved agent launch), agent writes (delegation of a user, never beyond their permission,dry_runfirst above the threshold), bulk AI apply (approval above the threshold), permanent deletion (legal hold, retention), Notion import (recorded consent), meeting import (source permission token), retention change (tenant_admin). Fail closed, sorted reasons, every decision audited. - The LLM neither decides nor executes: every AI output is a proposal (
notes_ai_edit_proposals) with preview and diff, explicitly accepted by a human, idempotent, undoable; page content given to the model is untrusted data and the tool calls it proposes are checked against the permissions of the caller before execution. - Manager integration through six components
runtime, migrate, backend, worker, realtime, web(dedicatedstrategy, servicenavin-notes-agent), development ports web 3489, API 8488, realtime 8489, Redis 6409. The template staysprepared/published: false; artifacts arependinguntil they are really built. - Out of scope of version 0.1.0: native desktop or mobile packaging, SCIM, Notion import beyond the documented
export format (zipped Markdown/CSV/HTML), full offline parity (only a local
y-indexeddbcache purged at logout or when access is lost is delivered).
Consequences
- An own frontend costs a BFF/i18n/theme base duplicated from UC-00 and maintained in the product; in exchange the editor, the views and the collaboration are not constrained by the shell.
- The
realtimeprocess is one more component to supervise (health, metrics, backup of the CRDT documents); the consistency of PostgreSQL + CRDT snapshots is verified by the restore test. - Every new sensitive action goes through a
can_*relation and aneai.notesdecision; an action missing from the policy is refused (unknown_action). - The template stays absent from the catalog until an explicit validation is given; real connectors (meetings, calendar, CRM) remain to be plugged through the Control Plane.
Alternatives considered
- Lexical (Meta) as the editor: rejected in favour of Tiptap/ProseMirror, whose Yjs integration (
y-prosemirror), strict node schemas and extension ecosystem (tables, mentions, mathematics) are more mature for a block model validated server side. - Hocuspocus (Node collaboration server): rejected to keep a single server language (Python) and enforce permissions,
persistence and revocation in the same code as the API;
pycrdt-websocketspeaks the samey-websocketprotocol. - EAV model without constraints for database properties: rejected; properties are
jsonbvalidated against the schema of the database (types, options, relations) with targeted indexes, and amounts are decimal strings. - Tailwind / shadcn for the UI: rejected, MUI 9 is the only kit (platform consistency, RTL, a11y), no second styling system.
Links
- Brief:
agent_uses_cases/tel_note_meeting/03_NAVIN_NOTES_IA_EQUIVALENT_NOTION.md(sections 2, 19, 30, 35) - Conventions:
navin-enterprise-notes/CONVENTIONS.md - Policies:
deploy/policies/openfga/products/notes.fga,deploy/policies/opa/notes.rego - Template:
navin-enterprise-notes/navin-integration/template/manifest.yaml,navin-manager/agent-templates/navin-notes-agent.yaml - French version:
docs/architecture/ADRs/ADR-0205-navin-enterprise-notes-agent.md