bowline
Concepts

Architecture

How Bowline keeps your code folder everywhere using a local Rust daemon, a cloud control plane, and an encrypted byte store you never have to manage.

Bowline is a local engine with a thin cloud behind it. A Rust daemon on each machine does the real work: encryption, hashing, materialization, and conflict resolution. The cloud is split into a control plane that holds metadata and a byte store that holds encrypted packs. Everything sensitive is encrypted on your machine before it leaves, so the cloud never sees your plaintext. This page explains how the pieces fit so you understand what's happening underneath, even though you never have to manage any of it.

Overview

Bowline runs as a background daemon on every Authorized Device. The daemon is the engine: it watches ~/Code, captures changes, encrypts them, and reconciles them against what other machines and agents have done. The cloud exists only to coordinate and to store encrypted bytes.

The cloud has two distinct planes, plus a separate web surface:

  • The Cloud Control Plane holds metadata, decisions, and pointers. It runs on hosted Convex.
  • The Byte Plane holds immutable encrypted packs and manifests. It runs on Cloudflare R2.
  • The Web Runtime runs your account, billing, and status surfaces. It is not the sync engine.

Nothing in the cloud holds your plaintext code or environment values. The daemon encrypts everything sensitive client-side before upload, so the control plane and byte plane only ever handle ciphertext and metadata.

The local daemon

The daemon is where sync correctness lives. It owns local encryption, content hashing, chunking, materialization, filesystem reconciliation, conflict detection, and Automatic Merge decisions. When you edit a file, the daemon coalesces the raw filesystem events into a settled version, hashes the content, encrypts it, packs it, and proposes a new Workspace Head. When another machine advances the head, the daemon imports those changes and merges what it safely can.

The daemon talks to the cloud only through one product-shaped Rust boundary, the ControlPlaneClient. Raw Convex schema names, function names, and document shapes must not spread through the local sync code. That boundary keeps the filesystem semantics independent of the cloud provider, so the control plane can change underneath without touching how sync behaves.

For steady-state workspace status and sync, the CLI and TUI talk to the daemon over a local Unix socket, and the daemon is the single authority on state. A small set of explicit setup and recovery commands can construct the hosted ControlPlaneClient directly: account login/session bootstrap, device approval and Recovery Key operations, remote bootstrap handoff, and the hosted smoke integration test. Those commands still go through the same Rust control-plane boundary rather than importing raw Convex shapes. The daemon remains the owner of continuous sync, event-backed workspace state, and byte movement.

Under the hood, the daemon uses Rust and Tokio for the async runtime, SQLite in WAL mode for local metadata and the locator cache, BLAKE3 for content identity, whole-file hashing for ordinary source files, and FastCDC-style chunking only for large files where whole-file hashing is wasteful.

When you save a file, the write path runs entirely on your machine before the cloud is involved:

  1. Coalesce the noisy editor and watcher events into one settled version.
  2. Hash the content and skip anything whose Content ID already exists.
  3. Pack and compress the new content, then seal it with authenticated encryption.
  4. Upload the immutable encrypted packs and the snapshot manifest to the Byte Plane.
  5. Commit the object metadata and propose the ref advance through the ControlPlaneClient.

If the compare-and-swap fails because another writer moved first, the uploaded packs are left unreferenced and garbage-collected later, so a lost race never corrupts anything.

Ordinary-directory materialization

~/Code is an ordinary directory, not a mount or projection filesystem. After the daemon accepts a remote snapshot, it schedules every required source, configuration, env, and portable Git path for background materialization. The selected project is prioritized so it becomes useful first; remaining projects continue automatically.

For each file, the daemon reads its authenticated pack range, decrypts and verifies the record, writes a temporary file on the destination filesystem, syncs it, and atomically installs it. A path is ready only after an ordinary read returns the intended bytes. Namespace discovery and cache residency are reported separately and never make an absent path look ready.

This architecture deliberately does not promise fetch-on-open. Normal directories cannot intercept arbitrary opens from editors, compilers, shells, Git, or containers. If the network is offline or an object is missing, the path remains visibly queued or blocked instead of exposing a placeholder or partial file. Ready files remain usable offline.

The Cloud Control Plane

The Cloud Control Plane is hosted Convex. It owns metadata, status, and decisions, never code bytes. Its job is to coordinate machines and to hold the small, authoritative records that say what the current state of a workspace is.

The control plane owns:

  • accounts, workspaces, and device records
  • Device Approval Requests and encrypted device grants
  • Recovery Key envelopes
  • workspace refs and machine refs
  • policies, env records, and env restrictions
  • compact events and status
  • manifest and object pointers

