saascode

Cortex: a self-hosted AI agent builder with the reasoning on a canvas

builds · aug 09, 2026 · 8 min read · product: Cortex

A support team lead at an online retailer's help desk reviewing her deployed agents in Cortex's dashboard while a colleague in a headset handles a handed-off conversation

The retrieval layer was designed with a fallback: if the primary embedding provider's key is absent, a router provider takes over. Same model either way — that was the specification. What the specification did not account for is that the two providers name the same model differently. The primary accepts the bare identifier; the router requires it prefixed with its own namespace. One constant was written into the code and sent to both. Every call down the fallback path sent an invalid model identifier, received a 400, retried twice, and failed. The primary provider was, in practice, mandatory. The redundancy existed only on paper.

The failure was found after the build had cleared its type check, lint pass, build verification, security scan, and design review. The reason it survived all of them is structural: provider keys are operator-supplied credentials that arrive after deployment. No gate that runs before deployment can exercise a code path that requires one. Fixed in commit bae1560 (2026-06-21) by making the model identifier an argument per provider rather than a shared constant. The fix was straightforward. The detection was not, and could not have been.

Cortex is a multi-tenant platform for building and deploying AI agents on a visual canvas — a product whose every execution path depends on a provider key that arrives after every gate has already run.

Starting point

The builder organizes six node categories — Brain, Knowledge, Tools, Decision, Guardrails, and Output — that a user wires into arbitrary topologies on a free-form canvas. The engine walks the graph depth-first from input nodes, follows edges, accumulates per-node outputs, and lets decision nodes prune traversal to their chosen successors. No linear trigger-then-actions progression; the topology is up to the operator. A live-preview chat panel runs the agent server-side and streams a per-node execution trace so the operator watches which node fired, in what sequence, and with what result. Finished agents ship as embeddable widgets or public shareable links.

One architectural commitment shaped the entire build: a single service, no execution worker, no queue. Stated as load-bearing, not a performance preference. Prior in-house work on an agent builder was brought in as architecture reference — algorithms to re-derive, not files to copy — with two explicit defects to correct: a widget that stored its allowed-domain list but never enforced it, and a canvas that wrote every save unconditionally.

Reading the market

The specification's positioning did not survive research. The founding claim distinguished Cortex from chatbot builders; the two largest named competitors had already moved to agentic framing and say so publicly on their own sites. A reader disproves the distinction in one visit. That framing does not appear in this series.

What research left: the per-node execution trace visible during live preview; guardrails and human escalation surfaced as first-class canvas nodes rather than configuration settings; the choice between renting a visual agent builder and owning one; and a category-level discontinuation signal worth naming honestly. Flowise archives its repository on 2026-08-10, code current 2026-07-29, support ending 2026-08-31, following an acquisition in August 2025. The vendor's own stated reason for sunsetting is that rigid low-code workflow hit a complexity ceiling once coding agents arrived — that concession stands. Apache 2.0 survives archiving. Flowise is unmaintained, not unusable. The conceded buyer is the engineer with a coding agent; the served buyer is the operator or agency that needs a GUI to hand a non-technical client.

The licence argument holds against two named competitors — one adds a clause forbidding multi-tenant operation without written authorisation; another limits use to internal business purposes — but not against Langflow, which ships plain MIT and is more permissive than Cortex. Against Langflow the differentiator is the commercial chassis: organisations, roles, billing, plan gating, and metering assembled into a product an operator can deploy and run as a business.

The decisions that shaped it

Single service, and what it cost. With no execution worker and no queue, a code-execution sandbox cannot run safely inside the request serving the application. The node was cut at specification time and recorded as a permanent exclusion — not an effort estimate — alongside voice and telephony, messaging channels, collaborative canvas editing, a template marketplace, and multi-agent orchestration. The shipped executor set has six categories; none of them runs code.

Four time budgets over one engine. The same generator runs under four distinct ceilings: 25 seconds per node, with a node error emitted on expiry and the walk continuing into downstream nodes that did not depend on it; 60 seconds on the live preview route; 300 seconds on the two deployed routes sized for longer agent traversals; and 45 seconds on the tool-protocol path, which returns a timed_out: true sentinel rather than stalling its transport. All four constants are present and distinct in the shipped code.

A custom streaming protocol. The execution stream carries two interleaved signals: model text chunks and node lifecycle events. The SDK's packaged data-stream response handles the first. Five custom server-sent events were specified instead — node start, chunk, done, error, and a final completion signal — with the shared chat helper explicitly prohibited on execution routes. The events are emitted by the runner and parsed by the client hook and the standalone widget.

