Development

Getting Started

These steps bring up a local database, generate JWT keys, configure the service, build it, and run tests.

Prerequisites

  • CMake, Ninja, a C++20 compiler, and Python.
  • Docker for local Postgres, or a compatible local Postgres instance.
  • Submodules initialized with git submodule update --init.

Local Environment

Create a local .env for Docker Compose:

POSTGRES_DB=smirkly_auth
POSTGRES_USER=smirkly_auth
POSTGRES_PASSWORD=smirkly_auth
MIGRATE_DATABASE_URL=postgres://smirkly_auth:smirkly_auth@smirkly-postgres:5432/smirkly_auth?sslmode=disable

Create configs/config_vars.yaml. Keep this file untracked because it may contain secrets.

worker-threads: 4
worker-fs-threads: 2
worker-email-outbox-threads: 2
logger-level: info
is-testing: false
server-port: 8080
postgres-dbconnection: postgresql://smirkly_auth:smirkly_auth@localhost:5432/smirkly_auth

AUTH_JWT_AUDIENCE: smirkly-api
AUTH_JWT_KEY_ID: smirkly-auth-local-rs256
AUTH_JWT_PRIVATE_KEY_PATH: ./configs/secrets/auth_jwt_private.pem
AUTH_JWT_PUBLIC_KEY_PATH: ./configs/secrets/auth_jwt_public.pem

AUTH_SMTP_HOST: smtp.localhost
AUTH_SMTP_PORT: 587
AUTH_SMTP_TLS_MODE: starttls
AUTH_SMTP_USERNAME: local-dev-user
AUTH_SMTP_APP_PASSWORD: local-dev-password
AUTH_SMTP_FROM_EMAIL: no-reply@localhost
AUTH_SMTP_FROM_NAME: Smirkly

For Gmail-based local testing, use an app password, port 465, and AUTH_SMTP_TLS_MODE: tls. Port 587 should use starttls only when the provider sends a plain SMTP greeting and then upgrades the connection.

AUTH_SMTP_HOST: smtp.gmail.com
AUTH_SMTP_PORT: 465
AUTH_SMTP_TLS_MODE: tls
AUTH_SMTP_USERNAME: your-account@gmail.com
AUTH_SMTP_APP_PASSWORD: "<gmail-app-password>"
AUTH_SMTP_FROM_EMAIL: your-account@gmail.com
AUTH_SMTP_FROM_NAME: "Smirkly"

Restart the running service after changing AUTH_SMTP_*. Rebuild only when source code changed.

JWT Keys

mkdir -p configs/secrets
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -out configs/secrets/auth_jwt_private.pem
openssl rsa -in configs/secrets/auth_jwt_private.pem \
  -pubout -out configs/secrets/auth_jwt_public.pem
chmod 600 configs/secrets/auth_jwt_private.pem

Database

docker compose up -d smirkly-postgres
docker compose --profile dev run --rm smirkly-migrate-dev

The migration runner applies pending *.up.sql files and tracks the schema version in Postgres. The Postgres init directory is not used for application schema migrations. Existing local databases created by the old init-directory flow should be recreated or baselined manually.

Build and Test

make cmake-debug
make build-debug
make test-debug

Docker

docker compose --profile dev up --build smirkly-auth-dev
docker compose --profile dev run --rm smirkly-migrate-dev
docker compose exec smirkly-auth-dev cmake --build build-debug --parallel --target smirkly-auth
docker compose restart smirkly-auth-dev

docker compose --profile prod up --build -d smirkly-auth

The dev profile mounts the source tree and keeps build-debug in a Docker volume. The prod profile builds release app and migration images and runs without mounting source code.

Compose uses the host's native platform by default. On Apple Silicon, if the userver base image has no linux/arm64 build, run with -f docker-compose.yml -f docker-compose.linux-amd64.yml to force linux/amd64.

Equivalent direct CMake workflow:

cmake -S . -B cmake-build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build cmake-build-debug -j$(nproc)
ctest --test-dir cmake-build-debug --output-on-failure

Run the Service

./cmake-build-debug/smirkly-auth \
  --config ./configs/static_config.yaml

Email Verification

Request a fresh verification code when the previous one expired or was not delivered:

curl -i -X POST http://localhost:8080/auth/v0/verify-email/resend \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@example.com"}'

Submit the latest code from the email body:

curl -i -X POST http://localhost:8080/auth/v0/verify-email \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@example.com","code":"123456"}'

Inspect local outbox state:

docker compose exec -T smirkly-postgres \
  psql -U smirkly_auth -d smirkly_auth \
  -c "SELECT id, to_email, status, attempts, next_attempt_at, locked_until, left(coalesce(last_error, ''), 300) AS last_error, created_at, updated_at FROM email_outbox ORDER BY created_at DESC LIMIT 10;"
Do not commit real SMTP credentials, JWT private keys, database passwords, or production hostnames into repository files. Use the deployment platform's secret store.