Context and ownership
Notespace is a self-hosted knowledge and execution workspace. I own the product and full-stack architecture across the TanStack/React client, Go API, explicit SQL migrations, SQLite persistence, editors, planning, search, recovery, and deployment.
The current user model is:
Today
└── Tasks[] <- explicitly chosen work
Category
└── Workspace
├── Notes[]
├── Canvas
├── Plan
│ ├── Milestones[]
│ └── Tasks[]
├── durable assets
└── activity history
Standalone tasks can also live in Inbox/Today without belonging to a Workspace.
Planning without turning Notespace into a task manager
Workspace Plan is deliberately subordinate to authored knowledge. A Workspace can contain milestones and concrete tasks; tasks may belong to a milestone or stay loose.
Today is a projection, not another source of truth. Workspace tasks are explicitly selected for a day, while standalone tasks can exist without a Workspace. Inbox stays low-ceremony for capture rather than becoming another backlog hierarchy.
This keeps the relationship clear:
Workspace Plan -> choose work -> Today -> optionally link Activity
instead of duplicating task state across multiple products.
Granular authoring consistency
The earlier whole-workspace save model has been replaced by more granular boundaries.
Each Note has its own optimistic version and save queue. Canvas has a separate durable version. Editing one Note no longer rewrites unrelated Notes or Canvas state, and Saved appears only after pending authoring queues are acknowledged by SQLite.
For Canvas opened in multiple tabs in the same browser, element changes synchronize through BroadcastChannel and reconcile with Excalidraw element-version semantics. Durable persistence remains server-owned.
This is intentionally not cross-device multiplayer or offline CRDT synchronization.
Search is derived state
Authored SQLite state is authoritative. FTS rows are a projection that can be repaired from authored revisions.
High-frequency Note saves commit authored state first and leave stale search projection repair to the next search. That prevents derived indexing failure from turning a successful Note save into a failed authoring operation.
Recovery is part of the product contract
Moving a Workspace to Trash captures authored state and assets transactionally before removing it from the active library.
Versioned backup/restore covers canonical user-owned data including Workspace Plans, standalone tasks, assets, activity sessions, and Trash. FTS rows are intentionally excluded because they are derived.
The application archive path currently caps backup/restore at 64 MiB. For larger installations, the operational boundary moves to SQLite-safe infrastructure backup until archive streaming exists.
Self-hosted runtime
The application can run as one Go process serving the built web application and API, with SQLite as the durable store.
Docker Compose owns the self-hosted runtime, version tags publish GHCR images, and an optional owner password enables HTTP Basic protection for remotely exposed single-owner installations. HTTPS termination remains a reverse-proxy/platform responsibility.
Result
Notespace now connects knowledge capture and execution without collapsing them into one model: durable Notes/Canvas remain the authored source of truth, Plan owns workspace execution structure, Today is an explicit projection, and Activity records what actually happened.
The engineering lesson is: a self-hosted productivity system stays understandable when authored state, projections, execution state, and recovery each have explicit ownership boundaries.