Conditional canvas saves. Every save carries the version the client believed it was editing. The update is conditional on that version; a concurrent save that has already advanced it matches zero rows, and the route returns 409 with a stale-lock error rather than overwriting. The prior in-house build wrote unconditionally; this one is the correction.

Fixed retrieval dimensions. The vector column, HNSW index (m=16, ef_construction=64), and match function are built for 1,536 dimensions. The constraint is migration cost: changing dimensions means re-embedding every document. The match function is SECURITY DEFINER to avoid policy recursion. Documents chunk at 512 tokens with 50-token overlap and embed in batches of 100.

The widget as a separate build. The embedded widget is vanilla JavaScript in a shadow root, bundled by a second toolchain into a self-contained immediately-invoked bundle with a 30KB ceiling. The main build command does not build it; the separation was wired into the release check as a named gate. The bundle shipped at 4,687 bytes raw.

Roles reconciled first. The baseline schema's role check constraint and its new-user trigger both hardcoded a role name this product does not use. Both were replaced — constraint, default, existing rows, trigger function — in migrations ordered entirely before the first authentication seed ran. An automated check correctly flagged the role drop as a hard-fail; the override is on the record with reasoning documented.

What fought back

Three were found after the build had cleared every pre-release gate and been tagged.

A gated capability was enabled in the database, the organisation's plan included it, and the product returned 403. The feature-gate function was written for browser execution; called from server code it ran with no session, the row-level policy blocked the read, and the secure default — no row means no access — misfired on eight server-side callers. The fix is a separate server-side module using the privileged client, with a comment in both files explaining the reasoning and prohibiting the same pattern from returning.

Body copy, usage counters, node descriptions, and pricing feature lists were painted with a colour token that reads like a text role and resolves, in this design system, to a near-white surface tint — approximately 1.05:1 contrast against a white page, and in practice invisible. Nineteen occurrences across seven files. It compiled, type-checked, linted, and passed an automated design scan. A rendered screenshot caught it. The fix redirected the standalone uses to the correct tertiary text token and deliberately left the paired foreground-background combinations untouched.

Two navigation systems resolved to the same URL. The public gallery and the in-application gallery were built independently and both targeted the same path, which the framework rejects as a build error. The resolution consolidated them into a single page that changes identity by viewer: anonymous visitors see the marketing gallery, authenticated users see the import gallery.

A query in the tool-protocol analytics surface selected three plan columns that do not exist on the plans table — a shape inherited from a different product's schema — and would have thrown at runtime the first time a plan matched. Found by static inspection and fixed before release. The remote verification tool crashes on a migration-registry table the file-by-file application path on this operating system never populates; worked around by verifying the database directly, and deferred when the crash repeated at deploy.

Open at time of writing: a page heading resolving to the wrong translation seed; a count-aware string reading "1 agents"; a transitive dependency with a published advisory logged rather than applied; and starter template names mismatched from the demo documentation — two of five align, one documented name is seeded as a demo agent instead. None are functional blockers.

Every execution surface — live preview, deployed chat, embedded widget, knowledge ingestion, and vector search — is inert until an operator supplies a model-provider key. Code paths were verified by inspection and by HTTP probe; none were verified end-to-end against a live model during this build. The failure mode was designed and confirmed: a missing key produces a node error or a configuration error, never an unhandled exception.

What shipped

Ten domain tables with row-level security on every one, inside a database of 53 tables total; the vector column, HNSW index, and two SECURITY DEFINER functions verified present against the live database. Thirty-three product API routes across six groups on top of the base application surface; 250 compiled in total. Twenty-three pages across 26 sections built in parallel waves, with loading, empty, and error states verified on every data page.

The six-category canvas with arbitrary topology, per-node execution trace in live preview, draft and published versioning with snapshot rollback, conversation history with a stored per-message node trace, knowledge bases with document ingestion and vector search, widget and public-link deployment, a cross-organisation template gallery with one-click import, per-agent analytics, message metering, and an agent-as-tool protocol surface.

Three database-driven plans: Starter at $29/month (2,000 messages, 3 agents, 1 deployed); Pro at $99/month (20,000 messages, 25 agents, 10 deployed); Scale at $299/month (150,000 messages, unlimited agents and deployments, export, and the tool-protocol tier). Tagged at version 1.0.0 after a release check confirming the widget bundle under its size ceiling. Deployed with 22 migrations applied and 26 correctly skipped, serverless region pinned to the database's region, final row counts verified directly. Thirty-two of 38 automated readiness checks passed against a live public demo; the six exceptions require subjective judgment or a live provider key. Build phases spanned approximately five hours of wall-clock, with roughly 2.25 million agent tokens recorded across measured phases.

See it

See Cortex →

end
Cortex: a self-hosted AI agent builder with the reasoning on a canvas