Architecture boundary

Vibes and Pulses

This is the core architecture split for Vibecodr: editable source lives in the capsule, published Vibes handle interactive client runtime on VXBE hosts, and Pulses own trusted backend operations on same-origin /api routes.

Authoritative client-versus-edge execution model for Vibecodr.

Implementation focus

Use this boundary first. It prevents accidental secret exposure and keeps user-facing surfaces fast and remixable.

Expected outcomes

Understanding Vibes & Pulses

A capsule can hold browser code, backend code, or both. Vibecodr keeps those jobs separate on purpose: the interface runs in the browser, trusted backend work runs on the edge, and the two talk over normal HTTP.

For the full browser-runtime contract, including WebAssembly, workers, import shape, and source-only projects, read What Can Be a Vibe?.

Pulse deployment rule: all serverless compute that should run as backend code belongs in src/server/ (preferred) or server/. Those files are deployed as Pulse endpoints and exposed as same-origin /api/* routes. In other words, src/server/ is the folder you author in, but callers still hit /api/*, not /server/*.

How Pulse State helps Pulses coordinate work

A Pulse is the backend function you write. Pulse State gives that function a small, platform-managed memory for operation keys, so repeated calls can agree on what is new, already running, completed, failed, or safe to retry.

A Pulse runs your backend code

Use a Pulse for server-side routes, webhooks, schedules, provider calls, secrets, connected accounts, and other trusted backend behavior.

Pulse State coordinates repeated calls

Use Pulse State when retries, duplicate deliveries, manual lifecycles, or guarded side effects need to share one stable operation key across calls.

How runOnce() chooses the operation

runOnce() protects one trusted operation key inside one named Pulse State resource. The key, not the browser tab or a single invocation, defines “same work.” Choose a stable key from the thing being protected, such as a verified Stripe event id, scheduled window key, normalized runtime/social event identity, or an idempotency token from a form submission.

Same key means same coordinated operation

const event = await env.webhooks.verify("stripe", {
  secret: "STRIPE_WEBHOOK_SECRET",
});
const key = `stripe:event:${event.id}`;

return env.state.webhookDedupe.runOnce(key, async () => {
  await env.fetch("https://api.example.com/fulfill", {
    method: "POST",
    auth: env.secrets.bearer("PROVIDER_API_KEY"),
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ eventId: event.id }),
  });

  return { accepted: true };
});

Vibes

Client-side code

Files: index.html, src/*.tsx, styles.css

Pulses

Managed backend endpoints

Files: src/server/*.ts (preferred), server/*.ts (also supported)

If you need advanced SQL compatibility, env.db is available inside pulse or combo handlers on eligible plans. Treat it as an advanced surface rather than the default Pulse backend model, and inspect that data from Operating Center.

Node.js compatibility in Pulses

Pulses are built to feel familiar if you already know modern JavaScript backend tooling. Many Node-style imports work, but they still run inside Vibecodr's managed backend environment with the usual safety boundaries around storage, secrets, and network access.

Virtual filesystem (node:fs)

node:fs works through a temporary virtual filesystem. It is useful for scratch files and libraries that expect fs, but it is not long-term storage. Use a durable backend surface or connected service when the data needs to last.

HTTP clients (node:http / node:https)

Node's http and https client APIs are available for compatibility, but most new code can use fetch().

Using node:fs in a Pulse (temp files)

import fs from "node:fs";

export default async function run() {
  // Virtual, ephemeral filesystem (not persistent storage).
  const file = `hello-${Date.now()}.txt`;
  fs.writeFileSync(file, "hello from a pulse");

  const text = fs.readFileSync(file, "utf8");
  return { file, readBack: text };
}

Note: this is for compatibility and temp workflows. Don't use it as an application database or long-term storage.

When to Use Each

Use Case

Use

Why

Interactive game or demo

Vibe

Everything runs in browser, no server needed

WASM compiler, emulator, or simulation

Vibe

Compiled engine runs as browser compute from artifact assets

Call third-party APIs with keys

Pulse

Secrets must stay server-side

Store user data persistently

Pulse

Keep durable state and private integrations on the backend side

Send emails or webhooks

Pulse

Keep keys safe; talk to APIs or send webhooks

Static visualization

Vibe

No backend logic needed

Game with leaderboard

Combo

Vibe for game, Pulse for scores

Node CLI, Express server, or VS Code extension

Adapt

Needs adaptation; the browser player cannot run local/server processes as-is

Runtime Isolation

Published vibes run in an isolated runtime frame, separate from the main Vibecodr app. That split keeps creator-authored code away from Vibecodr cookies and local storage.

Published runtime

The player loads the published artifact in a dedicated runtime frame. Viewers use the player, embed, or vanity URL; they do not need the internal launch URL.

Studio and Composer previews

Previews use the same isolation model where possible. If a preview cannot use the normal runtime host, Vibecodr falls back to a stricter browser sandbox.

Runtime isolation is a browser boundary, not a replacement for app-level authorization in Pulses.

Runtime bridge (vibecodrBridge)

Vibes run inside a sandboxed iframe. The runtime bridge is the public browser API for readiness signals, safe logs, lightweight stats, and host-mediated capability requests.

Bridge usage

async function boot() {
  const bridge = window.vibecodrBridge;

  bridge.ready({ capabilities: { popups: true } });
  bridge.log("info", "vibe ready");
  bridge.stats({ fps: 60 });

  const result = await bridge.runPulse("pls_abc123", { city: "SF" });
  console.log(result.status, result.body);

  const allowed = await bridge.requestCapability(
    ["clipboard-write"],
    "Copy result to clipboard",
    { type: "clipboard-write", text: "Hello" }
  );

  if (allowed) {
    bridge.log("info", "Clipboard write approved");
  }
}

Runtime capability gating

Capabilities are host-mediated. Some map to iframe permissions (camera, mic, location); action-based capabilities use requestCapability().

Capability names

Action types

Action requests include a payload (URL, filename, or clipboard text) and may be approved or denied by the viewer.

requestCapability example

const allowed = await window.vibecodrBridge.requestCapability(
  ["downloads"],
  "Export report CSV",
  { type: "download", url: "/exports/report.csv", fileName: "report.csv" }
);

if (allowed) {
  console.log("Download approved");
}

Vanity URLs (vxbe.space)

Vanity subdomains serve published vibes at https://{slug}.vxbe.space. Direct-serve is available for client-only runners (client-static, webcontainer).

Plan

Max Vanity Subdomains

Free

50

Creator

100

Pro

250

Automation Trigger Kinds

Automations start from a moment that wakes a Pulse:

Emit runtime events

Use Vibecodr's event and trigger controls to grant the vibe permission to emit events, then filter those events to the smallest set your automation needs. Product controls keep event grants scoped to the right public work instead of asking creators to hand-build platform requests.

Event types are lowercased and must match [a-z0-9._-] (max 64 chars). internal.* namespaces are rejected.

Inbound webhook signature options

These automation-event receiver formats are separate from Pulse provider helpers: Pulse ships Stripe as the first certified helper, while other signed webhooks use env.secrets.verifyHmac(...) until fixture-backed helpers exist.

Automation Action Types

When a trigger fires, it can run one supported action:

Action examples

Webhook: send a POST to a URL you control.
Discord webhook: send a templated message to a Discord webhook.
Pulse: wake one of your Pulse handlers with a moment payload and Pulse State memory hint.

Configure actions from the Pulse Operating Center or Studio automation UI. Use the product UI so auth, plan checks, secret handling, and trigger ownership stay on the supported path.

Cron Scheduling Rules

Cron uses 5 fields: minute hour day month weekday.

Plan limits for Pulses

Each plan sets a clear ceiling for how many Pulses you can keep deployed and how much backend traffic they can handle each month. Monthly runtime headroom also scales by plan; Vibecodr shows that as in-product usage guidance instead of publishing CPU-hour promises that would make the platform harder to tune safely.

Plan

Max pulses

Runs / month

Max runtime

Secrets

Advanced SQL

Free

3

1,500

5s

No

Creator

15

150,000

15s

Yes

No

Pro

50

1,000,000

30s

Yes

Calling Pulses from Your UI

Pulses are normal HTTP endpoints. In a combo app, call the Pulse route your app exposes and keep the request body small, explicit, and shaped around the action the viewer is taking.

Call a Pulse from React

async function callWeatherPulse() {
  const res = await fetch("/api/weather", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ city: "San Francisco" }),
  });

  if (!res.ok) {
    throw new Error(`Pulse failed with status ${res.status}`);
  }

  const data = await res.json();
  console.log(data); // e.g. { temp: 68, conditions: "sunny" }
}

Pulse Handler

// Pulse code (server-side). Default export is your handler.
// Enhanced style: (input, env) => result
export default async function handler(input, env) {
  const { city } = input;

  // Call weather API with a policy-bound secret capability
  const response = await env.fetch(`https://api.weather.com/v1/forecast?city=${city}`, {
    auth: env.secrets.query("WEATHER_API_KEY", "apikey"),
  });

  const forecast = await response.json();
  return { city, forecast };
}

Deployed pulse source stays owner-only, but the HTTP endpoint is public by default. Add signed requests or owner-authored checks only when the pulse's own behavior should be restricted.

Auth planes in plain English

For external systems, prefer a webhook trigger or a product-provided caller token flow instead of copying platform headers. Keep your own Authorization header for the app or partner protocol your Pulse code validates.

Client and trusted execution

Vibes run in the browser as sandboxed client experiences. Pulses run as trusted backend endpoints on the platform-managed edge. The distinction protects creators and viewers by keeping credentials and privileged operations away from client-visible code.

A Combo is a project that uses both sides: the vibe owns interactive UI while Pulses own backend work such as secrets, schedules, webhooks, storage, or third-party APIs.

  • Use vibes for UI, graphics, interaction, and public browser behavior.
  • Use Pulses for secrets, OAuth/token work, provider calls, schedules, and durable side effects.
  • Do not put private API keys, platform grants, or owner-only operational metadata in browser code.

Same-origin backend shape

Pulse endpoints are called through same-origin /api routes so the browser app can talk to backend behavior without hardcoding a separate service host. That does not make the endpoint private by itself.

If endpoint behavior should be restricted, implement request validation and authorization in the Pulse handler.

  • Public endpoint availability is separate from private source visibility.
  • The platform mediates secrets and grants; the application still owns its business rules.
  • Keep response bodies viewer-safe.

How Pulse State helps Pulses coordinate work

A Pulse is creator-authored backend code. Pulse State gives that code a small, platform-managed memory for operation keys, so repeated calls can agree on what is new, already running, completed, failed, or safe to retry.

runOnce() coordinates one named Pulse State resource plus one trusted key inside the Pulse owner/deployment scope. The key defines the operation being protected, so stable keys such as verified webhook event ids, scheduled window keys, or form idempotency tokens are the best fit.

  • Same Pulse, same state resource, same key: coordinated as the same operation.
  • Same Pulse, same state resource, different key: a new operation.
  • Different Pulse or different owner: a separate coordination scope.
  • Use Pulse State for operation lifecycle. Use a real data surface or connected service for application records that need querying, editing, or long-term storage.
Same key means same coordinated operation typescript
const event = await env.webhooks.verify("stripe", {
  secret: "STRIPE_WEBHOOK_SECRET",
});
const key = `stripe:event:${event.id}`;

return env.state.webhookDedupe.runOnce(key, async () => {
  await env.fetch("https://api.example.com/fulfill", {
    method: "POST",
    auth: env.secrets.bearer("PROVIDER_API_KEY"),
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ eventId: event.id }),
  });

  return { accepted: true };
});

Example and read next

Example: a vibe needs to call OpenAI, Stripe, or GitHub. Keep the interactive UI in the browser, put the credential-backed provider call in a Pulse, and have the vibe call a same-origin /api route.

Use these related pages when you need the next layer of guidance. They point to the most likely follow-up tasks, not every page that happens to touch the same system.

Related documentation