Documentation

Running software on Videan

Videan is a cloud for small software. You push code, it comes back as a live URL with TLS, sign-in enforced at the edge, and — if you asked — a Postgres database, in about a minute. This page is the whole platform: how deploys work, what your app must do, and every command, method and endpoint.

Introduction

Videan exists for the software that never justified a platform team: the internal dashboard, the client portal, the API one team needs. Traditionally that meant wiring hosting, a database, version history and access control out of three or four products. Here it is one deploy — through the CLI, the TypeScript SDK, or a plain HTTP API your agent can call.

Three ideas carry everything else:

  • Every push is an immutable snapshot. The deploy history is the version history — inspect any version, diff between them, restore one. No git required.
  • Production is a pointer. Deploying moves the pointer to a new snapshot; rolling back moves it to an old one. Neither rebuilds anything, and the current version keeps serving until its replacement is up.
  • Auth lives at the edge, not in your app. Visitors sign in once at the platform; your code receives their identity as request headers and never renders a login page.

Each app is isolated: its own runtime (a container or a serverless function), its own database schema, its own secrets scope. Apps cannot see each other.

Quickstart

You need an account (sign up) and Node 18+. videan login opens your browser to sign in — password, Google or SSO, whatever your account uses — and stores a CLI key. In scripts, skip login and set VIDEAN_API_KEY (keys start with vdn_; create one in the dashboard).

terminal
$ npm install -g @videan/cli
$ videan login
✓ logged in · via your browser
$ videan apps create --name notes --postgres
✓ created notes · https://softrocket.videan.xyz
$ cd my-project && videan deploy -m "first deploy"
✓ pushed 23 files · image built · postgres provisioned
● live at https://softrocket.videan.xyz

videan deploy packages the current directory (build output and node_modules are excluded automatically), pushes it, builds it, waits, and prints two URLs: the production URL and a preview URL pinned to exactly this version.

The hostname is assigned, not derived from the name. Apps get a readable generated slug (softrocket, lushlife, …). Read it from the create response rather than predicting it. Renaming an app never moves its URL.

The contract

Your app has one obligation:

Listen for HTTP on port 3000. Log to stdout. Don't daemonize. That is the entire requirement.

  • The runtime is detected from the files you push — no manifest, no YAML, no Dockerfile needed (though your own Dockerfile is honored if you ship one).
  • If the app has a managed database, DATABASE_URL arrives as an environment variable. Just read it.
  • Everything written to stdout/stderr becomes the app's searchable logs.
  • TLS, routing, health checks and identity are the platform's problem, not yours.

Runtimes

The builder inspects the pushed files and picks a runtime. Detection is automatic; there is nothing to configure.

RuntimeDetected byHow it runs
Next.jsnext in package.jsonnext build, then next start on port 3000
Nodepackage.jsonnpm install, then the start script (or node server.js)
Pythonrequirements.txt / main.pypip install, then your entry point on port 3000
Gogo.modCompiled to a static binary, run on port 3000
DockerfileA Dockerfile at the rootBuilt exactly as written — you own the contract

Your own Dockerfile wins over detection when both are possible. Custom Dockerfiles are a container-mode feature — serverless apps use generated runtimes only.

Execution modes

Every app has a mode: container or serverless. The contract is identical either way — same code, same port, same URL, same gate, same snapshots and rollback. Only the machine underneath differs, and only the bill follows from it.

serverless

Runs as a function that scales to zero: it costs nothing while nobody is visiting and scales up with traffic. Choose it when the app answers HTTP requests and nothing else — a dashboard, a JSON API, a webhook receiver, a form handler, a Next.js site. On the Free plan, new apps default to serverless.

Know these four things before picking it:

  • Cold starts. A request arriving after an idle period waits for the function to boot. Fine for internal tools; think twice when first-paint latency is the product.
  • Secrets and env vars are capped at 4 KB total, baked in at deploy time. Over the cap is a 400 that names the problem.
  • Requests are limited to 60 seconds.
  • With a managed database, concurrency is capped at 10 — each concurrent invocation opens its own Postgres connection. Plenty for an internal tool; use container for real concurrent load.

container

