features

The whole harbor, in one hull.

boatramp is more than a static host. The same single binary publishes sites, serves them over modern HTTP, runs code at the edge, fronts your private services, scales to a cluster, and hands you the keys to all of it — with the same commands everywhere.

Everything is pre-1.0. Where a capability's live validation is still in flight, it's marked: live seam beta in progress
Publish & Serve
01 / PUBLISH

Cast off, roll back, never look down.

Atomic deploys
Every publish is an immutable, content-addressed deployment; going live is one atomic pointer flip — readers never see a half-written mix.
sync
Dedup + delta uploads
Identical bytes are stored once; re-running sync streams only the blobs that actually changed.
content-addressed
Instant rollback
Re-activate any previous deployment in a single flip — nothing to rebuild, nothing to re-upload.
rollback
History & status
Inspect the live deployment and the full history — id, age, and file count.
deployments · status
Aliases & previews
Named pointers for staging and per-branch previews, plus immutable per-deploy preview hosts.
alias
Build step + bundler
Run any build tool before publishing, or the in-process Rolldown + lightningcss bundler.
build · bundle
Dry-run & validate
Preview exactly what an upload would change, and validate routing before anything ships.
--dry-run · validate
02 / PROJECTS

Many sites, one tenant boundary.

First-class projects
A project owns many sites plus their functions and compute. A site name is unique only within its project, so two teams can each own a blog — deploys, aliases, crons, and workflow runs stay independent.
project
Tenant data isolation
Each project is the tenant a handler's row-level scope resolves to: kv, blobstore, sql, and messaging namespaces are scoped to the owning project, so same-named resources never share a data plane.
tenant
Per-project RBAC
A Cedar Project resource with project_admin / project_publisher / project_viewer roles; a token scoped to one project is refused (403) on any other.
cedar
Declarative apply
One RON manifest declares a whole project — its sites (content + build + routing), top-level functions (with their imports, env, and limits), and compute — reconciled to desired state in a single pass.
apply.cfg
Upsert, never prunes
apply is create-or-replace and touches only what it names, so declarative and imperative management coexist; --dry-run prints the plan and writes nothing.
--dry-run
Default project = no change
Everything that predates projects lives in a reserved default project, so an omitted --project is byte-identical to single-site boatramp — no migration of your URLs or behaviour.
default
03 / SERVE

A real web server, not a bucket.

Static serving
Range requests, conditional GETs, and strong ETags — automatic, nothing to configure.
range · 304
HTTP/1.1, /2, /3
HTTP/3 over QUIC served alongside the TLS listener.
h1 · h2 · h3
First-party HTTP stack
Every connection is served through boatramp's own boatramp-http — a hand-rolled HTTP/1.1 codec and a multiplexed HTTP/2 driver, no hyper in the accept path — conformance-gated against h2spec, a differential oracle, and fuzzing.
boatramp-http
Routing
Redirects, internal rewrites, reverse-proxy targets, header rules, clean URLs, and a trailing-slash policy.
routing
Conditional routing
Gate redirects and rewrites on a when predicate (a bounded CEL subset) — language, cookie, header, or file_exists — with computed ${…} destinations and automatic Vary. Edge logic without a handler.
when
SPA & error docs
SPA fallback and custom error documents, folded into the immutable deployment so they roll back with content.
spa · 404
Caching
Sensible Cache-Control + ETag defaults, with per-path immutable overrides.
cache-control
Edge response cache
Serve a cacheable GET/HEAD from a host-level cache without re-instantiating the handler — opt-in via Cache-Control, keyed per project scope, honoring Vary and never storing a private (Set-Cookie, no-store) response.
handlers.cache
Compression
Brotli and gzip precompressed at publish; on-the-fly compression for dynamic output.
br · gzip
Netlify / CF Pages compatible
Existing _redirects and _headers files are folded into the routing manifest on sync.
migrate
04 / DOMAINS & TLS

Bring a domain; keep your certs.

