Postgres for AI agents — instant branches, and a tamper-evident record of every schema change.
FoxByte is PostgreSQL with instant branching, for a situation that did not use to exist: code writing to your database without a person having read it first.
Branching is how an agent gets a database of its own — a full copy in seconds,
thrown away when it is done. The Blackbox is why you can let it
near one. Every CREATE, ALTER, DROP and GRANT is recorded as it happens,
attributed to the login that made it, in an append-only hash chain you can
verify. A change an agent made at 3am is distinguishable from one you made, and
a policy gate can refuse the destructive ones before they run.
Underneath it is stock PostgreSQL 18 on local NVMe, so commits keep native
latency. Branching and time-travel move off the commit path — ZFS
copy-on-write clones, and asynchronous WAL archival (wal-g) for
point-in-time recovery. It speaks the native Postgres wire protocol, so your
driver, ORM and SQL are unchanged.
The command-line tool is fox. Everything below is a fox … command.
New here? Jump to Install · Quickstart · Blackbox
The web console is served by the engine itself at https://localhost:8080 — no separate dev server to run.
| Ops dashboard | Blackbox |
|---|---|
![]() |
![]() |
| SQL console | Realtime |
![]() |
![]() |
- Blackbox — every
CREATE/ALTER/DROP/GRANTrecorded with the actor (human or agent), tool, and branch; tamper-evident (hash-chained and append-only, checked byfox blackbox verify, with signed anchors for what a database superuser could rewrite). The actor is the login the session authenticated as, which nothing the session runs can change: people and agents each log in through the gateway as a role of their own. Where an application acts for one of its users, it can say which with a signed token, and the record shows the change was made for that user, asserted by the application. - Instant branching —
fox branch create qaclones the whole database in seconds (copy-on-write), fully isolated;mainis untouched. Plusfox branch reset(start over) andfox branch diff(what changed, from Blackbox). - Time travel / PITR — continuous WAL archival; restore to any point.
- One endpoint — connect to
:6432; the database name is the branch. Idle branches scale to zero and wake on connect. TLS on by default, sosslmode=requireclients connect out of the box. - A database per AI agent — the Agent Branch API over HTTP, or the
Model Context Protocol (
fox mcp): an agent gets a database, runs SQL, sees what it changed (from Blackbox), and throws it away — one standard interface. - Migrate from anything — import from PostgreSQL, MySQL/MariaDB, MongoDB, and
.sql/.csv/.json/.ndjsonfiles, each landing in a fresh branch. - ETL pipelines (experimental) — dbt-style SQL models with data-quality tests, each run against a throwaway branch.
- High availability — a hot standby with transparent
ha failover(single-VM demonstration). - Accounts, API keys, RLS — email/password or GitHub/Google OAuth; keys are the gateway password; Postgres row-level security and GRANTs apply to clients.
- Web console — a React UI served by the engine: dashboard, SQL console, Blackbox viewer, import, pipelines, API keys.
- Client SDKs — an OpenAPI spec (served at
/api/openapi.yaml) plus dependency-free Python and TypeScript clients.
Per-install credentials are generated on first run — nothing is hardcoded.
The differentiator: Blackbox, the database's own flight recorder for schema
changes (formerly called the Schema Ledger — fox ledger …, the /ledger API
routes and the original MCP tool names all still work). Three Postgres event triggers, installed into main and
inherited by every branch, capture every schema change and attribute it:
- who — the actor (a human email, or
agent-alice) and whether it was a human or an agent; - for whom — where an application said so, the user it acted for:
asserted by shopwhen the application vouched with a token signed by its key, orclaimedwhen a client only said so; - what — the command, the object, and the full statement;
- context — the tool (
application_name, e.g.cursor/opus), the branch, and the session; - a guardrail — destructive commands (
DROP TABLE,DROP SCHEMA,TRUNCATE) are blocked by policy unless an admin overrides them, and blocked attempts are recorded too; - data changes by agents — every
UPDATEandDELETEan agent runs is recorded with its table and statement (people's are not, to keep ordinary writes fast), and everyTRUNCATEfrom anyone; - limits on agents' changes — an agent's
UPDATEorDELETEis refused once it has changed more rows of a table than that table's limit (100 by default, whatever itsWHEREsays), and refused outright with noWHEREat all, and over time each agent may change at most 1,000 rows of a table per hour across all its statements.fox guardrail limitandfox guardrail budgetchange them, per table if needed; an admin can let one statement through.
fox blackbox # every schema change on this branch, most recent first
fox blackbox verify # check the hash chain: an edited or deleted entry breaks itIt cannot be quietly rewritten. The table is append-only, and each row is
hash-chained to the one before it, so a deleted or edited entry breaks the chain
and fox blackbox verify catches it. A database superuser could rewrite rows
and recompute every hash after them; that is what anchors are for. Every
few minutes the engine writes a checkpoint of the chain to a file outside the
database, signed with a key the database cannot read, and
fox blackbox integrity (or the standalone fox-verify --pubkey) checks the
rows against them. Writing those anchors for a branch's Blackbox is an
Enterprise feature; checking them is not, in any edition — an independent
verifier you have to buy is not much of an independent verifier, and an install
that anchored before upgrading keeps every anchor it has and can still prove it.
The security log's own anchors are free as well: that is the accountability
trail, and safety stays free. Keep anchors off the machine and give the
public key (fox blackbox anchor-key) to whoever audits; someone with root on
the machine itself holds the key too, so for them anchors held elsewhere are
the proof. fox blackbox anchor-target set s3://bucket … copies every anchor to
a bucket of its own, ideally one the database administrators do not control,
locked in Object Lock's COMPLIANCE mode for a year so nobody can remove or
rewrite it; fox check warns while there is none. And
because the gateway logs each client in as a per-user Postgres role, the
recorded actor is the login the session authenticated as, and no SET in the
session changes it: a person's own role, an agent key's agent role
(agent:<email>), an agent branch's role (agent-<id>), and for MCP the key's
account's agent role. A database superuser is outside that, as for any record a
database keeps about itself: they can attribute anything to anyone, which is
what anchors held where they cannot reach are for. There is a Blackbox page
in the web console too.
Who a change was for. An application that serves many people through one
account can say, per transaction, which of its users a change is for. Register
it once (fox app register shop --account shop-svc@example.com --key shop.pub),
sign a short-lived token per user with its private key, and pass it with the
change (SELECT bb.act_for($1), or withActor in the clients). The record then
shows alice, asserted by shop. That means the application vouched for alice,
with a key only it holds and from its own login, not that alice was present:
the application, or whoever holds both its key and its credentials, can assert
anyone, which is why a key can be revoked and reported compromised from a given
moment. A value a client sets by hand is never taken for an assertion.
And what the event triggers miss. The record is written by event triggers,
and a database superuser can stop them (SET event_triggers = off, SET session_replication_role = replica, ALTER EVENT TRIGGER … DISABLE); roles,
role memberships, databases and settings never reach them at all. So every five
minutes the engine compares the catalogue with what it saw last time, and writes
what no entry accounts for into the Blackbox: UNRECORDED for a change made
with the triggers stopped, OBSERVED for a role, a membership or a setting, and
risk kill-switch for one that switches the record off. Who made such a change
is unknown, but it is on the record. fox blackbox scan runs it now, and
docs/blackbox-coverage.md lists exactly what the
Blackbox records and what it does not.
Undo a change you should not have made. Because every entry carries the
transaction that made it, and main archives its write-ahead log, the record is
not only evidence — it is a way back. Point at an entry and get a new branch
holding main exactly as it was just before that change, with main left
alone:
fox blackbox entries # the newest entries with their ids
fox blackbox branch-before 42 # a branch of main as it was just before entry 42
fox blackbox branch-before 42 --as before-the-drop # …under a name you chooseIt restores a base backup and replays WAL, so it takes a few minutes and needs a
base backup taken before the change. Inspect the new branch, copy what you need
back, and throw it away — or point your application at it. The same action is on
every row of the Blackbox page in the console, and at
POST /api/branches/main/ledger/{id}/branch. main is the only branch this
works from: it is the only one that archives WAL.
Bring a branch's changes back. Branching was half a workflow until this: you
could copy the database, change it, and prove what changed — and then there was
no way to apply it to main. A change request offers a branch's recorded
statements for review, and someone who can manage the target applies them:
fox branch request dev --to main # what dev has that main does not; nothing is applied
fox request show 3 # the statements, in the order they ran on dev
fox request approve 3 # applied to main in one transactionWhat is applied is the Blackbox's own record of what ran on dev, so main ends
up holding the same statements, each attributed to whoever wrote it, with the
approver beside it. It is refused if both branches changed the same object since
they split, if main's policy gate would block a statement, or if there is
nothing to promote. Schema only — no data is moved. An agent can ask
(MCP request_changes) and cannot approve its own: approving needs the right to
manage the target. There is a Requests page in the console.
Beside it, a security log records what happens at the doors — sign-ins and
failed ones, sign-ups, API keys made and revoked, password changes, deleted
accounts, admin grants and every refused request — chained and anchored the
same way: fox audit, fox audit verify, and GET /api/audit for admins.
One command installs the fox CLI; a second brings everything up. On macOS and
Windows the Linux engine (ZFS/btrfs + Docker + Postgres) runs transparently
inside a managed VM, so day-to-day you only ever type fox ….
Every install pulls the latest engine build and (re)installs it, so re-running setup always updates you to the newest version rather than reusing an old one.
Needs Lima for the local Linux VM.
brew install lima
curl -fsSL https://get.thefoxbyte.com/install.sh | sh
fox setupfox setup creates a dedicated Lima VM, installs Docker + ZFS in it, and brings
the stack up. Your first fox setup downloads Ubuntu (a few minutes); after that
it's fast.
The engine runs directly (no VM). ZFS + Docker are provisioned on first start.
curl -fsSL https://get.thefoxbyte.com/install.sh | sh
sudo fox startThe services listen on 127.0.0.1 only. To reach them from other machines, set
FOX_LISTEN=0.0.0.0 — TLS is then required, so give it a real certificate with
FOX_TLS_CERT/FOX_TLS_KEY — and put a firewall in front (see
Security defaults).
Run in PowerShell (not Command Prompt — irm/iex are PowerShell commands).
Needs Windows 10 21H2+/11 with virtualization enabled; everything else is handled
for you. The engine runs in a dedicated WSL2 distro that stores branches on
btrfs, so it works on any WSL kernel — nothing kernel-specific to build.
irm https://get.thefoxbyte.com/install.ps1 | iexThat installs WSL if absent (no Linux distribution of your own needed —
FoxByte brings its own dedicated distro), downloads FoxByte, puts it on your
PATH, and runs fox setup. If Windows needs a reboot to finish enabling WSL, it
says so and resumes automatically afterwards. Your other WSL distros and Docker
Desktop are untouched. Full prerequisites and troubleshooting:
docs/windows-setup.md.
make build # host binary -> ./bin/fox
make vm-build # build the Linux engine into the Lima VM (macOS)Set FOX_NO_REFRESH=1 when running fox setup from a source build, so it
keeps your locally-built engine instead of downloading a release.
Every release's SHA256SUMS is signed with the FoxByte release key
(SHA256SUMS.sig, Ed25519). install.sh checks the signature against the key
it carries — which comes from this repository, not from the release — wherever
openssl can do Ed25519 (OpenSSL 3; macOS's LibreSSL cannot, and the installer
says so; FOX_REQUIRE_SIGNATURE=1 refuses instead). fox update checks it on
every update and never installs a release that fails. Each file also has a
GitHub build attestation and the release an SBOM (foxbyte-sbom.spdx.json):
gh attestation verify fox-darwin-arm64 --repo thefoxbyte/foxbyte
gh attestation verify oci://ghcr.io/thefoxbyte/postgres-walg:18 --repo thefoxbyte/foxbyteFor maintainers: the release key's private half is the FOX_RELEASE_SIGNING_KEY
Actions secret (and a password manager); make release-key made it once, and
replacing it would stop released builds from updating. The Actions are pinned
by commit and the images by digest; Dependabot proposes Action and module
updates, and scripts/check-image-digests.sh reports image pins that have
fallen behind their tags.
After fox setup (macOS/Windows) or fox start (Linux), the console opens by
itself at https://localhost:8080, with the one-time setup token already
in the link — choose an email and a password and you are in. (fox setup-token
prints the token if you need it; fox user create <email> works too, and
FOX_NO_BROWSER=1 leaves the browser alone.) The first account on an install can
override the destructive-change guardrail, which is why it needs the token.
main already has three small sample tables — users, projects and events,
with five rows each — so there is something to query, alter and branch straight
away. FOX_NO_DEMO=1 starts empty; fox demo drop removes them later.
Then make an API key on the API keys page (or fox apikey create <email> <name>;
it is shown once) and use it as the password in the connection string below:
fox check # is this install healthy? each failure says what to run
fox status # servers, primary readiness, branches
fox branch create qa # copy-on-write branch of main; prints its connection string
fox connect qa # that string again, then a psql shell on the branch
fox sql --branch qa "SELECT 1" # run SQL and print the result
fox sql --format csv "SELECT …" > out.csv # or tsv, markdown, json, insert --table <t>
fox sql --format xlsx "SELECT …" > out.xlsx # an Excel workbook
fox export "SELECT …" > all.csv # every row, streamed (csv, tsv, json, xlsx); read only
fox history # the SQL you ran (never results), kept 90 days
fox history --search orders # find one; then: fox history run <id>, show <id>, save <name> --from <id>
fox schema # what a branch holds; fox schema orders [--ddl] for one, with its CREATE script
fox track orders # keep created_at, updated_at and last_modified_by on every row
fox pii scan # where personal data is (Aadhaar, PAN, mobile, card…); never the values (Enterprise)Connect any Postgres client through the gateway — the database name is the branch, and the password is your API key:
psql "postgresql://dbadmin:<API_KEY>@localhost:6432/qa?sslmode=require"CREATE TABLE notes (id serial PRIMARY KEY, body text, created_at timestamptz DEFAULT now());
INSERT INTO notes(body) VALUES ('hello');fox blackbox qa # see that CREATE TABLE, attributed to you
fox branch delete qa # throw it away; main is untouchedMint more keys with fox apikey create <email> (or on the web API keys page).
fox start serves the console at https://localhost:8080 (a self-signed cert,
so your browser shows a one-time "not private" warning to accept; point
FOX_TLS_CERT/_KEY at a real pair to avoid it). It has:
- a dashboard (live status + branch create/suspend/resume/delete),
- a SQL console (run queries against any branch, expand rows as JSON),
- a Blackbox viewer (filter by actor, table, risk, kind),
- import and pipelines pages, and API keys.
The web app is embedded in the engine binary and served same-origin — there is no separate dev server to run.
fox start # bring EVERYTHING up in the background: stack + gateway + APIs
fox status # servers, main readiness, backups, HA, branches
fox logs gateway # tail a background server's log
fox stop # stop servers and containers (data preserved)Branching
fox branch create qa # instant copy-on-write branch of main
fox branch list # branches and their containers
fox branch reset qa # re-clone from parent, discarding changes
fox branch diff main qa # schema changes distinguishing two branches (from Blackbox)
fox branch suspend qa # stop a branch (data preserved); wakes on connect
fox branch resume qa
fox branch delete qa
fox branch owner qa you@example.com # hand a CLI-made branch to an account (shows the owner without an email)Blackbox (fox ledger … works too)
fox blackbox [branch] [--limit N] # captured DDL — attributed and policy-checked
fox blackbox verify [branch] # verify the tamper-evident hash chain
fox blackbox revert --to <ts> # point-in-time restore of main on :5433 (like fox restore)Durability / time travel
Send the backups off the machine before you rely on them. By default the write-ahead-log archive and every base backup go to the object store beside
main, on the same disk — so one disk or host failure takes the database and every backup of it at the same moment, and there is nothing left to restore from.fox checksays so until a remote target is set. One command fixes it, and a bucket with Object Lock means nothing written there can be deleted before its retention ends.
fox backup create # base backup -> object storage
fox backup list
fox backup target # where backups go (the local store, or a bucket)
fox backup target set s3://my-bucket/fox --endpoint https://s3.eu-west-1.amazonaws.com \
--access-key AKIA… --region eu-west-1 # asks for the secret key
fox backup prune --keep 7 # delete all but the newest 7 full backups
fox restore --to latest # PITR into a disposable container on port 5433
fox restore --to '2026-08-24 15:07:00+00'
fox backup export # portable copy of every branch, on this machine
fox backup restore <file> --as <new-branch>The object store's images come from FoxByte's own registry. MinIO's images
left Docker Hub, and then quay.io stopped serving them without credentials, so
they are mirrored to ghcr.io/thefoxbyte and pinned there. If you run your own
copy, point the engine at it with FOX_MINIO_IMAGE and FOX_MC_IMAGE.
Backups are taken for you. fox start takes a first base backup on an
install that has none, so point-in-time restore works from the first minutes;
the control plane takes another whenever the newest is older than
FOX_BACKUP_INTERVAL (24h) and keeps the newest FOX_BACKUP_RETAIN (7), with
the WAL they need. fox status shows how old the newest one is, and warns.
Keep them off this machine. By default the WAL archive and base backups are
in the object store beside main — on the same disk, so a disk failure takes
both. fox backup target set sends them to any S3-compatible bucket instead
(the secret key is asked for, or read from FOX_BACKUP_S3_SECRET_KEY, and kept
in a 0600 file). Create the bucket with Object Lock and a default retention,
and nothing written there — backups, WAL, and the copies of the Blackbox's and
security log's anchors that go there too — can be changed or deleted before it
expires, not even by whoever controls this machine. Tested against MinIO with
Object Lock; AWS S3 and other providers are expected to work the same way.
Moving the target restarts main and takes a first backup there; backups left on
the old target stay there, and are restorable after moving back.
Branches are disposable. Only main archives WAL; a branch is a
copy-on-write clone for experiments and agents. If a branch holds work that
matters, fox backup export --branch <name> keeps a copy.
A base backup belongs to one PostgreSQL major; an export (pg_dump, roles and
the Blackbox, verified against its own checksums) restores into the same or any
newer one.
High availability
A standby streams from main, authenticating as db_replica — a role that can
copy the database and read none of it — admitted by a single pg_hba rule
naming it and this install's own network. fox check carries a replication
line saying so.
fox ha enable # hot standby streaming from main
fox ha status
fox ha failover [--force] # promote the standby; 'main' reroutes to it
fox ha disable
fox ha failback # after a failover: back to main's own container, keeping every write
fox ha sync on | off # zero data loss: a commit waits for the standby
fox ha drill --yes # kill the primary for real, and report what it cost
fox node expose --address A --peer B --yes # let a standby on another machine copy this one
fox node join <token> # on the other machine: become its standby
fox node unexpose | leave | status | tokenfox node expose is the only thing that puts Postgres on a network, and it is
off until you ask. It publishes on the one address you name, over TLS with a
certificate that standby verifies, admitting only db_replica — which can copy
the database and read none of it — and only from the one peer address. The join
token it prints carries that CA and that role's password, and deliberately not
the install secret. fox check reports the exposure on every run.
On the second machine, fox node join <token> copies that primary over TLS and
runs the copy as a standby, verifying the primary's certificate. Everything else
then works as it does on one machine: fox ha status shows how far behind it is,
fox ha failover promotes it, and fox ha drill measures what a failover costs.
That machine's own main sits idle while it follows another primary.
fox ha failover on a machine that follows a remote primary refuses while that
primary still answers — promoting would make a second primary and the two copies
would diverge, and this command cannot stop a database on another machine. Stop
it first, or pass --force if something else already has. The check cannot tell
a dead primary from one it merely cannot reach, so a network partition still
needs a failover manager to arbitrate.
Replication is asynchronous by default: commits never wait, and losing the
machine costs up to a minute of writes (archive_timeout=60). fox ha sync on
trades the first for the second — the primary holds a commit until the standby
has it on disk, so nothing is lost, and every write is slower by the round trip
to the standby. There is one standby, so if it stops, writes stop;
fox ha sync off releases them and works while they are waiting. fox check
carries a durability line saying which of the two is in effect.
fox ha drill is how you find out what a failover actually costs here rather
than taking anyone's word for it: it kills the primary with writes in flight,
recovers through the same path fox ha failover uses, and writes a dated report
to ~/.fox/drills/ with the writes lost and the time to service — alongside the
configuration that produced them, and what the drill did not test.
Accounts & keys
fox apikey create <email> [name] # mint an API key (shown once)
fox apikey create <email> [name] --agent # a key to give an AI agent
fox apikey list <email>
fox apikey revoke <email> <id>
fox user create <email> # create an account (prompts for a password)
fox user passwd <email> # forgotten password: set a new one
fox user list | delete <email> # accounts: list, deleteGiving a key to an AI agent? Mint an agent key (--agent, or "for an AI agent"
on the console's Keys page). It opens your branches through the Gateway, but as an
agent: its changes are recorded as an agent's and the agent guardrails apply to it.
It never holds your admin grant, and the console and the control-plane API refuse it.
Forgotten a password? It cannot be recovered — only a hash of it is stored, so nobody can read it back, not even an admin. Set a new one from the machine FoxByte runs on:
fox user passwd you@example.comThat signs the account out everywhere and is recorded in the security log
(fox audit). It needs no sign-in, only access to the machine — which is what
owning an install means. On macOS and Windows run it exactly the same way; the
launcher passes it into the VM for you.
FoxByte is PostgreSQL, so every driver and ORM connects unchanged — set one env var to the gateway address (database = branch, password = API key):
DATABASE_URL="postgresql://dbadmin:<API_KEY>@localhost:6432/main?sslmode=require"Point Prisma, Drizzle, SQLAlchemy, Django, GORM, ActiveRecord, etc. at that URL.
For the REST API there is an OpenAPI spec (served at
https://localhost:8080/api/openapi.yaml) and thin, dependency-free SDKs under
clients/:
from foxbyte import FoxByte
db = FoxByte(api_key="key_…", verify_tls=False) # local self-signed cert
db.create_branch("qa")
print(db.query("qa", "select 1"))
print(db.verify_blackbox("qa"))Generate a client for any other language from the spec (see clients/README.md).
Give each agent its own instant, disposable database — over HTTP:
fox serve --addr :8088 # the Agent Branch API
curl -k -H "Authorization: Bearer $FOX_KEY" -X POST https://localhost:8088/agents/alice/branch
# -> { "dsn": "postgresql://agent-alice:key_…@localhost:6432/agent-alice?sslmode=require", … }
curl -k -H "Authorization: Bearer $FOX_KEY" -X DELETE https://localhost:8088/agents/alice/branchThe agent connects with that DSN — through the gateway, so it works from your machine as well as inside the VM. Its password is an API key scoped to that one branch: it opens no other branch, and the control plane and this API both refuse it. Deleting the branch revokes the key.
…or over the Model Context Protocol, so an agent framework drives it through one standard interface:
FOX_API_KEY=key_… fox mcp # MCP server on stdio (needs a key)Client setup and the full tool reference: docs/mcp.md.
MCP tools: create_branch, run_sql, changes (what did I change, from
Blackbox), verify_blackbox, list_branches, delete_branch. DDL an agent runs is
attributed to that agent automatically.
Every import lands in a fresh branch, so migration is safe and reversible.
fox import --from postgres://user:pw@host/db --as prod-copy
fox import --from mysql://user:pw@host/db --as legacy
fox import --from mongodb://host/db --as events # collections -> JSONB
fox import --from ./dump.sql --as fromfile # .sql/.csv/.json/.ndjson
fox import --from postgres://… --continuous --as live # logical replication
fox import-cutover live # finish the cutoverA fresh install runs PostgreSQL 18 (stock, plus wal-g). An install created
on an older major keeps running that major: FoxByte reads the version from your
data directory and runs the matching image, so fox update never moves your data
between majors behind your back. That is a migration, and it stays your call.
| Fresh install | PostgreSQL 18 |
| Still supported for existing installs | PostgreSQL 16 |
| Moving an install to a newer major | fox pg upgrade — shows every change first, takes an export, keeps the old databases until you say so (how) |
A base backup (fox backup create) belongs to one major and cannot be restored
into another; an export (fox backup export) can. docs/postgres-versions.md
has the details, and the checklist for moving FoxByte itself to the next major.
A fresh install exposes nothing it does not have to:
-
Loopback only. The control plane (
:8080), Gateway (:6432) and Agent API (:8088) listen on127.0.0.1; the backup store's ports and a point-in-time restore's:5433are published on127.0.0.1too. On macOS and Windows the VM forwards them to your machine's loopback.FOX_LISTEN=0.0.0.0exposes the three services. -
TLS, and no silent fallback. All three serve TLS (self-signed unless you give a certificate). A service that cannot load its certificate refuses to start rather than serve plain HTTP, unless it listens on loopback, and the Gateway refuses a client on another machine that has not switched to TLS (
sslmode=require). -
The first account needs the setup token, and sign-up is closed after it: an admin adds people with
fox user create, orFOX_SIGNUP=openlets them register. Signing in with GitHub or Google needs a verified email, and makes no account while sign-up is closed. -
Brakes on guessing and flooding. Ten failed sign-ins for one address or one email in 15 minutes hold further attempts; request bodies are capped; the Gateway serves at most
FOX_GATEWAY_MAX_CONNS(1000) connections and gives a client 30 seconds to authenticate. -
Branches belong to whoever made them. Every account may use
main; a branch made through the console, the API, the Agent API or MCP is its maker's to use and delete; an admin (the first account, or anyone givenfox admin grant) may do anything. Other people's branches are not listed, and answer "not found" — through the Gateway too. A branch made with the CLI is an admin's. -
A password per database role. Each account, each agent and the client role log in to Postgres with their own password, derived from the install secret; the secret itself is the superuser's alone and never appears on a command line.
-
Imports reach public databases. Importing from this machine, a private network or a container is for admins; cloud metadata addresses are refused.
-
No superuser switches in release builds.
FOX_AGENT_SUPERUSERandFOX_MCP_SUPERUSER, likeFOX_GATEWAY_NOAUTH, only work in a build made with-tags insecure. -
Backups can leave the machine.
fox backup target setsends the WAL archive, base backups and anchor copies to an S3 bucket (see Durability); the keys never appear on a command line.
- One machine, for now. Compute and storage are separated logically (stateless containers over persistent storage, scale-to-zero) but still run on one host; there is no multi-host deployment. Networked storage disaggregation is on the roadmap.
- HA is a single-VM demonstration, not a production multi-host deployment.
- Not a vector database — it is PostgreSQL (use
pgvectoron it if you like). - One instance, shared — accounts own their branches and share
main; there are no separate projects or per-project quotas yet. - Not the first to branch Postgres. The copy-on-write-clone-as-a-database idea is postgres.ai / Database Lab's, and Neon, Supabase and PlanetScale all offer branching of some kind. What is ours is what happens after the branch exists: the Blackbox, the attribution, and the policy gate.
make build # host binary -> ./bin/fox
make vm-build # Linux engine binary into the Lima VM (macOS)
make vet test # go vet + unit tests (also run in CI on Linux and Windows)
make integration # full end-to-end test, in a throwaway test VM
make integration-sdks # the published Python and TypeScript clients, against a live engine
make integration-ui # the web console in a real browser (Playwright; first run downloads Chromium)All of them run nightly in CI as well (Actions → nightly), one runner per suite, and one can be run there on demand.
The integration suites (make integration, integration-v2, integration-update,
integration-pg-upgrade, integration-sdks, integration-ui)
are destructive — they wipe Blackbox history, restore main to an earlier point and
fail HA over — so they run in a VM of their own (fox-test, created on first use by
make test-vm), never in the VM that holds your install, and refuse to start
anywhere else. make test-vm-stop frees its memory; make test-vm-delete removes it.
The web app lives in web/ (Vite + React + TypeScript) and is embedded into the
engine binary via the embedui build tag, then served same-origin by fox start.
Releases are automated: pushing a v* tag builds every binary + the install
assets and publishes the multi-arch Postgres image to GHCR.
FoxByte ships as two binaries built from this one tree. fox is the Standard
edition and is what the installer gives you by default. fox-enterprise is the
same engine with the code under enterprise/ compiled in, and its
features need a licence — without one it behaves exactly like Standard.
| Standard | Enterprise | |
|---|---|---|
| Branching, gateway, backups, PITR, time travel | yes | yes |
| Blackbox: the hash-chained record of every schema change | yes | yes |
| Agent guardrails: TRUNCATE blocked; agents' DELETE/UPDATE limited by rows changed, and refused without a WHERE | yes | yes |
| Realtime row-level change feed | — | yes |
| Writing signed Blackbox anchors (checkpoints, on a schedule) | — | yes |
Checking anchors: fox blackbox integrity, fox-verify, anchor-key |
yes | yes |
The security log's own anchors (fox audit verify) |
yes | yes |
| Writing Blackbox policy rules; impact analysis; asking for and applying a change request | — | yes |
| Reading rules, checking a statement against them, the evaluations log, listing and rejecting requests | yes | yes |
| Blackbox export (the compliance bundle) | — | yes |
ETL pipelines (fox import is free: migrating in is onboarding, not ETL) |
— | yes |
Scanning for personal data (fox pii scan); reading what earlier scans found |
— / yes | yes |
The access review report (fox access review) |
— | yes |
curl -fsSL https://get.thefoxbyte.com/install.sh \
| FOX_EDITION=enterprise shThe command is fox either way. On macOS and Windows the engine runs in a managed
Linux VM, and the installer puts the matching edition on both sides.
Already running Standard? An admin can choose Upgrade → Install Enterprise in the console, or switch from a terminal, at the version you have:
fox upgrade --edition enterprise # sudo on Linux; --edition standard switches backIt shows the Enterprise licence and asks you to accept it before downloading
anything, checks the signed release the way fox update does, and replaces fox
on both sides of the VM. Databases, accounts, keys and settings are not touched.
The command is part of the free edition, but all it knows about Enterprise is the
file to fetch: the paid code only ever arrives in that download.
- Core / server (this repo, except
clients/andenterprise/): AGPL-3.0-or-later — seeLICENSE. - Clients & SDKs (
clients/): Apache-2.0 — seeclients/LICENSE. - Enterprise edition (
enterprise/): a commercial licence — seeenterprise/LICENSE. Source-available, not open source. The Standard binary contains none of this code, which is checked against the built binary rather than the source (scripts/test_editions.sh).
Using FoxByte does not make your application AGPL. Connecting over the Postgres wire protocol is not a derivative work, and the client libraries are Apache-2.0. The copyleft applies only if you modify FoxByte itself and offer it to others as a service.




