Skip to content

Hosted Repo Maintainer

The first hosted implementation lives in hosted/. The open-source Rust engine and existing self-hosted workflows remain usable independently.

Deployment status

As of November 1, 2026, https://repomaintainer.com serves the landing page and an explicitly labelled demonstration workspace. This static preview does not accept credentials or execute jobs. D1 repo-maintainer and private R2 repo-maintainer-artifacts are provisioned; the initial D1 migration is recorded. R2 deletes job artifacts after 30 days.

The execution backend and hosted CLI/API integration are implemented on the hosted branch, not enabled on the public website. Apply every D1 migration before deployment. Before enabling it, register the GitHub App, configure Worker secrets, build the container and pass a live smoke test. HOSTED_ENABLED defaults to false.

Architecture

flowchart LR
  Browser[Website / workspace] --> Worker[Cloudflare Worker]
  Worker --> GitHub[GitHub App sign-in]
  Worker --> D1[D1: users, sessions, encrypted credentials, jobs]
  Worker --> Workflow[Durable Workflow]
  Workflow --> Container[One isolated container per job]
  Container --> Engine[Existing Rust engine + Codex CLI]
  Container --> Egress[Worker egress gateway]
  Egress --> API[OpenAI API / read-only Git for the selected repo]
  Workflow --> R2[Private R2: patch and report]
  Worker --> R2

The Worker pins the default branch to a commit before accepting a job. The job runs the existing repo-maintainer run --json command with a server-owned configuration, the requested task and user-supplied check command arrays. Results are downloadable patches and reports. Nothing pushes, opens a PR or merges changes automatically.

D1 acts as the durable dispatch outbox. A five-minute reconciler recovers jobs whose Workflow creation response was lost. Idempotency keys prevent duplicate HTTP submissions. A durable launch marker prevents silently replaying paid inference after an ambiguous runner failure. Such failures require a new run. Cancellation revokes new outbound requests immediately and destroys the runner. A separate Durable Object alarm enforces cleanup even if a Workflow stalls.

Identity and credentials

Create a GitHub App, not a broadly scoped OAuth App. Open the prepared registration form. Review the permissions before registering; GitHub sign-in is required. The link uses GitHub’s supported configuration parameters.

Configuration:

  • Homepage: https://repomaintainer.com
  • Callback: https://repomaintainer.com/auth/callback
  • Repository permissions: Contents read-only, Metadata read-only.
  • Request user authorization during installation. Users choose repositories.
  • No webhook is required for this version; disable it.
  • Keep expiring user tokens enabled. This preview requires reauthentication when a token has less than 30 minutes remaining; it deliberately does not persist refresh tokens. It supports immediate jobs, not unattended recurring jobs.

Set the app's client ID and slug in hosted/wrangler.jsonc. Store its client secret as the Worker secret GITHUB_CLIENT_SECRET. No GitHub App private key is needed for the user-token flow in this version.

OAuth uses PKCE, a single-use expiring state bound to an HttpOnly browser cookie, and server-side identity verification. Session cookies are Secure, HttpOnly, SameSite=Lax, host-only cookies; D1 stores only their SHA-256 hashes. Mutations require the configured Origin. Browser mutations require the configured Origin. Repository-scoped bearer tokens are supported only on job endpoints; they cannot manage credentials or tokens. CLI and GitHub automation covers creation, expiry and revocation. Only token hashes are stored in D1. Every job/artifact/credential lookup is scoped to the verified GitHub user ID. GitHub and OpenAI connections are separate.

The VAULT_KEYS Worker secret is a JSON object mapping version IDs to base64-encoded, random 32-byte AES keys. ACTIVE_VAULT_KEY_ID chooses the write key. Generate the key locally and submit through wrangler secret put VAULT_KEYS; never put keys in chat, source, deployment variables or logs. For example, the shape is {"v1":"<base64 of 32 random bytes>"}. Back up keys in your password manager: losing one makes its encrypted records unreadable. To rotate, retain the old ID, add a new key, switch the active ID, then re-encrypt old records before removing the old key. This version has no automated re-encryption command.

D1 contains AES-256-GCM ciphertext with a fresh IV for each write. Authenticated additional data binds it to the owner, provider, format and key version. The browser can replace or delete its OpenAI key but cannot retrieve it. A compromise of the Worker with access to both D1 and the vault key could decrypt credentials; encryption does not protect against that operator/runtime compromise.

The sandbox receives only a placeholder API key. Its public Internet access is disabled. The outbound Worker injects real credentials only into exact OpenAI inference/model-list endpoints or Git's read-only upload-pack operations for the job's repository. Redirects do not carry credentials onward. The sandbox cannot push to GitHub or read another user's data. All code inside one sandbox shares a trust boundary; patches and reported checks are untrusted output for human review, not evidence suitable for automatic publication.

Preview limits and the $10/month target

