Documentation

Build on Darwa

Darwa runs applications from your repository — web services, static sites, workers, agents, databases, and object storage — without you configuring infrastructure. This reference covers every service, the REST API, and the CLI.

API v1CLI 2.4.0Updated 3 Aug 2026Base darwa.com/api/v1

Quickstart

Install the CLI, authenticate, and deploy the repository you are standing in. Darwa detects the framework, writes the build and start commands, and returns a URL.

terminal
# 1 — install
npm i -g @darwa/cli

# 2 — authenticate (opens a browser)
darwa login

# 3 — deploy the current directory
darwa deploy

# 4 — follow the logs
darwa logs --follow
Note

darwa deploy creates the service on first run and updates it after that. Nothing is asked interactively unless detection is ambiguous.

Core concepts

ConceptMeaning
ProjectA repository and everything deployed from it. Billing and team access attach here.
ServiceOne running thing — a web service, static site, worker, or agent.
Environmentdevelopment, testing, staging, production, or a temporary preview. Same build, different values.
ReleaseAn immutable build plus its configuration. Rollbacks restore a release.
ResourceA database or storage bucket attached to a project and injected into services.

darwa.yaml reference

Detection covers most projects. Commit darwa.yaml when you want the configuration in version control, or when one repository holds several services.

darwa.yaml
services:
  - name: storefront          # web service
    type: web
    runtime: node22
    build: npm ci && npm run build
    start: npm start
    regions: [us-east, eu-central]
    scale: { min: 1, max: 8, on: cpu }

  - name: image-worker        # background worker
    type: worker
    runtime: node22
    start: node worker.js
    queue: { name: images, type: priority }
    concurrency: 20
    retries: { attempts: 5, backoff: exponential }

resources:
  - postgres: storefront-db
  - bucket: user-uploads

Web services

A web service is a process that listens for HTTP requests and stays running. It gets adarwa.app subdomain immediately, plus any custom domains you add.

Port binding

Bind to the port in PORT on host 0.0.0.0. If you bind elsewhere, Darwa detects the listening port at build time rather than failing the deploy.

server.js
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));

Environment variables

Values are set per environment and injected at runtime. Secrets are never written to build output or logs, and a value matching a stored secret is redacted wherever it appears.

terminal
darwa env set DATABASE_URL=... --env production
darwa env set LOG_LEVEL=debug     --env preview
darwa env diff staging production

Scaling rules

FieldTypeDescription
minrequiredintegerInstances kept running at all times. 0 allows scale to zero.
maxrequiredintegerHard ceiling. Never exceeded, even under a traffic spike.
onenumcpu | memory | requests | queue_depth. Defaults to cpu.
targetintegerUtilisation percentage to hold. Defaults to 70.
predictivebooleanScale ahead of recurring traffic patterns. Defaults to true on Pro.

Static websites

Framework, build command, and output directory are read from the project. Node version comes from .nvmrc, package.json, or the latest LTS.

Redirects, rewrites, and headers

darwa.json
{
  "redirects": [
    { "from": "/old-page", "to": "/new-page", "status": 301 },
    { "from": "/blog/:slug", "to": "/articles/:slug" }
  ],
  "rewrites": [
    { "from": "/api/*", "to": "https://api.acme.com/*" }
  ],
  "headers": [
    { "for": "/*", "set": { "X-Frame-Options": "DENY" } }
  ]
}

Image optimization

Reference the original path. The response is AVIF or WebP with a JPEG fallback, sized to the request, with a blur placeholder available at ?blur.

index.html
<img src="/hero.jpg" width="1600" height="900" alt="…" />
<!-- served as AVIF 188 KB instead of JPEG 4.2 MB -->

Background workers

A worker consumes jobs from a managed, Redis-compatible queue. Existing BullMQ, Celery, Sidekiq, and Asynq code connects with the injected connection string.

worker.js
import { Worker } from "bullmq";

