Secrets Reference
semalink-frontend — GitHub Environment Secrets
Each of the three GitHub Environments (staging, test, prod) holds these 6 secrets:
| Secret | Description | Where to Regenerate |
|---|---|---|
VITE_API_BASE_URL | API base URL for the environment | Change only if subdomain changes |
VITE_CF_ACCESS_CLIENT_ID | CF Access service token Client ID | Cloudflare → Zero Trust → Access → Service Auth → roll credentials |
VITE_CF_ACCESS_CLIENT_SECRET | CF Access service token Client Secret | Same as above — ID and Secret roll together |
CLOUDFLARE_API_TOKEN | CF API token with Pages write permission | Cloudflare → My Profile → API Tokens |
CLOUDFLARE_ACCOUNT_ID | Cloudflare account ID (a9e39304...) | Cloudflare Dashboard sidebar — does not change |
SLACK_WEBHOOK_URL | Slack Incoming Webhook URL | Slack → api.slack.com/apps → Incoming Webhooks |
semalink-admin — GitHub Environment Secrets
The admin portal has a single staging GitHub Environment. Production environment is not yet set up.
| Secret | Description |
|---|---|
VITE_API_BASE_URL | https://staging-arc.semalink.africa for staging |
CLOUDFLARE_API_TOKEN | CF API token with Pages write permission |
CLOUDFLARE_ACCOUNT_ID | a9e39304b10e79b6b6e7c73a128b8ce7 |
SLACK_WEBHOOK_URL | Slack Incoming Webhook URL (same webhook as frontend) |
semalink-api — GitHub Environment Secrets
The API deploy workflow SSHes into the DigitalOcean droplet. All secrets live in the staging GitHub Environment on the semalink-api repo.
| Secret | Description |
|---|---|
DATABASE_URL | Neon staging pooler URL (ep-...-pooler...neon.tech/neondb?sslmode=require) |
REDIS_URL | Upstash staging Redis (rediss://default:...@...upstash.io:6379) |
RABBITMQ_PASS | RabbitMQ password — 32+ random chars; see rotation runbook in api-ops.md |
JWT_SECRET | 64+ char random secret for customer access tokens |
JWT_REFRESH_SECRET | 64+ char random secret for customer refresh tokens |
ADMIN_JWT_SECRET | 64+ char random secret for admin staff tokens (separate namespace) |
MAILGUN_API_KEY | Mailgun API key for transactional email |
MAILGUN_DOMAIN | mailer.semalink.africa |
MAILGUN_FROM | noreply@mailer.semalink.africa |
CELCOM_API_URL | Celcom Africa SMS gateway base URL |
CELCOM_API_KEY | Celcom Africa API key |
R2_ACCOUNT_ID | Cloudflare account ID for R2 |
R2_BUCKET_NAME | staging |
R2_PUBLIC_URL | Public r2.dev URL for the staging bucket |
R2_ACCESS_KEY_ID | R2 access key ID |
R2_SECRET_ACCESS_KEY | R2 secret access key |
STAGING_DROPLET_IP | 209.38.197.79 |
STAGING_SSH_KEY | ED25519 private key for the deploy user on the droplet |
SLACK_WEBHOOK_URL | Slack Incoming Webhook URL for deploy notifications |
How to Rotate a Secret
- Generate the new credential from the source system first
- Go to GitHub → Sema-Link/semalink-frontend → Settings → Environments → [environment]
- Find the secret and click Edit
- Paste the new value and save
- Trigger a new deployment — the next build will use the new secret automatically
Checklist
Completed ✅
- GitHub Environments created (
staging,test,prod) - All 6 secrets set in each environment (18 total)
- Cloudflare Pages projects created and custom domains attached
production_branch: mainset on all three app projects- Cloudflare Zero Trust enabled
- Service tokens created for all three environments
- Service token credentials stored in GitHub secrets
- Slack notifications working on success and failure
- All three environments live and deploying
Pending ⚠️
- Create Cloudflare Access Application policies for
arc,staging-arc, andtest-arcsubdomains in Cloudflare → Zero Trust → Access → Applications. Service tokens exist but are not yet enforced — the API is not actively protected until these policies are created. - Wire CF Access headers into the Axios client in
src/core/api/(see Zero Trust)