MDDB Authentication & Authorization

Complete guide to JWT authentication and RBAC (Role-Based Access Control) in MDDB.

Table of Contents


Overview

MDDB supports optional authentication and authorization to secure your markdown database. Key features:

  • Disabled by default - Opt-in via MDDB_AUTH_ENABLED=true
  • JWT tokens - Stateless authentication with configurable expiry
  • API keys - Long-lived credentials for services and CI/CD
  • RBAC - Per-collection read/write/admin permissions
  • BoltDB storage - Auth data stored alongside your documents
  • All protocols - Works with HTTP and gRPC

Quick Start

1. Enable Authentication

Start MDDB with authentication enabled:

cd services/mddbd MDDB_AUTH_ENABLED=true \
MDDB_AUTH_JWT_SECRET=$(openssl rand -hex 32) \
MDDB_AUTH_ADMIN_USERNAME=admin \
MDDB_AUTH_ADMIN_PASSWORD=changeme \
go run .

2. Login

curl http://localhost:11023/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"changeme"}'

Response:

{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresAt": 1772563099
}

3. Use Token

TOKEN="your-token-here" curl -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/stats

Authentication Methods

1. JWT Tokens (Username + Password)

Best for: Interactive users, web applications

Login to receive a JWT token:

curl http://localhost:11023/v1/auth/login \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "changeme" }'

Use the token in subsequent requests:

curl -H "Authorization: Bearer YOUR_TOKEN" \ http://localhost:11023/v1/add \ -d '{ "collection": "docs", "key": "readme", "lang": "en", "contentMd": "# Welcome" }'

Token Properties:

  • Default expiry: 24 hours (configurable)
  • Algorithm: HS256
  • Contains: username, admin flag, expiry

2. API Keys

Best for: Services, CI/CD pipelines, MCP servers

Create an API key:

curl -H "Authorization: Bearer YOUR_TOKEN" \ http://localhost:11023/v1/auth/api-key \ -H "Content-Type: application/json" \ -d '{ "description": "CI server", "expiresAt": 0 }'

Response:

{ "key": "mddb_live_b9a2604ba923ea920451d139a5f366eb384434600aff7e1d", "description": "CI server", "expiresAt": 0, "createdAt": 1772476811
}

โš ๏ธ Important: The full API key is shown only once! Save it securely.

Use API key:

curl -H "X-API-Key: mddb_live_..." \ http://localhost:11023/v1/stats

API Key Format:

  • Prefix: mddb_live_
  • Length: 48 hex characters (24 bytes of randomness)
  • Storage: SHA256 hash
  • Expiry: Optional (0 = never expires)

Authorization (RBAC)

MDDB implements Role-Based Access Control with per-collection permissions.

Permission Types

PermissionOperationsDescription
readGet, Search, Export, FTSView documents
writeAdd, Update, Delete, ImportModify documents
adminBackup, Restore, Stats, User managementDatabase operations

Granting Permissions

Only admins can grant permissions:

curl -H "Authorization: Bearer ADMIN_TOKEN" \ http://localhost:11023/v1/auth/permissions \ -H "Content-Type: application/json" \ -d '{ "username": "alice", "collection": "blog", "read": true, "write": false, "admin": false }'

Wildcard Collection

Use "*" to grant database-wide permissions:

{ "username": "bob", "collection": "*", "read": true, "write": true, "admin": false
}

Permission Lookup Order

  1. Check if user is admin (bypasses all checks)
  2. Check collection-specific permission
  3. Check wildcard ("*") permission
  4. Deny by default

Example Scenarios

Scenario 1: Read-only user for analytics

curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/register \ -d '{"username":"analyst","password":"view123"}' curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/permissions \ -d '{ "username": "analyst", "collection": "*", "read": true, "write": false, "admin": false }'

Scenario 2: Collection-specific editor

curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/register \ -d '{"username":"editor","password":"edit123"}' curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/permissions \ -d '{ "username": "editor", "collection": "articles", "read": true, "write": true, "admin": false }'

Environment Variables

VariableDefaultRequiredDescription
MDDB_AUTH_ENABLEDfalseNoEnable authentication
MDDB_AUTH_JWT_SECRET-Yes (if auth enabled)Secret for JWT signing (use strong random value)
MDDB_AUTH_JWT_EXPIRY24hNoJWT token expiration (e.g., 1h, 7d)
MDDB_AUTH_ADMIN_USERNAMEadminNoBootstrap admin username
MDDB_AUTH_ADMIN_PASSWORD-Yes (if auth enabled)Bootstrap admin password

Example Configuration