An always-on container — what every app ran on before serverless existed. Choose it (or just ask for it) when any of these is true:

  • The app needs cron schedules or background jobs (serverless refuses both).
  • It serves WebSockets or any other connection upgrade.
  • It ships its own Dockerfile.
  • A single request can take longer than 60 seconds.
  • It keeps state in process between requests — an in-memory cache, a warm ML model, a queue it drains itself.

Switching is one PATCH /v1/apps/:id with mode (or videan apps set --mode). It applies on the next deploy, start or restart, and is refused while the app has cron schedules or jobs configured.

An always-on container is a metered machine. Even a small one runs to roughly $16/month of usage — several times the Free plan's $3 monthly allowance. On Free, keep HTTP-only apps serverless; a container there is a deliberate, metered choice.

Deployments & versions

The pipeline has three stages, each observable:

  1. Push. Your files become an immutable snapshot. Always pass a message (videan deploy -m "…") — it is this platform's commit message, and versions without one are unreadable to the humans reviewing them later.
  2. Build. The snapshot becomes a container image. Status: building. Build failures surface their logs at GET /v1/deployments/:id/build-logs.
  3. Deploy. The image launches, passes health checks, and is promoted. Status: deploying → running. The version it replaces keeps serving until the new one is up, then retires — deploys are zero-downtime by construction.

A deployment that ends failed never served traffic. One that ends crashed started, then exited — read the runtime logs. Either way the fix is the same: fix the code and push again. Pushes are cheap and versioned; there is no state to clean up.

videan pull [snapshot] restores any version's files to disk — useful when the agent that wrote version 12 is gone and a human needs the source.

Previews & rollback

A preview per deploy

Every deployment gets its own URL — d-<label>.videan.xyz — pointing at exactly that version's instances. Verify the new version on its preview URL while the old one serves production. Preview URLs die when their version is retired.

Rollback is a pointer switch

Production is a pointer to a snapshot. videan rollback (or POST /v1/apps/:id/rollback) re-launches the image a previous version was built from — no rebuild, no surprises, and the current version serves until the old one is healthy. Name a specific deployment to roll to it; omit it for the previous one.

Images are kept for the app's most recent 100 builds. Rolling back to something older fails loudly rather than serving the wrong code — push the old files again instead (videan pull gets them back).

Access & identity

Every app has a visibility, enforced at the platform edge before a request ever reaches your code:

TierWho gets in
privateOrg admins and people the app is explicitly shared with
org defaultMembers of the app's organization, plus shares
authenticatedAnyone signed in to Videan, plus shares
publicThe internet — no gate, no sign-in

Each tier is a strict superset of the one above it, and a share admits its holder at every tier. Shares are per person, as viewer or editor — give a client view access and a contractor edit access on day one, no accounts to provision.

Organizations can also wire up SAML SSO (Okta, Entra, Shibboleth, …) in org settings: claim your email domains and members sign in through your identity provider — first-time sign-ins join the org automatically as members. Password, Google and SSO accounts are interchangeable everywhere, the gate and the CLI included.

Gated apps: do not build a login page

Behind the gate, every request arrives with the visitor's identity injected as headers:

what your app receives
X-User-Id: usr_…
X-User-Email: dana@example.com
X-User-Roles: member

If the app wants its own user records (roles, profiles), find-or-create a local user from X-User-Email and start your session from that — and never render a login form when the header is present. The visitor already signed in once, at the edge.

Public apps and webhooks

A public app gets no gate and no headers — bring your own auth or none. External services (Stripe or GitHub webhooks, say) cannot pass the gate, so an app that must receive them needs visibility: public and its own shared-secret check on the receiving route.

Managed Postgres

One flag — --postgres on create, postgres_enabled: true over the API — provisions a real PostgreSQL database for the app:

  • Isolated per app: its own schema and its own database role. An app can only ever see its own tables.
  • Injected, not configured: DATABASE_URL arrives as a secret environment variable. TLS is preconfigured in the runtime images.
  • Stable: the credentials survive every deploy. Backups run through the platform and are restorable from the dashboard.

The SQL console

The dashboard's Database tab (and POST /v1/apps/:id/sql) runs SQL against the app's database as the app's own role — read-only by default, one statement at a time. Handy for a quick look at production data without wiring a client.

Secrets

Secrets are encrypted at rest, scoped to one app, and injected as environment variables. Keys must be UPPERCASE_WITH_UNDERSCORES.