Virtualhosts
Attach any number of hostnames — exact and wildcard — to a site, with canonical redirects.
domain add
Ownership verification
Prove control over a host via HTTP self-verify or a DNS-TXT record before it ever routes.
domain verify
Automatic HTTPS (ACME)
HTTP-01 certificates requested on first start, cached, and renewed before expiry — no cron, no certbot.
--tls acme
Wildcard / DNS-01
Wildcard certificates via the DNS-01 challenge, plus per-deploy preview certs.
--tls acme-dns
10 managed DNS providers
Cloudflare, Route 53, Google, Azure, DigitalOcean, Hetzner, NS1, DNSimple, OCI, and Akamai — plus a manual fallback.
auto-dns
BYO cert + transport security
Serve your own certificate, and set HSTS, CSP, and frame-options per site.
--tls custom
Compute & Data
05 / FUNCTIONS

Everything that runs is a function.

The function model
One primitive, many doors
A function is a portable WASI 0.2 component. Reach the same component as a route handler, invoke it by name, put it on a cron, or drive it from a queue — a different door, one sandbox.
function
Invoke by name
Top-level functions carry their own version line — function deploy / alias / rollback — and invoke sync or async: durable, idempotency-keyed, and dead-lettered on failure.
function invoke
Function mesh
A site handler can call sibling functions in-process through a deny-by-default invoke_targets allowlist — the caller's bearer is forwarded unchanged, the callee quota-admitted and depth-capped. A mesh orchestrator with no network hop.
invoke_targets
Streaming invoke
Consume a sibling function's response as a stream — status and headers up front, the body pulled incrementally through an incoming-response resource — so a large or @defer-style result is never buffered whole in host memory.
invoke --stream
Compose components
Author resolvers and middleware as separate WIT-typed components and fuse them into one linked component in-process with boatramp compose — no network hop, checked at compile time — then deploy the single .wasm.
compose
Event triggers
Fire a function from a signature-verified HMAC webhook, or a blob-change event — the changed key arrives as the invocation body — via the backend's native notifications.
webhook · blob
Multi-language
Author in Rust, JavaScript (jco), or Python (componentize-py); scaffold, build, and test locally — function init · build · test · dev.
rust · js · py
Metering & quotas
Every invocation is metered and exported to Prometheus, with fail-closed rate-limit and concurrency quotas.
function usage
Workflows
Chain functions into a durable DAG — fan-out, barrier fan-in, per-step retries, and saga compensation on failure. Runs survive restarts.
workflow
Long-running jobs
A durable background job — an LLM generation, an --async batch, a workflow step, a cron or queue trigger — runs on a separate async lane (a larger wall-clock ceiling on its own concurrency budget), so it runs to completion without ever starving live site traffic. The drain leases each invocation and redrives it if a node dies mid-run.
async_max_timeout_ms
Streaming response lane
A long-lived streaming handler — SSE, chunked output, agent token streaming — runs on its own engine lane with a dedicated concurrency budget and a large wall-clock (defaults 15 min, 64 concurrent), isolated from both the fast request pool and the durable async drain, so a burst of streams starves neither.
streaming = true
Data bindings
Per-site SQL
A real libsql database per site — an embedded file on one node, or a sqld namespace across a cluster.
sql
Bring your own SQL live seam
Point the sql binding at your own Postgres or MySQL (Neon, Supabase, PlanetScale) — opened by name, read-only enforceable, the URL kept in a secret. Guest code is unchanged.
sql-postgres · sql-mysql
Managed database
Source the sql binding from a Postgres/MySQL workload boatramp runs: it resolves the live endpoint so the binding follows the database across restarts, and can fully manage the credential — generate, seal with the [secrets] envelope, and inject — so the operator sets no DB secret at all.
compute: db
Named SQL bindings
Grant a handler named databases — sql:product for the RLS-enforced product role, sql:privileged for a separate cross-tenant role (or sql:* for all) — each its own connection and credential, capped by the site's allow_imports. Least-privilege that makes Postgres FORCE ROW LEVEL SECURITY real.
sql:name · sql:*
Per-tenant databases
A managed sql binding isolates each tenant into its own database — a shared server with a database + login role per tenant, or a dedicated server per tenant — each with its own sealed credential, so one tenant's role can never reach another's. Or set rls_session and boatramp injects the request's tenant into the SQL session for your own row-level-security policies.
tenant · rls_session
Typed query builder
Build queries as a typed tree instead of SQL strings — joins, GROUP BY/HAVING, IN/BETWEEN/LIKE, upserts, RETURNING, and JSON key-paths — compiled to parameterized SQL for the backend's dialect (a closed function enum, every value bound, so a guest can't construct an injection), with an optional row-scope folded into every write for sub-tenant isolation.
handlers/orm
Vector search experimental
Nearest-neighbour search from the typed query builder — cosine (<=>) or L2 (<->) distance over a bound ?::vector in a select list or ORDER BY, on Postgres + pgvector. The metric is a compiler constant and the vector binds as a parameter, so nothing is interpolated.
pgvector
KV · blob · queue
Per-site key/value, blob store, and durable messaging bindings, granted to the function.
kv · blobstore · messaging
Message-queue fabric
A project-scoped topic bus (bus:<topic>) carries events between a project's sites and functions. Subscribe as a competing-consumer work queue (lease, redelivery, dead-letter after max_attempts) or a named durable consumer group — fan-out where every group sees every message on its own cursor. A verified inbound webhook can publish straight onto the bus.
bus · consumer groups
Streams (SSE / WebSocket)
Fan messaging topics out to connected clients over Server-Sent Events or WebSocket.
streams
The sandbox
Deny-by-default imports
A function receives only the capabilities the site grants; anything else is refused at activation.
imports
Portable capabilities
A function declares the capability features it requires; the host advertises what it implements (boatramp capabilities, each tagged stable or experimental) and activation fails loud, naming any missing feature. boatramp capabilities check gates a component in CI against the boatramp version it targets.
capabilities check
06 / GRAPHQL

