Quick Start

Get Kluis running in 5 minutes with Docker.

Prerequisites: Docker, Docker Compose, and a running Vault/OpenBao instance.

1. Clone the Repository

git clone https://github.com/your-org/kluis.git
cd kluis

2. Configure Environment

Copy the example environment file and update with your settings:

cp .env.example .env

Required environment variables:

# Database
DATABASE_URL="postgresql://user:password@localhost:5432/kluis"

# Vault Connection
VAULT_ADDR="http://localhost:8200"
VAULT_TOKEN="your-root-token"

# JWT Secret (generate a secure random string)
JWT_SECRET="your-secure-jwt-secret"

# Session
SESSION_SECRET="your-session-secret"

3. Start with Docker Compose

docker-compose up -d

This starts:

  • PostgreSQL database
  • Kluis backend (Express.js)
  • Kluis frontend (React)

4. Access Kluis

Open http://localhost:3000 in your browser. The first user to register becomes SUPERADMIN.

Prerequisites

Required

  • Node.js 20+ (for local development)
  • PostgreSQL 15+
  • OpenBao or HashiCorp Vault (with admin token)

Optional

  • Docker & Docker Compose (recommended for deployment)
  • OAuth Provider Credentials (Google, GitHub, Azure AD)

Vault Requirements

Kluis requires a Vault token with permissions to:

  • Read and write policies
  • List and manage auth methods
  • Mount and configure secrets engines
  • Create tokens for users
Security Note: In production, create a dedicated Vault policy for Kluis rather than using the root token.

Installation

Option 1: Docker (Recommended)

# Production single-container deployment
docker run -d \
  -p 3000:3000 \
  -e DATABASE_URL="postgresql://..." \
  -e VAULT_ADDR="http://vault:8200" \
  -e VAULT_TOKEN="..." \
  -e JWT_SECRET="..." \
  -e NODE_ENV=production \
  -e SERVE_FRONTEND=true \
  ghcr.io/your-org/kluis:latest

Option 2: From Source

# Clone repository
git clone https://github.com/your-org/kluis.git
cd kluis

# Install dependencies
cd backend && npm install
cd ../frontend && npm install

# Run database migrations
cd ../backend
npx prisma migrate deploy

# Build frontend
cd ../frontend && npm run build

# Start production server
cd ../backend
NODE_ENV=production SERVE_FRONTEND=true npm start

Option 3: Development Mode

# Terminal 1: Backend
cd backend
npm install
npm run dev

# Terminal 2: Frontend
cd frontend
npm install
npm run dev

Frontend runs on :5173, backend on :3000.

Configuration

Environment Variables

Variable Required Description
DATABASE_URL Yes PostgreSQL connection string
VAULT_ADDR Yes Vault server address
VAULT_TOKEN Yes Vault authentication token
JWT_SECRET Yes Secret for signing JWT tokens
SESSION_SECRET Yes Secret for session cookies
NODE_ENV No Environment (development/production)
SERVE_FRONTEND No Serve frontend from backend (production)
GOOGLE_CLIENT_ID No Google OAuth client ID
GOOGLE_CLIENT_SECRET No Google OAuth client secret
GITHUB_CLIENT_ID No GitHub OAuth client ID
GITHUB_CLIENT_SECRET No GitHub OAuth client secret

Architecture

Kluis is a management layer that sits between your team and Vault:

Users (Browser/API)
↓
Kluis Platform RBAC • Policy Sync • Audit
↓
OpenBao / Vault
PostgreSQL

Key Principles

  • Secrets stay in Vault - Kluis never stores secrets
  • Enhanced RBAC - Permissions Vault doesn't offer natively
  • Automatic policies - Generated from role assignments
  • Single source of truth - PostgreSQL for app state, Vault for secrets

Access Control

Permission System

Kluis provides 44 granular permissions across 11 resource types:

Resource Actions
KV Secrets read, write, delete, admin
Database read, create, revoke, admin
PKI read, issue, revoke, admin
Policies read, write, delete, admin
Engines read, write, delete, admin
Users read, write, delete, admin
Roles read, write, delete, admin
Audit read

Built-in Roles

  • SUPERADMIN - Full access to everything
  • MAINTAINER - Administrative operations
  • USER - Read-only access
  • KV_ADMIN - KV engine administration
  • DATABASE_OPERATOR - Database credential management
  • PKI_OPERATOR - Certificate operations
  • CUSTOM - User-defined permissions