terminal
$ videan secrets set STRIPE_KEY=sk_live_…
$ videan secrets list
$ videan secrets remove STRIPE_KEY

A secret change applies on the app's next deploy, start or restart — not immediately. Serverless apps additionally live under a 4 KB total cap on secrets plus env vars, enforced with a clear 400 at deploy time.

Object storage

--storage gives the app a private object-storage prefix. The app (and you) read and write through presigned URLs — ask the API for a URL, then PUT/GET the bytes directly, so large files never squeeze through the platform.

terminal
$ videan storage put ./report.pdf reports/q3.pdf
$ videan storage ls reports/
$ videan storage url reports/q3.pdf # a time-limited download link

Quota is per plan (see Plans & usage); stored bytes meter as storage_mb.

Cron & background jobs

Schedules and queues don't get a separate runtime — they call your app's own HTTP routes. Same code, same secrets, same database, and testable with curl before you schedule anything.

  • Cron: a schedule plus a path. At each tick the platform requests that path on your app directly (not through the public gate).
  • Jobs: enqueue a payload against a path (videan jobs enqueue); the platform delivers it, records the outcome, and retries failures. videan jobs list / get / retry / cancel manage the queue.

Cron and jobs are container-mode only. A serverless app refuses both with a 400, and an app with schedules or jobs configured refuses to switch to serverless until they are removed.

Custom domains

Any app can serve on your own domain (Pro plan and above). Add the domain in the app's Domains tab, create the two DNS records it shows you — one CNAME to validate the certificate, one to route traffic — and the platform provisions and renews the TLS certificate automatically. The *.videan.xyz URL keeps working alongside.

App email

--email lets the app send mail through the platform: POST /v1/apps/:id/email with a recipient, subject and body. Mail is sent from app-<slug>@ the platform's domain — the sender address is fixed and cannot be spoofed by the caller, which is what keeps every app's mail deliverable. Sends meter as emails_sent; videan email quota shows where you stand.

Logs & metrics

Logs

Everything the app writes to stdout/stderr is queryable — with a time window, a search term, and follow mode:

terminal
$ videan logs -f # follow live output
$ videan logs --since 2h -q error
$ videan logs --since 2026-09-01 --until 2026-09-02

Build logs are separate (they explain a failed deploy): GET /v1/deployments/:id/build-logs.

Metrics

The Metrics tab (and GET /v1/apps/:id/metrics) charts requests, 4xx and 5xx counts, latency (average and p95), and — for container apps — CPU and memory, measured at the platform edge so an app doesn't have to instrument itself.

Rate limiting

Per-app request caps are enforced at the edge (--rate-limit, requests per minute per client). Over the cap is a 429 with a Retry-After header.

CLI reference

npm install -g @videan/cli. Every prompt has a flag or an environment variable — the CLI refuses to prompt when stdin is not a TTY rather than hanging, so it is safe in scripts and agents.

CommandWhat it does
videan login / logoutSign in and store a CLI key. Opens your browser by default (works for password, Google and SSO accounts); --key vdn_… stores an existing key, --no-browser prompts for email and password
videan initLink the current directory to an app
videan deploy -m "msg"Push the directory, build, wait, print production + preview URLs
videan pull [snapshot]Restore a version's files to disk
videan logs [-f] [--since] [-q]Runtime logs: follow, window, search
videan rollback [-a app]Return production to the previous version
videan statusThe app's current deployment and URL
videan metricsRequests, errors and latency in the terminal
videan secrets set/list/removeManage the app's secrets
videan apps list/create/set/deleteManage apps — set changes access, size, instances, mode, rate limit, stack
videan orgs list/create/use/members/inviteOrganizations and membership
videan keys create/list/revokeAPI keys — scopable to one app, with optional expiry
videan storage ls/put/get/rm/url/usageObject storage
videan jobs enqueue/list/get/retry/cancelBackground jobs
videan email send/quotaSend app mail, check the quota
videan usage / usage plansThis month's metered usage; the plan table

Environment variables for non-interactive use: VIDEAN_API_KEY, VIDEAN_ORG_ID, VIDEAN_APP_ID, VIDEAN_API_URL.

SDK reference

npm install @videan/sdk — typed, fetch-based, no dependencies. What the dashboard does, the SDK does.

deploy.ts
import { CloudClient } from "@videan/sdk";

