Managed Mode

December 28, 2025 ยท View on GitHub

Run Hemmelig with all instance settings controlled via environment variables. Perfect for containerized deployments, GitOps workflows, and infrastructure-as-code setups where you want configuration locked down and version-controlled.

Overview

When managed mode is enabled, all instance settings are read from environment variables instead of the database. The admin dashboard becomes read-only, preventing any runtime modifications.

Key benefits:

  • Immutable configuration - Settings can't be changed through the UI
  • GitOps-friendly - Version control your configuration
  • Reproducible deployments - Same config across all environments
  • Security hardening - No accidental configuration changes

Quick Start

Enable managed mode by setting:

HEMMELIG_MANAGED=true

Then configure your settings via environment variables:

# Enable managed mode
HEMMELIG_MANAGED=true

# Instance branding
HEMMELIG_INSTANCE_NAME="Company Secrets"
HEMMELIG_INSTANCE_DESCRIPTION="Secure secret sharing for our team"

# Security
HEMMELIG_ALLOW_PASSWORD_PROTECTION=true
HEMMELIG_ALLOW_IP_RESTRICTION=true
HEMMELIG_ENABLE_RATE_LIMITING=true

# Organization
HEMMELIG_REQUIRE_REGISTERED_USER=true
HEMMELIG_ALLOWED_EMAIL_DOMAINS="company.com,partner.com"

Environment Variables

Core

VariableDescriptionDefault
HEMMELIG_MANAGEDEnable managed modefalse

General Settings

VariableDescriptionDefault
HEMMELIG_INSTANCE_NAMEDisplay name for your instance""
HEMMELIG_INSTANCE_DESCRIPTIONDescription shown on the homepage""
HEMMELIG_INSTANCE_LOGOBase64-encoded logo image (max 512KB)""
HEMMELIG_ALLOW_REGISTRATIONAllow new user signupstrue
HEMMELIG_REQUIRE_EMAIL_VERIFICATIONRequire email verificationfalse
HEMMELIG_DEFAULT_SECRET_EXPIRATIONDefault expiration in hours72
HEMMELIG_MAX_SECRET_SIZEMax secret size in KB1024
HEMMELIG_IMPORTANT_MESSAGEAlert banner shown to all users""

Security Settings

VariableDescriptionDefault
HEMMELIG_ALLOW_PASSWORD_PROTECTIONAllow password-protected secretstrue
HEMMELIG_ALLOW_IP_RESTRICTIONAllow IP range restrictionstrue
HEMMELIG_ALLOW_FILE_UPLOADSAllow users to attach filestrue
HEMMELIG_ENABLE_RATE_LIMITINGEnable API rate limitingtrue
HEMMELIG_RATE_LIMIT_REQUESTSMax requests per window100
HEMMELIG_RATE_LIMIT_WINDOWRate limit window in seconds60

Organization Settings

VariableDescriptionDefault
HEMMELIG_REQUIRE_INVITE_CODERequire invite code for registrationfalse
HEMMELIG_ALLOWED_EMAIL_DOMAINSComma-separated list of allowed domains""
HEMMELIG_REQUIRE_REGISTERED_USEROnly registered users can create and read secretsfalse
HEMMELIG_DISABLE_EMAIL_PASSWORD_SIGNUPDisable email/password registration (social only)false

Webhook Settings

VariableDescriptionDefault
HEMMELIG_WEBHOOK_ENABLEDEnable webhook notificationsfalse
HEMMELIG_WEBHOOK_URLWebhook endpoint URL""
HEMMELIG_WEBHOOK_SECRETHMAC secret for webhook signatures""
HEMMELIG_WEBHOOK_ON_VIEWSend webhook when secret is viewedtrue
HEMMELIG_WEBHOOK_ON_BURNSend webhook when secret is burnedtrue

Metrics Settings

VariableDescriptionDefault
HEMMELIG_METRICS_ENABLEDEnable Prometheus metrics endpointfalse
HEMMELIG_METRICS_SECRETBearer token for /api/metrics""

Docker Compose Example

services:
    hemmelig:
        image: hemmeligapp/hemmelig:v7
        restart: unless-stopped
        ports:
            - '3000:3000'
        volumes:
            - ./database:/app/database
            - ./uploads:/app/uploads
        environment:
            # Required
            - DATABASE_URL=file:/app/database/hemmelig.db
            - BETTER_AUTH_SECRET=your-secret-key-min-32-chars
            - BETTER_AUTH_URL=https://secrets.example.com
            - NODE_ENV=production

            # Enable managed mode
            - HEMMELIG_MANAGED=true

            # General
            - HEMMELIG_INSTANCE_NAME=ACME Secrets
            - HEMMELIG_INSTANCE_DESCRIPTION=Internal secret sharing
            - HEMMELIG_ALLOW_REGISTRATION=true
            - HEMMELIG_DEFAULT_SECRET_EXPIRATION=24
            - HEMMELIG_MAX_SECRET_SIZE=2048

            # Security
            - HEMMELIG_ALLOW_PASSWORD_PROTECTION=true
            - HEMMELIG_ALLOW_IP_RESTRICTION=false
            - HEMMELIG_ENABLE_RATE_LIMITING=true
            - HEMMELIG_RATE_LIMIT_REQUESTS=50
            - HEMMELIG_RATE_LIMIT_WINDOW=60

            # Organization
            - HEMMELIG_REQUIRE_REGISTERED_USER=true
            - HEMMELIG_ALLOWED_EMAIL_DOMAINS=acme.com

            # Metrics
            - HEMMELIG_METRICS_ENABLED=true
            - HEMMELIG_METRICS_SECRET=prometheus-scrape-token