Engine-Level Access

Beyond permissions, Kluis controls which specific engine instances each role can access:

// Grant "dev-team" role access to specific engines
POST /api/management/engines/:engineId/roles
{
  "roleId": "dev-team",
  "permissions": ["kv:read", "kv:write"]
}

Policy Synchronization

Kluis automatically generates and syncs Vault policies based on role assignments.

How It Works

  1. Admin assigns role access to engine via UI
  2. Kluis queries accessible engines for that role
  3. HCL policy generated with correct paths
  4. Policy pushed to Vault
  5. User tokens reflect new permissions

Manual Resync

Force a complete policy resync:

POST /api/management/roles/resync-all

Fault Tolerance

Sync is non-blocking. If Vault is temporarily unavailable, Kluis queues changes and syncs when connectivity returns.

API Reference

Kluis exposes a REST API. Full documentation available at /api-docs when running.

Authentication

POST /api/auth/login
{
  "email": "[email protected]",
  "password": "..."
}

// Response
{
  "token": "eyJhbG...",
  "refreshToken": "...",
  "user": { ... }
}

Secret Engines

// Read KV secret
GET /api/engines/kv-v2/secrets/path/to/secret

// Write KV secret
POST /api/engines/kv-v2/secrets/path/to/secret
{
  "data": { "key": "value" }
}

// Generate database credentials
GET /api/engines/database/creds/:role

// Issue PKI certificate
POST /api/engines/pki/issue/:role
{
  "common_name": "service.example.com"
}

Management

// List roles
GET /api/management/roles

// Create custom role
POST /api/management/roles
{
  "name": "custom-role",
  "permissions": ["kv:read", "database:create"]
}

// Grant engine access
POST /api/management/engines/:id/roles
{
  "roleId": "..."
}

// Query audit logs
GET /api/audit?user=alice&action=secrets:read&from=2024-01-01

Environment Variables

Core Settings

# Required
DATABASE_URL=postgresql://user:pass@host:5432/kluis
VAULT_ADDR=http://vault:8200
VAULT_TOKEN=your-token
JWT_SECRET=long-random-string
SESSION_SECRET=another-random-string

# Production
NODE_ENV=production
SERVE_FRONTEND=true
PORT=3000

OAuth Providers

# Google
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback

# GitHub
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback

# Azure AD
AZURE_CLIENT_ID=...
AZURE_CLIENT_SECRET=...
AZURE_TENANT_ID=...
AZURE_CALLBACK_URL=http://localhost:3000/api/auth/azure/callback

Deployment

Production Checklist

  • Set NODE_ENV=production
  • Use strong, unique secrets for JWT and sessions
  • Configure TLS termination (nginx, load balancer)
  • Set up database backups
  • Create dedicated Vault policy (not root token)
  • Configure OAuth callback URLs for your domain

Docker Production

version: '3.8'
services:
  kluis:
    image: ghcr.io/your-org/kluis:latest
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - SERVE_FRONTEND=true
      - DATABASE_URL=postgresql://...
      - VAULT_ADDR=http://vault:8200
      - VAULT_TOKEN=...
      - JWT_SECRET=...
      - SESSION_SECRET=...
    depends_on:
      - postgres

  postgres:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=kluis
      - POSTGRES_USER=kluis
      - POSTGRES_PASSWORD=...

volumes:
  pgdata:

Horizontal Scaling

Run multiple Kluis instances behind a load balancer. Ensure:

  • All instances share the same database
  • JWT_SECRET is identical across instances
  • Sticky sessions if using in-memory session store

Security

Best Practices

  • Use TLS - Always terminate TLS in production
  • Rotate secrets - Periodically rotate JWT and session secrets
  • Dedicated Vault token - Create a scoped policy for Kluis
  • Network isolation - Keep Vault and PostgreSQL on private networks
  • Regular updates - Keep dependencies updated

Kluis Vault Policy

Example minimal policy for the Kluis service token:

# Policy management
path "sys/policies/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Secrets engines
path "sys/mounts/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Auth methods
path "sys/auth/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Token creation
path "auth/token/create" {
  capabilities = ["create", "update"]
}

# All secrets (adjust paths as needed)
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
Note: Adjust the secrets path patterns based on your mounted engines.