GitHub Environment Variables Reference¶
Each CampusCore client requires a GitHub Environment with the following secrets and variables. The environment name must match the client identifier used in deployment (e.g., vsu_pilot, howard).
Repo-Level Configuration (Set Once)¶
These are shared across all client environments.
| Type | Name | Description |
|---|---|---|
| Secret | ADMIN_AWS_ROLE_ARN |
Our admin account's IAM role ARN (for Terraform state access) |
| Secret | SLACK_BOT_TOKEN |
Bot User OAuth Token (xoxb-...) for the CampusCore Slack app. Used by every tenant's deploy unless overridden at the env level. See Slack Setup. Leave unset to disable Slack workflow notifications across all envs. |
| Variable | CAMPUSCORE_HOSTED_ZONE_ID |
Route53 hosted zone ID for campuscoreai.com (Z03938461UVED2L50LENJ) |
Environment Variables (Per-Client, Non-Sensitive)¶
| Variable | Example | Description |
|---|---|---|
AWS_ROLE_ARN |
arn:aws:iam::123456789012:role/CampusCore-Deploy-Role |
Client's deploy role ARN (created by client via CloudFormation) |
AWS_REGION |
us-east-1 |
AWS region for deployment (defaults to us-east-1 if omitted) |
TF_STATE_BUCKET |
campuscore-tfstate-howard |
S3 bucket for this client's Terraform state |
POSTGRES_MAJOR |
17 |
Optional. Pins this client to an older PostgreSQL major; omit to inherit the current default (18). Changing it does not upgrade a running database - see PostgreSQL version upgrades |
SSL_MODE |
managed |
HTTPS capability gate, defaults to off. off: no subdomain, no certificate, HTTP only. self_managed: provisions {env}.campuscoreai.com + ACM + HTTPS; the client terminates TLS for their own domain. managed: additionally issues and attaches certificates for client domains added in-app (Settings > Custom Domain) |
CUSTOM_DOMAIN_WITH_PROTOCOL |
https://ai.howard.edu |
Only takes effect when SSL_MODE=off. In any other mode the app job substitutes https://{env}.campuscoreai.com (deploy-aws.yml), and the base layer prefers the subdomain module's output (infrastructure/base/main.tf - effective_custom_domain_with_protocol) |
SSO Configuration¶
SSO providers (SAML, OIDC) are configured via the Django admin panel after deployment — no GitHub Environment variables needed. See Post-Deployment Step 4b.
Observability (Sentry)¶
All optional except SENTRY_DSN. Workflow defaults apply when a variable is unset. See Sentry Setup for the full operator playbook (creating the Sentry project, picking shared vs per-tenant, Slack/GitHub integration, troubleshooting).
| Variable | Example | Description |
|---|---|---|
SENTRY_ENVIRONMENT |
vsu-troy-pilot |
Sentry environment tag for filtering. Defaults to the GitHub Environment name via Terraform fallback if unset. |
SENTRY_TRACES_SAMPLE_RATE |
0.2 |
Fraction of transactions sampled for performance tracing. Workflow default: 0.2. |
SENTRY_ENABLE_LOGS |
true |
Whether to ship logs to Sentry's Logs product. Workflow default: true. Set to false to send only errors + traces. |
SENTRY_PROFILES_SAMPLE_RATE |
0.0 |
CPU profiling sample rate. Workflow default: 0.0 (disabled). |
RELEASE_SHA is auto-set from ${{ github.sha }} by the deploy workflow — no manual config required.
Notifications (Slack workflow_runs)¶
All optional. Setting them lights up start/complete/fail messages from the index health check, HNSW rebuild, and scrape pipelines. See Slack Setup for the operator playbook (creating the app, channel, inviting the bot, troubleshooting).
| Variable | Example | Description |
|---|---|---|
SLACK_CHANNEL_WORKFLOW_RUNS |
C0B4C4Q57HP |
Channel ID, not name. The Slack channel the bot posts to for this tenant. ⚠ Must be set as a variable, not a secret — the workflow reads ${{ vars.SLACK_CHANNEL_WORKFLOW_RUNS }}. Leaving empty disables Slack posts for this tenant only. |
Alerting (CloudWatch alarms)¶
All optional; consumed by Terraform only - they never enter the container.
| Type | Name | Description |
|---|---|---|
| Variable | ALERT_EMAIL |
Email address subscribed to the alarm SNS topic (infrastructure/app/monitoring.tf). It is also the email on the AWS account's SECURITY alternate contact, which is set only when SECURITY_CONTACT_PHONE is set too. Empty means no email subscription and no contact. |
| Variable | SECURITY_CONTACT_PHONE |
Phone number, in international format, for the AWS account's SECURITY alternate contact - the number AWS uses for abuse reports and vulnerability disclosures. AWS accepts a contact only with both an email and a phone, so this does nothing without ALERT_EMAIL, and the contact is only set where the security posture bundle is on. Empty leaves whatever contact the account already has. |
| Secret | SLACK_ALERT_WEBHOOK_URL |
Slack incoming-webhook URL for alarm delivery. Empty means no Slack delivery. This is the alarm path, separate from the bot-token path above. |
Index Maintenance Scheduling¶
All optional. The daily EventBridge schedule is on by default, so a deployment that sets neither variable gets automatic index health checks and rebuilds. Set the toggle to false to opt out. See Vector Index Observability.
| Variable | Example | Description |
|---|---|---|
ENABLE_INDEX_MAINTENANCE_SCHEDULE |
false |
Set to false to remove the daily EventBridge schedule that runs auto_rebuild_if_justified. Terraform default: true. |
INDEX_MAINTENANCE_SCHEDULE_CRON |
cron(0 6 * * ? *) |
EventBridge cron expression for the schedule. Terraform default: cron(0 6 * * ? *) (06:00 UTC daily). |
Database Sizing & Read Replica¶
All optional. An unset variable inherits the Terraform default (the value in parentheses), and these are declarative: removing a variable reverts that setting to its default on the next deploy. Deep doc: RDS Configuration.
| Variable | Example | Description |
|---|---|---|
RDS_ALLOCATED_STORAGE |
100 |
Database storage in GB (default 20). Applies at deploy time with no downtime, but a large increase keeps that deploy running - RDS storage optimization can take hours. RDS only grows storage: a smaller value fails the deploy when autoscaling is off, and is ignored when autoscaling is on. |
RDS_MAX_ALLOCATED_STORAGE |
200 |
Storage-autoscaling ceiling in GB (default 0 = autoscaling off). Must be greater than RDS_ALLOCATED_STORAGE when set. No downtime. |
RDS_INSTANCE_CLASS |
db.t3.medium |
Declared database instance class (default db.t3.large). A new environment is created on this class directly. A live database is not resized by the deploy - the deploy warns "class change pending", and an operator applies it with the db-upgrade workflow's Blue/Green run (seconds of switchover, no reboot). See PostgreSQL version upgrades. |
RDS_BACKUP_RETENTION_DAYS |
7 |
Automated backup retention in days (default 3, floor 1, max 35). Applies at deploy time with no downtime. |
ENABLE_READ_REPLICA |
true |
Provision the RDS read replica for agent read traffic (default false). The application wiring (USE_READ_REPLICA, routing) follows automatically. Adds the replica instance's cost to the AWS bill. |
REPLICA_INSTANCE_CLASS |
db.t3.medium |
Instance class for the read replica (default db.t3.medium). A change reboots only the replica for a few minutes; the primary is unaffected. |
Network Resilience¶
Optional. An unset variable inherits the Terraform default, same as the database knobs above.
| Variable | Example | Description |
|---|---|---|
ENABLE_PER_AZ_NAT |
true |
One NAT gateway per availability zone instead of a single shared one (default false), so an AZ failure cannot take down the other AZ's outbound connectivity. Adds ~$37/month per extra AZ to the client's AWS bill. Flip it in a maintenance window: private-subnet egress is interrupted for a few minutes while routes move to the new gateways, and the deploy can fail once when the S3 endpoint update races the old route tables' teardown - re-running the deploy completes the switch. |
AWS Security Services¶
One switch for the whole security posture bundle. Production client environments set it to true - that is a named step of the onboarding checklist, and the onboarding script asks for an explicit decision. Default false, so sandboxes carry none of the cost.
| Variable | Example | Description |
|---|---|---|
ENABLE_FULL_AWS_SECURITY_SERVICES |
true |
Provisions GuardDuty (threat detection with S3 Protection, RDS Protection, ECS Fargate Runtime Monitoring, and malware scanning of the upload bucket), AWS Config recording, Security Hub with the AWS Foundational Security Best Practices standard, and Inspector ECR image scanning. Roughly $50-120/month on the client's AWS bill, scaling with usage. The GuardDuty runtime agent runs as a sidecar inside each ECS task's existing CPU/memory allocation. Findings are viewed in the AWS consoles (GuardDuty, Security Hub). |
Environment Secrets (Per-Client, Sensitive)¶
Generated by Us¶
The secrets section of the onboarding script generates and uploads these (./scripts/onboard-client.sh secrets <env>):
| Secret | Description |
|---|---|
DB_USERNAME |
RDS PostgreSQL username |
DB_PASSWORD |
RDS PostgreSQL password |
DJANGO_SECRET_KEY |
Django SECRET_KEY for cryptographic signing |
APP_FERNET_KEY |
Fernet key for encrypting connector credentials |
Provided by Client¶
| Secret | Description |
|---|---|
OPENAI_API_KEY |
OpenAI API key for embeddings and LLM |
GEMINI_API_KEY |
Google Gemini API key for document OCR |
COHERE_API_KEY |
Cohere API key for search reranking |
Set by Us (Observability)¶
| Secret | Description |
|---|---|
SENTRY_DSN |
Sentry project DSN. Required for Sentry error/trace/log reporting. Leave unset to disable Sentry (the SDK becomes a no-op). See Sentry Setup for how to obtain the value. |
SLACK_BOT_TOKEN (optional override) |
Per-tenant Slack bot token. Defaults to the repo-level SLACK_BOT_TOKEN secret (one bot for all tenants). Set here only when a client requires their own isolated bot identity. See Slack Setup. |
Hardcoded Environment Variables (Not Configurable)¶
These are set directly in the ECS task definition (infrastructure/app/ecs.tf) and do not need GitHub Environment configuration:
| Variable | Value | Purpose |
|---|---|---|
IS_CLOUD_ENV |
True |
Gates cloud-only behavior: DB TLS (sslmode=require), HTTPS redirect, HSTS, Secure cookies, JSON logging. Always True in deployed environments, False in local dev. |
Triggering a Deployment¶
Option 1: Push to deploy branch¶
Option 2: Manual dispatch¶
Go to Actions > Deploy to AWS > Run workflow > Enter the client name.