Skillset reads its configuration exclusively from environment variables at startup. Required variables are validated before any request is handled — a misconfiguration surfaces immediately as a startup error rather than as a runtime failure on the first request that needs it. This page documents every variable fromDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/org-quicko/skillset/llms.txt
Use this file to discover all available pages before exploring further.
.env.example and the loadConfig function in apps/api/src/config.ts.
Copy
.env.example to .env before first boot. The example file is pre-populated with values that work against the local MinIO and Postgres containers, so local development requires only filling in BETTER_AUTH_SECRET and PUBLIC_URL.Required variables
These variables must be set. The app will not start if any of them is missing or invalid.Postgres connection string in
postgres://user:password@host:port/dbname form.For the Docker Compose stack this must match POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB. Do not add a search_path parameter — all queries are schema-qualified at runtime from DB_SCHEMA instead.Signing secret for session tokens. Must be set explicitly — the app refuses to start without one because a randomly generated fallback would silently invalidate every active session on restart.Generate a strong secret with:
Absolute base URL the Registry is reachable at, with no trailing slash. Every session cookie boundary and OAuth redirect URI is derived from this value.Validation: must be a well-formed Register the OAuth redirect URI in each Identity Provider’s console as
http:// or https:// URL. The app strips any trailing slash before storing the value. A value that is not an absolute HTTP(S) URL causes a startup failure.<PUBLIC_URL>/api/auth/callback/<kind>, where <kind> is google, microsoft, or github.Name of the S3 (or S3-compatible) bucket where Skill and Plugin Artifacts are stored. Publishing presigns an upload URL against this bucket, so the app refuses to start without a value.
Postgres superuser username passed to the
postgres Docker Compose service. Must match the username in DATABASE_URL.Postgres superuser password passed to the
postgres Docker Compose service. Must match the password in DATABASE_URL.Database name passed to the
postgres Docker Compose service. Must match the database name in DATABASE_URL.Optional variables
These variables have defaults or are only needed in specific deployments.Server
TCP port the API server listens on.Validation: must be a valid port number (integer between 1 and 65535). An invalid value causes a startup failure.
Logging verbosity. Accepts standard log-level strings:
trace, debug, info, warn, error, fatal.Comma-separated list of proxy IP addresses the app sits behind.When empty (the default), the client address is read directly from the socket and the
x-forwarded-for header is ignored entirely. This is the safe default: it keeps login rate limiting and sessions.ip_address accurate when there is no proxy.Set this to your load balancer’s IP when running behind one. Without it, every request appears to originate from the proxy — rate limiting falls back to a single shared bucket and install counts stop being deduplicated per client.Database
Postgres schema all tables, enums, and views are created in and queried against.Use this when the database is shared with other applications — give each application its own schema rather than its own database.Validation: must be a plain lowercase Postgres identifier (
[a-z_][a-z0-9_$]*) of at most 63 characters. Uppercase letters are rejected because Kysely quotes the identifier — DB_SCHEMA=Registry would silently become a different schema from anything unquoted resolves to. The pg_ prefix is reserved by Postgres and is also rejected.The schema is created on startup if it does not already exist. Changing this value after first boot points the app at an empty schema and re-runs all migrations against it; no existing data is moved.Do not add
search_path to DATABASE_URL to achieve the same effect. All queries and migrations are schema-qualified from this single variable — DATABASE_URL needs no search_path parameter.Analytics
How often the
resource_analytics materialized view (install counts) is refreshed, as a node-cron expression. The seconds field comes first.Validation: must be a valid node-cron expression. An invalid value causes a startup failure.Storage
AWS region of the S3 bucket. Required for AWS S3; can be omitted for S3-compatible endpoints that do not require a region.
AWS access key ID. Leave unset in production on AWS to fall back to the default credential chain (instance profile, ECS task role, etc.) rather than embedding a long-lived key.
AWS secret access key. Leave unset alongside
STORAGE_ACCESS_KEY_ID to use the default credential chain.Base URL of a self-hosted S3-compatible endpoint, e.g.
http://minio:9000. Leave unset when using real AWS S3.The endpoint embedded in presigned URLs, when the browser reaches storage at a different address than the API does. Defaults to See Storage for a full explanation of when and why this differs from
STORAGE_ENDPOINT when unset.This is needed in a Docker Compose setup where the API reaches MinIO over the Docker network hostname (minio) but presigned URLs must use localhost so a browser can follow them.STORAGE_ENDPOINT.Startup validation summary
The table below shows exactly when each validation is applied and what error is raised on failure:| Variable | Condition that fails startup |
|---|---|
DATABASE_URL | Missing or empty |
BETTER_AUTH_SECRET | Missing or empty |
PUBLIC_URL | Missing, empty, or not an absolute http(s):// URL |
STORAGE_BUCKET | Missing or empty |
PORT | Set to a non-integer or a value outside 1–65535 |
DB_SCHEMA | Not a lowercase Postgres identifier, longer than 63 chars, or starts with pg_ |
ANALYTICS_REFRESH_CRON | Not a valid node-cron expression |