Use the public API, CLI, or dashboard with actual native harnesses in local Docker containers. PostgreSQL owns execution and billing state; encrypted local storage owns verified files and conversation state. Docker does not make upstream model calls free.
Start
Complete simulation setup, then stop its application and dispatcher before changing modes. Keep the local PostgreSQL and mail services running.
docker build -f infra/runtime.Dockerfile -t platform-runtime:0.1.0 .
The image uses the pinned runtime packages and shared lockfile. Docker caches downloads; retrying a failed registry download can reuse that cache.
Configure the same .env used by the API and dispatcher:
PLATFORM_MODE=local
EXECUTION_PROVIDER=docker
ORCHESTRATION_BACKEND=poller
DOCKER_RUNTIME_IMAGE=platform-runtime:0.1.0
COMPUTE_MICRO_USD_PER_MINUTE=0
ALLOW_PAID_EXECUTION=false
Use the model catalog; no model JSON configuration is required. For managed inference, securely configure the corresponding OPENAI_API_KEY, ANTHROPIC_API_KEY, or OPENROUTER_API_KEY. Alternatively, add an encrypted model connection in the dashboard and choose BYOK. Change ALLOW_PAID_EXECUTION to true only after choosing an inference budget. A saved credential alone does not authorize spending.
In terminal 1:
pnpm doctor:docker
pnpm dev:docker
In terminal 2:
pnpm worker:docker
These aliases validate the Docker settings and start the existing application or dispatcher. They read .env, preserve it, and let exported environment variables take precedence. Open http://localhost:3210; the dashboard identifies Local Docker, and the existing local demo login works.
Measure API latency with a compiled server
pnpm dev:docker runs Next's development server, which compiles routes on demand. A cold request can spend seconds in compilation and module loading before Macrofold's request timer starts. For integration timing, stop that API process before building, keep the same local .env, and run:
pnpm doctor:docker
pnpm build
pnpm start
This serves the compiled application on the same localhost port with the same database, Docker profile and spending controls. Keep pnpm worker:docker in its separate terminal for native/queued work. Immediate inference runs in the API process and needs no worker. This is a production build of a local deployment; it does not switch to cloud execution or enable paid calls.
In another terminal, wait for curl --fail --silent --show-error http://localhost:3210/health to succeed before sending application traffic. Next can print “Ready” before router initialization finishes. On self-hosted servers, this check loads the same compiled REST route and its shared dependencies. It verifies a database round trip and configuration, then compiles trusted contract validators and initializes configured tracing before reporting ready. It emits no trace and makes no model request and does not prove provider connectivity. Record process startup through this check separately from request latency.
Rebuild after code changes; the compiled server has no hot reload. Compiled pages load on demand instead of all dashboard routes competing with the first API request. Record the first request separately from warm requests: route initialization, upstream connections and first-use caller-schema validation can still cost time. To return to editing, stop pnpm start and restart pnpm dev:docker. Use a separate checkout, port and disposable database for isolated benchmarks rather than interrupting another developer's preview. See performance profiling for correlation and measurement boundaries.
Update an existing local installation
Keep the application, database schema, and runtime image on the same revision. Drain active work, stop the API and dispatcher, and preserve the existing .env, database, and encrypted content store. Take a paired recovery backup before a schema-changing upgrade, then run:
pnpm db:migrate
pnpm exec tsx scripts/provision-cli.ts
docker build -f infra/runtime.Dockerfile -t platform-runtime:0.1.0 .
pnpm doctor:docker
Use your configured DOCKER_RUNTIME_IMAGE tag if different. Restart pnpm dev:docker and pnpm worker:docker in separate terminals after these commands succeed. Do not rerun setup or replace credentials to update an existing installation.
The idempotent CLI provisioner refreshes the first-party OAuth client's supported scopes and API resources after an upgrade; it does not seed or reset customer data. Existing access tokens do not acquire new permissions. Sign in again and approve the displayed scopes when using a newly added capability. An invalid_scope response during CLI sign-in can indicate a stale client registration. Run this operator command with the existing owner-database configuration, without adding owner credentials to model-serving containers.
The Worker cutover (migration 045) requires all previous Sandbox allocations to be destroyed and their reservations settled. Drain and destroy them through the previous version before upgrading; paused allocations also need retirement. If old records outlive their containers, reconcile their provider state and financial reservations before retiring them. Never bypass the migration guard or reset saved Runs, files, or Sessions.
If every dispatch class, maintenance and dispatch_wakeup_unavailable fail together, check local PostgreSQL first:
docker compose -f infra/compose.yml ps --all
docker compose -f infra/compose.yml start postgres mail
This starts the existing infrastructure containers without resetting the database volume. PostgreSQL and mail restart automatically after Docker restarts unless explicitly stopped. Live pollers reconnect through their existing retry paths; restart the dispatcher if its process exited. Do not restart agent execution containers to replay work.
If PostgreSQL is healthy but maintenance_failed and unavailable dispatch continue immediately after an update, check the migration command's result before investigating provider availability. An old runtime image can separately prevent Host startup even after the schema is current.
If Runs stay queued, inspect the Worker's failure_code and run pnpm doctor:docker. docker_image_missing means the exact DOCKER_RUNTIME_IMAGE tag is unavailable. Rebuild that tag with the command above, or explicitly select an already-built compatible image; Docker never pulls or substitutes an image automatically. docker_daemon_unavailable, docker_network_missing, docker_disk_full and docker_command_timeout identify other local setup/health failures. Diagnostics omit raw Docker output. Repair the reported cause; provisioning will retry through the existing reconciliation loop. Do not restart a stopped execution container to replay an agent.
Diagnose a queued request
Check GET /v1/runs/{run_id} for waiting_reason, wait_seconds and started_at. A null start means execution has not begun. Immediate POST /v1/inferences calls use the API process directly and need no Docker Host or standalone dispatcher; queued inference on that endpoint is unsupported. Bounded-agent and Task children still need the dispatcher. A healthy API alone does not prove the dispatcher or local PostgreSQL service is running.
Stop the existing poller gracefully, then start it with content-free progress diagnostics in its own terminal:
WORKER_DIAGNOSTICS=1 pnpm worker:docker
Keep that terminal's output separate from pnpm dev:docker. Correlate the Run ID with worker.advance.* and worker.inference_provider.started. The latter confirms provider dispatch; five-second *.waiting messages identify an unfinished operation. worker.maintenance.*, worker.candidates.* and worker.claim.* locate other waits without exposing prompts or credentials. Logging does not cancel, retry or reset a Run. Restart the poller after dispatcher code changes; no runtime-image rebuild is needed for a control-plane-only fix.
Run and continue a task
Follow the API quickstart against localhost with a real enabled model instead of fixture-model. For a first task, ask the agent to create hello.txt containing a short note and read it back. Start with a 120-second timeout and an explicitly approved Run budget.
Follow events and inspect the published file. Continue with the returned session_id; Macrofold restores compatible native history and files when compute is replaced. Execution, verified persistence, and optional Git synchronization have separate outcomes.
The seeded account has synthetic application credits, so Stripe is unnecessary for local development. Synthetic credits do not make provider inference free. Local Docker compute defaults to zero, but model/tool budgets, exact BYOK selection, revocation, reservations, and settlement still apply.
Reuse capacity with a Worker
Omit worker_id for automatic per-Run compute. Create a Worker to retain baseline capacity or run independent agents concurrently on shared local compute. Both server and sandbox offerings can use Docker locally; no Render credentials are required.
For sustained trusted workloads:
{
"name": "local-agent-service",
"compute": "server",
"dedicated": true,
"isolate_runs": false,
"min_instances": 1,
"max_instances": 2,
"max_concurrency": 8,
"idle_timeout_seconds": 300,
"max_hourly_compute_cost_micro_usd": "1000000"
}
Send this to POST /v1/workers, then include its ID as worker_id on Runs. The dollar ceiling is illustrative, not a model budget. The offering catalog and effective settings are authoritative. Each Run reserves memory and CPU; Macrofold adds backing Hosts within resource, plan, concurrency, and cost bounds.
For bursts, choose min_instances: 0. The Worker remains addressable after its idle Hosts stop. A compatible native process may remain warm between turns while its Host survives. Different Worktrees can run concurrently; one Worktree still has one writer globally. isolate_runs: true requires the advertised isolated environment and does not share that environment between Runs.
Pause stops admissions and drains active work before releasing compute. Resume enables new Hosts lazily. Destroy retires the Worker but never its Worktrees, Sessions, or verified checkpoints. These operations use the same API locally and in hosted deployments.
Stop and resume the application
Pause explicit Workers, cancel unwanted queued Runs, and let active Runs finish persistence and cleanup before stopping the dispatcher. Press Ctrl-C in both terminals. Restart with pnpm dev:docker and pnpm worker:docker; durable state is independent of the process and Host lifecycle.
A dispatcher crash does not authorize replaying a native prompt. Its replacement inspects the original Host binding and execution marker. A stopped or externally restarted container is a recovery condition; do not use docker start to resume the agent. Continue the logical Session through the API from verified state instead.
To return to simulation, drain native work, stop the application and dispatcher, change EXECUTION_PROVIDER=simulator and ALLOW_PAID_EXECUTION=false in .env, then run pnpm dev and pnpm worker. Use separate databases and data directories for concurrent development environments. Both processes must restart after configuration changes; accepted native jobs cannot be executed by a leftover simulator.
Boundaries and resource limits
Docker is trusted contributor infrastructure, not a claim of production hostile-code isolation. Host allocations use the selected offering's CPU/memory shape with no additional swap and a bounded process count. The protected controller assigns separate native process identities, paths, and cancellation scopes. Sharing trusted Runs is distinct from granting another customer access.
No host checkout, Docker socket, database credential, object-store credential, or upstream model key is mounted into the execution environment. Run-scoped gateway capabilities enter protected configuration through stdin. The adapter requires an already-built image and does not pull arbitrary images on admission. DOCKER_RUNTIME_IMAGE selects a compatible local image; DOCKER_NETWORK selects an optional bridge, not host networking.
Choose NATIVE_CONCURRENT_RUN_LIMIT and Worker limits within Docker's actual resource allocation; configure lightweight inference separately with LIGHTWEIGHT_CONCURRENT_RUN_LIMIT, leaving room for the application and PostgreSQL. A high configured concurrency limit is not proof that a particular harness mix fits in memory. Worker operations explains headroom, usage receipts, and deployment-specific guarantees.
Networking
The Docker adapter rewrites only application model/MCP URLs to host.docker.internal, retaining the port and exact Run path. The app must listen on a container-reachable interface. Local firewall/VPN rules can affect reachability; hosted HTTPS and default user-URL SSRF rules remain enforced. To connect a local tool server without a tunnel, configure the local MCP origin allowlist. DOCKER_HOST_GATEWAY_IP is an optional exact IPv4 mapping for a controlled local network; ordinary development leaves it unset. See Docker host networking and host-gateway mapping.
Recovery and cleanup
SQL leases, generation checks, Worktree writer claims, and hash-verified checkpoints are shared with hosted execution. Pending cleanup keeps its Host slot owned even after a Run becomes terminal. Failed persistence quarantines that Worktree's local materialization and retains the previous durable checkpoint; it must not stop unrelated Runs sharing a Host. Local unpublished bytes are not an independent backup and can be lost if the Host is deleted. Back up the database, encrypted objects, and vault keys together.
Remove only confirmed-owned idle resources after examining recovery state. Never use a global Docker prune as application cleanup. A Host name identifies provider infrastructure, not the durable Worker or Worktree identity.
Free runtime verification
With local infrastructure and the matching image available, the existing isolated runners are:
pnpm test:journey:docker
pnpm test:native codex
pnpm test:native claude-code
pnpm test:native opencode
pnpm test:native hermes deepseek pi
They use synthetic data and scripted model responses, not provider credentials. The complete journey owns a disposable database/object directory and its network; native-only runners exercise the image/harness boundary separately. An unavailable daemon or image is a failure, not a passing live-provider check. Consult Worker verification and harness acceptance for the scope actually exercised.
For paid model behavior, use the separately opted-in complete journey runner with AGENT_JOURNEY_ENVIRONMENT=docker, a synthetic customer, and a bounded approved budget.
Return to development modes.