GraphQL, understood at the edge.

GraphQL from your database
Generate a read + write GraphQL API from a managed database with no resolver code: boatramp introspects the schema and compiles each query to one parameterized SQL statement — relationships become correlated JSON subqueries, so a nested query is one round-trip with no N+1.
graphql.data
Deny-by-default, tenant-isolated
Only the tables and columns the policy names are visible; a per-table row filter bound to the host-asserted project claim isolates tenants at every depth — a missing claim denies, never widens. Mutations are opt-in and transactional.
row_filter
Multi-tenant SaaS by claim
A row filter can bind a claim from your app's own verified bearer (claims_from_token — sig / iss / exp / kid checked), so many tenants live as rows in one project, isolated by a claim like tid and never able to override the project.
claims_from_token
Edge query-guard
Parse operations at the edge and reject them before the handler runs when they exceed a depth / complexity limit or (unless allowed) introspect — fragments expanded so nesting can't hide, cyclic ones terminated.
query-guard
Persisted queries + safelist
Clients send a query hash instead of the full query; in safelist mode only pre-registered hashes run and the edge never registers a new one — turning persisted queries into a security allowlist.
persisted
Federation gateway
Register each subgraph's SDL — or let a wasm function self-register on deploy — and boatramp composes + validates a supergraph, then plans and executes a query across subgraphs over the in-process invoke path; the caller's identity is forwarded and re-verified per subgraph.
federated · @key
Subscriptions over SSE
Stream a GraphQL subscription to the browser over graphql-sse — with Last-Event-ID resume, a heartbeat, and the site's stream connection caps.
graphql-sse
GraphiQL & in-process supergraph
A baked GraphiQL IDE (opt-in) for exploring the schema in the browser, and a guest graphql::run capability so a handler runs a safelisted supergraph operation in-process — the same planner, no network hop.
graphiql
07 / CONTAINERS & microVMs

Heavier cargo, still one hull.

