Deploying ScamSafe
ScamSafe is a single server binary. It serves the web app and the API from one port and keeps everything it stores in one SQLite database file. This page covers what it needs to run.
Requirements
- A host that can run the server and keep one file on persistent storage.
- An OpenID Connect identity provider for sign-in, such as Google Workspace, Microsoft Entra ID or Okta.
- An OpenAI API key, for the tutor and the features that read free text. ScamSafe runs without one, with those features switched off.
- For a partner deployment, a license key from GCA. See Licensing.
Configuration
Every setting can be given as an environment variable or a command-line flag. The server is started with server serve.
Required
| Environment variable | Flag | Value |
|---|---|---|
SSO_ISSUER | --ssoIssuer | Your identity provider's issuer URL |
SSO_CLIENT_ID | --ssoClientID | The client ID registered for ScamSafe |
SSO_CLIENT_SECRET | --ssoClientSecret | The client's secret |
SSO_REDIRECT_URL | --ssoRedirectURL | https://<your domain>/auth/callback, registered at the identity provider |
UI_URL | --uiURL | https://<your domain>, used to build links in outbound email |
ADMIN_EMAIL | --adminEmail | The email of your first administrator. See Administrators |
LICENSE_KEY | --licenseKey | Your license key (partner deployments) |
LICENSE_SERVER_URL | --licenseServerURL | https://scamsafe.license.globalcyberalliance.org (partner deployments) |
The server refuses to start without a complete sign-in configuration.
Optional
| Environment variable | Flag | Default | Purpose |
|---|---|---|---|
DB_PATH | --dbPath | data.db | Path to the database file |
HTTP_ADDR | --addr | :8080 | Listen address |
DISABLE_TLS | --disableTLS | false | Serve plain HTTP. See TLS |
OPENAI_API_KEY | --openAIAPIKey | Enables the tutor, free-text answers and recommendation explanations | |
OPENAI_VECTOR_CORPUS_ID | --openAIVectorCorpusID | The OpenAI vector store the tutor searches for reference documents | |
OPENAI_VECTOR_PERSONAS_ID | --openAIVectorPersonasID | The OpenAI vector store for learner personas | |
MAILGUN_SEND_KEY | --mailgunSendKey | A Mailgun sending key. Turns on invitation emails | |
MAIL_FROM | --mailFrom | scamsafe@mg.globalcyberalliance.org | The address email is sent from. Its domain must be the Mailgun domain the key belongs to |
SSO_SCOPES | --ssoScopes | openid profile email | Scopes requested at sign-in |
SSO_PROMPT | --ssoPrompt | select_account | The OIDC prompt parameter. Empty omits it |
SSO_POST_LOGOUT_REDIRECT_URL | --ssoPostLogoutRedirectURL | Where the identity provider sends a user after signing out, if it supports it | |
RATE_LIMIT | --rateLimit | 0 (off) | Requests allowed per IP address in each 3-second window |
LOG_LEVEL | --logLevel | info | Log level |
PRETTY_LOG | --prettyLog | true | Human-readable logs. Set false for JSON |
LOG_FILE | --logFile | ~/.config/scamsafe/scamsafe.log | Rotating log file |
Some settings you set once, such as the vector store IDs, the Mailgun key and sender, and the license key, are remembered in the database. If you later omit one, the server keeps using the stored value rather than silently turning the feature off. The OpenAI API key is not remembered: set it on every start.
The server also accepts Thinkific settings, for signing learners in to GCA Academy courses. They are tied to GCA's own Thinkific account, so partner deployments should leave them unset.
Email
ScamSafe sends one kind of email: the invitation when an administrator invites someone. It is optional. Without it, invites still work, since an invited person only has to sign in with the invited address, but you have to tell them yourself.
To turn it on, set up a sending domain in Mailgun, such as mg.example.org, and set:
MAILGUN_SEND_KEYto a sending key for that domain;MAIL_FROMto the address to send from, such asnoreply@mg.example.org. It must be a bare address on that domain. The sender name shown to recipients is your app name, from Branding.
The Mailgun domain must be in Mailgun's EU region. At startup the server only checks that MAIL_FROM is a bare address, and refuses to start if it isn't. It doesn't contact Mailgun, so a key for a different domain, or a domain outside the EU region, only shows up when an invitation fails to send. Invite yourself once after configuring email to confirm it arrives.
Replies go to the MAIL_FROM address, so use one you can receive at, or that says plainly it isn't monitored. The support address in the email itself comes from your branding.
Sign-in
Register ScamSafe as a web application with your identity provider, using the authorization code flow and the redirect URL above.
- The provider must send
email_verified. ScamSafe refuses any sign-in whose email the provider has not verified, because accounts and invites are matched by email. Google Workspace sends it. Check yours does, or every sign-in will be refused. - Accounts are invite-only. Someone without an invite can't create an account, even with a valid sign-in. An administrator invites people from the admin panel. See Users and invites.
TLS
By default the server serves HTTPS with a self-signed certificate it generates itself. Browsers will warn about it, so in production either:
- run ScamSafe behind a reverse proxy or load balancer that holds your real certificate, and set
DISABLE_TLS=trueso the proxy talks to it over HTTP; or - expose the server directly only on a private network.
Running with Docker
docker run -d --name scamsafe -p 8080:8080 \
-v scamsafe-data:/data \
-e DB_PATH=/data/data.db \
-e DISABLE_TLS=true \
-e UI_URL=https://scamsafe.example.org \
-e ADMIN_EMAIL=admin@example.org \
-e SSO_ISSUER=https://accounts.google.com \
-e SSO_CLIENT_ID=... \
-e SSO_CLIENT_SECRET=... \
-e SSO_REDIRECT_URL=https://scamsafe.example.org/auth/callback \
-e OPENAI_API_KEY=... \
-e LICENSE_KEY=... \
-e LICENSE_SERVER_URL=https://scamsafe.license.globalcyberalliance.org \
<scamsafe image>
Mount a volume for the database. Without one, every restart starts from an empty install.
Data and backups
Everything ScamSafe stores is in the database file: accounts, assessments, roadmaps, your branding and logos, edited content and the full version history of every edit. Nothing else needs backing up.
Back it up while the server runs with a tool that understands SQLite, such as Litestream (continuous replication to object storage) or sqlite3 data.db ".backup backup.db". Copying the file while the server is writing to it can produce a corrupt copy.
Run one server per database file. Two servers sharing a file will corrupt it.
First start
-
Start the server. It creates the database, loads the default question bank, recommendations and tutor content, and (on a partner deployment) activates its license.
-
Check the logs for errors, then open
https://<your domain>/api/feature-status. It reports which optional features are available:Field truewhenchatAvailableThe tutor is usable: an OpenAI key is set and, on a partner deployment, the license is active thinkificAvailableThinkific course sign-in is configured licenseStateactiveon a licensed deployment. See Licensing -
Sign in as the
ADMIN_EMAILaddress and invite your team from the admin panel. See Administrators.
Administrators
Set ADMIN_EMAIL to the address of your first administrator. On start, if no account has that address yet, the server creates an admin invite for it, and that person becomes an administrator the first time they sign in. From then on, administrators invite everyone else from the admin panel.
ADMIN_EMAIL must be an address your identity provider can sign in. On a licensed deployment, leaving it unset creates no invite, so nobody can reach the admin panel. If that happens, set ADMIN_EMAIL and restart the server.
The same applies if every administrator loses access: set ADMIN_EMAIL to an address that has no account yet and restart. An address that already has an account is left as it is, so it can't be used to promote an existing learner.
Known limitations
These are open for partner deployments and are being worked on:
- The sign-in button reads "Sign in with Google" whatever your identity provider is.