Kubernetes Example

apiVersion: v1
kind: ConfigMap
metadata:
    name: hemmelig-config
data:
    HEMMELIG_MANAGED: 'true'
    HEMMELIG_INSTANCE_NAME: 'ACME Secrets'
    HEMMELIG_ALLOW_REGISTRATION: 'true'
    HEMMELIG_REQUIRE_REGISTERED_USER: 'true'
    HEMMELIG_ALLOWED_EMAIL_DOMAINS: 'acme.com'
    HEMMELIG_ENABLE_RATE_LIMITING: 'true'
    HEMMELIG_METRICS_ENABLED: 'true'
---
apiVersion: v1
kind: Secret
metadata:
    name: hemmelig-secrets
type: Opaque
stringData:
    BETTER_AUTH_SECRET: 'your-secret-key-min-32-chars'
    HEMMELIG_WEBHOOK_SECRET: 'webhook-signing-secret'
    HEMMELIG_METRICS_SECRET: 'prometheus-token'
---
apiVersion: apps/v1
kind: Deployment
metadata:
    name: hemmelig
spec:
    replicas: 1
    selector:
        matchLabels:
            app: hemmelig
    template:
        metadata:
            labels:
                app: hemmelig
        spec:
            containers:
                - name: hemmelig
                  image: hemmeligapp/hemmelig:v7
                  ports:
                      - containerPort: 3000
                  envFrom:
                      - configMapRef:
                            name: hemmelig-config
                      - secretRef:
                            name: hemmelig-secrets
                  env:
                      - name: DATABASE_URL
                        value: 'file:/app/database/hemmelig.db'
                      - name: BETTER_AUTH_URL
                        value: 'https://secrets.example.com'
                      - name: NODE_ENV
                        value: 'production'

Admin Dashboard Behavior

When managed mode is enabled:

  1. Settings are read-only - All form inputs are disabled
  2. Save buttons are hidden - No option to modify settings
  3. Banner is displayed - Admins see a clear "Managed Mode" indicator
  4. API rejects updates - PUT /api/instance/settings returns 403 Forbidden

Admins can still view all settings, making it easy to verify the configuration.

API Behavior

Check Managed Mode Status

curl https://secrets.example.com/api/instance/managed

Response:

{
    "managed": true
}

Settings Update (Blocked)

When managed mode is enabled, attempting to update settings returns:

curl -X PUT https://secrets.example.com/api/instance/settings \
  -H "Content-Type: application/json" \
  -d '{"instanceName": "New Name"}'

Response:

{
    "error": "Instance is in managed mode. Settings cannot be modified."
}

Migration Guide

From Database Settings to Managed Mode

  1. Export current settings - Note your current configuration from the admin dashboard

  2. Create environment configuration - Translate settings to environment variables

  3. Enable managed mode - Set HEMMELIG_MANAGED=true

  4. Deploy - Restart with new configuration

Your database settings will be ignored once managed mode is active. The database is still used for secrets, users, and other data - only instance settings come from environment variables.

Reverting to Database Settings

Simply remove or set HEMMELIG_MANAGED=false. Settings will be read from the database again, and the admin dashboard becomes editable.

Best Practices

  1. Use secrets management - Store sensitive values like HEMMELIG_WEBHOOK_SECRET and HEMMELIG_METRICS_SECRET in a secrets manager (Vault, AWS Secrets Manager, etc.)

  2. Version control your config - Keep your docker-compose or Kubernetes manifests in git

  3. Use CI/CD for changes - Deploy configuration changes through your pipeline, not manual edits

  4. Document your settings - Add comments in your configuration files explaining each setting

  5. Test in staging first - Validate configuration changes in a non-production environment

Troubleshooting

Settings Not Applying

  • Verify HEMMELIG_MANAGED=true is set (case-insensitive)
  • Check environment variables are being passed to the container
  • Restart the application after changing environment variables

Dashboard Still Editable

  • Clear your browser cache
  • Verify the /api/instance/managed endpoint returns {"managed": true}
  • Check server logs for configuration errors

Rate Limiting Not Working

  • Ensure HEMMELIG_ENABLE_RATE_LIMITING=true
  • Verify HEMMELIG_RATE_LIMIT_REQUESTS and HEMMELIG_RATE_LIMIT_WINDOW are set
  • Rate limiting applies per IP address