flats

module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT

README

flats caretaker gopher logo

Open-source alternative to sites/artifacts

Flats

Flats lets agents deploy sites and small server apps directly to your own machine. Flats are Private by default, through local loopback or permitted Tailscale, and optionally Public through permitted Portal, zrok or Tailscale Funnel. There is no hosted Flats control plane.

Your machine runs the code and keeps the versions, SQLite databases, persistent files and secrets. Flats is a single CGO-free Go binary for macOS and Linux, with a CLI, HTTP API, MCP endpoint and web console.

  • Isolated origins. Each flat and preview gets its own origin; explicitly permitted Tailscale routes get their own private node.
  • Drafts and versions. Draft saves preserve the live version; approved, successful publishes create immutable numbered versions. Health checks and previews use isolated data.
  • Static and server. Static sites and sandboxed JavaScript/WASI server apps share the same deploy flow.
  • Human approval. Publish, activation, rollback and both visibility directions wait for an approval in the web console.
Agent / CLI / web console → Flats on your machine → version + local data
                                    │
                                    ├─ Tailscale → private browser origin
                                    └─ Portal relay → optional public browser origin

Quick start

On macOS or Linux (amd64 or arm64), run the installer:

curl -fsSL https://raw.githubusercontent.com/gosuda/flats/main/install.sh | sh

It verifies the release checksum, installs flats to ~/.local/bin and runs it as a background service (launchd on macOS, a systemd user service on Linux) with the defaults: Local network, Portal off. It does not use sudo or edit shell profiles. Open the console at http://127.0.0.1:7878.

Deploy a minimal static site:

mkdir -p hello
printf '<h1>Hello from Flats</h1>\n' > hello/index.html
flats deploy ./hello --flat hello

flats deploy saves a Draft and returns a pending publish request (exit code 3, including with --json). Follow its approval URL, approve the frozen Draft in the console, then open http://hello.localhost:7879. Local mode serves loopback development origins and does not join Tailscale.

Run the installer again to upgrade. flats status shows whether the host answers, and flats uninstall removes the service while keeping your data. Releases and installation covers installer options, upgrades and backups.

Other ways to run it
  • Container. A Linux image for amd64 and arm64 is published with every release. On Linux, use host networking so the console, CLI and agents reach it on loopback:

    docker run -d --name flats --restart unless-stopped --network host \
      -v flats-data:/data ghcr.io/gosuda/flats:latest
    

    Never publish the console port to your network: anything that reaches it can approve deploys. See Running Flats in a container.

  • Foreground. flats serve --config ./flats-demo-data/config.json creates a new host with the defaults in ./flats-demo-data on its first start. It never does that over a directory that already holds Flats data. Each running host needs its own data directory and ports; see Configuration and storage.

  • From source (Go 1.27.1 or newer):

    go install github.com/gosuda/flats/cmd/flats@latest   # highest stable release; @main for unreleased changes
    CGO_ENABLED=0 go build -o flats ./cmd/flats           # from a checkout
    

Static and server flats

Static builds need an index.html. For a JavaScript server, include flats.json containing {"kind":"server"} and a server.js ES module:

export default {
  async fetch(request, env) {
    env.DB.exec("CREATE TABLE IF NOT EXISTS hits (n INTEGER)");
    env.DB.exec("INSERT INTO hits VALUES (1)");
    const [{ n }] = env.DB.query("SELECT count(*) AS n FROM hits");
    return Response.json({ hits: n });
  }
};

Deploy that directory with the same flats deploy command.

  • JavaScript runs in QuickJS on WebAssembly with SQLite (env.DB), persistent string files (env.FILES), Web Crypto and WebSocket callbacks. It has no Node.js APIs; bundle imports into the uploaded version. Server-side fetch can call exact operator-granted HTTP(S) origins; see Calling external APIs.
  • WASI. A .wasm server is a WASI preview1 command instantiated afresh per request: request JSON on stdin, response JSON on stdout, only the flat's configured environment variables and secrets, with no inherited host environment, plus clocks and randomness. It has no SQLite/FILES host ABI, filesystem mounts, outbound network or WebSocket API. See the capability table and response format.
  • Markdown documents. A flat with type: "docs" runs the built-in collaborative editor: private visitors edit live Markdown, public visitors read it. Documents have no Draft, versions or publishing: creating or saving one runs it at once, Private, and an agent's save merges into human edits. For small changes, agents can edit the live text with update_document (guarded find/replace and block operations, no approval; on a Public flat the change is visible at once). Making a document Public still waits for approval. See Content types.

