Skip to content

Environment Variables

This page consolidates all environment variables across the three CTFHive components. Set them in .env files (loaded by python-dotenv) or directly in the process environment. The detailed per-area configuration walkthrough is in Getting Started → Configuration.

Production secrets

SECRET_KEY, ADMIN_KEY, ENCRYPTION_KEY, PROVISION_AUDIT_SECRET, and all Stripe/Linode keys must be replaced with strong random values before going live. Their defaults are intentionally obvious placeholders.


CTFHive app (ctfapp)

Source: ctfapp/config.py

Core secrets

Variable Default Required Description
SECRET_KEY change-me-in-production Yes Flask session secret. Rotate requires all sessions to be invalidated.
ADMIN_KEY change-me-admin-key Yes HMAC key for flag derivation (SHA3-256) and the application audit chain (SHA-256). Changing this invalidates all derived flags and breaks audit chain verification.
ENCRYPTION_KEY change-me-encryption-key Yes Key for XChaCha20-Poly1305 column encryption (password hashes, IPs, VPN keys, etc.). Falls back to SECRET_KEY if unset.
APP_ENV development No development | production | testing. Selects config class.

Database and cache

Variable Default Description
DATABASE_URL sqlite:///ctfapp.db SQLAlchemy connection string. Use postgresql+psycopg2:// in production.
REDIS_URL redis://localhost:6379/0 Redis connection string used for caching, rate limiting, and flag cache.

Application identity

Variable Default Description
CTF_NAME GIDK CTF Displayed in the browser title and header.
CTF_DESCRIPTION (GrizzHacks8 description) Shown on the homepage.
CTF_LOGO img/logo.png Relative path to logo image.
CTF_FAVICON favicon.svg Relative path to favicon.
SITE_URL http://localhost:5000 Canonical URL; used in email links and CSP.
BASE_DOMAIN (empty) Apex domain for this tenant (e.g. abc123.ctfhive.us). Set by the provisioner.
DEFAULT_FLAG_PREFIX GRIZZ Prefix for derived flags, e.g. GRIZZ{...}.
PREFERRED_URL_SCHEME http http | https. Set to https in production.

Admin bootstrap

Variable Default Description
ADMIN_USERNAME admin Username for the auto-created admin account.
ADMIN_PASSWORD GrizzAdmin!2025 Password for auto-created admin. Change immediately.
ADMIN_EMAIL admin@grizzhacks8ctf.local Email for auto-created admin.
BOOTSTRAP_ADMIN_IN_PRODUCTION false Set true to run admin bootstrap in production environments.
Variable Default Prod default
SESSION_COOKIE_SECURE false true
SESSION_COOKIE_SAMESITE Lax Lax

Rate limiting

Use Redis in production

The default RATELIMIT_STORAGE_URI=memory:// counts requests per-process. Under multiple Gunicorn workers, each worker has its own counter, so the effective limit becomes workers × configured_limit. Set RATELIMIT_STORAGE_URI=redis://localhost:6379/1 to share counts correctly.

Variable Default Description
RATELIMIT_STORAGE_URI memory:// Rate limit backend. Use redis://... in production.
LOGIN_RATE_LIMIT 10 per minute Login endpoint limit.
REGISTER_RATE_LIMIT 5 per minute Registration endpoint limit.
PASSWORD_RESET_RATE_LIMIT 5 per hour Password reset limit.
TEAM_JOIN_RATE_LIMIT 10 per minute Team join endpoint limit.
ADMIN_SENSITIVE_RATE_LIMIT 30 per minute Admin sensitive actions limit.
FLAG_WRONG_LIMIT 3 Wrong submissions before lockout.
FLAG_LOCKOUT_SECONDS 30 Lockout duration after wrong-submission limit.

Email

Variable Default Description
MAILTRAP_API_KEY (empty) Mailtrap API key. Email features (verification, password reset) are silently disabled when this is empty.
MAIL_SENDER no-reply@grizzhacks8ctf.us From address for outbound email.
EMAIL_VERIFICATION_ENABLED false Require email verification on registration.

Security headers

Variable Default Description
SECURE_HEADERS_ENABLED true Enable security response headers.
ENABLE_HSTS false Add Strict-Transport-Security header. Enable in production with TLS.
HSTS_SECONDS 31536000 max-age value in HSTS header.
CONTENT_SECURITY_POLICY (strict default) Full CSP header value. Overridable.
TRUST_PROXY false Trust X-Forwarded-* headers (set true behind Caddy/nginx).
PROXY_FIX_X_FOR 1 Number of trusted X-Forwarded-For hops.

Labs and containers

Variable Default Description
LAB_ENABLED false Enable Docker container/lab spawning. Requires Docker access.
LAB_DOCKER_HOST (host socket) Docker daemon socket or TCP address.
LAB_SUBNET_POOL_CIDR 10.200.0.0/16 Pool from which per-team Docker bridge subnets are carved.
LAB_SUBNET_PREFIX 24 Prefix length for each team subnet.
LAB_VPN_PEER_POOL_CIDR 10.50.0.0/24 Pool for WireGuard peer IPs.
LAB_VPN_PLAYERS_PER_TEAM 4 Max WireGuard peers per team per lab.
LAB_APPLY_IPTABLES false Apply iptables forwarding rules for team isolation.
LAB_WG_INTERFACE wg0 WireGuard interface name on the host.
LAB_ISSUE_WG_CONFIG false Issue WireGuard configs to players.
LAB_WG_ENDPOINT vpn.example.com Public WireGuard endpoint address:port.
LAB_WG_LISTEN_PORT 51820 WireGuard listen port.
LAB_WG_SERVER_PUBLIC_KEY (empty) Server's WireGuard public key (injected into client configs).
LAB_REQUIRE_PINNED_IMAGES false Require image@sha256:... digest pinning. Recommended true in production.
LAB_ALLOW_BASENAME_IMAGE_FALLBACK true Allow tag-miss fallback to same-named image. Set false to prevent supply-chain confusion.
LAB_PACKET_CAPTURE_ENABLED false Enable per-lab packet capture recording.
LAB_PACKET_CAPTURE_TOOL tcpdump Capture tool on the host.
LAB_PACKET_CAPTURE_SNAPLEN 256 Packet snaplen in bytes.
LAB_PACKET_CAPTURE_MAX_PACKETS_PER_CHUNK 250 Packets per stored chunk.
LAB_PACKET_CAPTURE_MAX_RAW_BYTES_PER_CHUNK 131072 Raw bytes per chunk (128 KB).
LAB_PACKET_CAPTURE_MAX_CHUNKS_PER_LEASE 256 Max chunks stored per lease.
INSTANCE_TTL_SECONDS 7200 Default lab instance lifetime in seconds (2 hours).
INSTANCE_DNS_DOMAIN (empty) Wildcard DNS domain for instance URLs. Empty shows localhost in UI.
CTF_DEPLOY_MODE local local | remote.
DISPATCH_INTERNAL_URL http://localhost:5001 URL of the lab dispatcher service.
DISPATCH_ADMIN_TOKEN change-me-dispatch-token Auth token for dispatcher API.
DISPATCH_USE_REMOTE true When false, use local Docker fallback instead of remote dispatcher.
DISPATCH_PREFER_LOCAL true Prefer local Docker when both are available.
LAB_WORKER_ID local-worker This worker's ID for drain management.
LAB_WORKER_MAX_CPU_UNITS 16000 CPU capacity for scheduling.
LAB_WORKER_MAX_MEMORY_MB 32768 Memory capacity (MB) for scheduling.

Container registry

Variable Default Description
REGISTRY_HOST (empty) Registry hostname (e.g. registry.ctfhive.us).
REGISTRY_USER (empty) Registry username.
REGISTRY_PASS (empty) Registry password.

Event timing

Variable Default Description
EVENT_STARTS_AT 2025-03-28T12:00:00+00:00 ISO 8601 datetime when the CTF opens.
EVENT_ENDS_AT 2025-03-29T12:00:00+00:00 ISO 8601 datetime when the CTF closes.

Theme and UI

Variable Default Description
DEFAULT_THEME minimal Default theme ID.
DEFAULT_BG_ANIMATION matrix Default background animation.

Miscellaneous

Variable Default Description
MAX_CONTENT_LENGTH 33554432 (32 MB) Maximum request body size.
CHALLENGES_ROOT {repo_root}/challenges Path to local challenge directory.

CTFHive control plane (ctrlapp)

Source: CTF_Saas_CTRL_Pane/ctrlapp/config.py

Variable Default Description
SECRET_KEY dev-insecure-change-me Flask session secret for the control plane.
BASE_DOMAIN ctfhive.us Apex domain used to construct tenant subdomains (e.g. {slug}.ctfhive.us).
STRIPE_SECRET_KEY (empty) Stripe secret key (sk_live_... or sk_test_...).
STRIPE_PUBLISHABLE_KEY (empty) Stripe publishable key (pk_live_...).
STRIPE_WEBHOOK_SECRET (empty) Webhook signing secret from the Stripe dashboard (whsec_...).
PROVISION_AUDIT_SECRET audit-dev-secret HMAC key for the provisioner JSONL audit chain. See Audit Chain.
LINODE_API_TOKEN (empty) Linode API token for server provisioning. Leave empty to use FakeExecutor (dry-run mode).
LINODE_REGION us-east Linode datacenter region for new servers.
LINODE_PLAN g6-standard-2 Linode plan / instance type.
LINODE_IMAGE linode/debian12 Base OS image for provisioned servers.

Stripe price IDs

The control plane codebase references STRIPE_PRICE_SOLO, STRIPE_PRICE_STANDARD, and STRIPE_PRICE_PRO in the pricing configuration (ctrlapp/pricing.py). These are Stripe Price object IDs (e.g. price_...) created in the Stripe dashboard and set in the environment before enabling live billing.


Provisioner

Source: provisioner/ package. Variables read at runtime by scripts.

Variable Default Description
PROVISION_AUDIT_SECRET (same as control plane) HMAC key for the JSONL audit chain written during provisioning runs.
LINODE_API_TOKEN (empty) Used by the real LinodeExecutor to call the Linode API.

The provisioner is invoked by the control plane's ProvisionService and inherits the control plane's environment. In dry-run mode (FakeExecutor), no Linode API calls are made and no actual server is created.


Quick-start minimal .env

# CTFHive app — minimum for local development
SECRET_KEY=dev-secret-change-me
ADMIN_KEY=dev-admin-key-change-me
ENCRYPTION_KEY=dev-enc-key-change-me
DATABASE_URL=sqlite:///ctfapp.db
REDIS_URL=redis://localhost:6379/0
APP_ENV=development
# CTFHive control plane — minimum for local development
SECRET_KEY=ctrl-dev-secret
PROVISION_AUDIT_SECRET=audit-dev-secret
# Leave STRIPE_* and LINODE_* empty → fake/dry-run mode