Repository navigation
A security release from a full review of 0.3.0. Update every install.
Changed
- Keel is licensed under the Functional Source License (FSL-1.1-ALv2). Using,
changing and running Keel, including inside a company, is free; selling it
or offering it as a competing product or hosted service needs permission.
Each release becomes Apache 2.0 two years after it comes out.
Security
- A project's owner role could make the Postgres superuser run its own code:
through the scheduler's nightly cleanup, the event triggers that turn on
row level security for new tables, and the Studio table editor. Superuser
connections now usesearch_path=pg_catalog, the trigger functions have a
fixed search path (migration 015), and the table editor works as the owner
role. - Edge functions could reach private addresses through
import(), which
--deny-netdoesn't cover. Module imports now follow the same address
rules asfetch. - Function secrets can no longer set runtime variables that change how Deno
or npm behave, such asSSLKEYLOGFILE,NPM_CONFIG_*orGLIBC_TUNABLES. - A realtime channel takes at most 100
postgres_changesbindings, so one
join can no longer stall realtime for every project. - Signed URLs need a numeric expiry, and
install.shmakes the install folder
readable by its owner only. - Default-branch builds no longer publish the branch name as an image tag.
0.3.0 - 2026-10-12
Postgres extensions, scheduled jobs and webhooks, more sign-in providers,
encrypted off-site backups and installers for Windows, macOS and NAS systems.
Read "Upgrading" before you update an existing install.
Upgrading
keel-dbruns a new image,keel-postgres: the same Postgres 17 as before
with pgvector and PostGIS added, so existing data is used as it is. It comes
with the new compose file: run the installer again to switch (a plain
./keel updatekeeps the old compose file and image). On ZimaOS, change
keel-db's image toghcr.io/<owner>/keel-postgres:latest. A third package
is published next tokeelandkeel-functions; make it public too if you
publish your own images.- The compose files have a new service,
keel-egress, and a new network.
With the installer's compose file,./keel up(ordocker compose up -d)
starts it; on ZimaOS, import the new compose file.
Added
- Sign-in with any OpenID Connect provider, such as Authentik, Keycloak,
Authelia, Pocket ID or Zitadel: add one or more per project in Studio
(Users > Sign-in settings) and sign in with
signInWithOAuth({ provider: 'custom:<id>' }), as in Supabase. Keel uses
discovery, PKCE and a nonce, and checks the provider's signed ID token.
A provider on your own network can be reached over plain http when you
allow it for that provider. - Sign-in with Discord, GitLab (gitlab.com or your own) and Microsoft
(azure). - pgvector 0.8.7:
create extension vector with schema extensions, vector
columns, HNSW and IVFFlat indexes and nearest-neighbour search through RPC,
as in Supabase's guides. A project's owner role can enable it. - PostGIS 3.6.4 (without raster):
geometryandgeography, spatial indexes
and.geojson()responses. Enabled by the superuser in Studio's SQL editor. - The Supabase importer recreates
vectorandpostgis. - Docs: a page on extensions.
- Scheduled jobs with
pg_cron's SQL API in every project:cron.schedule
(five-field schedules,$,@dailyand friends, or'N seconds'),
cron.unschedule,cron.alter_job,cron.jobandcron.job_run_details.
Jobs run as the project's owner role, never as a superuser, one run at a
time, stopped afterKEEL_CRON_TIMEOUT. keel-studio runs them. - HTTP requests from SQL with
pg_net's API:net.http_get,net.http_post,
net.http_deleteandnet._http_response, and Supabase's Database Webhooks
(supabase_functions.http_requesttriggers). Calls to the project's own
edge functions are routed inside Keel. A full queue skips a webhook's
request instead of failing the write that fired it. keel-egress, a new container that sends those requests. It logs in as
keel_egress_admin, which can only work the request queues, and sits on a
network of its own with the database and the gateway.- Studio: a Jobs tool listing scheduled jobs, recent runs and outgoing
requests. - A Windows installer,
install.ps1, for Docker Desktop in Windows PowerShell
5.1 or PowerShell 7: no WSL terminal or Git Bash needed. It installs one
exact version into%USERPROFILE%\keel, writes.envwith random
passwords (readable by your account only), keeps Studio and Postgres on the
PC unless you pass-Lan/-LanDb, waits until Keel answers and prints
your login. Running it again updates (with a database backup first);
-Rollbackgoes back.-DryRunshows what it would do. - A Windows
keelcommand (keel.cmd) with the same commands as the bash one,
includingupdateandrollbackwith the same backups, so either can roll
back the other's update. - On Windows, Postgres keeps its data in a Docker volume
(docker-compose.windows.yml) instead of on the Windows drive, where it is
several times slower. - Install guides for Windows, macOS (Docker Desktop, OrbStack, Colima),
Unraid, TrueNAS SCALE and Synology. nas-docker-compose.yml, the self-contained compose file for NAS app
screens (Unraid's Compose Manager, TrueNAS's Install via YAML, Synology's
Container Manager, Portainer). It is the ZimaOS file under a second name.KEEL_PUBLIC_NETWORKnames the network a tunnel or Traefik joins, so a
second Keel on the same machine no longer shares the first one's
keel-publicnetwork.- Full backups: with
KEEL_BACKUP_PASSPHRASEset, Keel writes one file each
night holding every project's database, Keel's own settings, keys and Studio
accounts, and every uploaded file, encrypted with the passphrase in the age
format (open it with Keel or with theagetool). The newest
KEEL_BACKUP_KEEP_LOCAL(2) stay on the server. - Off-site copies: full backups are uploaded to an S3-compatible bucket (AWS
S3, Backblaze B2, Cloudflare R2, Wasabi, MinIO...), set up in Studio or with
KEEL_BACKUP_S3_*in.env. The bucket keeps the newest backup of each of
the last 7 days and 4 weeks. Uploads go in parts, so a NAS with 1 GB of RAM
can upload backups of any size. ./keel restorebrings a whole install back from a full backup, on the same
machine or a fresh install elsewhere, from a file or straight from the
bucket. It checks the passphrase and the whole file before changing
anything../keel restore --listshows the backups.- Studio's Backups drawer has the backup schedule, the retention, the backup
target with a connection test, full backups with their upload state, and the
backups in the bucket. .\keel restoreon Windows, with the same checks and confirmation as the
bashkeel.- Studio's Postgres connection strings (Connect, the agent prompt's
KEEL_DB_URL) ask for TLS with?sslmode=requirewhen Postgres has it. - A new landing page and a restyled documentation site: one sidebar in
reading order, a quickstart per platform, Coming from Supabase, a glossary,
an FAQ, more troubleshooting, image zoom and better search.
Security
- Requests from SQL follow the edge functions' network rules: private, LAN,
loopback and metadata addresses (IPv4 and IPv6), this server and Keel's
containers are refused unless opened withKEEL_FUNCTIONS_ALLOW_LAN; every
address a name resolves to is checked and the connection is pinned to it;
redirects aren't followed. Only the project owner role and superusers can
schedule jobs or send requests; the API roles can't reach thecronand
netschemas. The sender has no superuser password and no route to
Studio or the Docker socket proxy. - Both installers check every file they download (compose files, the
keel
command) against the publishedSHA256SUMSand stop before changing
anything when one doesn't match. - Keel's own CLI (
fn deploy,secrets) follows thesslmodein
KEEL_DB_URLlike libpq (require,verify-ca,verify-full,disable);
before, it never used TLS from another machine. A repeatedsslmodeis
refused. The docs recommendverify-cawith the server's certificate. - cloudflared 2026.10.0, the Dockerfile frontend pinned by digest, and the
keel-functionsimage takes Debian's security updates at build time.
Changed
- Sign-in provider secrets are encrypted in the project settings when you
save them. Secrets saved before stay as they are until you enter them again. GET /auth/v1/settingslists the new providers underexternal.- The database image no longer contains gosu (a Go binary); its entrypoint
drops root with Alpine's su-exec, and it gets Alpine's security updates at
build time. - Releases now include
install.ps1withinstall.ps1.sha256, the Windows
keelcommand,docker-compose.windows.ymlandnas-docker-compose.yml. - CI runs the installers end to end on Linux, on macOS with its bash 3.2, and
on Windows PowerShell 5.1, runs PSScriptAnalyzer, and checks that the main,
source and NAS compose files define the same services and pinned images. - Studio mounts the storage folder (read-only) to back up uploaded files, and
keel-migrate mounts the storage and backups volumes in a source checkout,
for./keel restore. Installer installs get the new compose file and
keelscript by running the installer again; on ZimaOS, import the new
compose file. Without it, a full backup of a project with uploaded files
fails with a message saying so, rather than leaving the files out. - The nightly backup time can be changed in Studio; it wins over
KEEL_BACKUP_SCHEDULE.
Fixed
- A failed sign-in with a provider redirects back to the app with 302, as
Supabase does, instead of 303. - Docs: Studio API examples use the cookie session (the bearer-token
examples got 401), Postgres TLS and bind defaults are described correctly,
and the owner role is no longer said to bypass row level security.
0.2.0 - 2026-10-12
A security release. Read "Upgrading" before you update an existing install.
Upgrading
- Studio keeps its session in a cookie now, so everyone signs in to Studio
again once. - Studio and Postgres listen on the machine itself. To reach Studio from your
network, setKEEL_STUDIO_BIND=0.0.0.0(or run./keel init --lanon a new
install); behind Caddy, nginx or another proxy on the same machine, add its
host name toKEEL_STUDIO_HOSTS. - Postgres refuses the
postgressuperuser from other machines. Connect as
the project owner role (Studio, Connect) or set
KEEL_DB_REMOTE_SUPERUSER=on; other machines need TLS. - Behind your own reverse proxy, list it in
KEEL_TRUSTED_PROXIESso
visitor addresses are counted correctly. - On ZimaOS, import the new compose file from this release.
Security
- Visitor addresses are taken from
X-Forwarded-Foronly when Keel's own
proxies (the gateway, the Cloudflare tunnel) or a proxy listed in the new
KEEL_TRUSTED_PROXIESforwarded the request. A forged header no longer
counts as "local" (which skipped two-factor for Studio and raised the auth
limits) or changes the rate-limit key. IPv6 clients are counted per /64. - Studio answers local requests only for known host names (IP addresses,
localhost, local names, the public addresses,KEEL_STUDIO_HOSTS), which
stops DNS-rebinding attacks from browsers on the local network. - Studio and Postgres listen on the machine itself by default;
--lanand
--lan-dbopen them. Postgres requires TLS (a self-signed certificate is
made on first start) and a password from other machines, and refuses the
postgressuperuser from them unlessKEEL_DB_REMOTE_SUPERUSER=on. - Edge functions can no longer reach private addresses (the Docker host, the
LAN, Keel's containers); they reach their own project's API through a
socket.KEEL_FUNCTIONS_ALLOW_LANopens chosen LAN addresses, such as a
local Ollama. - Password sign-ins are limited per account across all addresses, and new
projects require passwords of at least 8 characters. - Request bodies that stall are closed (
KEEL_BODY_IDLE_TIMEOUT), and open
connections and realtime sockets per client and project are capped. - Sign-in links, codes, email addresses and phone numbers are kept out of
the logs (KEEL_LOG_PII=onshows the personal data for debugging). Postgres
error text that names tables and constraints is no longer sent to API
clients. - App users' TOTP secrets are encrypted at rest; new passwords use bcrypt cost
12. Studio sessions, two-factor secrets, backup download links and the
functions service each use their own key derived fromKEEL_SECRET.
Backup download links only work for the Studio session that made them. - Sessions that expired or were unused for 90 days are deleted, with the IP
addresses and browsers they store. - New projects on a public server don't start with open, auto-confirmed
sign-ups. Passwords that appeared as examples in the docs are refused. - Every service role has its own database password, and services read only
the project columns they use. Keel's service roles and the superuser can't
sign in from outside its containers. - Studio keeps its session in an HttpOnly cookie and refuses cross-site
calls. - Project owner roles don't bypass row level security, so one project's owner
can't see another project's functions or secrets. - Containers are hardened: read-only root filesystems where possible, no
capabilities for migrations, and image-resize requests are signed on ZimaOS
too.
Added
- API keys can be rolled, stopped and emergency-rolled from Studio's Connect
page and./keel keys. Tokens carry a key id, and the old key keeps working
until you stop it. - A project owner role for each project, for developers and coding agents
that need to change the schema; Studio Connect and the agent prompt use it. - Releases: version tags (
v*) publish theX.Y.Z,X.Y,X(from 1.0) and
latestimage tags and a GitHub release with these notes; pushes to the
default branch publishedge. Images are published only after CI passes. - Images carry their version, source and commit as OCI labels, an SBOM and
build provenance, are signed with cosign (keyless), and are scanned for
critical vulnerabilities before they are published. ./keel updatebacks up every database before it changes anything, moves
between version numbers and remembers the previous one;./keel rollback
goes back, and restores the backup when the newer version upgraded the
schema. The installer does the same, with--rollbackand--restore.keel-migraterefuses to start an image older than the database.- Studio shows the running version and, once a day at most, whether a newer
release exists (KEEL_UPDATE_CHECK=offturns that off). - The installer comes with a SHA-256 checksum, and each GitHub release has its
own installer that installs exactly that version. - Health checks on every service, and
docker-compose.small.ymlfor a
Raspberry Pi or a NAS with 1-2 GB of RAM (KEEL_DB_SETTINGS). - The installer finds the right folder and network defaults on Synology,
Unraid, TrueNAS SCALE, macOS and WSL 2, detects cloud servers, installs a
chosen--version, and fails loudly when Keel doesn't start. - Docs for macOS, Windows and NAS systems, and a Traefik example.
Changed
- Every GitHub Action is pinned by commit, every base and third-party image by
digest, and Dependabot keeps them and the npm packages current. - Installs no longer follow the moving
latesttag: the installer writes an
exact version to.env. The first./keel updateof an older install keeps
its images aspreviousfor a rollback. - Every file is checked out with LF line endings, so the scripts work from a
Git clone on Windows.
Fixed
./keel initno longer writes Windows'ipconfighelp into.envin Git
Bash, and a failed init no longer leaves an empty.envbehind.- Oversized uploads sent in small pieces get their 413 answer right away.
- Edge functions start in about a second on Docker Desktop for Windows instead
of about 24. - Images ship without Postgres's unused
gosuand with Debian's latest
security updates, so they pass the vulnerability scan. ./keel upno longer stops early when.envhas noKEEL_STUDIO_PORT.
0.1.0 - 2026-10-03
The first version, built from source or from images that followed the default
branch (no version tags were published).
Added
- Postgres 17 with one database per project, a REST API (PostgREST), auth
(email and password, magic links and codes, SMS, GitHub and Google sign-in,
two-factor), storage with
resumable uploads and image transformations, realtime (broadcast, presence,
database changes, private channels) and edge functions (Deno), all speaking
the Supabase APIs so apps use@supabase/supabase-js. - Studio: tables, SQL editor, users, storage, functions, logs, backups,
project settings, Studio accounts with two-factor authentication, and going
online through a public address or a built-in Cloudflare Tunnel. - Nightly backups with restore, project defaults, and an importer that copies
a Supabase project into Keel. - The documentation site, served by Keel at
/docsand published as a
website, with a prompt for AI coding agents. - The
keelcommand, an installer for Linux and NAS systems, and a ZimaOS
App Store compose file.
Install or update
Images: ghcr.io/niklan1110/keel:0.3.2, ghcr.io/niklan1110/keel-functions:0.3.2 and ghcr.io/niklan1110/keel-postgres:0.3.2 (amd64 and arm64, signed with cosign).
Download the installer for this version, check it, then run it:
curl -fsSLO https://github.com/niklan1110/keel/releases/download/v0.3.2/install.sh
curl -fsSLO https://github.com/niklan1110/keel/releases/download/v0.3.2/install.sh.sha256
sha256sum -c install.sh.sha256 # macOS: shasum -a 256 -c install.sh.sha256
sudo bash install.shOn Windows with Docker Desktop, in PowerShell:
Invoke-WebRequest https://github.com/niklan1110/keel/releases/download/v0.3.2/install.ps1 -OutFile install.ps1 -UseBasicParsing
Invoke-WebRequest https://github.com/niklan1110/keel/releases/download/v0.3.2/install.ps1.sha256 -OutFile install.ps1.sha256 -UseBasicParsing
if ((Get-FileHash install.ps1).Hash -eq (Get-Content install.ps1.sha256).Split(' ')[0]) {
powershell -ExecutionPolicy Bypass -File .\install.ps1
} else { 'install.ps1 does not match its checksum: do not run it' }The build attestations check the files independently: gh attestation verify install.ps1 --repo niklan1110/keel.
Both installers check every file they download against SHA256SUMS.
On an existing install, ./keel update --version 0.3.2 in the install folder backs up every database first; ./keel rollback goes back.
SHA-256 of every file:
1483a44bb8ef6050bda1d4fab7bc44ea6d2219c0e3f1a28403de9113abd0b3f3 cloudflare-tunnel.yml
17b83db87241d239f73c56c279f93099069a0f2a089c3e0e14160e2408b4340c docker-compose.small.yml
c77c9176ba5b3dcf412219c929d08bd3065b729f82702b8a4ab5aa208c6b7e5f docker-compose.windows.yml
0369d503fd29d0a7d2cb06599206fec77fa1f0f7b6b34ddd2c7a3095e1838144 docker-compose.yml
0f9d4d4f0bcc49830c44ca13bb03c0086082a1d6bd768644c1d502b0ccef6d07 icon.svg
6f94aefe3110cb5eba46a6a5a4dfe6c6335310e1766f57006326e752e0ecfed0 install.ps1
4a9482af9d0791e32a20f4a5439cdae7b23c6311c305df40deebc7043f15b23d install.sh
8fd3b8b91e9ddc4f35793853d7b4af2ab644417563f3efa94e0e8096103a8dcf keel-cli.ps1
656e2955ec52a0df285ef7e62c7cf9cee37717779a35a6b55dca2048808d9405 keel-cli.sh
2e5deead488758d9970e25ddacfc818c15e1ee8595372ca3151421a329bd4d66 keel.cmd
2c867096ede7ea836b61be0f2c2a7a11b67df82446c5d14397c18aedd5a4c4f5 keel.env.example
83acb3cc860a0159ae8cac909010c90cae0f034fa6302af9e74bfc744226dd92 nas-docker-compose.yml
83acb3cc860a0159ae8cac909010c90cae0f034fa6302af9e74bfc744226dd92 zimaos-docker-compose.yml