Firecracker-class microVMs
Run an OCI image in a hardware-isolated VM with its own kernel, via an embedded rust-vmm VMM.
--isolation untrusted
Native OCI containers
Shared-kernel containers (namespaces, cgroups, seccomp) for code you trust.
--isolation trusted
macOS-native microVMs live seam
On Apple silicon (macOS 15+), each replica runs as a lightweight Linux VM via Apple's Virtualization.framework — strong per-VM isolation, the same compute surface, still one binary.
vmm-vz
Remote Docker
Run workloads against a remote Docker host — the native-container path on Windows and macOS.
remote-docker
Scale-to-zero live seam
Park an idle replica to disk and wake it with its in-RAM state intact — a microVM (Linux KVM or macOS vmm-vz) or a native container (via CRIU). Capability-detected, so a workload only lands on a node that can wake it.
--scale-to-zero
Persistent volumes
Back a container workload with an external volume — a daemon-managed named volume or a host bind-mount — so app state survives restarts and scale-to-zero.
volumes
Managed sql for containers
A docker or native workload can --bind sql and reach the same per-tenant SQL backend a handler gets, over a token-multiplexed shim — no long-lived database secret ever enters the guest.
--bind sql
Rootless stock images
Run a stock image whose entrypoint chowns its data dir and drops privileges (a database) — rootless via --user against a pre-chowned volume, or add just the caps it needs with --cap-add (single-tenant). A managed DB picks the strategy automatically.
--user · --cap-add
Internal service discovery
Co-located workloads, handlers, and functions reach each other — and their managed databases — by name: boatramp runs a per-project DNS responder on the compute bridge, resolving <workload>.<project>.boatramp.internal to a live healthy replica. Source-IP-scoped, so a cross-project lookup is refused.
boatramp.internal
Verified boot + posture
A signed first-party kernel is verified before boot; strict posture forces microVM isolation for untrusted code.
security explain
Cloudflare Containers live seam
The same binary runs on Cloudflare Containers as a single durable instance — all state in R2 (blobs + a SlateDB KV) so it survives scale-to-zero — fronted by an edge Worker + container Durable Object. Validated end-to-end live.
cloudflare
Operate at scale
08 / GATEWAY

Front your private services.

Reverse-proxy pools
Route paths to a pool of backends — a compute workload, a server pool, or a private service.
gateway route add
Streaming reverse proxy
The upstream response streams straight through with no intermediate copy and a capped per-connection read buffer (--read-buffer-bytes), so large responses stay memory-bounded and per-request overhead stays low.
--read-buffer-bytes
TLS & WebSocket upstreams
Tunnel HTTP upgrades and WebSockets to an upstream — now including wss/https targets over a real TLS handshake (webpki roots, pinned to the posture-validated address), so a private graphql-ws service proxies through the edge.
wss
Load balancing
Round-robin (default) or random across the backend pool.
--lb
Geo dispatch live seam
Route each request to the nearest healthy backend by region (--lb nearest) — compute-backed pools auto-tag replicas by their node's region, so there's no manual map.
--lb nearest
Health checks
Passive ejection after consecutive failures, plus active path probing with a cooldown.
--probe-path
Retries
Retry a body-less request against another backend on a connect failure.
--retries
DNS discovery
Resolve the pool from an A/AAAA record set instead of listing backends by hand.
--discover-host
Request rewriting
Override the upstream Host header, strip a path prefix, and set per-route timeouts.
--strip-prefix
09 / SCALE

One node to a fleet, same commands.

Single node
Filesystem blobs + an embedded SlateDB KV store. Nothing else to run.
serve
Shared-store frontends
Many stateless serving processes over one shared KV, kept coherent by a changelog — scales reads without consensus.
shared-cache
Raft cluster (HA) live seam
An openraft-replicated control plane: voters form quorum in one region, learners serve local reads elsewhere. In-process complete; live multi-host operation is the remaining seam.
ha · raft
Dynamic join
Grow a cluster with a one-paste join ticket (cluster addserve --cluster-join) — no static peer map; ids derive from each node's mesh key.
cluster add
Kubernetes operator live seam
The in-binary operator reconciles a BoatRampCluster CRD into a StatefulSet or Deployment + HPA, with GitOps Site and quorum-safe rolling upgrades.
operator
Database per site
A real libsql database per site — a file on one node, or a sqld namespace across the cluster.
sqld
Cluster certificates
The leader issues each cert once and commits it to the log; every node replicates and hot-swaps on renewal.
cert-status
Root-key rotation
Rotate the cluster root key make-before-break: both anchors stay trusted during the overlap, so no node ever rejects a valid token — and no per-node edit.
auth rotate-root
10 / SECURE