const cloud = new CloudClient({ apiKey });

const app = await cloud.apps.create({
  name: "notes", orgId, postgres: true, mode: "serverless"
});
// read app.slug / app.url from the response

const snap = await cloud.apps.push(app.id, files, { message: "first deploy" });
const dep = await cloud.apps.deploy(app.id, snap.id);
// poll cloud.apps.checkDeployment(dep.id) until status === "running"

Errors throw CloudError with status and code. A status of 0 means the request never got an answer (network) — retrying is reasonable, unlike a 4xx.

HTTP API

Base URL https://api.videan.xyz. Every request takes Authorization: Bearer <API key>. Any language that can speak HTTP can run the whole platform — the CLI and SDK are conveniences over exactly these endpoints.

EndpointWhat it does
POST /v1/appsCreate an app: name, org_id, optional postgres_enabled, visibility, mode, stack
POST /v1/apps/:id/pushPush files (files as strings, binary_files as base64, message) → a snapshot
POST /v1/apps/:id/deployDeploy a snapshot (snapshot_id) → a deployment
POST /v1/deployments/:id/statusPoll a deployment (this also advances the build — call it, don't just wait). When running: url, previewUrl
GET /v1/apps/:id/logsRuntime logs — ?since=15m&q=error
GET /v1/deployments/:id/build-logsWhy a build failed
POST /v1/apps/:id/rollbackPrevious version, or name a deployment
POST /v1/apps/:id/secretsSet a secret: { key, value }
POST /v1/apps/:id/sqlQuery the managed database: { query, read_only }
PATCH /v1/apps/:idChange name, visibility, mode, size, instances, rate limit, stack
GET /v1/plansThe plan table — unauthenticated, good as a health check

Metrics, storage, cron, jobs, domains, email, backups, usage and the audit log all live under /v1/apps/:id/* and /v1/orgs/:id/* with the same bearer auth. Most responses use snake_case; the SDK normalizes everything to camelCase.

Plans & usage

Plans bound spend, not activity: the limits are on the knobs that scale the bill (app count, instance count, task size). There is deliberately no limit on requests, builds or deploys — an app that gets popular should get a bill, not an outage.

FreePro — $20/moScale — $200/mo
Included usage$3/mo$20/mo$200/mo
Apps350500
Instances per app1410
Max task size0.5 vCPU / 1 GB4 vCPU / 8 GB16 vCPU / 120 GB
Object storage1 GB100 GB1 TB
Managed Postgres✓✓✓
Custom domains—✓✓

What usage costs

Usage is metered per app and counted against the plan's included allowance first. The meters: container compute (vCPU-seconds and GB-seconds), serverless compute (GB-seconds and invocations), requests, build time, object storage, managed-Postgres days, and emails sent. Bandwidth out is measured and shown but not currently charged. The dashboard's Usage page and videan usage show the running total; GET /v1/plans returns the current rates.

The Free plan is free while it sleeps. A serverless app that nobody visits meters nothing, so the $3 allowance goes a long way. An always-on container runs to ~$16/month on its own — which is why Free-plan apps default to serverless.

Troubleshooting

  1. Deployment ends failed → read the build logs. The usual causes: a missing dependency in package.json, listening on a port other than 3000, a start script that exits. Fix and push again.
  2. Status crashed → the app started, then exited. Read the runtime logs. An app that needs the database should fail fast when DATABASE_URL is missing — that turns a mystery 500 into a clear failed deploy.
  3. The URL 404s right after a deploy → you caught the retiring version during promotion; wait a few seconds. A persistent 404 → check the app's status.
  4. 429 from the API → back off; the Retry-After header says how long.
  5. curl against a gated app redirects to sign-in → that is the gate working. Verify deploys on the preview URL, or in a browser with a platform session.

Deploy failures and crashes also email the organization's admins, so the person who can fix infrastructure hears about it without watching a dashboard.

For agents

Videan is built to be called, not clicked: everything above is a plain HTTP API, keys can be scoped to a single app with an expiry, and failures come back as readable text an LLM can act on. The platform never bills you tokens — bring your own agent.

The agent-facing version of this page lives at videan.xyz/llms.txt — the same facts, phrased as operating instructions. Point your agent at it, or paste it into context. Signed-in users get a ready-made setup block (key, org, instructions) on the dashboard's Apps page.