Data survives deploys and ordinary rollbacks. flats rollback hello restores code; --restore-data requests a separately approved restore of the pre-deploy DB and FILES snapshot. Preview data is isolated from live data.

Ordinary environment variables (flats env set hello GREETING 'Hello') and encrypted secrets reach server code as env.NAME, or as environment variables for WASI. Saving a value leaves running workers on their snapshot. The live worker picks up current values at the next approved deployment, redeployment, rollback or data restore, or when the Flats host restarts; a newly created preview also loads them. See App environment variables and Secrets.

Private and public access

The default network is Local; every other provider is off. Turn a provider on for the host in Settings → Network providers, then allow it for a flat under Networks on the flat's Settings tab:

Visibility Providers
Private Local (always on), Tailscale (tailnet ACLs decide access)
Public Tailscale Funnel, Portal, zrok

Permitting a network never publishes a version or changes visibility, and Tailscale does not imply Funnel. Making a flat Public needs a published version, a permitted public route and an approval. See Private and public access for Tailscale setup, running the console on your tailnet and upgrading an older host.

Agent integration

Install the Flats plugin for Claude Code, Codex or Cursor. Its MCP server runs flats mcp, which relays the host's MCP endpoint, so choose the host once per machine:

flats connect https://flats.example.ts.net   # or skip it on the host itself: loopback is the default
claude plugin marketplace add gosuda/flats && claude plugin install flats@flats
codex plugin marketplace add gosuda/flats && codex plugin add flats@flats

Without the plugin, register the Streamable HTTP endpoint http://127.0.0.1:7878/mcp directly; flats mcp-config prints the setup for each client. Connecting agents covers Cursor, remote hosts and llms.txt.

The agent starts each task with read-only check_client, then reads the current server guide. It can identify stale local resources and explicitly update its CLI with flats update or a standalone skill with flats skill sync. Managed plugins stay under their agent's plugin manager. Updating files does not reload an active MCP process: reconnect and recheck. See Keeping clients current.

For the whole server contract in one document, read resource flats://docs/runtime-api/v1 or call get_runtime_reference with {}. The runtime API v1 reference is embedded in the host and available through MCP without an installed skill or source checkout.

Security

  • Server code runs in separate worker processes with WebAssembly memory and time limits and restricted host capabilities.
  • The loopback management listener and the console are privileged control surfaces. Approval decisions require the console header and a same-origin browser request. That is CSRF protection, not authentication: a local process that sends those headers can decide approvals, so run only trusted agents on the host, and restrict console access with tailnet ACLs.
  • Secrets are encrypted at rest with the local secret.key, and secret APIs expose names only. A flat can read and return its own secrets, so deploy code you trust with them. Anyone who can read the data directory can recover them.
  • Immutable code versions are not data backups. Back up the data directory, including secret.key, as described in Configuration and storage.

See the design and trust model for the details.

Documentation

The documentation index lists every page: operating a host, building on Flats, design contracts and verification gates.

Directories