Own the controls.

COSE/CWT tokens
Signed control-plane tokens verified offline with a public key — no per-request database lookup.
token create
Cedar RBAC
Roles map to rights (read / write / deploy / admin) per resource, via a replaceable Cedar policy.
cedar
OIDC login
Exchange an IdP JWT — Okta, Keycloak, Auth0, Entra ID — for a short-TTL boatramp token.
oidc
External signers live seam
Keep the signing key in HashiCorp Vault, a PKCS#11 HSM, or AWS / GCP / Azure KMS. Each specific service has a live seam.
signer
Offline delegation
Attenuate a token — one site, read-only, shorter expiry — by signing a restrict-only block, no server round-trip.
cnf
PoP-bound tokens (DPoP)
Bind a token to a holder key with token create --pop: a leaked token is inert without the separately-held key, and every request signs a fresh, short-lived proof.
token --pop
Day-zero control plane
Reach the control plane from the first second over raw-public-key TLS (--tls rpk) — encrypted and pinned before any CA-signed certificate exists.
--tls rpk
Visitor access control
Basic auth, IP allow/deny, rate limiting, and a WAF stage — per site, applied before any content is read.
access
Cookie session auth
Treat a named HttpOnly session cookie as the application bearer when a request carries no Authorization header — so a browser app authenticates its GraphQL / handler calls without shipping a token to JS — CSRF-checked against an allowed-origins allowlist.
cookie_auth
Postures & secrets-at-rest
Operator-only security postures (multi-tenant / single-tenant / dev), and envelope-encrypted certificate keys.
security explain
Project secret store
Seal operator secrets per project with the [secrets] envelope — boatramp secrets set/rotate/ls/rm, sealed server-side and never readable back. Handlers and functions reference them by scheme (boatramp:NAME from the store, env:NAME from the process env, gated to single-tenant), resolved at instantiation and never persisted in the manifest.
boatramp secrets
Guest egress control
A handler's outbound wasi:http has its own SSRF gate — blocked to non-global addresses by default, relaxed to private/loopback upstreams only under a trusted posture (allow_guest_private_egress). A guest may still call its own front door (allow_guest_self_egress), re-entering the full serve pipeline behind visitor auth and a forge-proof depth cap.
guest egress
11 / STORAGE

Your bytes, your backend.

Content-addressed blobs
Every file is a SHA-256 blob, stored once and streamed end-to-end — never held whole in memory.
blobs
Filesystem or S3
Local disk, or any S3-compatible object store — AWS, Cloudflare R2, or MinIO.
--blobs s3
Cloud object stores live seam
Google Cloud Storage (--blobs gcs) and Azure Blob (--blobs azure), streaming, with change-notification pipelines (GCS→Pub/Sub, Azure→Event Grid) that drive blob triggers.
gcs · azure
Metadata KV
Embedded SlateDB (an LSM over disk / S3 / R2 / GCS), in-memory, or Cloudflare KV.
--kv slatedb
Write-through cache
Hot metadata is fronted by an LRU cache, kept coherent across processes by the shared store.
cached-kv
12 / OPERATE

See everything, fix anything.

