MDDB Authentication & Authorization
Complete guide to JWT authentication and RBAC (Role-Based Access Control) in MDDB.
Table of Contents
- Overview
- Quick Start
- Authentication Methods
- Authorization (RBAC)
- Environment Variables
- API Endpoints
- Client Configuration
- Security Best Practices
- Testing
- Troubleshooting
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
| Permission | Operations | Description |
|---|---|---|
| read | Get, Search, Export, FTS | View documents |
| write | Add, Update, Delete, Import | Modify documents |
| admin | Backup, Restore, Stats, User management | Database 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
- Check if user is admin (bypasses all checks)
- Check collection-specific permission
- Check wildcard (
"*") permission - 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
| Variable | Default | Required | Description |
|---|---|---|---|
MDDB_AUTH_ENABLED | false | No | Enable authentication |
MDDB_AUTH_JWT_SECRET | - | Yes (if auth enabled) | Secret for JWT signing (use strong random value) |
MDDB_AUTH_JWT_EXPIRY | 24h | No | JWT token expiration (e.g., 1h, 7d) |
MDDB_AUTH_ADMIN_USERNAME | admin | No | Bootstrap 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 /healthGET /v1/healthPOST /v1/auth/loginGET /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:
- On first load, attempts to access
/v1/stats - If
401 Unauthorized, shows login form - Stores JWT token in
localStorage - Includes
Authorization: Bearer TOKENin all requests - 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
.envfiles 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:
- Check token expiry:
echo $TOKEN | base64 -d | jq .exp - Login again to get fresh token
- Verify
MDDB_AUTH_JWT_SECREThasn'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:
Check environment variables:
echo $MDDB_AUTH_ADMIN_USERNAME echo $MDDB_AUTH_ADMIN_PASSWORDCheck server logs for bootstrap admin creation:
tail -f /var/log/mddb.log | grep "bootstrap admin"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:
auth_users
- Key:
user|{username} - Value: JSON with username, passwordHash (bcrypt), createdAt, disabled
- Key:
auth_apikeys
- Key:
apikey|{keyHash} - Value: JSON with keyHash (SHA256), username, createdAt, expiresAt, description
- Key:
auth_permissions
- Key:
perm|{username}|{collection} - Value: JSON with collection, read, write, admin flags
- Key:
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
Plan migration window (brief downtime required)
Backup database:
cp mddb.db mddb.db.backupEnable 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=changemeRestart MDDB
Create users and API keys for all services
Update all clients (CLI, MCP, Panel, custom apps)
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+