Docs

Deploy an app

From code on your machine to an address that answers: import, detection, the check, the build — and the machine-readable result your coding agent reads back.

Agent tools

  • ingest
  • detect
  • setFramework
  • deploy
  • status
  • listDeployments
  • rollback

One delivery, end to end

The dashboard and your coding agent drive the same work: check the code, build it, and publish only a build that passed a health check.

  1. Import

    A folder or ZIP upload, a GitHub import, or ingest from your agent; an update replaces the same project's source and keeps its settings.

  2. Detect

    Deterministic — no model call, no network — returning a preset (the list below) or unknown, for you to confirm.

  3. Check

    Fail-closed rules over the exact snapshot about to be uploaded: a committed provider key (a Supabase service_role JWT), a credential form posting off-origin, or seed-phrase collection.

  4. Build and publish

    The snapshot goes to the provider. A READY build must then answer an anonymous HTTP request before the address moves, and a failure leaves the previous version serving.

What detection returns

Detection reads the app’s root — no model call, no network — and answers with one of these presets, or unknown for you to choose.

Web
Next.jsnextjsViteviteAstroastroSvelteKitsveltekitNuxtnuxtjsRemixremixGatsbygatsbyStatic HTMLstatic
Python server
FastAPIfastapiFlaskflaskDjangodjangoPythonpython
Go server
Gogo
JavaScript server
ExpressexpressHonohonoNestJSnestjsNode.js servernode
Container
Dockerfile (container)container

static skips the build only for a genuinely prebuilt site: an upload that declares a build step is refused with the preset choices rather than published unbuilt. A JavaScript server preset builds one entrypoint file the provider finds by name (app, index or server at the app root or under src/; server alone for the plain Node.js server preset, and the src/main.ts bootstrap for NestJS), and a Python or Go preset builds from the app's own manifest. A container app ships its own container file at its root, which the provider builds into an image.

The result your agent reads

deploy waits, bounded (about twenty seconds), for a settled answer; status continues from there. The answer is machine-readable:

tofu · deployexample · excerpt
{
  "status": "ready",
  "managedUrl": "https://franks-kitchen-3f9a1c07b2e4.trytofu.app/",
  "serving": true,
  "publiclyReachable": true,
  "verification": {
    "access": "public",
    "httpStatus": 200
  }
}
publiclyReachable is true only when an anonymous request was served — the answer to “can a visitor open it now?”
Result fieldWhat it tells you
publiclyReachabletrue only when an anonymous request was served.
verification.accesspublic (served), protection (refused anonymously: access protection, not a failed build) or unreachable (retried once).
servingWhether this deployment answers the app's address; ready with serving: false was not published.

The address to report is managedUrl; a row with no verdict carries a note, never a failed deployment, and an agent must not open that address in a browser to confirm a build. Detection and the check are rules; the build and network are the provider's.

Automatic updates from GitHub

A project imported from a GitHub branch can follow it. With automatic updates on — a paid capability, switched on in the dashboard's Code Source — a push to that branch is deployed through the same check and build as an upload.

  • One branch and one app root per project; branch imports only, with no tags or pull-request previews.
  • Tofu deploys the latest head of the branch it observes, not every commit.
  • A manual upload, a manual GitHub import, an applied repair or a changed preset pauses updates instead of being silently overwritten; turning them on again starts from the next push.
  • Tofu stores no GitHub token: each run uses a short-lived token for that one repository with read access only, and checks your access to it again before downloading and before submitting.
  • Your agent reads where a project stands with githubStatus and can turn updates off with githubSyncDisable; importing and turning them on use your own GitHub session in the dashboard.

History, logs and rollback

History and logs. The dashboard, listDeployments, getBuildLogs and getRuntimeLogs read the hosting provider's own records for the app. Lines that look like credentials are masked — a conservative filter, so an app should still never print its secrets.

Rollback. rollback, the dashboard's Restore, moves the app's address back to an earlier deployment the provider still reports healthy. It builds nothing new, and a target that is no longer healthy is refused with the reason.

Repair. When a build fails, built-in repair diagnoses it and proposes a bounded fix for you to review and apply; nothing reaches your code until you apply it. repairContext hands your agent the same read-only brief.

Quotas

Quotas are enforced before the work. Free is one prebuilt static site — a root index.html, up to 10 MB and 500 files — with 20 uploads and 20 deployments a day; a paid plan publishes up to 5 apps with 50 uploads and 50 deployments a day.

A platform-wide daily bucket sits behind both, shared by every account: a request refused by it is refused by that shared limit, not by your own plan. hostingStatus reads the allowances actually enforced for your account, before a paid call is refused rather than after.

Limits

What a deploy does not do

  • Nothing resident. A background worker, a scheduler of the app's own or a non-HTTP service has no path here, and an app's own vercel.json crons entry does not register. The whole boundary, and the way forward, is in Limits.

  • No guessing a monorepo. Tofu does not pick a workspace subdirectory for you: upload the app’s own root.

  • No preview deployments. Automatic updates follow one branch; a pull request gets no deployment of its own.

  • Container runtime logs. A container app’s runtime logs have not been readable so far; the read says it is unavailable rather than showing an empty log.

Early access · every preview is labelled in your dashboard.