/articles/building-ai-workflow-studioBack to articles
AI Product Engineering12 min

Building AI Workflow Studio: from portfolio idea to observable production system

A practical case study of turning an AI portfolio concept into a standalone workflow operations console with secure controls, persisted execution stages, live SSE updates, auditability, and rollback-ready delivery.

Published
Read time
12 min

AI Workflow Studio began as a missing piece in my portfolio. I already had the public portfolio, its Go backend, a custom Open WebUI theme, and reusable AI skills, but they did not yet demonstrate how I would operate an agentic product end to end. The goal was therefore not to add another static demo. It was to build a real control plane that could create workflows, launch runs, expose execution-specific timelines, protect mutations, and survive production deployment failures.

Why the portfolio needed an operations product

A chat interface can show that an application talks to a model, but it does not answer the harder production questions: Who may start or cancel work? Where are workflow definitions stored? Can an operator see the exact stages of one execution? What happens when the backend is unavailable, a request is repeated, or a deployment fails? AI Workflow Studio was designed to make those questions visible rather than hiding them behind a polished prompt box.

The resulting ecosystem has clear responsibilities. portfolio-2026 introduces the work and hosts the case study. AI Workflow Studio is the responsive operations interface. portfolio-backend-2026 owns authentication, mutation rules, streaming, and persistence access. Supabase stores workflows, executions, stages, and audit records. custom-ai-skills and n8n/workers remain explicit integration points for reusable tools and long-running jobs rather than being presented as capabilities already wired into the deployed runtime.

Starting with a production-shaped foundation

The Studio was created as a separate Next.js 16 and React 19 repository using TypeScript, Tailwind CSS, Bun, a non-root standalone Docker image, and a small page composition layer. State and effects live in hooks, while workflow inventory, execution details, admin access, and forms live in focused components. This keeps the interface maintainable as the control plane grows.

The first public integration was deliberately read-only. GET /api/studio/overview returns safe workflow and execution summaries from the Go backend, while the UI has a bounded request timeout, contract validation, and seeded fallback data. A backend interruption therefore does not turn the flagship demo into a blank page, and the interface clearly indicates whether it is connected to the live backend or presenting a safe fallback.

Closing the security gap before adding more controls

An early control proxy could perform mutations without exposing its server token, but that still made the public route a mutation oracle. The fix was architectural: public visitors remain read-only, while protected commands require an authenticated admin session. The browser receives a host-only Secure, HttpOnly, SameSite=Lax cookie, and the same-origin BFF forwards only the named session cookie. Privileged credentials and internal backend origins remain server-side.

Authorization is role-aware: administrators and editors can mutate, while viewers can inspect authenticated lists and audit records. Login attempts are limited to five per IP per fifteen minutes, and Studio mutations are limited to thirty per IP and identity per minute. Redis provides atomic counters with a local fallback. Forwarded client addresses are trusted only when proxy trust is explicitly enabled, and the implementation uses the right-most valid hop appended by Caddy rather than accepting an arbitrary spoofable header.

Every protected action creates a persistent audit record without storing passwords, raw tokens, cookies, Authorization headers, or other credentials. This makes workflow creation, execution launch, pause, retry, approval, and cancellation traceable without turning observability data into a secret store.

Making executions real, reversible, and observable

Workflow and execution data moved from presentation fixtures into Supabase through additive migrations. The creation API accepts only active workflows and creates a running execution with normalized cost and duration values. Production smoke tests exercised creation, pause, retry, approval, and cancellation through the real Studio boundary. Approval and cancellation were tested against restorable records, then the original demo states were restored while the audit trail remained intact.

A workflow-level node list is not enough for a live inspector because two runs of the same workflow may be at different stages. StudioExecutionStage therefore persists an ordered timeline for each execution, including status, timestamps, safe detail and tool metadata. A PostgreSQL RPC creates the execution and its stages in one transaction, avoiding partially-created runs. Existing executions were seeded idempotently through an additive migration.

The backend exposes public read-only stage snapshots and an SSE stream. The stream sends an initial snapshot, change events, heartbeats, event IDs, reconnect guidance, bounded polling, and a bounded lifetime while cleaning up timers when the client disconnects. The browser connects through a same-origin Next.js proxy. A validated React hook manages reconnect backoff, cleanup, connection status, and fallback data so the selected run panel reflects its own persisted stages rather than a generic animation.

Shipping with immutable deployment and rollback

The deployment pipeline validates tests, lint, builds, Go vet, deployment scripts, and Docker images before publishing. Backend and Studio images are tagged with the full forty-character commit SHA instead of latest. Native OpenSSH uses pinned known_hosts entries, temporary registry authentication is removed after use, and deployment scripts update only the relevant image variable rather than replacing the shared production environment file.

Repository-level concurrency was not enough because Backend and Studio deploy from separate repositories. A VPS-wide flock now serializes changes to the shared Compose deployment. After Compose starts a candidate image, a public health gate verifies the API or Studio URL. Failure restores the previously successful immutable image automatically, and workflow_dispatch supports manual rollback to a chosen full SHA. The live pipeline and rollback path were verified against the VPS rather than documented as an untested promise.

What final QA changed and what comes next

The final pass tested desktop, tablet, and mobile layouts for both the Studio and bilingual portfolio case study. It found concrete issues that unit tests could not: the admin form widened a 390-pixel viewport, keyboard focus escaped the dialog, expected unauthenticated session checks produced noisy 401 console errors, and execution-table columns overlapped on mobile. The fixes introduced a viewport-bounded dialog, initial focus and a focus trap, Escape and Cancel focus restoration, a clean read-only session response, and compact mobile execution cards. Playwright screenshots and production checks confirmed the corrected layouts.

The product now demonstrates more than an AI-themed interface. It shows a coherent path from public portfolio story to a separately deployed control plane, Go API, Supabase persistence, secure sessions, rate limits, auditability, execution-specific streaming, and rollback-aware operations. The next truthful expansion is to connect custom-ai-skills and n8n or dedicated workers behind the existing execution model, then add evaluation, token and latency telemetry without weakening the security boundary that the current system establishes.