Locksmith is an open-source OAuth2 Identity and Access Management (IAM) platform. It provides multi-tenant user authentication, fine-grained role-based access control, OAuth2 Authorization Code flow with PKCE, social login, session tracking with device fingerprinting, and a fully featured management dashboard β all deployable as a single Docker container.
- Features
- Architecture
- Quick Start
- Configuration
- Default Credentials
- Seeder Configuration
- OAuth2 Flow
- Social Login
- API Reference
- Access Control (ACL)
- Multi-Tenancy
- Session & Device Tracking
- Security
- Management Dashboard
- Development
- OAuth2 Authorization Code + PKCE β S256-only code challenge enforcement;
require_pkceflag per client makes PKCE mandatory - Social Login β Google, GitHub, Facebook, Discord, LinkedIn via per-client configurable providers
- Multi-tenant Projects β fully isolated users, OAuth clients, roles, and sessions per project
- Role-Based Access Control β Casbin-powered RBAC with domain, module, and action granularity
- Session Management β per-session tracking with browser, OS, device type, IP address, and GeoIP location
- Refresh Token Rotation β SHA-256 hashed refresh tokens with chain revocation on reuse detection
- Logout Session Revocation β logout marks the refresh token and its session as revoked server-side
- Customizable Login/Register pages β per-client UI theming, custom CSS/HTML, and field visibility controls
- User Account Management β CRUD, Argon2id password hashing, forced password change on next login
- Rate Limiting β per-IP limits on all authentication endpoints
- Production Error Mode β stack traces hidden in production; opaque error IDs logged server-side
- Management Dashboard β Vue 3 + Vuetify web UI for managing all resources
- Single Binary + SPA β API and dashboard bundled into one Docker image (no separate containers in production)
- Multi-arch image β
linux/amd64andlinux/arm64published to Docker Hub - Runs as non-root β production container uses a dedicated unprivileged user
βββββββββββββββββββββββββββββββββββββββββββββββ
β Locksmith Container β
β β
β βββββββββββββββββββ βββββββββββββββββββββ β
β β Go REST API β β Vue 3 Dashboard β β
β β (Chi router) β β (served as SPA) β β
β ββββββββββ¬βββββββββ βββββββββββββββββββββ β
β β β
βββββββββββββΌββββββββββββββββββββββββββββββββββ
β
ββββββββΌβββββββ
β PostgreSQL β
βββββββββββββββ
Tech Stack:
| Layer | Technology |
|---|---|
| Backend | Go 1.25+, Chi v5, Casbin v2, JWT |
| Frontend | Vue 3, Vuetify 3, Vite, Bun |
| Database | PostgreSQL 16 |
| Auth | OAuth2 PKCE (S256), Argon2id, SHA-256 tokens |
| Social | goth (Google, GitHub, Facebook, Discord, LinkedIn) |
| ACL | Casbin RBAC with domain support |
1. Create a compose.yaml:
services:
locksmith:
image: booscaaa/locksmith:latest
container_name: locksmith
ports:
- "4000:4000"
environment:
- APP_ENV=production
- LOCKSMITH_APP_PORT=4000
- LOCKSMITH_BASE_URL=http://localhost:4000
- LOCKSMITH_APP_CLIENT_ID=my-client-id
- LOCKSMITH_APP_CLIENT_SECRET=my-client-secret
- POSTGRES_HOST=database
- POSTGRES_USER=locksmith
- POSTGRES_PASSWORD=locksmith123
- POSTGRES_DB=locksmith
- POSTGRES_PORT=5432
- SCHEMA=locksmith
- SSL_MODE=disable
- SEED_ADMIN_EMAIL=admin@example.com
- SEED_ADMIN_PASSWORD=changeme123
depends_on:
database:
condition: service_healthy
restart: unless-stopped
database:
image: postgres:16-alpine
container_name: locksmith-db
environment:
- POSTGRES_USER=locksmith
- POSTGRES_PASSWORD=locksmith123
- POSTGRES_DB=locksmith
volumes:
- locksmith-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "locksmith"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
locksmith-data:2. Start it:
docker compose up -d3. Open the dashboard:
http://localhost:4000
1. Create a Docker network:
docker network create locksmith-network2. Start PostgreSQL:
docker run -d \
--name locksmith-db \
--network locksmith-network \
-e POSTGRES_USER=locksmith \
-e POSTGRES_PASSWORD=locksmith123 \
-e POSTGRES_DB=locksmith \
-v locksmith-data:/var/lib/postgresql/data \
--health-cmd="pg_isready -U locksmith" \
--health-interval=10s \
--health-timeout=5s \
--health-retries=5 \
--restart unless-stopped \
postgres:16-alpine3. Start Locksmith:
docker run -d \
--name locksmith \
--network locksmith-network \
-p 4000:4000 \
-e APP_ENV=production \
-e LOCKSMITH_APP_PORT=4000 \
-e LOCKSMITH_BASE_URL=http://localhost:4000 \
-e LOCKSMITH_APP_CLIENT_ID=my-client-id \
-e LOCKSMITH_APP_CLIENT_SECRET=my-client-secret \
-e POSTGRES_HOST=locksmith-db \
-e POSTGRES_USER=locksmith \
-e POSTGRES_PASSWORD=locksmith123 \
-e POSTGRES_DB=locksmith \
-e POSTGRES_PORT=5432 \
-e SCHEMA=locksmith \
-e SSL_MODE=disable \
--restart unless-stopped \
booscaaa/locksmith:latestdocker run -d \
--name locksmith \
--network locksmith-network \
-p 4000:4000 \
-e APP_ENV=production \
-e LOCKSMITH_APP_PORT=4000 \
-e LOCKSMITH_BASE_URL=https://auth.example.com \
-e LOCKSMITH_APP_CLIENT_ID=my-client-id \
-e LOCKSMITH_APP_CLIENT_SECRET=strong-secret-here \
-e POSTGRES_HOST=your-postgres-host \
-e POSTGRES_USER=locksmith \
-e POSTGRES_PASSWORD=strong-password-here \
-e POSTGRES_DB=locksmith \
-e POSTGRES_PORT=5432 \
-e SCHEMA=locksmith \
-e SSL_MODE=require \
-e SEED_ADMIN_EMAIL=admin@yourdomain.com \
-e SEED_ADMIN_PASSWORD=strong-admin-password \
--restart unless-stopped \
booscaaa/locksmith:latestNote: Place a reverse proxy (nginx, Caddy, Traefik) in front of Locksmith to handle TLS termination. Locksmith itself runs on plain HTTP internally.
All configuration is done through environment variables.
| Variable | Description | Example |
|---|---|---|
LOCKSMITH_APP_PORT |
Port the API listens on | 4000 |
LOCKSMITH_BASE_URL |
Public base URL (used for OAuth callbacks and cookie domain) | https://auth.example.com |
LOCKSMITH_APP_CLIENT_ID |
Client ID of the built-in Locksmith management client | my-client-id |
LOCKSMITH_APP_CLIENT_SECRET |
Client secret of the built-in management client | my-client-secret |
POSTGRES_HOST |
PostgreSQL hostname | database |
POSTGRES_USER |
PostgreSQL username | locksmith |
POSTGRES_PASSWORD |
PostgreSQL password | locksmith123 |
POSTGRES_DB |
PostgreSQL database name | locksmith |
POSTGRES_PORT |
PostgreSQL port | 5432 |
SCHEMA |
PostgreSQL schema name | locksmith |
SSL_MODE |
PostgreSQL SSL mode (disable, require, verify-full) |
require |
| Variable | Description | Default |
|---|---|---|
APP_ENV |
Runtime environment. Set to production to hide internal error details from HTTP responses. |
development |
SEED_ADMIN_EMAIL |
Email of the default admin account created on first boot | admin@locksmith.rs |
SEED_ADMIN_PASSWORD |
Password of the default admin account | admin |
SEED_APP_CLIENT_ID |
Overrides LOCKSMITH_APP_CLIENT_ID in the seeder (useful when they differ) |
β |
SEED_APP_CLIENT_SECRET |
Overrides LOCKSMITH_APP_CLIENT_SECRET in the seeder |
β |
SEED_APP_USER_PASSWORD |
Password assigned to seeded user accounts | β |
VITE_LOCKSMITH_API_BASE_URL |
Override the API base URL used by the frontend SPA | window.location.origin |
- Always set
APP_ENV=productionin production deployments. In development mode, error responses include stack traces and internal details. In production mode, errors return only a message and an opaqueerror_id(logged server-side for debugging). LOCKSMITH_BASE_URLmust be reachable from the browser β it is used as the OAuth redirect target and for setting the cookie domain.SSL_MODE=disableis only acceptable in local development with a co-located database. Userequireorverify-fullin any networked environment.- On first boot, Locksmith automatically runs all database migrations and seeds the default project, admin account, and management OAuth client. All seed operations are idempotent.
On first boot, Locksmith creates the following defaults (overridable via env vars):
| Resource | Default value | Override via env |
|---|---|---|
| Default project | Default Project (domain: domain:locksmith) |
β |
| Admin email | admin@locksmith.rs |
SEED_ADMIN_EMAIL |
| Admin username | admin |
β |
| Admin password | admin |
SEED_ADMIN_PASSWORD |
| Management client | Uses LOCKSMITH_APP_CLIENT_ID/SECRET |
SEED_APP_CLIENT_ID/SECRET |
Change the admin password immediately after your first login, or set
SEED_ADMIN_PASSWORDbefore the first boot.
On first boot, Locksmith reads /etc/locksmith/config/seeder.yaml to create the default project, admin account, and management OAuth client. All values support ${ENV_VAR} interpolation at startup.
default_project:
name: Default Project
description: Default project for Locksmith
domain: domain:locksmith
default_account:
name: Default Account
email: ${SEED_ADMIN_EMAIL}
password: ${SEED_ADMIN_PASSWORD}
username: admin
default_client:
name: Default Client
redirect_uris: ${LOCKSMITH_BASE_URL}/api/locksmith/callback ${LOCKSMITH_BASE_URL}
grant_types: authorization_codeThe projects key lets you declare additional projects with roles, OAuth clients, and users that are created automatically on startup. All entries are idempotent.
projects:
- name: My Application
description: Main application for ACME Corp
domain: domain:myapp
roles:
- title: role:admin
policies:
- module: module:accounts
actions:
- action:create:one
- action:read:all
- action:update:one
- action:delete:one
- module: module:sessions
actions:
- action:read:all
- action:revoke
- title: role:user
policies:
- module: module:accounts
actions:
- action:read:own
- action:update:own
clients:
- name: My App Web Client
client_id: ${MY_APP_CLIENT_ID}
client_secret: ${MY_APP_CLIENT_SECRET}
redirect_uris: ${MY_APP_BASE_URL}/callback ${MY_APP_BASE_URL}
grant_types: authorization_code
login:
enabled: true
show_sign_up: true
show_social: true
primary_color: "#1976D2"
signup:
enabled: true
default_role_name: role:user
users:
- name: App Admin
email: admin@myapp.com
username: admin
password: ${SEED_APP_USER_PASSWORD}
role: role:admin# compose.yaml
services:
locksmith:
image: booscaaa/locksmith:latest
volumes:
- ./my-seeder.yaml:/etc/locksmith/config/seeder.yaml
environment:
LOCKSMITH_BASE_URL: https://auth.example.com
# ...# docker run
docker run -d \
-v $(pwd)/my-seeder.yaml:/etc/locksmith/config/seeder.yaml \
-e LOCKSMITH_BASE_URL=https://auth.example.com \
booscaaa/locksmith:latestLocksmith implements the Authorization Code flow with PKCE (RFC 7636). Only S256 is accepted as code_challenge_method β plain is rejected with HTTP 400.
1. Start authorization:
GET /api/oauth2/authorize
?client_id=<client_id>
&redirect_uri=<redirect_uri>
&response_type=code
&state=<random_state>
&code_challenge=<sha256_of_verifier_base64url>
&code_challenge_method=S256
2. User login:
POST /api/oauth2/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password",
"client_id": "<client_id>",
"code_challenge": "<sha256_of_verifier_base64url>",
"code_challenge_method": "S256"
}If the OAuth client has
require_pkce: true, thecode_challengefield is mandatory. Requests without it are rejected with HTTP 400.
2b. (Alternative) User registration:
POST /api/oauth2/register
Content-Type: application/json
{
"name": "User Name",
"email": "user@example.com",
"username": "username",
"password": "password",
"client_id": "<client_id>"
}3. Exchange code for tokens:
POST /api/oauth2/access-token
Content-Type: application/json
{
"code": "<authorization_code>",
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"grant_type": "authorization_code",
"code_verifier": "<original_verifier>"
}Response:
{
"access_token": "<jwt>",
"refresh_token": "<uuid>",
"token_type": "Bearer",
"expires_in": 300
}4. Refresh tokens:
POST /api/oauth2/refresh-token
Content-Type: application/json
{
"refresh_token": "<refresh_token>",
"client_id": "<client_id>",
"client_secret": "<client_secret>"
}The management dashboard uses Locksmith's own callback handler which sets HTTP-only cookies:
LOCKSMITHACCESSTOKENβ JWT access token (5-minute expiry,HttpOnly,Secure,SameSite=Strict)LOCKSMITHREFRESHTOKENβ UUID refresh token (15-day expiry,HttpOnly,Secure,SameSite=Strict)
The cookie domain is derived automatically from the request's Origin header.
Locksmith supports social login via OAuth2 providers configured per OAuth client. Each provider requires a client_key (OAuth App Client ID) and client_secret from the provider's developer console.
| Provider | Provider name (API) | Required scopes |
|---|---|---|
google |
openid email profile |
|
| GitHub | github |
user:email |
facebook |
email |
|
| Discord | discord |
identify email |
linkedin |
r_liteprofile r_emailaddress |
- Go to Project Details β OAuth Client β Social Providers in the dashboard
- Enable the desired provider and paste your
client_keyandclient_secret - Set the allowed scopes (space-separated)
- Configure the OAuth callback URI in your provider's developer console:
{LOCKSMITH_BASE_URL}/api/oauth2/social/{provider}/callback
For example: https://auth.example.com/api/oauth2/social/google/callback
1. Begin social authorization:
GET /api/oauth2/social/{provider}/begin
?client_id=<client_id>
&redirect_uri=<redirect_uri>
&state=<random_state>
&code_challenge=<sha256_of_verifier_base64url>
&code_challenge_method=S256
Returns:
{
"auth_url": "https://accounts.google.com/o/oauth2/auth?..."
}2. Redirect the user to auth_url. After the provider authenticates the user, it redirects back to Locksmith's callback endpoint, which issues an auth code and redirects to your redirect_uri.
3. Exchange the auth code for tokens using the same /api/oauth2/access-token endpoint.
On social login:
- If an account with the provider's user ID already exists β authenticated directly
- If an account with the same email exists β the social provider is linked to that account
- If no account exists and signup is enabled for the client β a new account is created automatically
- If no account exists and signup is disabled β returns HTTP 403
All API routes are prefixed with /api.
Auth notation:
- π Requires a valid
LOCKSMITHACCESSTOKENcookie (dashboard session) - π Requires HTTP Basic Auth (
client_id:client_secret) - (no marker) Public
| Method | Path | Description |
|---|---|---|
POST |
/api/oauth2/authorize |
Start authorization β create auth code |
POST |
/api/oauth2/login |
Authenticate user credentials |
POST |
/api/oauth2/register |
Register a new user account |
POST |
/api/oauth2/access-token |
Exchange auth code for access + refresh tokens |
POST |
/api/oauth2/refresh-token |
Rotate refresh token and get new access token |
GET |
/api/oauth2/resolve-domain |
Resolve OAuth client by custom domain |
GET |
/api/oauth2/social/{provider}/begin |
Start social OAuth flow |
GET |
/api/oauth2/social/{provider}/callback |
Social OAuth callback (provider β Locksmith) |
| Method | Path | Description |
|---|---|---|
GET |
/api/locksmith/callback |
OAuth callback β exchanges code, sets cookies |
GET |
/api/locksmith/status |
Check if current session is authenticated |
POST |
/api/locksmith/r |
Refresh access token using cookie |
POST |
/api/locksmith/logout |
Revoke session + tokens and clear cookies |
| Method | Path | Description |
|---|---|---|
GET |
/api/projects |
List all projects (paginated) |
GET |
/api/projects/:id |
Get a single project |
POST |
/api/projects |
Create a new project |
PUT |
/api/projects/:id |
Update a project |
DELETE |
/api/projects/:id |
Delete a project |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/projects/:project_id/accounts |
π | Create account (dashboard) |
PUT |
/api/projects/:project_id/accounts/:account_id |
π | Update account (dashboard) |
GET |
/api/projects/:project_id/accounts |
π | List accounts (paginated) |
GET |
/api/projects/:project_id/accounts/count |
π | Count accounts |
GET |
/api/projects/:project_id/accounts/:id |
π | Get a single account |
POST |
/api/accounts |
π | Create account (client credentials) |
GET |
/api/accounts/:id |
π | Get account by ID |
PUT |
/api/accounts/:account_id |
π | Update account |
POST |
/api/accounts/change-password |
β | Change password (JWT verified inline) |
| Method | Path | Description |
|---|---|---|
GET |
/api/projects/:project_id/clients |
List OAuth clients |
GET |
/api/projects/:project_id/clients/:id |
Get a single OAuth client |
POST |
/api/projects/:project_id/clients |
Create OAuth client |
PUT |
/api/projects/:project_id/clients/:id |
Update OAuth client |
OAuth client fields:
| Field | Type | Description |
|---|---|---|
name |
string | Display name |
redirect_uris |
string | Space-separated allowed redirect URIs |
grant_types |
string | Space-separated grant types (e.g. authorization_code) |
require_pkce |
bool | When true, code_challenge is mandatory on login and social begin |
The
client_secretis returned in full only on creation. Subsequent reads show only the last 4 characters (****xxxx) for security. The field is never overwritten if you submit a masked value.
| Method | Path | Description |
|---|---|---|
GET |
/api/projects/:project_id/clients/:id/social-providers |
List social providers for a client |
POST |
/api/projects/:project_id/clients/:id/social-providers |
Create or update a social provider |
Social provider fields:
| Field | Type | Description |
|---|---|---|
provider |
string | Provider name: google, github, facebook, discord, linkedin |
client_key |
string | OAuth App Client ID from the provider's developer console |
client_secret |
string | OAuth App Client Secret (write-only; submit **** to keep existing) |
scopes |
string | Space-separated OAuth scopes |
enabled |
bool | Whether this provider is active for login/signup |
| Method | Path | Description |
|---|---|---|
GET |
/api/projects/:project_id/clients/:id/login |
Get login page config |
POST |
/api/projects/:project_id/clients/:id/login |
Create login page config |
PUT |
/api/projects/:project_id/clients/:id/login |
Update login page config |
GET |
/api/projects/:project_id/clients/:id/signup |
Get signup page config |
POST |
/api/projects/:project_id/clients/:id/signup |
Create signup page config |
PUT |
/api/projects/:project_id/clients/:id/signup |
Update signup page config |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/projects/:project_id/sessions |
π | List sessions for a project (paginated) |
GET |
/api/projects/:project_id/sessions/count |
π | Count sessions |
GET |
/api/projects/:project_id/accounts/:account_id/sessions |
π | List sessions for a specific account |
DELETE |
/api/projects/:project_id/sessions/:session_id |
π | Revoke a session |
GET |
/api/projects/:project_id/accounts/:account_id/refresh-tokens |
π | List refresh tokens for an account |
GET |
/api/accounts/:account_id/sessions |
π | List sessions (client auth) |
DELETE |
/api/accounts/:account_id/sessions/:session_id |
π | Revoke a session (client auth) |
| Method | Path | Description |
|---|---|---|
GET |
/api/dashboard/stats |
Aggregated statistics (projects, accounts, sessions) |
| Method | Path | Description |
|---|---|---|
GET |
/api/acl |
Fetch full ACL data (roles, modules, actions, policies) |
POST |
/api/acl/role |
Create a role |
POST |
/api/acl/module |
Create a module |
POST |
/api/acl/action |
Create an action |
GET |
/api/acl/roles |
List all roles |
GET |
/api/acl/modules |
List all modules |
GET |
/api/acl/actions |
List all actions |
POST |
/api/acl/enforce |
Check if a subject has permission |
GET |
/api/acl/permissions/user/:user/domain/:domain |
π Get user permissions in a domain |
Enforce request body:
{
"subject": "role:admin",
"domain": "domain:locksmith",
"object": "module:accounts",
"action": "action:read:all"
}Response 200 OK = permission granted. 403 Forbidden = denied.
| Method | Path | Description |
|---|---|---|
GET |
/api/config |
Returns baseUrl and clientId for the SPA to bootstrap OAuth |
Locksmith uses Casbin with a domain-aware RBAC model:
subject β role (e.g. role:admin)
domain β tenant (e.g. domain:locksmith)
object β module (e.g. module:accounts)
action β op (e.g. action:read:all)
POST /api/acl/enforce
Content-Type: application/json
Cookie: LOCKSMITHACCESSTOKEN=<token>
{
"subject": "role:admin",
"domain": "domain:myproject",
"object": "module:orders",
"action": "action:delete:one"
}Every resource is scoped to a Project. Projects are fully isolated:
| Resource | Isolated by project |
|---|---|
| User accounts | β |
| OAuth clients | β |
| Social provider configs | β |
| Sessions | β |
| ACL policies | β |
| Login/Register page config | β |
When a user authenticates, Locksmith captures:
| Field | Source |
|---|---|
ip_address |
X-Forwarded-For / X-Real-IP header |
device_type |
User-Agent parsing (mobile, desktop, tablet) |
browser |
User-Agent parsing (Chrome, Firefox, Safari, etc.) |
browser_version |
User-Agent parsing |
os |
User-Agent parsing (Windows, macOS, Linux, iOS, Android) |
os_version |
User-Agent parsing |
location_country |
GeoIP via ip-api.com |
location_city |
GeoIP via ip-api.com |
Sessions are visible in the dashboard under Project Details β Logs.
| Token | Format | Storage | Expiry |
|---|---|---|---|
| Access token | JWT | HTTP-only cookie | 5 min |
| Refresh token | UUID v4 | SHA-256 hash in DB | 15 days |
- Rotation β each refresh issues a new token and revokes the previous one
- Reuse detection β if a revoked refresh token is presented, the entire session is revoked immediately and all tokens in the chain are invalidated (
revoked_reason = 'token_reuse_detected') - Logout revocation β logout marks the refresh token and its associated session as revoked server-side (
revoked_reason = 'user_logout'); the tokens cannot be reused even if stolen
| Mechanism | Details |
|---|---|
| Password hashing | Argon2id (64 MB memory, 3 iterations, parallelism 2) |
| Timing attack protection | Dummy Argon2id hash always run on "email not found" path (CWE-204) |
| PKCE | S256 only β plain rejected; require_pkce flag per client |
| Rate limiting | Per-IP limits on all auth endpoints (login: 5 req/min) |
| Request size limit | 1 MB maximum body size on all endpoints |
| Soft-deleted accounts | deleted_at IS NULL enforced on every authentication query |
| Cookie | HttpOnly | Secure | SameSite |
|---|---|---|---|
LOCKSMITHACCESSTOKEN |
β | β | Strict |
LOCKSMITHREFRESHTOKEN |
β | β | Strict |
pkce_cv |
β | β | Strict |
client_secretis masked in all read responses (shows only last 4 chars:****xxxx)- Social provider
client_secretis write-only (always returned as****) - Submitting a masked value (
****) in an update request preserves the existing credential without overwriting it - Seeder credentials are injected via environment variables β no hardcoded secrets in config files
Set APP_ENV=production to enable opaque error responses. In production:
- HTTP error responses contain only
{ "message": "...", "error_id": "<uuid>" } - Full error details (stack trace, internal error) are logged server-side with the same
error_idfor correlation - In development mode, full details are returned in the response for easier debugging
The repository includes GitHub Actions workflows:
security.ymlβ runsgovulncheck,gosec,golangci-lint(Go) andnpm audit(frontend) on every push and pull requestcodeql.ymlβ GitHub CodeQL SAST for Go and JavaScript (taint analysis, weekly schedule + push/PR)dependabot.ymlβ automated weekly dependency update PRs for Go, npm, and GitHub Actions
The production Docker image:
- Based on
scratch(no OS, no shell, no package manager) - Runs as a dedicated non-root user (
locksmith, UID assigned by Alpine) - Multi-arch:
linux/amd64andlinux/arm64 - Binary compiled with
-ldflags="-s -w" -trimpath(debug symbols stripped, build paths removed)
The web dashboard is served at the root path (/) and provides a full UI for managing all Locksmith resources.
| Section | Description |
|---|---|
| Dashboard | Overview with aggregated statistics |
| Projects | Create and manage projects |
| Project Details | Config, Roles, OAuth Clients, Users, Logs tabs |
| OAuth Client Details | Config, Login page, Signup page, Social Providers |
| ACL | Manage global roles, modules, and actions |
Docker and Docker Compose.
git clone https://github.com/locksmithhq/locksmith.git
cd locksmith
cp .env.example .env
make upOpen the dashboard at http://localhost:${LOCKSMITH_APP_PORT} (default: http://localhost:4000).
The development setup uses Air for Go hot reload and Vite HMR for the frontend. Changes to Go or Vue files are reflected without restarting containers.
| Command | Description |
|---|---|
make up |
Start all services (builds if needed) |
make down |
Stop all services |
make restart |
Stop and restart all services |
make rebuild |
Stop, rebuild images, and restart |
make logs |
Tail logs for all services |
make logs-api |
Tail API logs only |
make logs-web |
Tail frontend logs only |
make logs-db |
Tail database logs only |
make shell-api |
Open shell inside the API container |
make shell-db |
Open psql inside the database container |
make clean |
Stop services and remove all volumes |
make status |
Show container status |
make open-web |
Open dashboard in browser |
make open-api |
Open API directly in browser |
make build-prod |
Build multi-arch production image and push to Docker Hub |
make build-prodThis runs docker buildx build --platform linux/amd64,linux/arm64 using the optimized multi-stage locksmith/Dockerfile with the project root as the build context. Requires docker buildx and an authenticated Docker Hub session (docker login).