The application is configured conservatively for a $10/month Cloudflare target:

  • One active job globally, at most 5 accepted jobs per rolling 24 hours, and 50 accepted jobs per UTC calendar month across all users. Failed/cancelled jobs count too; limits are enforced in the same D1 INSERT as job creation.
  • A 25-minute outer job timeout, 26-minute cleanup alarm and 27-minute container inactivity timeout. Codex itself has a 15-minute timeout.
  • Worker request CPU limited to 50 ms; sign-in and authenticated APIs rate-limited.
  • Result size capped at 256 KB; patch size capped at 2 MB. R2 retention is 30 days.
  • Logs sampled at 10%, invocation logs disabled, no raw agent output persisted.

The account already has enabled $10 billing budget email alerts, verified during setup. Alerts notify; they do not stop charges.

These are application usage controls, not a guaranteed $10 Cloudflare bill cap. The account also hosts other applications. Base subscriptions, other apps, request traffic and other billable usage are outside the run counter. Cloudflare's spend policy API supports account/product-level controls, but the connected credential returned HTTP 403 for billing access. Do not claim those controls are enabled.

Using the November 1 published standard-1 rates, 50 runs lasting the full 27-minute container window would cost about $1.67 in container CPU/memory/disk before included allowances, assuming 100% of the instance's 0.5 vCPU is used. This excludes Workers, Durable Objects, Workflows, storage operations, network and taxes. The Workers Paid plan starts at $5/month; included allowances are shared with other apps. This is a planning estimate, not a spending guarantee. OpenAI usage is billed to each user's own API account and is separate from Cloudflare.

Sources: Container pricing, R2 pricing, billing and budget alerts.

Development and deployment

Requires Node 22+, Docker for runner builds and Cloudflare Workers Paid with Containers, D1, R2 and Workflows available.

cd hosted
npm ci
npm run types
npm run check
npm test
npm run db:local
npm run dev

Set local values in an ignored .dev.vars. For localhost OAuth, register a separate development GitHub App with a localhost callback and override APP_ORIGIN. Do not use production keys or repositories for local tests.

Production setup:

  1. Authenticate Wrangler to the intended Cloudflare account.
  2. Use the configured existing D1 ID and R2 bucket. On another account, create replacements and update the bindings. Apply migrations with npx wrangler d1 migrations apply DB --remote.
  3. Set GitHub App variables. Use npx wrangler secret put GITHUB_CLIENT_SECRET and npx wrangler secret put VAULT_KEYS for secrets.
  4. Keep HOSTED_ENABLED=false. Run npm run build to compile the Worker and build its container, then npm run deploy to replace the static preview.
  5. Validate sign-in and repository selection with a test account. Temporarily enable hosted runs for a controlled smoke test using a disposable repository and limited OpenAI key. Verify result download, cancellation, cleanup and the upstream credentials policy before inviting users.

The runner image includes Git, Node and Python. It cannot download dependencies or read arbitrary external documentation: outbound access is intentionally restricted. Add approved toolchains and an explicit egress policy before enabling projects that need package installation. The UI lets users supply check commands; the default whitespace check alone does not establish application correctness. Editable defaults are the existing engine's source/tests/crates/docs paths and README. No custom editable-path UI, automatic PR publication, scheduling, teams, payments or ChatGPT plan connection is included yet.

npm run preview:build produces a standalone static Worker at hosted/dist/preview.js for the current no-credentials preview. It is deliberately separate from the execution deployment and must not be redeployed over an enabled backend. Both modes use the same HTML/CSS/JS source in hosted/public.

ChatGPT / Codex account connection

Use API keys for this first version, as agreed. OpenAI now documents Sign in with ChatGPT and app-server token-sharing flows, including specific host and account requirements. Do not assume a third-party hosted service can reuse a CLI client ID or accept uploaded auth.json credentials. Validate the appropriate registered client/host flow before offering this connection.

References: Sign in with ChatGPT, Codex app-server, Cloudflare sandbox security, GitHub App user authentication.

Documentation domain

The documentation is served at https://repomaintainer.com/docs/ by the separate repo-maintainer-docs static-assets Worker. The website and execution service can be deployed independently without replacing the documentation. mkdocs.yml sets canonical URLs and the sitemap to the new domain.

Build and publish from the repository root:

python -m pip install -r docs/requirements.txt
mkdocs build --strict
python scripts/prepare-docs.py
npm ci --prefix hosted
hosted/node_modules/.bin/wrangler deploy --config docs-worker/wrangler.jsonc

The preparation script creates site-cloudflare/docs/ and a separate site-redirects/ tree. GitHub Pages serves only the latter so existing page links continue to the matching new page, preserving query strings and anchors when JavaScript is enabled. It includes a normal link and meta-refresh fallback.

The documentation workflow builds on pull requests and publishes on main. Automatic Cloudflare publishing requires the repository Actions secret CLOUDFLARE_DOCS_API_TOKEN: Workers Scripts Edit for the hosting account, Workers Routes Edit and Zone Read restricted to repomaintainer.com. The connected Cloudflare app cannot create that token, so this secret still needs to be configured. The initial static deployment was published through the Cloudflare connection; future publishing should use the workflow above.