new Worker("images", async job => {
  await resize(job.data.key);
}, { connection: { url: process.env.QUEUE_URL } });

Schedules

terminal
darwa schedule add nightly-export --cron "0 2 * * *" --overlap skip
darwa schedule add health-sweep   --every "5 minutes"
darwa schedule run nightly-export        # trigger once, now

Retries and dead letters

FieldTypeDescription
attemptsrequiredintegerTotal tries including the first. Maximum 25.
backoffenumfixed | linear | exponential. Defaults to exponential.
dead_letterbooleanMove exhausted jobs to the dead-letter queue. Defaults to true.
timeoutdurationKill and retry a job that exceeds this. Defaults to 15m.

AI agents

An agent is a long-running process with memory, triggers, tool credentials, and optional human approval steps. Runs are traced step by step with token and cost attribution.

Triggers

darwa.yaml
agent: invoice-recovery
runtime: python3.12
entry: agents/recovery.py

triggers:
  - webhook: stripe/invoice.payment_failed
  - schedule: "0 9 * * 1-5"
  - queue: billing-events

approvals:
  - when: amount > 1000
    notify: slack#finance

Memory

agents/recovery.py
from darwa import memory

profile = memory.get("customer:cus_9F21")               # long-term
memory.set("customer:cus_9F21", {"last_outcome": "recovered"})
hits = memory.search("payment failures resolved", k=5)  # semantic

Tools and secrets

Tools are granted per agent with a scope. An agent with read access to Postgres cannot write to it, and every call is recorded with its arguments and result.

terminal
darwa tools grant invoice-recovery postgres:read --tables invoices,customers
darwa tools grant invoice-recovery gmail:send   --from billing@acme.com
darwa tools deny  invoice-recovery stripe:refunds

Databases

Creating a database injects DATABASE_URL into the services you select. TLS is required and certificates are managed for you.

terminal
darwa db create storefront-db --engine postgres:17 --size standard
darwa db url storefront-db --pooled
darwa db psql storefront-db          # opens a session, no tunnel needed

Connection pooling

Use the pooled URL (:6543) for serverless and worker pools, and the direct URL (:5432) for migrations and anything needing session state.

Careful

Prepared statements and LISTEN/NOTIFY require the direct connection. Running migrations through the pooler can fail with transaction-mode errors.

Backups and recovery

terminal
darwa db backups storefront-db
darwa db restore storefront-db --to "2026-08-02 14:12:09"
# restores into a NEW instance; promote it once you have verified it

Cloud storage

Buckets are S3-compatible. Point any existing SDK at the injected endpoint and credentials.

upload.ts
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: process.env.DARWA_STORAGE_ENDPOINT,
  region: "eu-central",
});

await s3.send(new PutObjectCommand({
  Bucket: "user-uploads",
  Key: "2026/06/IMG_4482.jpg",
  Body: file,
}));

Signed uploads

terminal
darwa storage sign user-uploads/2026/06/photo.jpg --put --expires 15m
POST/v1/buckets/{bucket}/searchSearch by content
Request
{
  "q": "product images with blue shoes",
  "limit": 10,
  "filter": { "size_gt": "1MB" }
}
Response · 200
{
  "results": [
    {
      "key": "uploads/2026/06/IMG_4482.jpg",
      "score": 0.94,
      "why": "blue suede trainers on a wooden floor",
      "tags": ["shoes", "blue", "product"],
      "size": 2411520
    }
  ],
  "searched": 1204882,
  "took_ms": 310
}

REST API

The API is the same interface the dashboard uses. Base URL https://darwa.com/api/v1. Authenticate with a bearer token created in your account settings or with darwa tokens create.

terminal
curl https://darwa.com/api/v1/services \
  -H "Authorization: Bearer $DARWA_TOKEN"

Services

