Your box. Your control plane.
One script on a Linux machine installs an edge with automatic TLS, a sandbox, a control plane, a dashboard and the runtime. Your source stays on your laptop; only the compiled binary is uploaded.
Alpha. It runs this website and holds real deployments, but the storage format and the CLI contract can still change between minor versions.
Your Linux box
blog
its data
api
its data
shop
its data
Install in four steps
A Linux box, a domain pointed at it, and the CLI on your laptop.
-
1
Get a Linux box
A Linux VPS or home server with a domain you control. Check the installation requirements before choosing a machine.
-
2
Run the installer
It provisions Caddy with automatic TLS, a bubblewrap sandbox, a firewall, one admin identity, and the control, edge and dashboard services. It pauses once so you can add a DNS record.
$ curl -fsSL https://sproutboat.com/install.sh | sudo bash -
3
Point the CLI at your endpoint
A device-code browser flow, or the admin token the installer printed. The CLI keeps credentials per endpoint, so you can hold several.
$ sproutboat login --api-url https://sproutboat.your-domain.com -
4
Deploy
Porffor compiles your handler on your machine. The CLI uploads only the binary; your node runs it behind Caddy. From here on it is just sproutboat deploy.
$ sproutboat deploy
Supported bindings
What a deployment gets on a self-hosted node. Each example is a runnable project.
| Binding | What it is | Example |
|---|---|---|
| Handlers | export default { fetch }: one request in, one Response out | hello |
| KV | key-value store: get / put / delete / list | kv |
| D1 | SQL database: prepared statements, batch, transactions | d1 |
| R21 | S3-style object storage with sha256 etags | r2 |
| Queues | a producer handle plus a queue(batch) consumer, with retries | queue |
| Durable Objects2 | addressable single-threaded instances, each with its own storageIncludes alarms: setAlarm / getAlarm / deleteAlarm and the alarm() handler. | durable-object |
| Cron triggers | scheduled(event), fired on cron expressions | cron |
| Static assets | files served from a directory via env.ASSETS.fetch() | assets |
| Analytics Engine | writeDataPoint() and query() | analytics |
| Rate limiting3 | env.NAME.limit({ key }) -> { success }, a fixed window per key | ratelimit |
| Vars & secrets | config values, and secrets that never enter the binary | vars-secrets |
| Outbound fetch | fetch() to an allowlisted host, http and https | outbound-fetch |
| Service bindings | env.PEER.fetch() to another deployment on the same nodeA service call routes through the node's edge, which a single binary does not have. | service-bindings |
| ctx.waitUntil4 | work that outlives the response, on fetch/scheduled/queue | waituntil |
| Streams5Not supported | ReadableStream / WritableStream, and streaming response bodies | - |
- An object is held whole in memory both ways. Keep them small for now; see the note in the docs.
- An alarm is not yet guaranteed to avoid running alongside a request to the same object.
- One node uses a fixed window; requests near a window boundary can exceed the intended per-interval rate.
- In-process drain, capped at 25s.
- Blocked upstream in Porffor (issue #349); a response body is a whole string today.
Don't want to run it?
A managed Sproutboat is coming: the CLI ships with a default endpoint,
sproutboat deploy just works, and you never touch a VPS. Same Porffor binaries,
same env.* bindings; you skip the operations. One email when it opens, nothing
else.