Skip to content

Deploy config for the Worker: staging + api.ghostwrit.in production, rewrite rate limit, deploy script with pinned smoke test and rollback - #13

Merged
ParallelEntrepreneur merged 1 commit into
mainfrom
colonizer/issue-1-e70c5c56
Oct 10, 2026
Merged

ParallelEntrepreneur merged 1 commit into
mainfrom
colonizer/issue-1-e70c5c56

Conversation

@ParallelEntrepreneur

Copy link
Copy Markdown
Contributor

Resolves the configuration side of #1. Nothing has been deployed. This environment has no Cloudflare credentials and no provider key, so the actual deploy, setting the secrets, and a rewrite against a real model are still for a maintainer to do.

What changed

  • crates/ghostwritin-worker/wrangler.toml
    • The top-level environment is the staging Worker ghostwritin-api.
    • [env.production] has routes = [{ pattern = "api.ghostwrit.in", custom_domain = true }] and workers_dev = false. It repeats vars and the rate limiter, because wrangler doesn't pass those down from the top level.
    • Each environment gets a [[ratelimits]] binding REWRITE_LIMITER with 10 requests per 60 s; staging uses namespace 1001 and production 1002.
    • The required secrets are listed by name only, never with values: GHOSTWRITIN_API_KEYS, HARNESS_SECRET, and one of ANTHROPIC_API_KEY / OPENAI_API_KEY.
  • crates/ghostwritin-worker/src/lib.rs (+ sha2 dep, 1-line Cargo.lock change): a rate-limit check on POST /v1/rewrite only, which runs before the harness handles the request.
    • Requests are keyed by the sha256 of the bearer token, falling back to cf-connecting-ip.
    • Over the limit, it returns a 429 RFC 9457 problem with code rate-limited and Retry-After: 60. Refused requests never reach the model.
    • It is deliberately fail-open: if the binding is missing or the limiter errors, the request is let through. The reason is commented in the code; please decide whether you agree.
    • Includes a unit test for how requests are keyed.
  • tools/deploy.sh <staging|production> (new):
    • Pins wrangler@4.149.0; set WRANGLER to override it.
    • Refuses to deploy until the secrets exist (wrangler secret list).
    • Records the version currently live, then deploys.
    • Smoke-tests /v1/health for ok, model_configured and version == the workspace version, so a stale build fails the gate.
    • If the smoke test fails, it runs wrangler rollback <previous> -y.
    • Staging needs STAGING_URL, because the workers.dev subdomain hasn't been chosen yet.
  • ship.ks:
    • Staging requires now includes the provider key, and the smoke gate checks the version.
    • fn.deploy isn't in Keep Shipping's catalog yet, so the header says tools/deploy.sh does the same steps by hand.
  • README.md: updated the status line, the rate-limited error code, and the self-hosting/deploy section, plus the plan requirement below.

How it was checked

  • CI gates all pass: cargo fmt --check, clippy --workspace --all-targets -D warnings, cargo test --workspace (50 passed), cargo doc -D warnings, and the wasm32 build of ghostwritin-worker.
  • Wrangler 4.149.0 accepts the config:
    • wrangler deploy --dry-run succeeds with no warnings for both --env "" (staging) and --env production, and the rate-limit binding validates.
    • In the wrangler source, --env "" falls back to the top-level environment and turns off the "multiple environments" warning.
  • tools/deploy.sh was tested against fake wrangler/curl scripts:
    • healthy deploy: no rollback
    • failed smoke test: rollback to the newest previous version
    • deployments list failing: the deploy still runs
    • first deploy where secret list fails: wrangler's error is shown, with a hint
    • production arguments, and a missing worker-build
    • bash -n and shellcheck are clean.
    • Two bugs found this way are fixed: the script used to run wrangler from the repo root, where it can't find wrangler.toml. It also read a version field that deployments list --json doesn't have, and took the oldest entry instead of the newest, so rollback could never trigger.
  • 10,000-word rewrite under wrangler dev --local, with a scripted OpenAI-compatible server (300 ms latency) standing in for the provider. The server was a throwaway in /tmp and is not in the repo.
    • A 9,946-word draft returned HTTP 200 in 6.5 s.
    • It made 19 model calls, one per ~600-word chunk, strictly one after another.
    • The Worker used about 0.2 s of CPU (an upper bound, measured from /proc).
    • Also checked: /v1/health returns 200, missing or wrong keys get 401, and a burst gets 10×200 then 429 rate-limited with no model call.

Reviewers, please look at

  • This needs the Workers Paid plan. The Free plan allows 10 ms of CPU per request, and a full rewrite measured about 0.2 s. Paid's default 30 s is plenty, so no [limits] cpu_ms is set.
  • With a real model, wall time is roughly 19 × the model's latency, because the chunk loop runs one call at a time (crates/ghostwritin-rewrite). Cloudflare has no hard wall-time limit for HTTP requests, but a slow model makes for a slow response.
  • Still to do from the issue:
    • run a rewrite against a real provider key
    • wrangler secret put for each environment (the first secret put creates the Worker)
    • pick the staging workers.dev URL
    • the first real tools/deploy.sh staging, then production
  • Overlap with Billing and word quotas through Polar #3 (billing): that colony also edits crates/ghostwritin-worker/src/lib.rs (in instance()) and README.md (Quota row, Billing note). My changes avoid those spots. If Billing and word quotas through Polar #3 adds a D1 DB binding, it has to be repeated under [env.production].

Size: 7 files, about 340 changed lines (including the new 140-line tools/deploy.sh).

Closes #1

Note: this branch was 1 commit(s) behind main when this pull request was opened.


🤖 Generated by Colonizer in a microVM

Co-Authored-By: Colonizer Settlers 331648616+colonizer-settlers@users.noreply.github.com

…rewrite rate limit, deploy script with pinned smoke test and rollback

Refs #1

Co-Authored-By: Colonizer Settlers <331648616+colonizer-settlers@users.noreply.github.com>
@ParallelEntrepreneur
ParallelEntrepreneur merged commit 2d268ec into main Oct 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deploy the Worker and route api.ghostwrit.in

1 participant