# BlackBull Deploy Gateway — Agent Guide You are deploying a lightweight website/Worker to Cloudflare THROUGH this gateway. You never see or use Cloudflare's real credentials. You authenticate with a per-project token and may only touch resources scoped to that project. ## Connection - Base URL: https://deploy.blackbull.technology/cf/ - Auth header on every /cf/* call: "Authorization: Bearer " - All paths below are relative to https://deploy.blackbull.technology/cf/ and map to the Cloudflare API under BlackBull's account. - Responses use Cloudflare's envelope: {"success":bool,"errors":[{"code","message"}],"result":...}. ## STEP 0 — Ask the user for these before doing anything 1. PROJECT_TOKEN (starts with "bbp_"). Required. If absent, stop and ask; an admin issues it at https://deploy.blackbull.technology/admin. 2. What to deploy: a dynamic Worker, or a static site (HTML/CSS/JS)? 3. Desired worker name. It MUST start with your project's worker prefix (discover via /whoami below). 4. Custom domain? If yes: the exact hostname (e.g. promo.acme.com) AND confirm they can add DNS records at that domain's DNS provider. If no, the site is served on *.workers.dev. 5. Any secrets/env values the site needs (API keys, etc). 6. Does it need a database (D1)? If yes, note it. Do NOT invent names, domains, or secret values. Ask. ## STEP 1 — Discover your scope GET https://deploy.blackbull.technology/cf/whoami -> result: { name, worker_prefix, domain_suffix, enabled, saas_configured, saas_cname_target } Use worker_prefix to choose a valid worker/D1 name (must start with it). If enabled=false, stop: the project is disabled — ask an admin. ## Error model (react, don't blindly retry) - 401 -> token missing/invalid or rotated. Ask the user for a fresh token. - 403 "gateway: ..." -> blocked by policy (name outside your prefix, domain outside suffix, unregistered D1, or an unsupported operation). Read the message; fix the name/target; do not retry as-is. - 404 with a Cloudflare code -> the upstream resource doesn't exist (e.g. reading a not-yet-created worker). - 501 -> a feature (custom hostnames) isn't configured on the gateway; tell the user. - Otherwise the body is Cloudflare's own error; surface message to the user. ## Isolation rules (know these so you pick valid inputs) - Workers: name MUST start with worker_prefix. You have full control over your own scripts only. - D1: create names MUST start with worker_prefix; you can only query DBs your project created. - Custom domains: if domain_suffix is set, the hostname must end with it; otherwise any hostname is allowed. The service (worker) attached must start with your prefix. - Blocked entirely: billing, account settings, API-token management, unrelated DNS, listing all resources, and anything owned by another project. ## DEPLOY A DYNAMIC / SIMPLE WORKER (recommended for most lightweight sites) Build a module worker (worker.js exporting a fetch handler; you may embed small HTML directly). Upload with the standard multipart form: curl -X PUT "https://deploy.blackbull.technology/cf/workers/scripts/-" \ -H "Authorization: Bearer $TOKEN" \ -F 'metadata={"main_module":"worker.js","compatibility_date":"2025-06-01"};type=application/json' \ -F 'worker.js=@worker.js;type=application/javascript+module' metadata fields you may set: main_module (entry filename), compatibility_date, compatibility_flags, bindings (e.g. D1: {"type":"d1","name":"DB","id":""}), vars, assets (see below). Enable a live *.workers.dev URL (also REQUIRED for custom domains to route correctly): curl -X POST "https://deploy.blackbull.technology/cf/workers/scripts/-/subdomain" \ -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" -d '{"enabled":true}' The URL is https://-..workers.dev (GET https://deploy.blackbull.technology/cf/workers/subdomain for the subdomain). Read status/metadata: GET https://deploy.blackbull.technology/cf/workers/scripts/- Deployments/versions: GET https://deploy.blackbull.technology/cf/workers/scripts/-/deployments (or /versions) ## DEPLOY A STATIC SITE (Worker static assets) Use when serving a folder of files. Three steps: 1) Start an upload session (returns a JWT + which file hashes to send): POST https://deploy.blackbull.technology/cf/workers/scripts/-/assets-upload-session body: {"manifest": {"/index.html": {"hash":"","size":}, ...}} -> result: { jwt, buckets: [[ "", ... ], ...] } 2) Upload each bucket's files. IMPORTANT: this step is authenticated by the SESSION JWT, which you pass in the X-Assets-Jwt header (NOT Authorization — that still carries your project token): POST https://deploy.blackbull.technology/cf/workers/assets/upload?base64=true -H "Authorization: Bearer $TOKEN" -H "X-Assets-Jwt: " multipart body: each file part named by its hash, value = base64 of file bytes, with header "Content-Type: " -> when all buckets are done, the response returns a completion JWT. 3) PUT the script with the assets manifest attached: metadata includes: "assets": {"jwt":"","config":{"html_handling":"auto-trailing-slash"}} (main_module optional if the site is purely static; include one to add dynamic routes.) Then enable the subdomain as above. For most simple sites, embedding HTML in a module worker (previous section) is simpler and fully supported — prefer it unless you have many files. ## SET WORKER SECRETS curl -X PUT "https://deploy.blackbull.technology/cf/workers/scripts/-/secrets" \ -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" \ -d '{"name":"API_KEY","text":"","type":"secret_text"}' ## D1 DATABASE Create (name MUST start with your prefix; auto-registered to your project): curl -X POST "https://deploy.blackbull.technology/cf/d1/database" -H "Authorization: Bearer $TOKEN" \ -H "content-type: application/json" -d '{"name":"-db"}' -> result.uuid List your DBs: GET https://deploy.blackbull.technology/cf/d1/database Run SQL: POST https://deploy.blackbull.technology/cf/d1/database//query body: {"sql":"...","params":[]} Bind it to a worker: add to PUT metadata.bindings: {"type":"d1","name":"DB","id":""} ## CUSTOM DOMAIN (team's own domain, kept at any DNS provider) IMPORTANT: deploy the worker FIRST (the domain routes straight to it), THEN claim the hostname. "service" is REQUIRED and must be one of your workers (name starts with your prefix). 1) Claim it: curl -X POST "https://deploy.blackbull.technology/cf/hostnames" -H "Authorization: Bearer $TOKEN" \ -H "content-type: application/json" -d '{"hostname":"","service":"-"}' -> result: { hostname, service, status, routed:true, dns:{...}, records:[...] } result.dns tells you exactly what to do (do not guess apex vs subdomain yourself): dns.apex -> true if the hostname is a root/apex domain dns.recommended -> "cname" (subdomain) or "a_aaaa" (apex) dns.instructions-> plain-English steps to relay to the user result.records lists every possible record with a "purpose" saying when to use it: - CNAME -> add this ONLY for a subdomain (their.domain -> the SaaS target) - A (and AAAA) -> add these INSTEAD of the CNAME for a root/apex domain (most registrars can't put a CNAME at the apex; that is the #1 cause of failed setups) - TXT -> ownership + TLS/DCV validation (always add these) RULE: add EITHER the CNAME (subdomain) OR the A/AAAA (apex) for the hostname — never both. Follow dns.recommended. If result.routed is false, deploy the worker then POST again. 2) User adds those records at their DNS provider. Then poll: GET https://deploy.blackbull.technology/cf/hostnames/ -> result.status goes "pending" -> "active" (status also returns updated records incl. the TLS validation record once issued.) Once active, the site is live at https://their.domain/ (routed directly to your worker). List: GET https://deploy.blackbull.technology/cf/hostnames Detach: DELETE https://deploy.blackbull.technology/cf/hostnames/ (also removes the route) ## VERIFY YOUR DEPLOY - workers.dev: curl the https://-..workers.dev URL. - custom domain: after status=active, curl https:///. - Report the live URL(s) back to the user. If anything returned 403 "gateway:", explain the policy limit (usually a name/domain outside your project's scope) rather than retrying. ## NOTES - Do not use the wrangler CLI pointed at this gateway — it calls account endpoints that are not proxied and needs the hidden account ID. Use these REST endpoints directly. - Every action is audit-logged. Keep the project token secret; store it as an env var, never in code. - Human version of these docs: https://deploy.blackbull.technology/docs Self-serve status page: https://deploy.blackbull.technology/status