An open-source zero-human ops platform π¦Ύπ©Ά
Most AI workflows treat work as an ephemeral thread: a tab closes, a process ends, and the memory vanishes. The industry's reflex has been to build better plumbing by standardizing the pipes between models and tools, or daisy-chaining local assistants to pass context down the wire. But passing state through a pipe doesn't cure a stateless architecture; it just scales the amnesia. When the chain snaps, the shared context falls apart. Hyper-optimizing the solo operator has hit its scaling limit. An asynchronous workforce bound to synchronous manual coordination will always block at the speed of the human in the loop.
UFO (Unified Fleet Orchestrator) shifts the center of gravity. Instead of shuttling work between transient agents, it anchors agents to the work. On the surface, it feels like a calm operations board; underneath, it is shared institutional memory. Whether you are automating a routine chore or architecting a sprawling greenfield platform, the pattern is the same. A rover picks up the task and hands the controls to an AI agent acting as your pilot. It handles the heavy lifting in an isolated workspace and brings the finished work back to the board, whether that's a simple answer or an end-to-end solution. Routines ground the system, turning ad-hoc prompts into compounding momentum.
Note
Public beta: UFO's core workflow is usable. Tagged release notes call out upgrade caveats; before 1.0, APIs, the database schema, configuration, storage paths, and the rover protocol may still change. Back up before upgrading, especially when testing untagged source against data you care about.
See CHANGELOG.md for release history.
UFO is built around the fleet. Users bring intent as operations, missions give that work a durable frame, the Hub keeps state and policy, and rovers extend the fleet onto local machines where private context and tools already live. The goal is zero-human ops over time: routine coordination, execution, handoff, and follow-up should move from humans nudging work forward to UFO carrying more of the loop.
Because the unit is an operation rather than a repository or a chat session, the same model works for general assistant work, team workflows, and software changes. Conversations, assets, relationships, signals, history, and routines stay attached to the operation. Routines are still actively evolving, but they are the foundation for turning schedules, triggers, and shared context into repeatable operation loops.
When source code is involved, UFO adds a software handoff path: rovers create isolated git worktrees, stream telemetry, return diffs for review, and can hand off results by applying changes to source, creating a source branch, or refreshing from upstream source changes.
Users sign in, and fleets scope all data. Missions group related
operations and provide short keys like MSJ, producing operation codes such
as MSJ-123.
flowchart LR
Browser["Browser<br/>operations board"]
subgraph Hub["UFO Hub"]
Web["Next.js web<br/>apps/web<br/>same-origin UI + /api/v1 facade"]
API["Go API<br/>apps/api<br/>REST, WebSocket, rover protocol"]
end
subgraph Data["State + storage"]
DB[("PostgreSQL<br/>tenancy, queues, leases<br/>LISTEN/NOTIFY")]
Assets[("Asset storage<br/>local filesystem, S3-compatible, GCS")]
end
subgraph Host["Rover host"]
Source["Source checkout<br/>user repo"]
Rover["Rust rover<br/>apps/rover<br/>enrolls, accepts, heartbeats"]
Worktree["Operation worktree<br/>isolated loop workspace"]
Pilot["Local AI CLI pilot<br/>Codex, Claude, Cursor, Copilot, ..."]
end
Browser <-->|UI, REST, WebSocket| Web
Web <-->|/api/v1 rewrite| API
API <-->|state, dispatch, notifications| DB
API <-->|upload/download URLs| Assets
API <-->|enroll, accept runs, stream config<br/>heartbeats, telemetry, results| Rover
API <-->|accept source handoff actions| Rover
Rover -. opens approval .-> Browser
Source -->|git worktree base| Worktree
Rover -->|prepare + supervise| Worktree
Rover -->|run assigned pilot| Pilot
Pilot <-->|tools + file edits| Worktree
Worktree -->|diff, artifacts, review handoff| Rover
Worktree <-->|apply to source<br/>create source branch<br/>refresh from source| Source
classDef user fill:#eef7ff,stroke:#2f6f9f,color:#0f2f44
classDef hub fill:#f5f0ff,stroke:#7c5cc4,color:#25164a
classDef data fill:#f0f8f3,stroke:#4f8a62,color:#16391f
classDef host fill:#fff6e6,stroke:#b7791f,color:#402600
class Browser user
class Web,API hub
class DB,Assets data
class Source,Rover,Worktree,Pilot host
apps/webβ Next.js product UI: a default drag-and-drop Kanban board plus List and Lanes views; operation detail pages with conversations, live run timelines, diffs, labels, reactions, sub-operations, relationships, and Signals. Proxies/api/v1.apps/apiβ Go Hub (pgx + sqlc): auth, fleets, memberships, invitations, pilots, crews, operations, comments, runs, artifacts, assets, missions, labels, reactions, signals, rover enrollment, and connection-token endpoints.apps/roverβ Rust CLI (ufo-cli): provides the rover command that enrolls by browser approval or code, stays ready for operation and accepts runs, lets the assigned pilot drive the rover, streams typed messages, uploads agit diff, and reports terminal state. One rover can hold many enrollments.apps/api/internal/migrate/migrations,apps/api/internal/db/queriesβ SQL migrations (embedded) and sqlc queries.apps/api/internal/spec/openapi.yamlβ OpenAPI source of truth; embedded and served at/openapi.yaml.
- Accounts + tenancy: email/password + httpOnly access JWT cookie; fleets + memberships scope every entity; invite teammates by email (owner/admin/member).
- Unified operations context: missions, comments, assets, relationships, signals, routines, and history keep team/org context attached to the work instead of scattering it across local agent sessions.
- Routines as loop foundation: routines are the actively evolving layer for turning schedules, triggers, and shared context into recurring operation loops.
- Rovers as teammates: each rover has its own connection token, reports available/full/offline status, and receives work only when its tags match.
- Pilots drive rovers: a pilot is a local AI CLI that drives a rover; assign an operation to a pilot and a capable fleet rover accepts it. Crews can include pilots and humans; assigning to a pilot or pilot-backed crew auto-dispatches, while human-only work stays in backlog. If the pilot has no rover to drive in the fleet, the operation is blocked with a signal instead of queueing forever.
- Operations as conversations + review handoff: pilots work in resumable sessions, stream typed telemetry, return a diff artifact, and hand successful runs to In Review instead of auto-closing them. Operation bodies, comments, and routines can reference uploaded assets backed by local storage in dev or object storage in production.
- Source/worktree handoff: rover-hosted source checkouts stay local; UFO creates per-operation git worktrees, then lets reviewers refresh from source, apply worktree changes back to source, or commit them to a source branch.
- Planning dates vs lifecycle time: operation
start_date/due_dateare editable planning dates;started_at/finished_atare UTC lifecycle timestamps set by status changes. - Board: Kanban, List, and Lanes views with configurable columns, filters, sorting, labels, reactions, sub-operations, relationships, and signals for review handoffs or blocked work.
- Real-time over PostgreSQL
LISTEN/NOTIFY: WebSocket UI updates and rover monitoring plus Hub push share the database as the coordination layer; no extra broker is required. - Orphaned-run lease: rover heartbeats; an API sweeper requeues silent
runs (
UFO_HUB_RUN_LEASE_SECONDS, default 30). - Multi-instance-safe: versioned migrator under a
pg_advisory_lock, accept viaFOR UPDATE SKIP LOCKED, events ordered by insertion id, stateless API.
Trust boundary: anyone in a fleet can dispatch work to connected rovers. Pilots run local CLIs with the rover user's privileges. Use dedicated users or hosts for rovers, and read
SECURITY.mdbefore sharing a fleet.
- Docker β runs PostgreSQL, the API, and the web board.
- Rust/Cargo β runs the rover from this checkout through
scripts/dev.sh rover. The rover always runs on the host because it touches local files and tools.
For using the rover outside this source checkout, install the released CLI instead:
curl -fsSL https://getufo.dev/install.sh | shHomebrew also works:
brew install fengsi/ufo/ufo-cliThe rover CI runs on macOS, FreeBSD, Linux, and Windows. Other operating systems may work, but are not tested yet.
Only needed for the optional host-based dev path (running api/web without Docker):
- Go β₯ 1.26, Node β₯ 20.9 (Next 16 requires it), and
sqlc(brew install sqlc, only if you change SQL).
Use the rover command that matches how you are running UFO:
| Context | First run | Later runs |
|---|---|---|
| From this source checkout | UFO_HUB_URL=<hub-url> scripts/dev.sh rover enroll |
scripts/dev.sh rover |
| Installed CLI | ufo rover enroll --hub <hub-url> |
ufo rover start |
The dev wrapper runs the local Rust crate with cargo run, points at the
local Hub, and keeps rover work rooted at this checkout. In both forms,
enroll starts the rover after approval. See
Rover CLI for installed CLI usage.
Recommended β Docker for everything except the rover:
# Docker (live watch): PostgreSQL + API + web
scripts/dev.sh upSource edits sync into the containers live β the web has Fast Refresh and the
API restarts on change (docker compose watch); no manual rebuild. Docker dev
stores local assets in ~/.ufo/assets, the same default path used when
running the Hub directly, so worktrees share local assets.
Release notes call out upgrade caveats for each tagged release. If you are testing arbitrary source commits that rewrite the beta schema, back up first; only discard the Docker volume when you intentionally want to delete local data.
-
Open http://localhost:3000 and sign up β a fleet is created for you.
-
Start rover enrollment on the host. The dev wrapper opens the Rovers page, tells you to sign in if needed, pick the fleet/name/units/tags in the approval modal, then waits until approval stores the rover token:
scripts/dev.sh rover enroll
That command enrolls through the browser, then starts the rover in the same terminal. For later runs, use
scripts/dev.sh rover.Start the rover from the git checkout you want it to work on. When it sees a checkout, each accepted operation runs in a detached git worktree under the outpost, with current non-ignored local changes copied in, instead of editing the running checkout directly.
Enrollment stores a connection token (keyed by rover id) in
~/.ufo/rovers.json; later runs use the stored token. For code-based or non-browser enrollment, the Rovers panel can still create an enrollment code forUFO_ROVER_ENROLLMENT_CODE=<code> scripts/dev.sh rover enroll.In an interactive terminal, the rover start command opens the live rover TUI. Use
scripts/dev.sh rover --headlessfor later runs, orscripts/dev.sh rover enroll --headlesson first run, for CI, launchd/systemd, or old log-oriented output.For list/status/remove commands and installed CLI usage, see Rover CLI.
-
Create a mission, then an operation on the board, assign it to a pilot, and watch the run move
queued β accepted β running β succeededlive, with its diff artifact. The rover shows available/full/offline in the Rovers panel.
Alternative β everything on the host (needs Go + Node β₯ 20.9 installed),
one process per terminal (api, web, then sign up and enroll rover):
# Docker: PostgreSQL only
scripts/dev.sh db
# host: Go API
scripts/dev.sh api
# host: Next.js dev server
scripts/dev.sh web
# host: Rust rover (approve in browser, then starts)
scripts/dev.sh rover enrollCopy .env.example to .env to override defaults:
| Var | Default | Used by |
|---|---|---|
UFO_HUB_URL |
http://localhost:8080 |
rover, web (Hub origin; clients append /v1) |
UFO_HUB_BIND |
:8080 |
api |
UFO_HUB_WEB_URL |
(unset) β web UI base URL advertised by the Hub for browser approval flows | api |
UFO_HUB_DATABASE_URL |
(optional) β full PostgreSQL URI or libpq keyword string; when unset, built from official libpq vars PGHOST / PGPORT / PGUSER / PGPASSWORD / PGDATABASE / PGSSLMODE; default local compose PostgreSQL URL |
api |
UFO_HUB_TEST_DATABASE_URL |
(unset) β integration-test DB only; must not be the same database as the runtime Hub URL; tests skip when unset | api / CI |
UFO_HUB_ALLOWED_ORIGINS |
(unset) β CORS + WebSocket origin allowlist; set in production | api |
UFO_HUB_SECURE_COOKIES |
(unset) β set when serving over HTTPS | api |
UFO_HUB_TRUST_PROXY |
(unset) β set when a reverse proxy sets X-Forwarded-For / X-Real-IP (auth rate limits) |
api |
UFO_HUB_JWT_PRIVATE_KEY |
(required in prod) β base64 Ed25519 seed/private key for EdDSA access tokens | api |
UFO_HUB_JWT_ALLOW_EPHEMERAL |
(unset) β set 1 only for local/dev so a missing private key uses a per-process ephemeral key |
api |
UFO_HUB_JWT_ACCESS_SECONDS |
3600 β user access JWT lifetime |
api |
UFO_HUB_JWT_ISSUER |
ufo-hub β user access JWT issuer |
api |
UFO_HUB_JWT_AUDIENCE |
ufo-api β user access JWT audience |
api |
UFO_HUB_MIN_ROVER_VERSION |
0.5.0 β oldest rover CLI version allowed to enroll/connect |
api |
UFO_HUB_MAX_ROVER_VERSION |
(unset) β optional newest rover CLI version allowed to enroll/connect | api |
UFO_HUB_RUN_LEASE_SECONDS |
30 |
api |
UFO_HUB_LONGPOLL_SECONDS |
25 |
api |
UFO_HUB_ROUTINE_SCHEDULER_SECONDS |
60 β seconds between due scheduled-routine checks |
api |
UFO_HUB_ROVER_ONLINE_WINDOW_SECONDS |
60 β seconds since last contact before a rover is offline |
api |
UFO_HUB_MAX_SUB_OPERATIONS |
8 β max sub-operations a captain can propose at once |
api |
UFO_HUB_ASSET_BACKEND |
local β use s3 for AWS S3, Cloudflare R2, MinIO, or another S3-compatible host; use gcs for Google Cloud Storage |
api |
UFO_HUB_ASSET_LOCAL_ROOT |
~/.ufo/assets β override the local backend root |
api |
UFO_HUB_ASSET_UPLOAD_MAX_BYTES |
26214400 β max bytes per uploaded file |
api |
UFO_HUB_ASSET_UPLOAD_ALLOWED_CONTENT_TYPES |
(unset) β comma-separated exact types or wildcards like application/pdf,image/* |
api |
UFO_HUB_ASSET_S3_BUCKET |
(unset) β bucket/container for s3 backend |
api |
UFO_HUB_ASSET_S3_ENDPOINT |
(unset) β set for Cloudflare R2, MinIO, or custom S3-compatible hosts | api |
UFO_HUB_ASSET_S3_REGION |
auto |
api |
UFO_HUB_ASSET_S3_ACCESS_KEY_ID |
(unset) β access key for S3-compatible storage | api |
UFO_HUB_ASSET_S3_SECRET_ACCESS_KEY |
(unset) β secret key for S3-compatible storage | api |
UFO_HUB_ASSET_S3_PATH_STYLE |
(unset) β set for path-style providers | api |
UFO_HUB_ASSET_S3_PREFIX |
(unset) β optional key prefix before v1/... |
api |
UFO_HUB_ASSET_GCS_BUCKET |
(unset) β bucket for gcs backend |
api |
UFO_HUB_ASSET_GCS_CREDENTIALS_FILE |
(unset) β service-account JSON file for gcs backend; falls back to GOOGLE_APPLICATION_CREDENTIALS |
api |
UFO_HUB_ASSET_GCS_CREDENTIALS_JSON |
(unset) β service-account JSON value for gcs backend; a private key is currently required for signed URLs |
api |
UFO_HUB_ASSET_GCS_ENDPOINT |
(unset) β optional Google Cloud Storage API endpoint override | api |
UFO_HUB_ASSET_GCS_PREFIX |
(unset) β optional object prefix before v1/... |
api |
UFO_HUB_ASSET_SIGNED_URL_SECONDS |
900 β signed upload/file URL lifetime |
api |
UFO_ROVER_VERSION |
latest β release used by the shell installer and rover upgrade flow |
rover |
UFO_ROVER_INSTALL_DIR |
~/.local/bin β shell-installer target directory |
rover |
UFO_ROVER_OUTPOST |
~/.ufo (operation work directories: <outpost>/rovers/<rover-id>/operations/<yyyy>/<mm>/<dd>/<operation-id-shard>/<worktree-name>) |
rover |
UFO_ROVER_CONFIG |
~/.ufo/rovers.json β local enrollment store |
rover |
UFO_ROVER_ENROLLMENT_CODE |
(optional fallback code from the Rovers panel; used by rover enroll) |
rover |
UFO_ROVER_UNITS |
(unset) β startup units fallback, 1-100, until hub config is available (--units) |
rover |
UFO_ROVER_RETRY_SECONDS |
1 β wait after a failed accept before retrying |
rover |
UFO_ROVER_HEARTBEAT_SECONDS |
5 β run lease-renewal interval |
rover |
UFO_ROVER_ASSET_UPLOAD_MAX_BYTES |
104857600 β max bytes for each rover-uploaded referenced file |
rover |
UFO_ROVER_ASSET_UPLOAD_MAX_FILES |
20 β max referenced files a rover uploads for one run |
rover |
UFO_ROVER_AUTO_UPGRADE |
(unset) β set to 1 to let the rover auto-upgrade when the Hub requires it |
rover |
- Web does not load: confirm Docker is running, then check
docker compose psanddocker compose logs -f web api postgres. - API cannot reach PostgreSQL: use
scripts/dev.sh uporscripts/dev.sh db, and confirmUFO_HUB_DATABASE_URLpoints at the running database. - Sign-in works but browser calls fail: set
UFO_HUB_ALLOWED_ORIGINSto the web origin and useUFO_HUB_SECURE_COOKIES=1only behind HTTPS. - Rover does not enroll: confirm the Hub URL points at the API origin, then approve enrollment in the browser. Use the command form from the table above.
- Rover is online but does not accept work: confirm the operation is assigned
to a pilot, the pilot CLI is on the rover
PATH, and tags match. - Intentionally discard local dev data:
scripts/dev.sh down -v && scripts/dev.sh updeletes the Docker database volume. Do this only after backing up anything you want to keep.
BSD 3-Clause. See LICENSE and THIRD_PARTY_NOTICES.md.
