Security Reference
Security Reference
Section titled “Security Reference”Application-level security
Section titled “Application-level security”API authentication (api.py)
Section titled “API authentication (api.py)”The FastAPI layer uses two authentication mechanisms, applied in order:
- Supabase JWT —
Authorization: Bearer <token>header. Token is validated by callingSUPABASE_URL/auth/v1/userusing an async HTTP client (non-blocking). - Static API key —
X-Api-Keyheader. Only checked if JWT validation fails andAPI_KEYis set.
If neither API_KEY nor SUPABASE_URL are configured, the API runs in unauthenticated mode and logs a startup warning. This mode is intentional for local development only — always set at least one auth mechanism in production.
For production hardening, enable:
APP_ENV=productionSTRICT_SECURITY_MODE=1
With strict mode enabled, API startup fails fast if critical auth/webhook secrets are missing, instead of only logging warnings.
Never trust X-User-ID or similar forwarded-identity headers. User identity is always derived from verified JWT claims only.
Webhook HMAC validation (webhooks/common.py)
Section titled “Webhook HMAC validation (webhooks/common.py)”All inbound webhooks (Supabase auth, premium invalidation, GDPR erasure, reflections, founder, legal-update) are verified with HMAC-SHA256. The shared utility validate_webhook_signature() uses hmac.compare_digest to prevent timing attacks.
If the secret for a webhook is not configured, validation is skipped and a debug log is emitted. For production:
SUPABASE_WEBHOOK_SECRET— required for GDPR erasure and Supabase auth eventsPREMIUM_INVALIDATE_WEBHOOK_SECRET— required for premium invalidationAPP_REFLECTIONS_WEBHOOK_SECRET— required for reflection sync
Leaving these unset in production means those endpoints are publicly triggerable.
Rate limiting (api.py — RateLimitMiddleware)
Section titled “Rate limiting (api.py — RateLimitMiddleware)”In-memory, IP-based sliding-window rate limiter applied to all endpoints:
| Endpoint type | Limit |
|---|---|
| Health/metrics probes | 60 req/min |
| Read requests (GET) | 30 req/min |
| Write requests (POST/PUT/DELETE) | 10 req/min |
The in-memory store is cleaned every 10 minutes. Note: this is a single-instance limiter — it does not share state across multiple API replicas.
Request tracing and API observability (api.py)
Section titled “Request tracing and API observability (api.py)”RequestObservabilityMiddlewareattaches/propagatesX-Request-IDon every response.GET /api/observabilityexposes rolling API/webhook metrics:- request counts
- success rates
- latency percentiles (
p50,p95,p99)
GET /api/observabilitynow requiresX-Api-Key(service key) and returns503when no service key is configured.
This endpoint is intended for operational monitoring and troubleshooting.
Input sanitization (utils/sanitizer.py)
Section titled “Input sanitization (utils/sanitizer.py)”safe_embed_text()— strips mentions, filters dangerous URL protocols, escapes Discord markdown. Must be used for all user-supplied content placed in embeds.safe_prompt()— detects jailbreak patterns and neutralizes them before passing to LLM APIs.safe_log_message()— removes control characters and truncates before logging.
Owner/admin IDs (config.py)
Section titled “Owner/admin IDs (config.py)”Bot owner and admin role IDs are loaded from environment variables OWNER_IDS and ADMIN_ROLE_ID (comma-separated integers). Hardcoding these values in source code is not allowed. The fallback defaults are left in place for backward compatibility, but must be overridden in production via env vars.
Dependency security
Section titled “Dependency security”Known-vulnerable transitive dependencies are pinned explicitly in requirements.txt:
cryptography>=46.0.6pyopenssl>=26.0.0requests>=2.33.0Run pip-audit -r requirements.txt regularly to catch new CVEs. Dependabot or Renovate can automate this.
Privileged Discord commands
Section titled “Privileged Discord commands”/migrate downgrade— restricted toOWNER_IDSonly (not guild admins). Triggersalembic downgrade -1which is a destructive database operation.- All admin commands use
validate_admin()fromutils/validators.py. - Owner-only commands use
requires_owner()decorator or an explicitOWNER_IDScheck.
Error disclosure
Section titled “Error disclosure”Internal exceptions must not be forwarded to Discord users or API clients. Log the full exception server-side with logger.error(...) and send only a generic message to the client (e.g. "Database error. Please try again later.").
Google Cloud Security Best Practices
Section titled “Google Cloud Security Best Practices”This document describes security best practices for Google Cloud credentials and API keys within the Alphapy project, in line with Google Cloud Security recommendations.
Overview
Section titled “Overview”Alphapy uses Google Cloud services for:
- Google Drive API: Reading PDF documents via service account credentials
All credentials are managed via Google Cloud Secret Manager in production, with fallback to environment variables for local development.
Credential lifecycle management
Section titled “Credential lifecycle management”1. Zero-code storage ✅
Section titled “1. Zero-code storage ✅”Status: Implemented
- ✅ Credentials are never committed to source code or version control
- ✅
.gitignoreexcludes.envandcredentials/ - ✅ Production uses Google Cloud Secret Manager for credential storage
- ✅ Local development uses environment variables (fallback)
Implementation:
utils/gcp_secrets.py: Helper for Secret Manager access with cachingutils/drive_sync.py: Loads credentials from Secret Manager or environment variable- Configuration via
GOOGLE_PROJECT_IDandGOOGLE_SECRET_NAMEenvironment variables
2. Secret Manager setup
Section titled “2. Secret Manager setup”For production deployments:
-
Create secret in Secret Manager:
Terminal window # Via gcloud CLIecho -n '{"type":"service_account",...}' | \gcloud secrets create alphapy-google-credentials \--data-file=- \--project=YOUR_PROJECT_ID -
Configure environment variables:
Terminal window GOOGLE_PROJECT_ID=your-gcp-project-idGOOGLE_SECRET_NAME=alphapy-google-credentials # Optional, default uses this name -
Grant access to service account:
Terminal window gcloud secrets add-iam-policy-binding alphapy-google-credentials \--member="serviceAccount:YOUR_SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com" \--role="roles/secretmanager.secretAccessor" \--project=YOUR_PROJECT_ID
For local development:
- Use
GOOGLE_CREDENTIALS_JSONenvironment variable - Secret Manager is skipped when
GOOGLE_PROJECT_IDis not set
3. Disable dormant keys
Section titled “3. Disable dormant keys”Manual action required in GCP Console:
- Go to “APIs & Services” > “Credentials”
- Review all API keys and service account keys
- Identify keys with no activity (30+ days)
- Decommission inactive keys:
- Click on the key
- Select “Delete” or “Disable”
- Confirm deactivation
Audit procedure (monthly):
- Check “APIs & Services” > “Credentials” for inactive keys
- Review Cloud Audit Logs for key usage patterns
- Document all deactivated keys in project changelog
4. Enforce API restrictions
Section titled “4. Enforce API restrictions”Manual configuration in GCP Console:
For API keys:
- Go to “APIs & Services” > “Credentials”
- Select an API key
- Click “Restrict key”
- API restrictions:
- Select “Restrict key”
- Choose only the required APIs (e.g. “Drive API”)
- Save
- Application restrictions (if applicable):
- IP addresses: Add allowed IP ranges
- HTTP referrers: Add allowed referrer URLs
- Android apps: Add package names
- iOS apps: Add bundle IDs
For service account keys:
- Service accounts automatically have limited scopes (see code:
drive.readonly) - No extra API restrictions needed (scopes are sufficient)
Current implementation:
- ✅ Service account uses only
https://www.googleapis.com/auth/drive.readonlyscope - ⚠️ TODO: Configure API key restrictions in GCP Console if API keys are used
5. Apply least privilege
Section titled “5. Apply least privilege”Service account permissions:
Current scopes (implemented in code):
- ✅
https://www.googleapis.com/auth/drive.readonly— Read-only, no write
IAM permissions review:
-
Use IAM Recommender:
Terminal window # Via gcloud CLIgcloud recommender recommendations list \--recommender=google.iam.policy.Recommender \--project=YOUR_PROJECT_ID \--location=global -
Review unused permissions:
- Go to “IAM & Admin” > “IAM”
- Select service account
- Review assigned roles
- Remove unused roles
-
Minimum roles for Secret Manager:
roles/secretmanager.secretAccessor— Read secrets only- No
roles/secretmanager.adminorroles/secretmanager.secretAccessoron project-level
Current implementation:
- ✅ Service account uses minimum scope (
drive.readonly) - ⚠️ TODO: Review IAM roles via IAM Recommender and remove unused permissions
6. Mandatory rotation
Section titled “6. Mandatory rotation”Organization policies (must be configured by GCP admin):
-
Key expiry policy:
Terminal window # Set maximum key lifetime (e.g. 90 days)gcloud resource-manager org-policies set \iam.serviceAccountKeyExpiryHours \--organization=ORGANIZATION_ID \--policy-file=policy.jsonPolicy file (
policy.json):{"spec": {"rules": [{"values": {"allowedValues": ["2160"]}}]}}Note:
2160= 90 days in hours -
Disable key creation (if keys are not needed):
Terminal window gcloud resource-manager org-policies set \iam.disableServiceAccountKeyCreation \--organization=ORGANIZATION_ID \--enforce
For this project:
- ⚠️ TODO: Configure
iam.serviceAccountKeyExpiryHourspolicy (recommended: 90 days) - Service account keys are used, so disable policy does not apply
Rotation procedure (when key expires):
- Generate new service account key in GCP Console
- Update secret in Secret Manager:
Terminal window echo -n 'NEW_CREDENTIALS_JSON' | \gcloud secrets versions add alphapy-google-credentials \--data-file=- - Cache is automatically invalidated after TTL (1 hour)
- Old key version can be removed after verification
Operational safeguards
Section titled “Operational safeguards”1. Essential contacts
Section titled “1. Essential contacts”Configuration in GCP Console:
- Go to “IAM & Admin” > “Essential Contacts”
- Add contacts for:
- Security: Security team email
- Billing: Finance team email
- Technical: DevOps team email
- Select notification categories:
- Security notifications
- Billing notifications
- Technical notifications
For this project:
- ⚠️ TODO: Configure Essential Contacts with appropriate email addresses
2. Billing anomaly and budget alerts
Section titled “2. Billing anomaly and budget alerts”Configuration in GCP Console:
-
Budget alerts:
- Go to “Billing” > “Budgets & alerts”
- Create new budget alert
- Set threshold (e.g. 80% of monthly budget)
- Add email notifications
-
Anomaly detection:
- Go to “Billing” > “Budgets & alerts”
- Enable “Anomaly detection”
- Configure threshold (e.g. 150% of average daily spend)
- Add email notifications
For this project:
- ⚠️ TODO: Configure budget alerts and anomaly detection
- ⚠️ TODO: Set threshold based on expected usage
Security checklist
Section titled “Security checklist”Code-level (implemented) ✅
Section titled “Code-level (implemented) ✅”- Credentials not committed in source code
- Secret Manager integration with caching
- Fallback to environment variables for local dev
- Error handling for Secret Manager failures
- Logging for security events (which method is used)
- Minimum scopes (
drive.readonly)
Infrastructure-level (manual configuration) ⚠️
Section titled “Infrastructure-level (manual configuration) ⚠️”- API key restrictions configured (if applicable)
- Service account IAM permissions reviewed via IAM Recommender
- Unused permissions removed
- Key rotation policy configured (
iam.serviceAccountKeyExpiryHours) - Essential Contacts configured
- Budget alerts configured
- Anomaly detection enabled
- Dormant keys audit performed (30+ days inactive)
Monitoring and alerting
Section titled “Monitoring and alerting”Secret Manager access logs
Section titled “Secret Manager access logs”Monitor Secret Manager access via Cloud Audit Logs:
# View Secret Manager access logsgcloud logging read "resource.type=secretmanager.googleapis.com/Secret" \ --project=YOUR_PROJECT_ID \ --limit=50Anomaly detection
Section titled “Anomaly detection”- Monitor for unexpected Secret Manager access patterns
- Alert on failed authentication attempts
- Review logs monthly for security events
Incident response
Section titled “Incident response”If credentials are compromised:
-
Immediate actions:
- Disable the compromised key in GCP Console
- Rotate secret in Secret Manager
- Clear cache in application (restart or
clear_cache()call)
-
Investigation:
- Review Cloud Audit Logs for unauthorized access
- Check for unexpected API calls
- Document incident in security log
-
Prevention:
- Review security configurations
- Update IAM permissions if needed
- Verify all best practices are followed