Skip to main content

1. Quick Start (Dev)

git clone https://github.com/mishrasanjeev/grantex.git
cd grantex
docker compose up --build
This starts PostgreSQL, Redis, and the auth service. Two developer accounts are seeded automatically:
AccountAPI keyMode
Livedev-api-key-localNormal consent flow
Sandboxsandbox-api-key-localAuto-approves grants, returns code immediately
Verify it’s running:
curl http://localhost:3001/health
# { "status": "ok" }

curl http://localhost:3001/.well-known/jwks.json
# { "keys": [{ "kty": "RSA", "alg": "RS256", ... }] }
The dev compose exposes database and Redis ports and uses hardcoded credentials. Never use it in production.

2. Generating a Production RSA Key

Grantex signs grant tokens with RSA-256. Generate a 2048-bit private key:
openssl genrsa -out private.pem 2048
Collapse to a single line for environment variables:
awk 'NF {sub(/\r/, ""); printf "%s\\n", $0}' private.pem
Copy the output and use it as RSA_PRIVATE_KEY.
Keep private.pem out of source control. The JWKS endpoint exposes only the public key.

3. Production Docker Compose

Prerequisites

  • Docker 24+ with Compose v2
  • A domain name with DNS pointing to your server
  • TLS certificate (Let’s Encrypt for production)

Step 1 — Fill in the env file

cp .env.prod.example .env.prod
Edit .env.prod and replace every change-me-* placeholder. Set RSA_PRIVATE_KEY to the collapsed PEM and JWT_ISSUER to your public base URL.

Step 2 — Provide TLS certificates

# Let's Encrypt (production)
certbot certonly --standalone -d auth.example.com
cp /etc/letsencrypt/live/auth.example.com/fullchain.pem deploy/nginx/certs/server.crt
cp /etc/letsencrypt/live/auth.example.com/privkey.pem   deploy/nginx/certs/server.key

Step 3 — Start the stack

docker compose -f docker-compose.prod.yml --env-file .env.prod up -d
Architecture:
Internet → nginx (:443) → auth-service:3001

           postgres + redis  (internal network only)

4. Kubernetes / Helm

Prerequisites

  • Kubernetes 1.26+, Helm 3.x
  • Managed PostgreSQL and Redis
  • An RSA private key (Section 2)

Install

helm install grantex deploy/helm/grantex/ \
  --namespace grantex --create-namespace \
  --set externalDatabase.url="postgres://user:pass@host:5432/grantex" \
  --set externalRedis.url="redis://:pass@host:6379" \
  --set rsaPrivateKey="$(awk 'NF {sub(/\r/, ""); printf "%s\\n", $0}' private.pem)" \
  --set config.jwtIssuer="https://auth.example.com"

Enable Ingress

helm upgrade grantex deploy/helm/grantex/ \
  --reuse-values \
  --set ingress.enabled=true \
  --set ingress.className=nginx \
  --set "ingress.hosts[0].host=auth.example.com" \
  --set "ingress.hosts[0].paths[0].path=/" \
  --set "ingress.hosts[0].paths[0].pathType=Prefix" \
  --set "ingress.tls[0].secretName=grantex-tls" \
  --set "ingress.tls[0].hosts[0]=auth.example.com"

Use an existing Secret

kubectl create secret generic grantex-secrets \
  --namespace grantex \
  --from-literal=RSA_PRIVATE_KEY="$(cat private.pem)"

helm install grantex deploy/helm/grantex/ \
  --namespace grantex \
  --set existingSecret=grantex-secrets \
  --set externalDatabase.url="..." \
  --set externalRedis.url="..."

5. Environment Variable Reference

VariableRequiredDefaultDescription
DATABASE_URLYesPostgreSQL connection string
REDIS_URLYesRedis connection string
RSA_PRIVATE_KEYYes*PEM private key for JWT signing. *Or set AUTO_GENERATE_KEYS=true (dev only)
AUTO_GENERATE_KEYSNofalseAuto-generate RSA keypair at startup (dev only)
JWT_ISSUERYeshttps://grantex.deviss claim in every JWT
PORTNo3001Port the auth service listens on
HOSTNo0.0.0.0Bind address
SEED_API_KEYNoPre-seed a live developer API key (dev only)
SEED_SANDBOX_KEYNoPre-seed a sandbox API key (dev only)
STRIPE_SECRET_KEYNoEnable Stripe billing integration
STRIPE_WEBHOOK_SECRETNoStripe webhook signature validation
STRIPE_PRICE_PRONoStripe price ID for Pro tier
STRIPE_PRICE_ENTERPRISENoStripe price ID for Enterprise tier

6. Database Migrations

Migrations run automatically on every startup. The auth service reads all *.sql files from the migrations/ directory and executes each one using idempotent DDL (CREATE TABLE IF NOT EXISTS, etc.). There are currently 9 migration files covering: core tables, webhooks, consent, delegation, compliance, policies, anomalies, SCIM/SSO, and developer email. To upgrade, just restart the service — new migration files are applied automatically.

7. Key Rotation

  1. Generate a new RSA key pair (Section 2)
  2. Update RSA_PRIVATE_KEY in your env file or Kubernetes secret
  3. Restart the auth service
Tokens signed with the old key remain valid until expiry because the JWKS endpoint always exposes the current public key — clients re-fetch it automatically when verification fails.

8. Health Checks & Monitoring

GET /health → 200 { "status": "ok" }
All logs are emitted as JSON to stdout, compatible with Datadog, Loki, and CloudWatch Logs.

9. Backup & Recovery

PostgreSQL

docker compose -f docker-compose.prod.yml exec postgres \
  pg_dump -U "$POSTGRES_USER" grantex | gzip > "grantex-$(date +%Y%m%d).sql.gz"

Redis

Redis holds ephemeral token metadata and rate-limiting state. If Redis data is lost, in-flight auth requests will fail temporarily, but no permanent data is lost. PostgreSQL is the source of truth.

10. Production Readiness Checklist

  • RSA_PRIVATE_KEY is a real 2048-bit RSA key
  • POSTGRES_PASSWORD and REDIS_PASSWORD are strong random values
  • SEED_API_KEY and SEED_SANDBOX_KEY are not set
  • TLS is enabled end-to-end
  • Database and Redis ports are not exposed publicly
  • JWT_ISSUER matches your public base URL exactly
  • Automated database backups are configured
  • Health checks are wired into your load balancer
  • CPU and memory limits are set
  • Log forwarding is configured