Ref advancement uses compare-and-swap, so exactly one writer wins a race and the other gets a clean stale-ref result to retry. Reactive queries return compact status and event pointers, not the full workspace tree, which keeps the control plane fast and cheap. It coordinates approvals and grants without ever holding plaintext workspace keys.

What the control plane deliberately does not do matters as much as what it does. It does not store raw file bytes, bulk manifests, or chunk contents, and it does not proxy bulk byte transfer. Those live in the Byte Plane, and the daemon moves them to and from R2 directly over signed URLs.

The Byte Plane

The Byte Plane is Cloudflare R2. It stores immutable encrypted byte packs and encrypted manifests, and nothing else. There is no plaintext code in R2, and there is never one object per source file.

A source path never maps directly to an R2 object key. Instead, a path maps to a Content ID, a workspace-scoped keyed BLAKE3 identity for the normalized file bytes:

path -> contentId -> packId + offset + length

A Pack Locator then maps a Content ID to packId + offset + length inside an encrypted manifest and the local SQLite locator cache. Keyed hashing gives deduplication inside a workspace without creating a cross-user content oracle:

contentId = BLAKE3_keyed(workspace_content_key, normalized_bytes)

R2 object keys must not leak project paths, filenames, repo names, env filenames, or your directory structure. Keys live under random-looking, workspace-scoped prefixes:

/v1/ws/<workspace-id>/manifests/<manifest-id>.bowlinem
/v1/ws/<workspace-id>/packs/<pack-prefix>/<pack-id>.bowlinep

Because identity is a keyed hash and object names are workspace-scoped, the byte store can't tell that two users have the same dependency, and it can't reconstruct your folder layout from object keys alone.

The Web Runtime

The Web Runtime is TanStack Start on Cloudflare Workers. It powers your account, billing, dashboard, support, and status surfaces. It is not the sync engine, and it is not the product center.

The dashboard exists for the workflows that are awkward in a terminal, such as managing billing or viewing account-level status. It does not compute sync truth, reconcile conflicts, apply work views, or replace the headless CLI and TUI path. Sync correctness stays in the daemon; the web surface only reads and presents.

Encryption and privacy

Encryption happens on your machine, before anything is uploaded. The daemon seals source packs and snapshot manifests with authenticated encryption using a fresh nonce per envelope. The control plane and the byte plane only ever see ciphertext, hashes, sizes, and pointers.

Device trust is built the same way. When you approve a new device, an existing Authorized Device wraps the workspace key material for the new device's public key and uploads only that encrypted grant. The cloud coordinates the request but never receives plaintext workspace keys. Account sign-in proves your identity; it does not release decryption access on its own.

Important

The Cloud Control Plane and the Byte Plane never see your plaintext code or environment values. If you lose every Authorized Device and your Recovery Key, Bowline can recover your account through your identity provider, but it cannot decrypt that workspace's data. There is no default server-side key escrow.

What this means for you

The machinery above is deliberately invisible. In practice:

  • Your code and environment values are end-to-end encrypted, sealed on your machine before upload.
  • The cloud can't read your code, can't reconstruct your folder layout, and can't tell two users apart by content.
  • A new machine shows the full ~/Code tree right away and fills in bytes as you touch them.
  • A lost write race is harmless: nothing is overwritten, and the daemon retries against the current head.
  • You never pick a backend, manage packs, copy keys, or move env files. The daemon does all of it.

Components at a glance

Each layer has one clear responsibility and one technology behind it. You interact with the daemon through the CLI, TUI, and ~/Code; the rest stays underneath.

LayerResponsibilityTechnology
DaemonLocal encryption, hashing, chunking, durable materialization, filesystem reconciliation, conflict detection, and Automatic MergeRust, Tokio, SQLite WAL, BLAKE3
Ordinary directoryHolds verified normal files; selected projects materialize first and remaining work continues in the backgroundNative filesystem
Cloud Control PlaneMetadata, status, device trust, refs, policies, env records, events, and object pointersHosted Convex
Byte PlaneImmutable encrypted byte packs and encrypted manifests onlyCloudflare R2
Web RuntimeAccount, billing, dashboard, support, and status surfacesTanStack Start on Cloudflare Workers

Next steps

  • Workspace model: how Workspace Snapshots and the Workspace Head define your project state.
  • Sync and continuity: how edits become snapshots and how Automatic Merge advances the head.
  • Device trust: how a new machine earns workspace decryption access.
  • Environment sync: how project env follows your machines and agents.
  • Status and health: how the daemon's event-backed status surfaces in the CLI and TUI.
  • CLI overview: the commands you use to drive the daemon.

On this page