Product Notes

We Built a Canvas That Refuses to Make Things Up

Most AI canvases are whiteboards with a chat window bolted on. Ours began as a research tool for contested questions, where every answer had to be tied to evidence someone could actually inspect.

Research workspace AI with citations Structured canvas
 
 
 
Claim

Ten outlets reported the same headline.

Source

All ten trace back to one wire story.

Missing

No primary document confirms the strongest sentence.

Verdict

Supported in part. Too strong as written.

What It Is

The product is a research workspace built around a canvas of cards. A card can be a person, an event, a source, a claim, a task, a note, a group, or any content type the user invents. Cards can be dragged, nested, searched, assigned, discussed, and connected with typed relationships like supports, contradicts, cites, depends on, or flows to.

The important decision is that a card is not just a visual shape with a label. Every card is a real content record with fields, relationships, permissions, and history. That one choice is what makes the rest of the system work.

  • Drop in a URL and the system fetches, evaluates, and stores it. If the page is a paywall, bot wall, empty shell, or failed fetch, the card says so instead of quietly storing garbage. Blocked pages can fall back through the Wayback Machine.
  • Drop in a Word document, PDF, or spreadsheet and it becomes structure. Documents split along their own headings into a linked tree. Spreadsheets become row-level cards, grouped and searchable. Scanned PDFs can be OCR'd by a vision model and clearly marked as machine-read.
  • Ask the assistant about the board and it sees both the cards and the connections between them. It has explicit verbs for the actions it can take: create, link, unlink, remove, trace, break apart, summarize, and compare.
  • Work together live with presence, selections, per-card assignments, locks, team chat, typing indicators, reactions, team invites, and read-only guest links.
The canvas is not a place for the model to perform confidence. It is a place for evidence to become visible.

The Rule: Absence Is an Answer

Nearly every hard bug we fixed had the same shape: a missing capability produced a confident invention instead of an honest gap.

When asked what was inside a PDF the system had not actually read, the assistant answered with an invoice number lifted from the filename. When asked how two people were related, it created a family link, then read its own fabrication back as fact. The fix was not a better prompt. The fix was to give the model the missing read operation, then make absence loud.

Now the system can trace relationships through the stored graph and answer, "No connection is recorded," instead of filling the blank. Files carry explicit verdicts such as ok, scanned, and service_down, so a card that was never read looks unread.

The same rule shapes the analysis layer. Claims cluster by meaning so users can see where sources agree. Sources that share a wire service collapse into one origin, because ten outlets running the same copy are not ten independent confirmations. Story outlines refuse to assert what the reporting has not established.

The Stack

The stack is boring where it can be and specific where the product needs it.

Layer Choice
Backend PHP 8.4, Laravel 13, Livewire 4, and Volt single-file pages
Canvas Svelte 5 runes, @xyflow/svelte, and dagre for layout, mounted as an island inside Livewire
Realtime Laravel Reverb with private and presence channels, plus client whispers for ephemeral signals
AI Laravel AI SDK with provider-agnostic support for Anthropic, OpenAI, and Gemini, with tools gated per turn
Data PostgreSQL, multi-tenant databases, and pgvector for semantic search
Files MarkItDown, Crawl4AI sidecars, and SearXNG for self-hosted search without third-party keys
Frontend Tailwind 4, Flux UI, and Vite 8
Tests PHPUnit and Vitest, including a test that mounts the full canvas in a DOM

Two Decisions That Did the Heavy Lifting

Cards Are Content, Not Shapes

Because every card is an Entry of a ContentType, and content types are user-definable, the canvas was general before we deliberately made it general. Adding databases, services, queues, and other node types required an icon, a color, and a shape. The graph, AI, search, permissions, and sharing model already knew what to do with them.

The Canvas Sends Operations, Not Documents

The canvas used to save the entire graph behind a version guard. That made the unit of concurrency the whole board: when two people dragged different cards, one person's work could silently overwrite the other's.

Now the client sends what the user actually did: move this card, draw this line, update this field, remove this relationship. Those operations merge cleanly, and they are exactly the messages that need to be broadcast to everyone else. Live collaboration came from the same architecture rather than being bolted onto it later.

What It Is Not

It will never be draw.io. There are no freeform arrows, no arbitrary swimlanes, and no shapes that mean nothing. Every node is a record. That constraint is what lets users trace it, search it, assign it, discuss it, and ask an AI about it without asking the AI to guess what it is seeing.

That trade, structure over freedom, is the product. The goal is not to make a prettier whiteboard. The goal is to build a canvas where evidence has weight, uncertainty has a place, and the assistant is allowed to say, clearly and usefully, "we do not know that yet."