Skip to main content

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 variableFlagValue
SSO_ISSUER--ssoIssuerYour identity provider's issuer URL
SSO_CLIENT_ID--ssoClientIDThe client ID registered for ScamSafe
SSO_CLIENT_SECRET--ssoClientSecretThe client's secret
SSO_REDIRECT_URL--ssoRedirectURLhttps://<your domain>/auth/callback, registered at the identity provider
UI_URL--uiURLhttps://<your domain>, used to build links in outbound email
ADMIN_EMAIL--adminEmailThe email of your first administrator. See Administrators
LICENSE_KEY--licenseKeyYour license key (partner deployments)
LICENSE_SERVER_URL--licenseServerURLhttps://scamsafe.license.globalcyberalliance.org (partner deployments)

The server refuses to start without a complete sign-in configuration.

Optional​

Environment variableFlagDefaultPurpose
DB_PATH--dbPathdata.dbPath to the database file
HTTP_ADDR--addr:8080Listen address
DISABLE_TLS--disableTLSfalseServe plain HTTP. See TLS
OPENAI_API_KEY--openAIAPIKeyEnables the tutor, free-text answers and recommendation explanations
OPENAI_VECTOR_CORPUS_ID--openAIVectorCorpusIDThe OpenAI vector store the tutor searches for reference documents
OPENAI_VECTOR_PERSONAS_ID--openAIVectorPersonasIDThe OpenAI vector store for learner personas
MAILGUN_SEND_KEY--mailgunSendKeyA Mailgun sending key. Turns on invitation emails
MAIL_FROM--mailFromscamsafe@mg.globalcyberalliance.orgThe address email is sent from. Its domain must be the Mailgun domain the key belongs to
SSO_SCOPES--ssoScopesopenid profile emailScopes requested at sign-in
SSO_PROMPT--ssoPromptselect_accountThe OIDC prompt parameter. Empty omits it
SSO_POST_LOGOUT_REDIRECT_URL--ssoPostLogoutRedirectURLWhere the identity provider sends a user after signing out, if it supports it
RATE_LIMIT--rateLimit0 (off)Requests allowed per IP address in each 3-second window
LOG_LEVEL--logLevelinfoLog level
PRETTY_LOG--prettyLogtrueHuman-readable logs. Set false for JSON
LOG_FILE--logFile~/.config/scamsafe/scamsafe.logRotating 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_KEY to a sending key for that domain;
  • MAIL_FROM to the address to send from, such as noreply@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=true so 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​

  1. Start the server. It creates the database, loads the default question bank, recommendations and tutor content, and (on a partner deployment) activates its license.

  2. Check the logs for errors, then open https://<your domain>/api/feature-status. It reports which optional features are available:

    Fieldtrue when
    chatAvailableThe tutor is usable: an OpenAI key is set and, on a partner deployment, the license is active
    thinkificAvailableThinkific course sign-in is configured
    licenseStateactive on a licensed deployment. See Licensing
  3. Sign in as the ADMIN_EMAIL address 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.