Skip to content

File Structure

Every Vulcan app has a set of files behind the scenes. You don't need to edit these directly — the AI handles it — but it's useful to know what each one does.


Why this structure exists

The file structure isn't arbitrary — it's designed around two goals: fast deploys and a reliable AI.

Fast deploys. When you make a change, only the files that actually changed are sent to the server. A tweak to a button in app.js doesn't re-upload your database logic or your HTML. This makes each deploy take seconds instead of minutes. For that to work, each concern lives in its own file — so the system can detect exactly what changed.

A reliable AI. The AI always knows where things go. When you ask for a new API endpoint, it goes in handlers.ts. When you ask to change how data is stored, it goes in data.ts. When you ask for a visual change, it goes in styles.css. That consistency means the AI rarely puts code in the wrong place or breaks something it didn't intend to touch. It also means the AI can edit just one file per task instead of hunting through the whole project.

The files you can't edit (index.ts, build.js, wrangler.toml) are the plumbing that connects everything together. They're fixed so the deploy pipeline always knows how to build and ship your app, regardless of what the AI generated inside it.


The files in your app

Every app has this core structure:

src/
  api/
    handlers.ts     ← Your app's backend logic (API routes, data operations)
    data.ts         ← How your app reads and writes data (storage layer)
  frontend/
    index.html      ← The main page of your app
    app.js          ← Frontend behavior (buttons, forms, navigation)
    styles.css      ← Visual styling (colors, layout, fonts)
  index.ts          ← App entry point (don't edit this)
package.json        ← App dependencies (don't edit this)
tsconfig.json       ← TypeScript config (don't edit this)
wrangler.toml       ← Deployment config (don't edit this)
build.js            ← Build script (don't edit this)

Depending on which features your app uses, additional folders appear:

src/
  migrations/       ← KV storage only: seed data and schema migrations
    runner.ts
    index.ts
    001-seed-data.ts
  jobs/             ← Background jobs only
    cron.json
    daily-report.ts

migrations/         ← SQL (D1) only: numbered .sql schema files
  0001_create_tables.sql
  0002_seed_data.sql

Files the AI edits for you

When you ask for changes, the AI typically edits:

  • handlers.ts — Adding or changing API behavior (e.g., a new endpoint to submit a form)
  • data.ts — Changing how data is stored or retrieved
  • index.html — Adding new sections, forms, or layout changes
  • app.js — Adding interactivity, fixing navigation, updating how data is displayed
  • styles.css — Visual changes, themes, colors, spacing

Multi-page apps

Vulcan apps are single-page applications — there's one index.html and all navigation happens inside it via JavaScript routing in app.js. This is a deliberate constraint, not a limitation you can work around.

What this means in practice:

  • You can have multiple views, screens, or sections — the AI builds client-side routes that swap content in and out when the user navigates
  • You cannot have separate .html files for separate pages — all content flows through index.html
  • Browser back/forward buttons work if the AI implements URL-based routing (which it usually does)
  • Sharing a deep link to a specific view may or may not work depending on how the AI structured the router

If you need multiple distinct pages, just ask:

"Add a separate page for settings" "I need a dashboard view and a detail view — make them feel like separate pages"

The AI will build the routing. Just don't expect it to create multiple HTML files — that's not how the platform works.


npm packages

Backend (TypeScript files): Yes — you can use npm packages in handlers.ts, data.ts, and any other backend file. The AI manages package.json for you. Just ask:

"Add a library to format dates nicely" "Use the AWS SDK to send an SQS message"

The package gets added to package.json and imported where needed. Everything is bundled by esbuild at deploy time.

Build time note: A set of commonly-used packages are pre-installed in the build environment and resolve instantly:

CategoryPackages
Workers frameworkhono, zod, drizzle-orm
AWS SDK@aws-sdk/client-lambda, client-dynamodb, client-s3, client-sqs, client-sns, client-eventbridge, client-cloudwatch, client-sts, aws4fetch

Any package not on this list triggers an npm install during the build, which adds time to that deploy — typically 15–60 seconds depending on the package size. Subsequent deploys are fast again because the installed packages are cached. If you're adding a large package for the first time, just expect the next deploy to take a little longer than usual.

Frontend (JavaScript files): No npm packages — frontend files are served directly to the browser with no bundler, so import from npm doesn't work there. Frontend code is vanilla JavaScript only. If you need a third-party library in the browser (e.g. a chart library, a date picker, or a map), just ask and the AI will load it from a CDN via a <script> tag in index.html — no installation needed.


Backend files and splitting handlers

Backend code can live in multiple TypeScript files — esbuild bundles everything starting from src/index.ts and follows all imports automatically. So handlers.ts can import from any other .ts file you add:

src/api/
  handlers.ts       ← must export handleAPI (required)
  data.ts           ← storage helpers
  email.ts          ← email sending logic (example)
  utils.ts          ← shared helpers (example)
src/services/
  stripe.ts         ← third-party integrations (example)

The only hard constraint: src/api/handlers.ts must always export a handleAPI function — that's the entry point src/index.ts calls for every API request. Everything else can be split however makes sense for the app.

Ask the AI to split out logic when a file gets large:

"Move the email logic into its own file" "Split the data helpers into separate files by resource type"


The CSS design system

Every Vulcan app's styles.css starts with a /* Vulcan design system */ block that defines a set of CSS variables:

css
:root {
  /* Backgrounds */
  --bg                 /* deepest background */
  --surface            /* card / panel background */
  --surface-elevated   /* slightly lighter surface */
  --surface-hover      /* hover state */

  /* Borders */
  --border             /* default border */
  --border-subtle      /* faint divider */

  /* Text */
  --text               /* primary text */
  --text-secondary     /* labels, secondary content */
  --text-muted         /* placeholders, disabled */

  /* Accent — changes per app based on the selected theme */
  --accent             /* primary action color */
  --accent-light       /* hover / lighter variant */
  --accent-glow        /* subtle glow shadow */
  --accent-glow-strong /* stronger glow shadow */

  /* Status */
  --success            /* green */
  --error              /* red */
  --warning            /* amber */

  /* Typography */
  --font-sans          /* Geist, system fallback */
  --font-mono          /* Geist Mono, monospace fallback */

  /* Shape & motion */
  --radius-sm / --radius-md / --radius-lg
  --transition
}

The AI uses these variables throughout your app — background: var(--surface), color: var(--text), border: 1px solid var(--border), etc. — so that everything stays visually consistent.

Themes — when your app is created, a theme is selected (coral, blue, emerald, violet, amber, cyan, or rose) that sets the four --accent values. The rest of the palette is fixed across all apps.

Important: the :root { } block at the top of styles.css is what defines all these variables. If it ever gets removed, every var(--...) reference breaks and the app will look unstyled. If your app's colors look wrong, ask the AI:

"The styles look broken — add back the missing :root { } CSS variable definitions block at the top of styles.css"


Frontend files and multiple JS files

Frontend files live in src/frontend/ and are served directly to the browser. All files in that folder are automatically bundled — so the AI can add additional JS or CSS files when your app grows large enough to warrant splitting (e.g. charts.js, utils.js).

Why plain JavaScript, not TypeScript?

The backend code (handlers.ts, data.ts, etc.) is TypeScript compiled by esbuild before deployment. Frontend files are served as-is directly to the browser, which can't run TypeScript natively — so frontend code stays in plain JavaScript.

The entrypoint app.js must always exist and be referenced by index.html. Additional .js files in src/frontend/ are included automatically — just ask the AI to split out a component.


Files you shouldn't touch

These files are managed by Vulcan's infrastructure. Editing them manually can break your app:

  • src/index.ts — The entry point that wires everything together
  • package.json — Dependency list (the AI manages this when needed)
  • tsconfig.json — TypeScript settings
  • wrangler.toml — Deployment configuration
  • build.js — How your app gets bundled and deployed

If something seems wrong with these files, ask the AI to fix it rather than editing manually.


Migration files

KV apps — If your app uses KV storage, you'll see a src/migrations/ folder with numbered TypeScript files like 001-seed-data.ts. These seed your app with example data on first load. Each migration runs exactly once — the system tracks which ones have already run in a migrations:applied KV key. Don't edit existing migration files; to add or change data, ask the AI to create a new numbered migration.

SQL apps — If your app uses a SQL database (D1), you'll see a migrations/ folder with numbered .sql files like 0001_create_tables.sql. These set up your database schema and seed data.

In both cases, the AI manages these files for you — don't delete, rename, or reorder them.


Viewing and editing files

You can see all your project files in the file panel inside the App Builder. Click any file to view its contents.

Direct editing is available — you can type into any file directly. When you do, the AI will see your changes on the next turn (it reads all files before responding). This is useful for small precise fixes — correcting a label, adjusting a constant, fixing a specific line — where describing the change in chat would take longer than just making it.

That said, direct edits carry a few risks:

  • The AI doesn't know you made a manual change — if it edits the same file next turn without reading it first, it could overwrite your work. To be safe, tell the AI: "I just manually edited handlers.ts — please read it before making any changes."
  • Changes made in the file panel don't trigger a deploy. You need to send a chat message (or ask the AI to deploy) to see your changes in the preview.
  • There's no undo in the file panel — save a version first if you're making significant manual edits.

Built by the Veho Developer Platform team