Skip to main content

Access and security

The platform holds real people's contact details, purchase history and private journal entries. The controls below exist to keep that data where it belongs. They are not optional in dev: the dev database contains real contacts.

How access is gated​

ResourceGateNotes
Dev database (development-database-vm)Tailscale only (or the vnet-emaw-dev virtual network from the dev Django VM)No public endpoint. DB_HOST in .env is a Tailscale IP.
Dev Django VMTailscale + SSH keyWhere the dev compose stack runs
Prod PostgreSQL (emawweb-server)vnet only (EMAWWebVnet), reached by the Django appNo SSH, no public access, no Tailscale yet
Prod DockerApp VMSSH key + IP whitelistTailscale is planned here too
Portainer (https://52.234.34.217:9443/)IP whitelistAsk Matt Birne to add your IP
Dev website, Admin CRM (dev and prod), these docsCloudflare Access: Google accounts ending in @everymanawarrior.comStaff API endpoints also verify the Access JWT server-side
Admin CRM app loginApp account in the CRM Staff group or with granted capabilitiesSecond login after Access
Backend .envPassword managerNever in git, chat or email

SSH​

  • Key-only. Password authentication is disabled everywhere.
  • Ask Troy or Matt to create your user with your public key. Send the public key (id_ed25519.pub); generate one with ssh-keygen -t ed25519 -C "<you>@everymanawarrior.com" if you have none.
  • You must be on the tailnet to reach the dev VM at all. For the prod VM, your IP must also be allow-listed.

Rules for developers​

Secrets​

  • .env files are git-ignored in the backend. In the React apps, .env.development and .env.production are committed on purpose because they only hold public VITE_* values; anything secret does not belong in a Vite app at all.
  • Stripe keys in dev are test-mode keys. Deposco points at the sandbox. If you see a live key outside production, report it.
  • Documentation (including this site) names variables, never values.
  • Rotating SECRET_KEY on the backend also invalidates GOOGLE_TOKEN_KEY's default derivation, so staff must reconnect Google. Rotate deliberately.

Data​

  • Never enable campaign sending against dev. Keep CAMPAIGN_SENDING_ENABLED unset. If you must test a send, also set CAMPAIGN_SEND_ALLOWED_GROUP_IDS=36 so only the Email Testing group can be targeted. The Admin CRM enforces the same guard client-side.
  • Do not log emails, tokens, verification codes, passwords or request bodies. Use EMAW.security_log.audit() for security events; log user ids only. The log formatter redacts JWTs, emails and Stripe keys as a backstop, but do not rely on it.
  • No email addresses in URLs except the q search parameter in the CRM. CSV exports run only on an explicit click and are never cached.
  • Crop personal data out of screenshots before adding them to these docs.

Database​

  • The dev DB is shared. Starting the compose stack runs migrate on it, so a branch with migrations changes the schema for everyone. Coordinate first.
  • Tests must never run against the shared DB; use the throwaway Postgres container described in Backend testing.
  • EMAW_SALT must match the database you point at, or no one can log in (usernames are salted email hashes).

Code​

  • DRF denies by default. Public views set AllowAny explicitly.
  • Staff views use HasCrmAccess("<capability>") / CrmAccessViewMixin and scope_contacts(); never hand-roll staff checks. Any view built on these is behind Cloudflare Access automatically.
  • Compare verification codes with VerificationCode.verify() (constant-time, attempt-limited), never ==.

Reporting a problem​

If you find exposed credentials, an open port, or data where it should not be, tell Troy or Matt directly and do not post details in a shared channel.