Path Synopsis
cmd
flats command
Command flats hosts agent-built websites on this machine.
Command flats hosts agent-built websites on this machine.
Package runtimeref contains the agent documentation shipped with the host: guide topics and refusal pages under agent/, and the two versioned references assembled from them.
Package runtimeref contains the agent documentation shipped with the host: guide topics and refusal pages under agent/, and the two versioned references assembled from them.
internal
api
Package api is the HTTP API shared by agents, the CLI and the web console.
Package api is the HTTP API shared by agents, the CLI and the web console.
app
Package app wires every Flats module into the `flats serve` process.
Package app wires every Flats module into the `flats serve` process.
apps/docs
Package docs embeds the built-in collaborative document editor that runs "docs" content-type flats on the Flats JavaScript runtime.
Package docs embeds the built-in collaborative document editor that runs "docs" content-type flats on the Flats JavaScript runtime.
buildinfo
Package buildinfo reports which build of Flats is running: the release version for a release, otherwise the commit it was built from.
Package buildinfo reports which build of Flats is running: the release version for a release, otherwise the commit it was built from.
bundle
Package bundle validates uploaded builds and writes them as immutable version directories.
Package bundle validates uploaded builds and writes them as immutable version directories.
cli
Package cli implements the `flats` command line.
Package cli implements the `flats` command line.
clientstate
Package clientstate describes compatibility between a Flats host and its clients.
Package clientstate describes compatibility between a Flats host and its clients.
config
Package config reads, validates and writes the host configuration file (config.json).
Package config reads, validates and writes the host configuration file (config.json).
console
Package console serves the operator's web console: a single-page app with no build step whose assets are embedded in the binary.
Package console serves the operator's web console: a single-page app with no build step whose assets are embedded in the binary.
contenttype
Package contenttype names the content adapters supported by the host.
Package contenttype names the content adapters supported by the host.
core
Package core implements Flats: flats, versions, deployments, exposure, previews and approvals.
Package core implements Flats: flats, versions, deployments, exposure, previews and approvals.
egress
Package egress provides a deny-by-default, bounded HTTP client for guest code.
Package egress provides a deny-by-default, bounded HTTP client for guest code.
expose/local
Package local serves flats on one loopback port, routing by Host header (<host>.localhost:<port>).
Package local serves flats on one loopback port, routing by Host header (<host>.localhost:<port>).
expose/portal
Package portal exposes public flats through Portal relays (github.com/gosuda/portal-tunnel).
Package portal exposes public flats through Portal relays (github.com/gosuda/portal-tunnel).
expose/provider
Package provider serves flats on explicitly permitted networks.
Package provider serves flats on explicitly permitted networks.
expose/tsnet
Package tsnet serves flats on the operator's tailnet.
Package tsnet serves flats on the operator's tailnet.
expose/zrok
Package zrok exposes public flats through zrok public shares (github.com/openziti/zrok).
Package zrok exposes public flats through zrok public shares (github.com/openziti/zrok).
forkwatch
Package forkwatch unwedges child processes stuck between fork and exec; it is only needed on macOS.
Package forkwatch unwedges child processes stuck between fork and exec; it is only needed on macOS.
launchd
Package launchd installs `flats serve` as a per-user launchd agent on macOS.
Package launchd installs `flats serve` as a per-user launchd agent on macOS.
lifecyclecheck/adapter command
Command lifecyclecheck is a test-only HTTP host for deterministic provider faults.
Command lifecyclecheck is a test-only HTTP host for deterministic provider faults.
lifecyclecheck/legacyadapter command
Command legacyadapter seeds historical exposure and approval references.
Command legacyadapter seeds historical exposure and approval references.
mcpx
Package mcpx exposes Flats to coding agents as an MCP server over Streamable HTTP (stateless, JSON responses).
Package mcpx exposes Flats to coding agents as an MCP server over Streamable HTTP (stateless, JSON responses).
qjs
runtime
Package runtime runs server flats.
Package runtime runs server flats.
selfupdate
Package selfupdate performs explicit local client resource updates.
Package selfupdate performs explicit local client resource updates.
site
Package site serves the files of one flat version.
Package site serves the files of one flat version.
slug
Package slug validates flat names and builds preview host names.
Package slug validates flat names and builds preview host names.
store
Package store keeps Flats metadata in a single SQLite database.
Package store keeps Flats metadata in a single SQLite database.
systemd
Package systemd installs `flats serve` as a systemd user service on Linux (the counterpart of package launchd on macOS).
Package systemd installs `flats serve` as a systemd user service on Linux (the counterpart of package launchd on macOS).
Package plugins embeds the bootstrap shipped by the Flats plugin.
Package plugins embeds the bootstrap shipped by the Flats plugin.
scripts
tsnet-logout command
Command tsnet-logout logs a stopped tsnet node out of its tailnet, given the node's state directory (for example <data>/tsnet/<host>).
Command tsnet-logout logs a stopped tsnet node out of its tailnet, given the node's state directory (for example <data>/tsnet/<host>).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL