Supabase Auth: one binary called auth, stamped by two package paths, configured entirely in GOTRUE_ keys
A JWT based API for managing users and issuing JWT tokens
At a glance
- What is it?
- supabase/auth is the Go authentication server behind Supabase, a fork of Netlify's GoTrue that has kept the old name in its environment variables, its migrations path and a compatibility symlink. Its compatibility promises are unusually specific, and its newest tags are release candidates for a patch 198 release.
- Who is it for?
- Supabase Auth suits a team that wants to run its own authentication server on Postgres and is prepared to own the upgrade work, because the project states plainly that running one in production is not easy and points self-hosters at the hosted product for regular security updates. Before committing, read the compatibility tiers end to end rather than the summary, since the schema guarantees hold for patch releases only.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- supabase/auth2,602 ★ · +31
| Repository | 2026-09-30 | 2026-11-14 |
|---|---|---|
| supabase/auth | 2.6k | 2.6k |
31 Stars in the last 45 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The version is stamped into two different package paths
The build step in the quick start shows the command it expects, and it does not match the one the Makefile runs.
go build -ldflags "-X github.com/supabase/auth/cmd.Version=`git rev-parse HEAD`"
GOOS=linux GOARCH=arm64 go build -ldflags "-X github.com/supabase/auth/cmd.Version=`git rev-parse HEAD`" -o gotrue-arm64That writes the version into a variable under `cmd`. The Makefile builds the same binary through a variable that points somewhere else entirely: `BUILD_VERSION_PKG = github.com/supabase/auth/internal/utilities`, with the flag string assembled from it. So the two documented routes produce binaries that stamp a different package, and a `--version` flag that reads the one path will report empty after a build through the other.
The second sample command also names its output `gotrue-arm64`. The project is called auth, the module path is `github.com/supabase/auth`, and the binary is `auth`, so the old name survives in three places: that example, the required environment prefix, and a symlink in the container image from the new name to the old one.
Two make targets build the same architecture
The release targets are listed as five names.
RELEASE_TARGETS = x86 arm64 darwin-arm64 amd64-strip arm64-stripBut the recipes behind them are not five distinct builds. `auth-x86` and `auth-amd64` both set `GOOS=linux GOARCH=amd64` and differ only in the output name, so the x86 target is a duplicate of the amd64 one under an older label. The aggregate `build` target depends on `auth`, `auth-amd64`, `auth-arm64` and `auth-darwin-arm64`, which means the x86 target is never built by default and exists for anyone still asking for it by name.
The archives that come out of a release use two formats. The plain x86, arm64 and darwin-arm64 builds are packed as gzipped tarballs, while the two stripped variants are packed as xz archives. That is a small thing to notice and an annoying one when a script assumes one format across the set.
Three third party analysis tools are fetched by make into a `tools/bin` directory, being gosec, staticcheck and govulncheck, and the default target runs the Go version check, vet, the security scan, staticcheck and the build in sequence.
Every configuration key still begins with GOTRUE_
Configuration comes from a `.env` file, from environment variables, or both, and one rule governs the combination: environment variables are prefixed with `GOTRUE_` and always take precedence over values from the file. There is no per-key exception, so a value written in the file cannot win against an exported variable of the same name.
The one required top-level key is `SITE_URL`, and the example given for it is a netlify.com address.
GOTRUE_SITE_URL=https://example.netlify.com/That default is a leftover from the GoTrue lineage, in a repository whose module path, binary and documentation are all Supabase. It is harmless as an example and worth changing, because `SITE_URL` is what email links are constructed from.
The second key shown, `URI_ALLOW_LIST`, is a comma separated list of permitted redirect destinations that defaults to empty and supports wildcard matching. The environment variable naming is consistent enough that a migration from another deployment is mostly a matter of knowing which of those old keys are still read, and the file's inherited features section names several that are not.
redirect_to accepts any URI sharing a host with SITE_URL
This is the security detail worth reading twice.
Any URI that shares a host with `SITE_URL` is a permitted value for `redirect_to` parameters, which is what the sign-in and authorize endpoints read. The scope is the host, not the origin: any path on that host is allowed, and the wildcard list in `URI_ALLOW_LIST` exists to widen it further rather than narrow it.
Combined with the fact that the allow list defaults to empty, the effective default is that everything on the site host is a legal redirect target. For a single host application that is convenient. For a deployment where several applications share a parent domain, or where a user account page can carry arbitrary path values, it widens the set of places a crafted link can land.
The self hosting guidance does not mention this key at all. It covers the schema, the database and the TLS proxy, so this behaviour has to be read out of the configuration section rather than found in the advice.
The schema is guaranteed at patch level and explicitly not at minor
The compatibility promise is tiered, and the tiers disagree about the database in a way that is easy to skim past.
Patch releases guarantee the database objects themselves, meaning tables, columns, indexes and functions, plus the REST API, the JWT structure and the configuration. The guaranteed examples include that a column will not change type, a table will not change its primary key, an index will not be removed and a uniqueness constraint will not be removed. The not guaranteed examples for the same tier are that a table may gain columns, columns may be reordered, non-unique constraints may be dropped, and the JWT may gain properties.
Minor releases guarantee only the REST API, the JWT structure and configuration. Their not guaranteed list contains, explicitly, deletion, truncation and significant schema changes to tables, indexes, views and functions.
So the database guarantee you are given at patch level is withdrawn one tier up, in the same document, a screen away. The same applies to configuration: it will not change in a patch, and in a minor it may change as long as the old value keeps working for a few releases.
One more asymmetry: minor guarantees may be broken for serious security issues that cannot be fixed any other way.
Two major releases or two weeks, whichever is shorter in practice
The deprecation clock is defined precisely, and it is the one place where release cadence matters.
A deprecation notice is to be present in the execution logs for at least two major version releases, or two weeks if multiple releases go out. Compatibility is guaranteed while the notice is live.
Read against the repository's own tags, that clause is doing most of the work. The three most recent tags are release candidates for a single version, at rc.15, rc.20 and rc.21, with two of them published on the same day. Once releases are going out several times a day, the two week branch is the one that applies, and a notice can go from appearing in a log to an interface disappearing inside a fortnight.
The two major version releases in the other branch would be a long time at a version number in the one hundreds. So the practical guarantee offered to a self hoster is the short one, and it is short because the project ships continuously.
The inherited features list mixes what was removed with what was kept
Five features inherited from the Netlify codebase are called unsupported and said to be removable without prior notice: multi-tenancy through an `instances` table and its configuration parameter, a system user represented as the zero UUID, a super admin column, group information carried in JWTs through a configuration field, and JWT signing.
The fifth item does not read like a removal. It describes what Supabase Auth does support: asymmetric keys, RS256 by default with ECC and Ed25519 optional, HS256 still accepted for compatibility, and a recommendation to migrate to asymmetric keys because it makes validation and rotation easier. So the list mixes two different things, inherited capabilities that were dropped and inherited capabilities that were replaced with something better.
A reader scanning the list for what is still available will misread that entry, and the file compounds it by saying the list is not exhaustive and may change.
The same section is where the migration advice lives: deprecations will be announced in the changelog, with two guides linked on signing keys and on JWTs.
The image bakes its migrations in and defaults its version to unspecified
The container build has a comment addressed to whoever publishes it.
# Make sure you change the RELEASE_VERSION value before publishing an image.
RUN RELEASE_VERSION=unspecified make buildLeft alone, the version becomes the string `unspecified`, prefixed by the Makefile, and nothing warns you at build time. The Makefile's own fallback is better: with no RELEASE_VERSION set it uses `git describe --tags`, so a local build carries real version information and only the container path hardcodes a placeholder.
Migrations travel inside the image. The Dockerfile copies the `migrations` directory to `/usr/local/etc/auth/migrations/` and points an environment variable at it, so the schema the server expects is shipped with the binary rather than applied from outside. Three Dockerfiles exist at the root, one for the server, one for development and one for Postgres, and the env parser is a fork vendored under `internal/` and copied in before dependencies are fetched.
The runtime half is thin and careful: a second stage on plain Alpine, a non-root user created with uid 1000, CA certificates installed, the binary at `/usr/local/bin/auth` and a symlink from the old `gotrue` name pointing at it.
Editorial conclusion
Supabase Auth suits a team that wants to run its own authentication server on Postgres and is prepared to own the upgrade work, because the project states plainly that running one in production is not easy and points self-hosters at the hosted product for regular security updates. Before committing, read the compatibility tiers end to end rather than the summary, since the schema guarantees hold for patch releases only. Decide now how you will consume configuration, because environment variables always beat the file and there is no per-key override. And if you build the image yourself, remember that migrations are baked into it and the version string defaults to a placeholder.
Frequently asked questions
Does Supabase provide auth?
Yes, and the server behind it is open source. The repository is a user management and authentication server written in Go, MIT licensed, that issues JWTs, drives row level security with PostgREST, manages users, and handles sign in by email and password, magic link, phone number, and external providers including Google, Apple, Facebook and Discord. It was originally based on Netlify's GoTrue and has since diverged significantly.
what is supabase auth
A standalone Go authentication and user management server that powers Supabase's own features. It is not intended to be consumed as a Go library, and the project states there are no API compatibility guarantees when it is used that way regardless of the version number. It talks to Postgres and keeps its schema in a migrations directory inside the repository.
what does supabase auth do
Four things the file names: issuing JWTs, row level security with PostgREST, user management, and sign in by email and password, magic link, phone number or an external provider. On top of that it keeps user data in Postgres, applies its own migrations, and exposes a REST API whose parameters keep working across patch releases and deprecating across minor ones.
Is Supabase auth free?
The server is MIT licensed and self hosting is possible, but the file's own advice for production is to use the hosted Supabase Auth product, which receives regular security updates, and to have a process ready to update promptly if you do run it yourself. Nothing in the repository says what the hosted service costs, so the two options cannot be compared on price from here.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/supabase-auth)Star history chart for your README
[](https://hysenlabs.com/projects/supabase-auth)Was this useful?