Skip to content
sproutboat
Esc
↑↓navigate↵open⌘Jpreview
On this page

Architecture

How the CLI builds a native sprout and how the platform routes requests, bindings, and triggers.

Sproutboat has two main paths: the CLI builds a native executable on your machine, and the platform runs that artifact. Porffor compiles the bundled JavaScript. Sproutboat adds bundling, capability checks, the handler prelude, HTTP hosting, bindings, and deployment. These layers together determine runtime compatibility.

CLI build path

The CLI reads your handler, its static imports, and sproutboat.jsonc. It runs Bun’s bundler first, so the capability check sees dependency code too. The wrapper then adds the Sproutboat prelude and selects a binding transport before Porffor compiles the program.

[ From code to a native sprout ]
01 / Build→
  1. App + configHandler, npm imports, sproutboat.jsonc
  2. Sproutboat CLIBundles, checks and wraps the code
  3. PorfforCompiles it for the native runtime
02 / Runparallel paths
  1. Managed sproutDeploy the artifact to Sproutboat
  2. Standalone sproutRun the binary yourself

Porffor turns the wrapped handler into C and links it with the native HTTP host. For a deployable linux-x86_64 build, Zig produces a static musl executable. The CLI uses a pinned, hash-verified Porffor source with Sproutboat’s local patches, and seeds Porffor’s uWebSockets dependency from a prebuilt archive. A host-target build instead uses the local C toolchain; sproutboat dev uses that path for fast local rebuilds.

The output is content-addressed. Its manifest records the target, capability profile, compiler version, hashes, and binary size. Managed builds carry binding names in bindings.json; static assets are sidecars. Standalone builds compile SQLite, TLS support, and small assets into the binary. deploy uploads the artifact, not your source or node_modules.

Platform path

The control API validates and stores immutable artifacts, records deployment versions, and publishes the active hostname routes. A rollback changes the active route without rebuilding the sprout. Caddy handles public TLS; the edge reads the route snapshot and either serves a matching static asset or forwards the request to the active sprout.

[ Managed platform at a glance ]
01 / Deploy→
  1. CLI artifactNative sprout + manifest
  2. Control APIStores a deployment version
  3. Active routeSelects the version to serve
02 / Serve requests→
  1. ClientHTTPS request
  2. Caddy + EdgeTLS, routing and static assets
  3. Native sproutRuns the handler
03 / Use bindings→
  1. HandlerCalls env.*
  2. Binding brokerMediates access
  3. Platform resourcesStorage, secrets, services and triggers

The supervisor assigns a loopback port to each active sprout. It starts the broker first, then starts the sprout under bubblewrap with a read-only artifact directory, private namespaces, and resource limits. The edge sends handler requests over loopback HTTP. HTTP-only sprouts can be evicted after idling and restarted on the next request; routes with cron, queues, or alarms stay available for timed work.

The sprout calls env.* through a token-gated loopback connection to its broker. The broker handles storage, secrets, outbound fetch, service bindings, and trigger delivery. KV, D1, R2 and queue data belong to account-level resources, not to a deployment version, so it survives a redeploy. See Bindings for the API exposed to handlers.

Standalone path

sproutboat build --standalone selects the embedded binding transport. The executable listens on $PORT and stores local binding state under $SB_DATA_DIR; no control plane or broker runs beside it. Public TLS needs a reverse proxy or tunnel, and service bindings need the platform’s edge. See Standalone binaries for the full differences.

Both modes run code compiled by Porffor. Test packages in the native sprout, and test binding behavior in the mode you intend to deploy.

Was this page helpful?