AI App Demo, No Signup: how a stranger watches the agent's actual stream of consciousness
Almost every page that promises a demo of an AI app builder, with no signup, shows you a recorded video, an animated mockup, or a polished progress bar. mk0r took the opposite trade. The demo is not a video. The demo is a live stream of every internal event the agent emits while it works on your prompt, and a stranger with no account gets the same stream a paying user gets. Here is the exact list.
What "no signup" usually buys you elsewhere
Look at any page that ranks for this topic and you will find one of three things. A recorded screen capture playing on loop. A static gallery of generated apps with a Create button that pops a signup form. A list of free AI tools where the demo is a chat box and the apps are paywalled. None of those let you watch the model think. None of those let you see the agent fail and retry. None of those let you read the plan it commits to before it starts editing files.
The reason is simple. Live demos are expensive. Recording is cheap. Once you record a demo, you can show it to a million visitors without spending a cent on inference. Once you serve a real demo, every visitor costs you tokens, compute, and provider quota. mk0r decided the trade was worth it, because a live demo proves something a recording cannot.
The first sixty seconds, what your browser receives
You land on mk0r.com. No form. No email field. The four steps below happen before you even pick a prompt.
- 1
Anonymous identity
signInAnonymously(auth) mints a Firebase UID. crypto.randomUUID() writes a session key to localStorage.
- 2
Pool prewarm
POST /api/vm/prewarm fires from the home page. topupPool() in src/core/vm.ts has a sandbox warming for you.
- 3
SSE channel open
Your textarea is wired to /api/chat. It will speak the ChatStreamEvent protocol the moment you press send.
- 4
First event arrives
session event with vmId, host, previewUrl, screencastUrl, vncUrl, and inputUrl all in one payload.
That is the part you cannot see in any recorded demo, because it has to happen in your tab against your IP, against your localStorage. The fact that nothing visible happened on screen during step one is the point. By the time you finish reading the prompt placeholder, the work is done.
The exact event union, copied verbatim
Here is the deception. Most demos abstract the model's behavior into a generic three step animation: typing, generating, done. Every model call gets the same three states. mk0r does not abstract. The TypeScript union below is the literal contract for what the server sends back. Each event you see in your devtools network tab is one of these.
What a no-signup demo surfaces
type DemoState =
| "idle"
| "loading"
| "generating"
| "done"
| "error";
// All you ever see, regardless of model behavior.The right column is what gets serialized into the SSE response of POST /api/chat. The left column is what most no-signup demos render no matter what the model is doing under the hood. The difference is not aesthetic. The right column is verifiable.
One real turn, in the order the events arrive
Below is the literal sequence a visitor without an account sees in devtools after sending the prompt "make a budget tracker with shared expenses". Times are approximate, types are exact.
Eighteen events for one turn, on the first message, with no account, no card, and no captcha. The screen on the right side of the page rerenders on every one. The thinking_delta events stack into a reasoning panel. The plan rewrites the sidebar. The tool_call lines render as collapsible cards with the args inline. The version event drops a new row in the version history. The rate_limit event nudges a small dial in the top corner.
The boot phase, made visible
Before the first thinking_delta even arrives, the visitor sees four boot_progress steps tick through in the preview panel. The labels live in src/components/boot-progress.tsx. They are the only progress UI we render, and they each carry a real durationMs field.
boot_progress steps a stranger watches
session_check
Each step is one concrete operation against one concrete service. None of them are fake or padded. If acp_init takes 420 ms, that is what the durationMs field will say. Most demos pad fake steps to disguise cold starts. We do not, because if a step is slow we want the visitor to know which one, so we can fix it.
What every other demo hides that an anonymous mk0r visitor gets
Visible without an email
- thinking_delta: the model's full reasoning trace, line by line
- plan: the structured task list the model commits to before editing
- tool_call_start with raw args: file paths, shell commands, browser URLs
- tool_call_update transitions: pending, in_progress, completed, failed
- version events: the seven char git SHA after every successful turn
- api_retry: the actual HTTP status and attempt count when the model errors
- rate_limit utilization: a number from 0.0 to 1.0, live, while you type
- credit_exhausted: the resetsAt unix timestamp if the shared key is depleted
- compact_boundary: the trigger and pre-token count when context is compacted
- tool_progress: elapsed seconds for any long-running tool that opts in
- session_recovering: the literal recovery message if the sandbox paused
The browser, the agent, and the VNC stream
One event family deserves its own line. When a tool_call_start arrives with a toolName starting with browser_ (or mcp__playwright__browser_), the handler at src/lib/acp-chat-store.ts:483 flips the preview tab to a live VNC stream of the in-sandbox Chromium. You watch the agent click. You watch it type. You watch it screenshot the page it just generated. The wss URL in the session event's vncUrl field is the stream the websockify-on-port-5901 forwarder serves out of the sandbox.
The product is the demo, the demo is the product.
We do not ship a separate "demo build" with extra logging. There is one server, one chat route, one event stream. A signed-in user with a paid subscription gets the same twenty two event types. The only difference is that their tokens come from their own Claude OAuth and their projects link to a permanent UID. The shape of the stream does not change.
That has a cost. Each anonymous turn burns shared key tokens, sandbox compute, and per-session Neon, GitHub, and Resend provisioning. We pay for it, because a live demo with the model's actual reasoning is the only thing that makes the rest of the site believable.
Why a recorded demo is the wrong default
A recorded demo answers one question: what does the output look like. A live event stream answers four more. Did the model think before it acted, and what did it think. Did it commit to a plan, and did it follow that plan. Which tools did it call, and on which files. Did it fail mid stream, and how did it recover. Those four are the questions a buyer who builds with AI for a living actually wants answered.
The cost of asking those questions, on a recording, is to download the binary, install the tool, sign up, and watch the model live. By that point the buyer has already invested fifteen minutes. mk0r compresses that to zero. The first paint of the home page is a working agent on a real sandbox, with the full event union streaming the moment you send a prompt. The demo answers all four questions before you spend an email.
Want to walk through one anonymous turn live?
Twenty minutes, no slides. Open mk0r.com, send a prompt, and we will narrate every event the stream emits while it happens, with the source file open next to it.
The home page is open. The textarea is empty. The sandbox is already warm. The first event arrives the moment you press send.
Send a prompt without signing upFrequently asked questions
Where does the live event stream actually come from?
It is a single SSE response from POST /api/chat. The server defines the event union as ChatStreamEvent in src/lib/chat-events.ts. The browser handler that consumes it is in src/lib/acp-chat-store.ts starting at line 454, where each event type is turned into UI state. The events are not sampled or polled. Each one is written to the response as it happens inside the sandbox, and the browser renders it on the next animation frame.
How many distinct event types does an anonymous visitor receive?
Twenty two. Counting the variants of the ChatStreamEvent union in src/lib/chat-events.ts: session, boot_progress, text_delta, thinking_delta, tool_call_start, tool_call_update, plan, available_commands_update, current_mode_update, done, version, session_recovering, credit_exhausted, rate_limit, api_retry, auth_required, compact_boundary, task_notification, tool_progress, error, plus the three sub-shapes of tool_call_update (pending, in_progress, completed) which the UI treats as transitions in their own right. Every one of these shows up in your browser without you signing in.
Do I really see the model's chain of thought, or is that paraphrased?
Real thinking_delta events. The handler at src/lib/acp-chat-store.ts:465 appends each delta into a part with type reasoning, which renders as a separate panel above the assistant message. The deltas come straight from the ACP bridge (acp-bridge.js on port 3002 inside the sandbox) which forwards them from Claude's extended thinking output. We do not paraphrase, summarize, or filter. If the model hesitated, you see the hesitation.
Why expose tool calls in raw form? Most demos hide them.
Two reasons. First, hiding them defeats the purpose of a demo: a recorded video proves nothing about what runs at runtime, while a live tool call with arguments and a status transition is checkable. Second, our tools are not toys. When you watch a tool_call_start with toolName 'browser_navigate' and a status update to in_progress and then completed, that is the agent driving real Chromium on port 9222 of an E2B sandbox. There is nothing to hide.
What does the plan event actually contain?
An array of entries with content, status, and priority. The handler at src/lib/acp-chat-store.ts:525 sets it directly into UI state, so when the model updates its plan mid turn, your sidebar updates within a frame. Most demos give you a fixed progress bar with three blank steps. The plan event is the model's own, structured by the model, on the fly, in your browser, with no auth on the request.
Do I see the git commits the agent makes?
Yes. The version event carries the seven character SHA and the commit message. The handler at src/lib/acp-chat-store.ts:560 forwards it to the version history panel, which gets a new row per turn. Each row is a real commit in a private GitHub repo provisioned for your anonymous session. You can click into any prior version and the preview rewinds to that SHA. None of that path requires a signup.
What if the model errors mid stream?
You see api_retry events. src/lib/acp-chat-store.ts:626 captures attempt, maxRetries, retryDelayMs, errorType, and httpStatus. We do not swallow the failure and replay a happy path. If Claude returned a 529 overload, the stream surfaces the retry attempt and the backoff. If credits are exhausted, a credit_exhausted event arrives with the resetsAt timestamp. The same mechanism rate_limit uses utilization to render a live percent. The demo includes the failure modes the demo would normally hide.
Is this just a marketing demo, or is it the actual product?
It is the actual product. There is no demo build that differs from the production build. When you arrive at mk0r.com without an email, you do not click a 'Try the Demo' button. The textarea on the home page is the input. The agent on the right is the agent. The events streaming back are the events every paying user gets. The only difference for a signed in user is that their projects are linked to a permanent UID and they can publish to a custom subdomain.