Skip to content
LogoLogo

Setup

The project Makefile builds and runs all components through docker-compose.

Building the images takes time, particularly the Better Auth component used for authentication service database migrations: @better-auth/cli.

First, clone the project:

git clone https://github.com/openfort-xyz/opensigner.git

To build the containers, run:

make build  # or `make clean build` to remove old images and volumes

To run them, use:

make run

The components are configured through environment variables. The docker-compose.yml file at the repository root lists every variable with its default value. Service-specific defaults are in files such as auth_service/.env.example.

Required Environment Variables

These variables have no defaults. Docker Compose refuses to start without them, which is deliberate: a credential that falls back to a default is a credential every reader of this repository already knows. Copy .env.example to .env and fill in each one.

VariableUsed byDescription
JWT_SECRETAuth serviceApplication secret for the auth service. Signs session cookies and encrypts the JWKS signing key at rest. Minimum 32 characters; generate with openssl rand -base64 48. The service refuses to start if this is unset or left at a library default.
SHARE_ENCRYPTION_KEYHot storageAES-256 key for encrypting shares at rest. Exactly 64 hex characters (32 bytes). Generate with openssl rand -hex 32.
POSTGRES_PASSWORDPostgreSQLSuperuser password.
AUTH_SERVICE_DB_PASSAuth servicePostgres password the auth service connects with — usually the same value as POSTGRES_PASSWORD.
HOT_STORAGE_DB_PASSHot storagePostgres password hot storage connects with — usually the same value as POSTGRES_PASSWORD.
COLD_STORAGE_DB_PASSCold storagePostgres password cold storage connects with — usually the same value as POSTGRES_PASSWORD.

Rotating JWT_SECRET

JWT_SECRET does more than sign tokens: the auth service uses it to encrypt the JWKS private signing key stored in the jwks table. Changing the secret without accounting for that key leaves the deployment in a broken or unsafe state, so rotation is a two-step operation.

Treat the old signing key as exposed. Anything that could read the previous secret — a configuration file, an environment dump, a database backup, a snapshot, or a log — could also decrypt the signing key it protected. A key is only as private as every copy of the secret that encrypted it, so rotate on any suspicion that a secret was disclosed, and do not reuse a jwks row across secrets.

Because hot_storage releases a wallet key share to any holder of a validly signed JWT, a signing key that can be recovered is equivalent to being able to authenticate as any user.

Optional Environment Variables

These variables have sensible defaults for local development but should be configured for production:

VariableDefaultDescription
ALLOWED_ORIGINShttp://localhost:7050,http://localhost:7051Comma-separated list of allowed CORS origins. Used by both the auth service and hot storage.
BETTER_AUTH_BASE_URLhttp://localhost:7052Public base URL of the auth service.
POSTGRES_USERpostgresPostgreSQL superuser name.
TRUST_PROXYfalseSet to true only when the auth service sits behind a proxy that sets x-forwarded-for. Rate limiting is keyed on the client address, so trusting that header from an untrusted source lets a caller send a fresh value per request and sidestep the limit entirely.
GOOGLE_JWT_AUDIENCEunsetOAuth client ID accepted for x-auth-provider: google. While unset, hot storage rejects Google tokens rather than accepting any Google-signed token regardless of the client it was issued to.
AUTH_JWT_ISSUER / AUTH_JWT_AUDIENCEvalue of BETTER_AUTH_BASE_URLExpected iss and aud claims that hot storage enforces on incoming JWTs. These are the auth service's externally visible base URL, which is why they cannot be derived from AUTH_SERVER_URL — that is the internal address used to fetch the JWKS.
ALLOW_INSECURE_AUTH_SERVERfalsePermits fetching the JWKS over plaintext HTTP. Hot storage otherwise requires https for AUTH_SERVER_URL, because anyone able to rewrite that channel can substitute the key set and mint tokens for any user. Local development only.

Each service also accepts database connection variables (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_SSLMODE) with defaults suitable for the Docker Compose setup. See docker-compose.yml for the full list.

Once you have everything running, head over to the Getting Started guide.

OpenSigner runs as a set of Docker Compose services. Generate the required SHARE_ENCRYPTION_KEY with OpenSSL (openssl rand -hex 32).