GET/v1/servicesList services
Request
?project=storefront&limit=20
Response · 200
{
  "data": [
    {
      "id": "svc_8f21a0",
      "name": "app-backend",
      "type": "web",
      "status": "live",
      "regions": ["us-east", "eu-central"],
      "release": "r-2841"
    }
  ],
  "next": null
}
POST/v1/servicesCreate a service
Request
{
  "name": "app-backend",
  "type": "web",
  "repo": "acme/storefront",
  "branch": "main",
  "regions": ["eu-central"],
  "scale": { "min": 1, "max": 8 }
}
Response · 200
{
  "id": "svc_8f21a0",
  "status": "building",
  "url": "https://app-backend.darwa.app"
}
PATCH/v1/services/{id}Update configuration
DELETE/v1/services/{id}Delete a service

Deploys

POST/v1/services/{id}/deploysTrigger a deploy
Request
{ "commit": "8f21a0c", "clear_cache": false }
Response · 200
{
  "id": "dep_4821",
  "release": "r-2842",
  "status": "queued",
  "logs": "/v1/deploys/dep_4821/logs"
}
GET/v1/deploys/{id}Deploy status
POST/v1/services/{id}/rollbackRestore a previous release

Databases

POST/v1/databasesCreate a database
Request
{
  "name": "storefront-db",
  "engine": "postgres:17",
  "size": "standard",
  "region": "eu-central"
}
Response · 200
{
  "id": "db_1204",
  "status": "provisioning",
  "pooled_url": "postgres://…:6543/storefront",
  "direct_url": "postgres://…:5432/storefront"
}
POST/v1/databases/{id}/restorePoint-in-time recovery

Errors and rate limits

StatusCodeMeaning
400invalid_requestA field is missing or malformed. The response names it.
401unauthenticatedToken missing, expired, or revoked.
403forbiddenThe token's role cannot perform this action.
404not_foundNo such resource, or not visible to this token.
409conflictA deploy is already in progress for this service.
429rate_limitedRetry after the seconds given in Retry-After.

Limits are 1,000 requests per minute per token, and 60 deploy-triggering requests per minute per project. Every response carries X-RateLimit-Remaining.

CLI

terminal
npm i -g @darwa/cli      # or: brew install darwa/tap/darwa
darwa --version
darwa login

Command reference

CommandScopeDescription
darwa deployserviceBuild and release the current directory
darwa logs --followserviceStream build and runtime logs
darwa env set / diffserviceManage and compare environment values
darwa rollbackserviceRestore the previous release
darwa scale --min 2 --max 8serviceChange scaling bounds
darwa queue explainworkerBacklog, ETA, and the scaling that would clear it
darwa job explain <id>workerWhy a job failed and what fixes it
darwa schedule add / runworkerCreate or trigger a schedule
darwa agent trace <run>agentStep-by-step trace with tokens and cost
darwa tools grant / denyagentScope a tool credential to an agent
darwa db create / psql / restoredatabaseProvision, connect, recover
darwa storage sign / searchstorageSigned URLs and content search
darwa cost reviewprojectIdle capacity and the saving available
darwa tokens createaccountCreate an API or agent token

Platform agents

Give your own agent scoped access to Darwa so it can deploy, inspect logs, and open pull requests on your behalf. Tokens are scoped by action and by project, and every call an agent makes appears in activity history attributed to that token.

terminal
darwa tokens create ci-agent \
  --scope deploy:staging,logs:read,metrics:read \
  --project storefront --expires 90d

Available tools

ToolScopeWhat an agent can do
deploydeploy:{env}Trigger a deploy or roll back a release
logslogs:readRead build and runtime logs, filtered by service
metricsmetrics:readLatency, error rate, saturation, queue depth
diagnosediagnose:readFetch the platform's own analysis of a failure
envenv:writeSet environment values — never read secret values back
dbdb:readRun read-only queries against a nominated database
Design note

Secret values cannot be read through the API at all — an agent may set a value or check that one exists, but never retrieve it. Anything else would make a leaked token a leaked vault.