Canvas Model
The canvas is the surface NEXUS gives agents and humans to work on the same visual scene at once. Its design choices mostly trace back to one fact: more than one caller — possibly several agents and a human, none of them coordinated in advance — can be looking at and changing the same scene at the same time.
Why Excalidraw-compatible
A scene's format didn't need to be invented from scratch, and NEXUS doesn't attempt to model every field Excalidraw's format defines — it preserves whatever it doesn't use. The practical effect is interoperability without a full reimplementation commitment: a scene exported from NEXUS opens in Excalidraw, and round-trips back with nothing lost, because unmodelled keys pass through unchanged rather than being dropped. See Canvas Roles for the exact guarantee.
Why no default role
An agent gets no role on a canvas until one is explicitly granted. The alternative — some ambient default, even a read-only one — would mean every new agent joining a workspace silently gains visibility or write access to whatever canvases already exist there, without anyone having decided that should happen. Requiring an explicit grant makes the blast radius of "add an agent to this workspace" an intentional decision rather than a side effect. It also means a caller can never mistake silence for permission: a call made with no grant at all returns a clear error, not a quiet no-op that could be misread as "there was nothing to do."
Why four distinguishable outcomes
applied, proposed, conflict, and rejected exist as four separate, always-distinguishable outcomes rather than a single success/failure boolean because the caller is often another agent deciding what to do next from the result alone, not a human reading a log. Collapsing conflict into a generic failure would be actively dangerous: an agent that treats "not applied" as "nothing happened" after a conflict might retry blindly and clobber a peer's intervening change, or move on assuming its edit landed when it didn't. Making conflict its own outcome — carrying both the revision the caller expected and the one that's actually current — gives the caller exactly what it needs to re-read and reconcile, and makes "I raced someone" structurally different from "I was refused."
Why optimistic concurrency
Every write states the revision it expects to be modifying. A stale expectedRevision produces conflict rather than a merge attempt, because merging two edits to a visual scene is not generally well-defined the way merging two text edits can be — NEXUS does not guess at a resolution on the caller's behalf. The caller re-reads, recomputes its change against what's actually there, and resubmits. See How to handle canvas conflicts for the mechanics.
Why reason is rejected, not sanitized, on a bad line
The reason a write carries is shown to humans in an audit trail and can be fed back into another agent's context window. That dual audience is exactly why a reason containing a newline or control character is rejected outright rather than cleaned up and let through: sanitizing would still deliver a modified version of whatever the caller was trying to smuggle through formatting tricks, and a human or an agent reading the cleaned string would have no way to know it had been altered. Rejecting is the only response that doesn't require the reader to trust that the sanitizer caught everything.
Why proposer and approver are recorded separately
A commenter's write becomes a proposal, approvable by a human or by an editor-and-above agent. Recording the proposer and the approver as two separate identities — even when both are agents — means the audit trail always answers "who asked for this" and "who signed off" as two different questions, which matters most exactly when an agent approved another agent's proposal and no human was in the loop for that decision at all.
Roadmap
The canvas is not yet a pane you can split into its own window, and comments cannot yet be persisted independent of a proposal. No standalone comment tool is exposed because there is nowhere durable yet for a comment to live outside the proposal mechanism described above.