Licensing
Surface Security requires a valid license signed by Surface Security to start in production mode. Licenses use Ed25519 public key cryptography and are verified entirely offline -- no network call is needed for license validation.
How Licensing Works
- Surface Security generates a license containing your tenant ID, tier, limits, and expiration
- The license payload is signed with Surface Security's Ed25519 private key
- Your deployment verifies the signature using the public key embedded in the binary at build time
- If the signature is valid and the license is not expired, the server starts normally
Security note: The Ed25519 public key is embedded into the binary at build time, not provided at runtime. This prevents substitution of the verification key.
First-Run Setup (Onboarding)
When the server starts with an empty database (zero admin users), it enters setup mode. A guided wizard walks the administrator through:
- Admin Registration -- create the first super_admin account (email, name, password)
- License Activation -- paste the license key into the dashboard
- Validation -- the backend verifies the license signature, creates the tenant and admin user atomically
- Dashboard Access -- JWT tokens are issued immediately so the admin can start using the platform
After setup completes:
- All
/api/v1/setup/*endpoints return403 Forbidden - The login page no longer redirects to
/setup - Existing deployments with admin users are completely unaffected
License Contents
| Field | Description |
|---|---|
id | Unique license identifier |
tenant_id | Your organization's tenant UUID |
tier | License tier (free, starter, team, business, enterprise) |
issued_at | When the license was issued |
expires_at | When the license expires |
max_users | Maximum number of employees allowed |
max_devices | Maximum number of browser extensions allowed |
entitlements | Feature flags (e.g., siem_integration, advanced_analytics) |
License Expiration and Grace Period
- If the license has not yet expired, the server starts normally
- If the license expired within the last 7 days, the server starts with a warning (grace period)
- If the license expired more than 7 days ago, the server refuses to start
- Licenses expiring within 30 days produce a warning log at startup
Obtaining a License
Contact Surface Security to obtain your license:
- Provide your organization name and desired tier
- Surface Security generates a signed license and provides:
- License key -- the signed JSON payload (one long base64 string)
- Public key -- the Ed25519 public key for verification (PEM format)
- API token (optional) -- if you want portal connectivity for heartbeats and remote signature updates
Setting Up Licensing
Kubernetes
Step 1: Store the License Key as a Secret
# Online deployment (with portal connectivity)
kubectl create secret generic surfacesec-surface-license \
--namespace=surfacesec \
--from-literal=license-key="YOUR_SIGNED_LICENSE_KEY" \
--from-literal=api-token="YOUR_PORTAL_API_TOKEN"
# Offline / air-gapped deployment (no portal)
kubectl create secret generic surfacesec-surface-license \
--namespace=surfacesec \
--from-literal=license-key="YOUR_SIGNED_LICENSE_KEY"
Step 2: (Optional) Configure Portal Connectivity
By default, Surface Security connects to the central management portal for heartbeats, signature sync, and update notifications. The portal URL is configurable at runtime.
Using Helm:
# values.yaml
config:
surfacePortalUrl: "https://portal.surface-security.com" # default
# surfacePortalUrl: "" # offline mode
Using Kubernetes ConfigMap:
data:
surface-portal-url: "https://portal.surface-security.com"
For offline or air-gapped deployments, set the portal URL to an empty string. The license is verified entirely offline -- the portal is only needed for optional features like:
- Heartbeat health reporting
- Automatic signature updates
- Extension config sync
- Update availability notifications
Step 3: Apply and Verify
# Restart the API pods to pick up the new config
kubectl rollout restart deployment/surfacesec-api -n surfacesec
# Check logs for license verification
kubectl logs -n surfacesec -l component=api --tail=20 | grep -i license
Successful output:
{"level":"info","msg":"license verified successfully","license_id":"abc-123","tenant_id":"...","tier":"enterprise","expires_at":"2027-01-15T00:00:00Z","max_users":1000,"max_devices":5000}
If offline mode (no portal URL):
{"level":"info","msg":"portal URL not configured - running in offline mode (license verified locally)"}
Docker Compose
Set license environment variables in your .env file:
SURFACE_LICENSE_KEY=eyJsaWNlbnNlIjp7...
SURFACE_PORTAL_URL=https://portal.surface-security.com # or empty for offline
SURFACE_PORTAL_API_TOKEN=your-token-here
Renewing a License
- Contact Surface Security for a renewed license key
- Update the Kubernetes secret:
kubectl create secret generic surfacesec-surface-license \
--namespace=surfacesec \
--from-literal=license-key="NEW_LICENSE_KEY_HERE" \
--from-literal=api-token="your-api-token" \
--dry-run=client -o yaml | kubectl apply -f -
- Restart the API deployment:
kubectl rollout restart deployment/surfacesec-api -n surfacesec
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
license verification failed | Invalid or missing license | Verify the license key is set correctly |
license signature verification FAILED | Wrong public key or tampered license | Confirm the public key matches the key Surface Security provided |
license expired (grace period ended) | License expired more than 7 days ago | Renew the license with Surface Security |
LICENSE EXPIRED - running in grace period | License expired less than 7 days ago | Renew soon; server is running but will stop after the grace period |