Son of Anton Docs
Self-host

Run on Your Cloudflare Account

Provision the native engine, enable a canary repository, separate publication authority and verify durable receipts.

Run these commands from an authorized son-of-anton-review checkout on feat/cloudflare-native. The private-repository and licensing prerequisites in Requirements apply. The source runbook cloudflare-native/DEPLOY.md mixes provisioning instructions with historical deployment receipts; historical IDs and outcomes are not your installation's state.

Engine and operator are separate trees. This procedure deploys cloudflare-native/src/main.js and the key broker. The expanded API lives in son-of-anton-operator-parity/cloudflare/ on feat/greptile-operator-parity; the session UI is in anton-ui-trace-parity. There is no combined command here, and the native ingress hostname does not serve their /operator/api/* routes.

Account and configuration

  1. Obtain human approval for the target Cloudflare account, spending and infrastructure changes. The checked-in runbook calls for Workers Paid with Containers enabled; have the owner confirm current entitlements and quota.
  2. Install locked engine dependencies with npm ci and an approved Wrangler version separately. Provide a working Docker/container-image build environment.
  3. Authenticate with wrangler login if needed, then confirm the intended account with wrangler whoami.
  4. Replace the account IDs in both native config files, the App ID in wrangler.key-broker.jsonc, and the D1/KV IDs after provisioning. Resource names and the KEY_BROKER service name must match your deployment.
  5. Change the checked-in GILF_PUBLISH_MODE=live and CRON_AUTHORITY=live to shadow before deploying. Replace CRON_AUDIT_INSTALLATIONS and PR_AGENT_LOOP_SINCE; do not operate on the checked-in estate.

Keep the OMP image_build_context set to "..". Its Dockerfile copies src/, package.json, prompts/ and config/ from the engine root. The configuration also declares a separate Sandbox container application and Durable Object binding. It is not a one-image deployment.

Provision resources

For a new deployment only:

ResourceBindingCommand
D1DBwrangler d1 create son-of-anton-review-state
KVDEDUPEwrangler kv namespace create DEDUPE
R2 payload bucketWEBHOOK_PAYLOADSwrangler r2 bucket create son-of-anton-webhook-payloads
R2 cron artifactsCRON_ARTIFACTSwrangler r2 bucket create son-of-anton-cron-artifacts
Review queueREVIEW_QUEUEwrangler queues create gilf-review-intents
Dead-letter queueDEAD_LETTERwrangler queues create gilf-review-dlq

Set the resulting IDs in cloudflare-native/wrangler.jsonc. For an existing deployment, inventory and reuse its state; do not create replacements merely because a probe fails.

Ingress deduplicates delivery IDs with KV. When a raw webhook exceeds 96 KiB and the R2 binding exists, it stores the payload there and queues a reference. The consumer hydrates that reference. R2 is also used for cron artifact bodies; D1 holds the indexes and durable run state.

Fresh database

wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --file cloudflare-native/schema.sql
wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --file cloudflare-native/migrations/002-cron-parity.sql

Both are required: schema.sql contains the native lease fences, outcomes ledger and trace indexes, but not the cron tables. Migration 002 adds cron_runs, cron_job_state, cron_artifacts and cron_dead_letters.

Existing databases need Upgrading: migration 001 is destructive, and an older publish_leases needs a guarded created_at column addition. CREATE TABLE IF NOT EXISTS cannot upgrade an existing table's columns.

Enable a repository

App installation and review enablement are different. The native adapter sets repository admission from stored repo_configs, previous review history, or autoEnableNewRepos (default false). GILF_REPOS is not read on this path. Even an authorized manual command remains blocked by explicit repository disablement.

After a human selects an installed canary repo, inspect its current setting. Replace OWNER/REPO with its exact full name; do not run the placeholder unchanged.

wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --command "SELECT repo, json_extract(config_json, '$.enabled') AS enabled FROM repo_configs WHERE repo='OWNER/REPO';"

During setup, before reviewing that repo, this upsert enables it without replacing other stored config fields:

wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --command "INSERT INTO repo_configs (repo, config_json) VALUES ('OWNER/REPO', json_object('enabled', json('true'))) ON CONFLICT(repo) DO UPDATE SET config_json=json_set(repo_configs.config_json, '$.enabled', json('true'));"

Repeat the readback and confirm enabled=1. This is a deliberate policy mutation, not discovery or installation. Do not race it with another policy writer. Existing run policy snapshots are pinned; use a new manual comment for a new attempt after changing settings.

Secrets and model selection

Have the authorized operator enter secret values, never commit or paste them into chat:

wrangler secret put GITHUB_WEBHOOK_SECRET --config cloudflare-native/wrangler.jsonc
wrangler secret put OPENROUTER_API_KEY --config cloudflare-native/wrangler.jsonc
wrangler secret put GITHUB_APP_PRIVATE_KEY --config cloudflare-native/wrangler.key-broker.jsonc

Only the broker holds the native App private key. The main Worker forwards selected model/validation secrets to OMP, which receives scoped broker tokens for GitHub access. Shadow still needs the broker for source access and PR metadata reads.

Set these plain vars in the main config:

VariableSetup choice
GILF_MODEL_PROVIDERopenrouter, preferred over legacy GILF_CODEX_PROVIDER
GILF_MODELYour approved, currently available OpenRouter model
GILF_OPENROUTER_REQUIRE_FREE1 unless paid usage is explicitly approved; the committed value is 0
GILF_MODEL_PRICESOptional accurate JSON price map, USD per million input/output tokens; remove stale values. Unknown OpenRouter prices yield null estimates, not zero.
GILF_VALIDATION_EXECUTORmanaged-cf-sandbox, with both SDK class export and binding intact
GILF_PUBLISH_MODEExplicit shadow for commissioning
CRON_AUTHORITYExplicit shadow until scheduler cutover is approved

Remove GILF_VALIDATION_SHADOW_EXECUTOR for a single-lane canary. If retaining managed-e2b, supply E2B_API_KEY; it is also required when E2B is primary. The committed E2B timeout is 600000 ms. The E2B budget gate only suppresses the shadow selector, and its ledger-read failure forwards the lane unchanged; it is not a hard account-wide spending limit.

Only container-env.js allowlisted vars reach OMP. Swarm/planner, inversion and artifact flags are not forwarded. The default image's Codex command is a failing placeholder, not an authenticated alternative ready to select.

Deploy and connect GitHub

wrangler deploy --config cloudflare-native/wrangler.key-broker.jsonc
wrangler deploy --config cloudflare-native/wrangler.jsonc

Deploy the broker first so the main service binding resolves. Confirm both container applications and their intended images, not only the Worker upload.

Configure the App webhook at your main Worker's /github/webhooks URL, using the matching secret, and install it on the selected repos. See Requirements for App permissions and event subscriptions.

# Shell variable for your deployment URL, not an engine configuration key.
export ANTON_ENGINE_URL='https://your-worker.example'
curl --fail-with-body --silent --show-error "$ANTON_ENGINE_URL/health"

Ingress returns {"ok":true,"service":"son-of-anton-ingress"}. That endpoint does not test resource bindings. The native fetch handler returns 404 on paths other than health and the webhook; it does not host the dashboard.

Verify a review

Post a new @anton review or @anton rerun comment as an authorized owner/member/collaborator. The consumer resolves a missing head from GitHub. It ignores status and help comments, although the Node server implements those replies.

Inspect the selected canary, not merely the newest unrelated row:

wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --command "SELECT review_key,head_sha,status,json_extract(record_json,'$.skipReason') AS skip_reason,json_extract(record_json,'$.publish.mode') AS publish_mode FROM review_runs WHERE repo='OWNER/REPO' ORDER BY rowid DESC LIMIT 10;"
wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --command "SELECT trace_id,review_key,status FROM review_traces WHERE repo='OWNER/REPO' ORDER BY started_at DESC LIMIT 10;"

Using the returned trace ID:

wrangler d1 execute son-of-anton-review-state --config cloudflare-native/wrangler.jsonc --remote --command "SELECT name,status,error FROM review_trace_spans WHERE trace_id='TRACE_ID' ORDER BY seq;"
wrangler tail --config cloudflare-native/wrangler.jsonc

Read policy decisions, semantic/validation gaps and applicable spans such as context.graph and publish. There is no invariant that every run has exactly seven successful spans: optional lanes, skips and errors change the result. Shadow suppresses GitHub publication, not model/sandbox execution or state writes. A stored publication status from a recording publisher is not a remote receipt.

For an approved live cutover, first drain competing publishers, then set GILF_PUBLISH_MODE=live, redeploy and trigger a new canary. Confirm actual GitHub review/check IDs at the expected head. Invalid publish-mode values throw; parsing trims and lowercases values, and an absent/blank value defaults to shadow. The shipped config explicitly overrides that default with live.

Cron authority and cadence

CRON_AUTHORITY is separate from review publication. Shadow cron runs can still record observations and artifacts; do not treat shadow as a read-only database mode. Replace installation scope before scheduling audits and stop legacy mutation timers before enabling live cron authority.

Cron expressionJob
2-59/15 * * * *pr-agent-loop
3-59/15 * * * *watchdog
* * * * *command-bridge
30 22 * * * and 30 23 * * *nightly-audit, gated to 23:30 Europe/London

The nightly handler checks local time so both UTC expressions do not run the audit twice. Prime-watch has a handler but is deliberately unscheduled. Verify actual persisted cron results; an enabled trigger or skipped DST-side event is not proof that the intended audit completed.

Rollback

  1. Set CRON_AUTHORITY=shadow, deploy, and let running cron mutations finish.
  2. Repoint the App webhook to the retained Node endpoint and verify the saved URL. Keep replacement workers stopped while accepted native reviews finish.
  3. Set native GILF_PUBLISH_MODE=shadow, deploy, and confirm no live native review remains. A config change cannot stop a running container.
  4. Reconcile or redeliver accepted unfinished work before enabling the replacement publisher. Preserve uncertain receipts and dead-letter evidence; do not assume independent D1 and SQLite stores deduplicate each other.
  5. Restore only the intended Node timers, then verify a real review and scheduled-job execution on that lane.

Retain fallback code, state, secrets and unit definitions until a separate health/rollback review approves removal. Historical retention dates in the source runbook do not prove today's fallback is safe to retire.

On this page