| 1 | // Diffinery: one Worker, everything on Cloudflare. |
| 2 | // `scripts/bootstrap.sh` creates the account resources and writes |
| 3 | // wrangler.deploy.jsonc (this file plus real IDs) for CI deploys. |
| 4 | { |
| 5 | "$schema": "node_modules/wrangler/config-schema.json", |
| 6 | "name": "diffinery", |
| 7 | "main": "src/index.ts", |
| 8 | "compatibility_date": "2026-10-01", |
| 9 | "compatibility_flags": ["nodejs_compat"], |
| 10 | "workers_dev": false, |
| 11 | // A zone route, not a Custom Domain: diffinery.com already has DNS records, and a |
| 12 | // route runs the Worker in front of them as long as the record is proxied (orange cloud). |
| 13 | "routes": [{ "pattern": "diffinery.com/*", "zone_name": "diffinery.com" }], |
| 14 | "observability": { "enabled": true, "head_sampling_rate": 1 }, |
| 15 | "upload_source_maps": true, |
| 16 | "limits": { "cpu_ms": 300000 }, |
| 17 | |
| 18 | "vars": { |
| 19 | "PUBLIC_URL": "https://diffinery.com", |
| 20 | "MAIL_FROM": "notifications@diffinery.com", |
| 21 | "BOOTSTRAP_ADMINS": "iletisim@canberkdemirci.com,canberkdemirci@icloud.com,iletisim@altugankarali.com", |
| 22 | "ACCESS_TEAM_DOMAIN": "", |
| 23 | "ACCESS_AUD": "" |
| 24 | }, |
| 25 | |
| 26 | "d1_databases": [ |
| 27 | { "binding": "DB", "database_name": "diffinery", "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "migrations" } |
| 28 | ], |
| 29 | "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "diffinery-data" }], |
| 30 | "queues": { |
| 31 | "producers": [ |
| 32 | { "binding": "EVENTS", "queue": "diffinery-events" }, |
| 33 | { "binding": "MAIL", "queue": "diffinery-mail" } |
| 34 | ], |
| 35 | "consumers": [ |
| 36 | { "queue": "diffinery-events", "max_batch_size": 50, "max_batch_timeout": 2, "max_retries": 8, "dead_letter_queue": "diffinery-events-dlq" }, |
| 37 | { "queue": "diffinery-mail", "max_batch_size": 20, "max_batch_timeout": 2, "max_retries": 8, "dead_letter_queue": "diffinery-mail-dlq" } |
| 38 | ] |
| 39 | }, |
| 40 | // Cloudflare Email Sending (notifications, security alerts, invites). The sending |
| 41 | // domain is onboarded under Compute > Email Service > Email Sending (see scripts/email-sending.sh). |
| 42 | "send_email": [{ "name": "EMAIL" }], |
| 43 | // Request rate limits per minute (src/lib/rate-limit.ts; RATE_LIMITS there must match). Local |
| 44 | // development declares none, so nothing is limited there. |
| 45 | "ratelimits": [ |
| 46 | { "name": "RL_AUTH", "namespace_id": "1001", "simple": { "limit": 20, "period": 60 } }, |
| 47 | { "name": "RL_ANON", "namespace_id": "1002", "simple": { "limit": 300, "period": 60 } }, |
| 48 | { "name": "RL_ACCOUNT", "namespace_id": "1003", "simple": { "limit": 2000, "period": 60 } }, |
| 49 | { "name": "RL_CREATE", "namespace_id": "1004", "simple": { "limit": 60, "period": 60 } } |
| 50 | ], |
| 51 | "durable_objects": { |
| 52 | "bindings": [ |
| 53 | { "name": "REPO", "class_name": "RepoDO" }, |
| 54 | { "name": "SCHEDULER", "class_name": "SchedulerDO" }, |
| 55 | { "name": "RUNNER", "class_name": "RunnerContainer" }, |
| 56 | { "name": "SANDBOX", "class_name": "SandboxContainer" }, |
| 57 | { "name": "IMPORTER", "class_name": "ImportContainer" }, |
| 58 | { "name": "PROJECT", "class_name": "ProjectDO" } |
| 59 | ] |
| 60 | }, |
| 61 | "migrations": [ |
| 62 | { "tag": "v1", "new_sqlite_classes": ["RepoDO", "RunnerContainer", "SandboxContainer"] }, |
| 63 | { "tag": "v2", "new_sqlite_classes": ["ImportContainer"] }, |
| 64 | // Agent tasks, one Durable Object per repository (agentic/ProjectDO.ts). |
| 65 | { "tag": "v3", "new_sqlite_classes": ["ProjectDO"] }, |
| 66 | // Workflow schedules (scheduler.ts). |
| 67 | { "tag": "v4", "new_sqlite_classes": ["SchedulerDO"] } |
| 68 | ], |
| 69 | "workflows": [ |
| 70 | { "name": "diffinery-run", "binding": "RUN_WORKFLOW", "class_name": "RunWorkflow" }, |
| 71 | { "name": "diffinery-import", "binding": "IMPORT_WORKFLOW", "class_name": "ImportWorkflow" } |
| 72 | ], |
| 73 | // A deploy that changes the image rolls the running containers: once a container has been up |
| 74 | // longer than rollout_active_grace_period seconds, it gets SIGTERM and is killed 15 minutes later. |
| 75 | // Runner and import containers are covered for their whole life (a job is capped at 6 hours, an |
| 76 | // import transfer too), so a deploy never cuts one short; they pick up the new image next start. |
| 77 | // The rollout is one step (rollout_step_percentage 100): with the default [10, 100], the first |
| 78 | // step still starts 90% of new containers on the old image, and the next step replaced such a |
| 79 | // job's container seconds after it started. The rollout's SIGTERM reaches every process in the |
| 80 | // container, so the job's steps died with it. |
| 81 | "containers": [ |
| 82 | { "class_name": "RunnerContainer", "image": "../../containers/runner/Dockerfile", "image_build_context": "../../containers/runner", "instance_type": "standard-2", "max_instances": 100, "rollout_active_grace_period": 23400, "rollout_step_percentage": 100 }, |
| 83 | { "class_name": "SandboxContainer", "image": "../../containers/runner/Dockerfile", "image_build_context": "../../containers/runner", "instance_type": "standard-1", "max_instances": 50, "rollout_active_grace_period": 3600, "rollout_step_percentage": 100 }, |
| 84 | // GitHub imports: a mirror clone of a large repository needs disk and memory. |
| 85 | { "class_name": "ImportContainer", "image": "../../containers/runner/Dockerfile", "image_build_context": "../../containers/runner", "instance_type": "standard-4", "max_instances": 20, "rollout_active_grace_period": 25200, "rollout_step_percentage": 100 } |
| 86 | ], |
| 87 | // Housekeeping, and a check that the workflow scheduler's alarm is set (src/scheduler.ts). |
| 88 | "triggers": { "crons": ["*/5 * * * *"] }, |
| 89 | |
| 90 | "env": { |
| 91 | // Local development and end-to-end tests: no Access, a fixed dev identity, no containers. |
| 92 | "dev": { |
| 93 | "routes": [], |
| 94 | "workers_dev": true, |
| 95 | "vars": { |
| 96 | "PUBLIC_URL": "http://localhost:8787", |
| 97 | "MAIL_FROM": "notifications@diffinery.com", |
| 98 | "BOOTSTRAP_ADMINS": "dev@diffinery.local", |
| 99 | "ACCESS_TEAM_DOMAIN": "", |
| 100 | "ACCESS_AUD": "", |
| 101 | "DEV_AUTH": "dev@diffinery.local", |
| 102 | // Containers do not run under wrangler dev: an import agent started by hand (or by the e2e test) claims Git transfers. |
| 103 | "IMPORT_GIT_AGENT": "external" |
| 104 | }, |
| 105 | "d1_databases": [{ "binding": "DB", "database_name": "diffinery-dev", "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "migrations" }], |
| 106 | "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "diffinery-data-dev" }], |
| 107 | "queues": { |
| 108 | "producers": [ |
| 109 | { "binding": "EVENTS", "queue": "diffinery-events-dev" }, |
| 110 | { "binding": "MAIL", "queue": "diffinery-mail-dev" } |
| 111 | ], |
| 112 | "consumers": [ |
| 113 | { "queue": "diffinery-events-dev", "max_batch_size": 50, "max_batch_timeout": 1 }, |
| 114 | { "queue": "diffinery-mail-dev", "max_batch_size": 20, "max_batch_timeout": 1 } |
| 115 | ] |
| 116 | }, |
| 117 | // Local email: Miniflare captures sent messages instead of delivering them. |
| 118 | "send_email": [{ "name": "EMAIL" }], |
| 119 | "durable_objects": { |
| 120 | "bindings": [ |
| 121 | { "name": "REPO", "class_name": "RepoDO" }, |
| 122 | { "name": "SCHEDULER", "class_name": "SchedulerDO" }, |
| 123 | { "name": "RUNNER", "class_name": "RunnerContainer" }, |
| 124 | // Sandboxes run in an `agent.mjs --sandbox-pool` process (SANDBOX_POOL_URL), not containers. |
| 125 | { "name": "SANDBOX", "class_name": "SandboxPoolDO" }, |
| 126 | { "name": "IMPORTER", "class_name": "ImportContainer" }, |
| 127 | { "name": "PROJECT", "class_name": "ProjectDO" } |
| 128 | ] |
| 129 | }, |
| 130 | "workflows": [ |
| 131 | { "name": "diffinery-run-dev", "binding": "RUN_WORKFLOW", "class_name": "RunWorkflow" }, |
| 132 | { "name": "diffinery-import-dev", "binding": "IMPORT_WORKFLOW", "class_name": "ImportWorkflow" } |
| 133 | ], |
| 134 | "triggers": { "crons": [] }, |
| 135 | "containers": [] |
| 136 | } |
| 137 | } |
| 138 | } |