DefendableCloud Operations
DefendableCloud is a three-part deploy:
site/ → defendablecloud.com → Cloudflare Pagesapp/ → app.defendablecloud.com → Cloudflare Pagesapi/ → api.defendablecloud.com → Fly.io or equivalent container hostMarketing Site
Section titled “Marketing Site”cd defendable-cloud-v2/sitenpm installnpm run devnpm run buildCloudflare Pages:
- root directory:
site/ - build command:
npm run build - output directory:
site/dist - Node:
22.12.0or newer
Vault App
Section titled “Vault App”cd defendable-cloud-v2/appnpm installnpm run devnpm run buildCloudflare Pages:
- root directory:
app/ - build command:
npm run build - output directory:
app/dist - SPA fallback through
app/public/_redirects - API base through
VITE_API_BASE
cd defendable-cloud-v2/apipython3 -m venv .venv. .venv/bin/activatepip install -r requirements.txtcp .env.example .envalembic upgrade headuvicorn app.main:app --reload --port 8080Production should run behind HTTPS with explicit CORS and non-default secrets. The API refuses unsafe production configuration.
Required Environment
Section titled “Required Environment”| Variable | Purpose |
|---|---|
DATABASE_URL | Postgres connection (asyncpg). |
JWT_SECRET | JWT signing secret; must be non-default in production. |
JWT_TTL_HOURS | JWT lifetime (default 720 = 30 days). |
CORS_ORIGINS | Explicit app/site origins. |
API_BASE_URL | Public API base for share/receipt link generation (used to build /share/{token} URLs). |
APP_BASE_URL | Vault SPA origin (magic-link landing, Stripe redirect allowlist). |
RESEND_API_KEY | Magic-link and notification email. |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | Object storage credentials. |
AWS_ENDPOINT_URL_S3 | Tigris/S3-compatible endpoint (default https://fly.storage.tigris.dev). |
TIGRIS_BUCKET | Object storage bucket (default defendable-cloud). |
INTERNAL_API_KEY | Internal staging surfaces. |
RUNNER_TOKEN | Cook runner claim/status surfaces. |
STRIPE_API_KEY / STRIPE_WEBHOOK_SECRET / STRIPE_PRICE_ID | Stripe membership checkout + webhook verification. |
DATASET_DOWNLOAD_DAILY_LIMIT | Application-level dataset anti-abuse limit (default 500). |
CI Gates
Section titled “CI Gates”Minimum merge gate:
cd defendable-cloud-v2/apipytest
cd ../clipytest
cd ../appnpm run build
cd ../sitenpm run buildThe cloud repo includes GitHub Actions for build/test checks. Keep OpenAPI contract tests in the required path before merging route or schema changes.
Incident Response
Section titled “Incident Response”First 15 minutes:
- Declare incident owner.
- Freeze deploys unless rollback is the fix.
- Capture affected domains, orgs, routes, and time window.
- Preserve logs.
- Rotate credentials first if exposure is plausible.
Containment examples:
- revoke API keys
- rotate internal/stager/runner secrets
- disable affected Cloudflare route
- pause dataset stager
- block abusive route at WAF
- publish status update for SEV-1/SEV-2
Every material incident should generate an incident receipt.
Backup and Restore Drill
Section titled “Backup and Restore Drill”Monthly:
- Restore Postgres into isolated environment.
- Point staging API at restored DB and staging bucket.
- Run
/healthz. - Run
/ledger/verify. - Open a sample
/share/{token}. - Verify one dataset-download receipt and one run receipt.
- Record the drill as a Defendable Run.