Prometheus metrics
Requests, response bytes, deployments, activations, and cert renewals at /api/metrics.
/api/metrics
Structured logs
JSON access logs, plus guest stdout/stderr with live tailing — per site (logs --site) or per standalone function (logs --function).
logs --follow
Handler stats & DLQ
Invocations, errors, consumer lag, and dead-letters — redrive or purge a stuck topic.
stats · dlq
Health endpoints
Liveness and a readiness probe that fails when the metadata backend is unreachable.
/healthz · /readyz
Backup & integrity
Snapshot each backend, and re-hash every stored blob to confirm it still matches its key.
scrub
Online store migrations
A versioned registry of forward-only store migrations the engine walks by a monotonic schema version — online, idempotent, and resumable (copy-before-delete), by hand with migrate or automatically via serve --auto-migrate.
migrate
GC + dynamic config
Prune orphaned deployments and blobs; change operational knobs fleet-wide with no restart.
prune · config
Operator SQL & exec
Run migrations and queries against a managed database with boatramp sql exec / query — server-side over the sealed credential, which never reaches the client — and open a shell in a running workload with boatramp compute exec (posture-gated) for a pg_dump or a debug session.
sql exec · compute exec
Compute controls
Live X-ray and manual levers over the co-located compute plane — compute status (per-replica health, phase, IP), force a reconcile, restart a wedged replica, set-health, inspect IP and internal-DNS assignments, netdiag, and sql ping a managed database.
compute status · reconcile
Ship & Automate
13 / SHIP

One hull, every port.

Single static binary
Server + publishing API + CLI in one file; every heavy backend lives behind a compile-time feature.
1 file
Config from the environment
Every serve setting is settable from BOATRAMP_* variables — the whole config, layered env > file > default — so a node needs no boatramp.cfg at all, even one running a managed co-located Postgres or issuing a wildcard cert. 12-factor deploys on fly.io, Cloudflare, or any container platform.
BOATRAMP_*
Prebuilt everywhere
Linux (x86_64 + arm64), macOS, and Windows binaries, an install script, and a Homebrew tap.
install.sh
OCI image
A multi-arch, non-root container image at ghcr.io/boatramp/boatramp — a fully-static musl binary with jemalloc, ~49 MB compressed with zero dynamic dependencies (just the binary and CA certs).
docker
NixOS + systemd
A flake, an overlay, and a declarative services.boatramp module with a hardened systemd unit.
services.boatramp
Embed as a library
boatramp_node::assemble builds the exact serve-node graph the binary runs, so you can embed a full boatramp node — single-node or cluster — inside your own Rust service.
boatramp_node
Cloudflare deploy live seam
Deploy to Cloudflare over the CF REST API with boatramp cloudflare — no wrangler, nothing generated for you to run: it provisions R2 + D1, uploads the edge Worker + Durable Object, and creates the container app. --dry-run previews the plan.
boatramp cloudflare
14 / AGENT-NATIVE

Hand the helm to an agent.

MCP server
The same binary ships a Model Context Protocol server, so an agent like Claude (Desktop, Code) or Codex operates your control plane in natural language — list sites, activate or roll back, tail logs, invoke functions, inspect the cluster.
boatramp mcp
Two transports
A boatramp mcp stdio subcommand a desktop agent spawns, and a POST /mcp endpoint on boatramp serve to drive a node over the network — on by default, same tool set on both.
stdio · /mcp
Typed, enumerated tools
One named tool per control-plane operation — no generic passthrough — so every call is legible in an audit log rather than an opaque shell.
no passthrough
Your token, your authority
Every call runs with the caller's own bearer, re-checked per operation; hand the agent a least-privilege token and the write and fleet-admin tools simply 403. The endpoint grants nothing the token doesn't.
least-privilege
Many instances, one agent
Register several boatramp nodes by name in mcp.toml and address them per request ("on prod, …"); secrets are stored as specs, never resolved into the file.
mcp.toml
Kill-switch & rebinding guard
Turn the HTTP surface off fleet-wide with no restart (config set mcp.enabled false), and a Host-header allowlist keeps it loopback- or origin-only as a DNS-rebinding defence.
mcp.enabled
Agent-ready scaffold
Ships an AGENTS.md guide so an AI coding agent (Claude Code, Codex, …) can build a project on boatramp and deploy it out of the box.
AGENTS.md
pre-1.0

Every capability above is implemented and dogfooded — this site runs on it. Interfaces may still shift before 1.0. Start with the quickstart, dig into the docs, or come crew it.