Development:

export MDDB_AUTH_ENABLED=true
export MDDB_AUTH_JWT_SECRET=$(openssl rand -hex 32)
export MDDB_AUTH_ADMIN_USERNAME=admin
export MDDB_AUTH_ADMIN_PASSWORD=dev123

Production (Docker):

docker run -d \ -e MDDB_AUTH_ENABLED=true \ -e MDDB_AUTH_JWT_SECRET=your-secret-key-here \ -e MDDB_AUTH_ADMIN_USERNAME=admin \ -e MDDB_AUTH_ADMIN_PASSWORD=secure-password \ -v /data:/data \ mddb:latest

API Endpoints

Authentication Endpoints

POST /v1/auth/login

Login with username and password.

Request:

{ "username": "admin", "password": "changeme"
}

Response:

{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresAt": 1772563099
}

POST /v1/auth/register

Create new user (admin only).

Request:

{ "username": "alice", "password": "secret123"
}

Response:

{ "username": "alice", "createdAt": 1772476836
}

POST /v1/auth/api-key

Generate API key (authenticated users).

Request:

{ "description": "CI server", "expiresAt": 0
}

Response:

{ "key": "mddb_live_b9a2604ba923ea920451d139a5f366eb...", "description": "CI server", "expiresAt": 0, "createdAt": 1772476811
}

GET /v1/auth/me

Get current user information.

Response:

{ "username": "admin", "admin": true, "createdAt": 1772476700
}

Authorization Endpoints (Admin Only)

POST /v1/auth/permissions

Set user permissions.

Request:

{ "username": "alice", "collection": "blog", "read": true, "write": false, "admin": false
}

GET /v1/auth/permissions?username=alice

Get user permissions.

Response:

[ { "username": "alice", "collection": "blog", "read": true, "write": false, "admin": false }
]

DELETE /v1/auth/users/:username

Delete user (admin only).

Response:

{ "status": "deleted"
}

Public Endpoints (No Authentication Required)

  • GET /health
  • GET /v1/health
  • POST /v1/auth/login
  • GET /metrics

Client Configuration

CLI (mddb-cli)

Using JWT Token

mddb-cli login admin changeme mddb-cli --token YOUR_TOKEN stats
mddb-cli --token YOUR_TOKEN add docs readme en -f README.md

Using API Key

mddb-cli --api-key mddb_live_... stats

MCP Server (mddb-mcp)

config.yaml

mddb: grpcAddress: "localhost:11024" restBaseURL: "http://localhost:11023/v1" transportMode: "rest-only" timeout: 30s apiKey: "mddb_live_your-api-key-here" # Add this line server: httpPort: 8080 enableHTTP: true enableStdio: true

Environment Variable

export MDDB_API_KEY=mddb_live_...
mddb-mcp --config config.yaml

Panel (React UI)

The Panel automatically detects if authentication is enabled:

  1. On first load, attempts to access /v1/stats
  2. If 401 Unauthorized, shows login form
  3. Stores JWT token in localStorage
  4. Includes Authorization: Bearer TOKEN in all requests
  5. Logout button clears token and reloads page

No configuration needed!


Security Best Practices

1. Use Strong JWT Secret

Generate a cryptographically secure secret:

openssl rand -hex 32

Never use a weak or default secret in production!

2. Change Default Admin Password

Immediately after first deployment:

TOKEN=$(curl -s http://localhost:11023/v1/auth/login \ -d '{"username":"admin","password":"changeme"}' | jq -r .token) curl -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/auth/register \ -d '{"username":"newadmin","password":"strong-password-here"}' curl -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/auth/permissions \ -d '{ "username": "newadmin", "collection": "*", "read": true, "write": true, "admin": true }' curl -X DELETE -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/auth/users/admin

3. Principle of Least Privilege

Grant minimum permissions needed:

  • Read-only for analytics/reporting
  • Collection-specific for editors
  • Admin only for trusted operators

4. Rotate API Keys Periodically

curl -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/auth/api-key \ -d '{ "description": "Q1 2026 CI key", "expiresAt": 1735689600 }'

5. Use HTTPS in Production

Never send credentials over unencrypted HTTP in production. Use a reverse proxy like nginx or Caddy:

server { listen 443 ssl; server_name mddb.example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://localhost:11023; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
}

6. Monitor Authentication Logs

Watch for suspicious activity:

tail -f /var/log/mddb.log | grep "authentication failed"

7. Secure Environment Variables

In production:

  • Use Docker secrets or Kubernetes secrets
  • Never commit .env files with real credentials
  • Rotate secrets regularly

Testing

Automated Tests

Run the comprehensive test suite:

./test-auth.sh ./test-mcp.sh ./test-panel.sh

Manual Testing

1. Test Login Flow

curl http://localhost:11023/v1/stats TOKEN=$(curl -s http://localhost:11023/v1/auth/login \ -d '{"username":"admin","password":"changeme"}' | jq -r .token) curl -H "Authorization: Bearer $TOKEN" \ http://localhost:11023/v1/stats

2. Test RBAC

curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/register \ -d '{"username":"readonly","password":"view123"}' curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/permissions \ -d '{ "username": "readonly", "collection": "docs", "read": true, "write": false }' RO_TOKEN=$(curl -s http://localhost:11023/v1/auth/login \ -d '{"username":"readonly","password":"view123"}' | jq -r .token) curl -H "Authorization: Bearer $RO_TOKEN" \ http://localhost:11023/v1/search \ -d '{"collection":"docs","limit":10}' curl -H "Authorization: Bearer $RO_TOKEN" \ http://localhost:11023/v1/add \ -d '{"collection":"docs","key":"test","lang":"en","contentMd":"# Test"}'

Troubleshooting

Problem: "missing authentication" error

Cause: No token provided or public endpoint misconfigured.

Solution:

curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:11023/v1/stats

Problem: "invalid token" error

Cause: Token expired or JWT secret mismatch.

Solution:

  1. Check token expiry: echo $TOKEN | base64 -d | jq .exp
  2. Login again to get fresh token
  3. Verify MDDB_AUTH_JWT_SECRET hasn't changed

Problem: "forbidden" error (403)

Cause: User lacks permissions for the operation.

Solution:

curl -H "Authorization: Bearer $ADMIN_TOKEN" \ "http://localhost:11023/v1/auth/permissions?username=alice" curl -H "Authorization: Bearer $ADMIN_TOKEN" \ http://localhost:11023/v1/auth/permissions \ -d '{ "username": "alice", "collection": "docs", "read": true, "write": true }'

Problem: Can't login as admin

Cause: Admin user not created or wrong password.

Solution:

  1. Check environment variables:

    echo $MDDB_AUTH_ADMIN_USERNAME
    echo $MDDB_AUTH_ADMIN_PASSWORD
    
  2. Check server logs for bootstrap admin creation:

    tail -f /var/log/mddb.log | grep "bootstrap admin"
    
  3. If needed, recreate database:

    rm mddb.db
    # Restart server to recreate bootstrap admin
    

Problem: MCP service can't authenticate

Cause: API key not configured.

Solution:

mddb: apiKey: "mddb_live_your-key-here"

Or use environment variable:

export MDDB_API_KEY=mddb_live_your-key-here

Storage Details

BoltDB Buckets

Authentication data is stored in three BoltDB buckets:

  1. auth_users

    • Key: user|{username}
    • Value: JSON with username, passwordHash (bcrypt), createdAt, disabled
  2. auth_apikeys

    • Key: apikey|{keyHash}
    • Value: JSON with keyHash (SHA256), username, createdAt, expiresAt, description
  3. auth_permissions

    • Key: perm|{username}|{collection}
    • Value: JSON with collection, read, write, admin flags

Password Hashing

  • Algorithm: bcrypt
  • Cost factor: 12
  • Salt: Automatically generated per password

API Key Generation

  • Random bytes: 24 (crypto/rand)
  • Encoding: Hex (48 characters)
  • Prefix: mddb_live_
  • Storage: SHA256 hash (for constant-time comparison)

Migration Guide

From No Auth to Auth Enabled

  1. Plan migration window (brief downtime required)

  2. Backup database:

    cp mddb.db mddb.db.backup
    
  3. Enable auth:

    export MDDB_AUTH_ENABLED=true
    export MDDB_AUTH_JWT_SECRET=$(openssl rand -hex 32)
    export MDDB_AUTH_ADMIN_USERNAME=admin
    export MDDB_AUTH_ADMIN_PASSWORD=changeme
    
  4. Restart MDDB

  5. Create users and API keys for all services

  6. Update all clients (CLI, MCP, Panel, custom apps)

  7. Test thoroughly before promoting to production

Rolling Back

If you need to disable auth:

export MDDB_AUTH_ENABLED=false

Auth data (users, keys, permissions) remains in database but is not enforced.


Support

For issues or questions:

  • GitHub Issues: https://github.com/anthropics/mddb/issues
  • Documentation: https://github.com/anthropics/mddb/docs
  • Email: [email protected]

Last Updated: March 2, 2026 MDDB Version: 2.3.3+