Skip to content

zllovesuki/anvil

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

43 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”¨ anvil

Your CI, on the edge. Define your pipeline in .anvil.yml, push to trigger, and watch runs execute in isolated containers β€” all on Cloudflare Workers. No servers to manage, no runners to babysit.

Deploy to Cloudflare

πŸ’‘ Container execution requires a Workers Paid plan ($5/mo). D1, Queues, Workflows, KV, and Durable Objects work on Free or Paid with platform-specific limits.

Built with GPT-5.4 and Claude Opus 4.6 agentic workflows. πŸ€–βœ¨

Upgrading an existing deployment from legacy invite/password auth? Follow MIGRATION-OIDC.md before deploying latest.


🎯 What is anvil?

anvil is a Cloudflare-native CI runner built for personal projects and small teams. If you've ever wanted a simple, self-hosted CI that doesn't require maintaining VMs, Docker daemons, or long-running processes β€” anvil runs entirely on Cloudflare's managed platform.

Push code β†’ anvil picks it up β†’ runs your steps in an isolated container β†’ streams logs back to your browser in real time. That's it.


πŸ“‹ Your pipeline in 10 lines

# .anvil.yml
version: 1
checkout:
  depth: 1
run:
  workingDirectory: .
  timeoutSeconds: 720
  steps:
    - name: install
      run: npm ci
    - name: test
      run: npm test
    - name: build
      run: npm run build

Drop this in your repo root, point anvil at it, and you're running CI. ⚑


✨ Features

πŸ”§ Repository-defined pipelines β€” version your CI config in .anvil.yml, right next to your code

πŸ“¦ Isolated sandbox execution β€” every run gets a fresh container. No leftover state, no cross-run contamination

πŸ“‘ Live log streaming β€” watch stdout/stderr flow in real time via WebSocket, with ANSI color support

πŸ”— Webhook triggers β€” push to GitHub, GitLab, or Gitea and anvil picks it up automatically. Per-provider secrets with rotation and delivery history

▢️ Manual triggers β€” kick off a run from the dashboard with optional branch override

πŸ“Š FIFO run queue β€” one active run per project, the rest queue up in order. No race conditions, no surprises

🧭 Selectable dispatch mode β€” keep the existing Queue-backed path or use Cloudflare Workflows for durable orchestration

πŸ”’ tessera OIDC access β€” no anvil passwords or invites. Verified tessera identities auto-provision or bind to existing users by email

🌐 Any HTTPS Git repo β€” GitHub, GitLab, Gitea, or any repo reachable over HTTPS with optional token auth

πŸ›‘οΈ Security baked in β€” encrypted credentials at rest, automatic secret redaction in logs, strict CSP, same-origin mutation guards, and HttpOnly cookie sessions


πŸ—οΈ How it works

  • Workers β€” stateless HTTP frontdoor: routing, auth, dispatch trigger
  • ProjectDO β€” per-project state machine: active run lock, pending queue, dispatch mode, webhook config
  • RunDO β€” per-run state: steps, rolling logs, WebSocket fanout to browsers
  • D1 β€” durable relational index: users, tessera identities, projects, run history
  • KV β€” ephemeral session storage with TTL
  • Dispatch β€” Queue-backed or Workflow-backed run orchestration, both preserving FIFO at the project level
  • Sandbox β€” isolated container per run via @cloudflare/sandbox

🧰 Tech stack

What Why
πŸ–₯️ React 19, React Router 7, Tailwind CSS 4, Vite 7 Modern frontend with fast HMR
βš™οΈ Hono on Cloudflare Workers Lightweight, edge-native HTTP framework
πŸ’Ύ D1 (SQLite), Durable Objects (SQLite), KV Right storage for each access pattern
πŸ—„οΈ Drizzle ORM Type-safe database access across D1 and DO SQLite
πŸ“¦ Cloudflare Containers, Queues, Workflows Isolated execution with ordered dispatch
βœ… @cloudflare/util-en-garde Runtime codec validation at every boundary
πŸ”€ TypeScript (strict) throughout One language, zero escape hatches

⚑ Quick start

git clone <repo-url>
cd anvil
npm install

cp .dev.vars.example .dev.vars       # local encryption and tessera OIDC settings
npm run db:migrate:d1:local          # set up local D1

npm run dev                          # πŸš€ go

Point the tessera OIDC values in .dev.vars at a reachable tessera issuer, then open the URL from the terminal and sign in with tessera. First sign-in creates or binds the anvil user from the verified OIDC email.


πŸ“ Project structure

src/
  client/           πŸ–₯️  React frontend (pages, components, hooks)
  worker/           βš™οΈ  Cloudflare Workers backend
    api/                 Route handlers (public + private)
    auth/                OIDC, cookie sessions, and auth middleware
    db/                  D1 and Durable Object schemas (Drizzle)
    dispatch/            Queue + Workflow dispatch and shared execution
    durable/             ProjectDO and RunDO
    sandbox/             Container lifecycle
  contracts/        πŸ“  Shared client/server API types
  lib/              πŸ”§  Shared utilities
tests/
  worker/           ⚑  Fast Vitest unit tests
  e2e/              🎭  Playwright browser tests
  integration/      πŸ”—  Queue and Workflow runner integration tests
drizzle/            πŸ“¦  Generated migrations (do not edit)
docker/             🐳  Runner container image
reference/          πŸ“–  Specs and design docs

πŸ“œ Common scripts

Command What it does
npm run dev Start local dev server
npm run build Production build
npm test Fast Vitest suite
npm run test:e2e Playwright browser tests
npm run test:integration:queue Queue-backed live integration test
npm run test:integration:workflows Workflow-backed live integration test
npm run typecheck Full TypeScript type check
npm run db:generate Regenerate Drizzle migrations from schema
npm run deploy Remote D1 migrate, production build, deploy
npm run format Prettier formatting

See OPERATOR.md for the full script reference, deployment guide, database operations, testing strategy, and Cloudflare binding details.


🚒 Deploying

npx wrangler login
npm run deploy

npm run deploy applies remote D1 migrations first, then builds and deploys the Worker. Production deployments need fresh encryption keys plus tessera OIDC client credentials configured as Worker secrets. Existing password/invite deployments should complete the OIDC migration first. For the full deployment guide, environment setup, and binding reference, see OPERATOR.md. πŸ“˜


πŸ›‘οΈ Security

anvil takes security seriously even at v1:

  • πŸ” AES-GCM encryption at rest for repo tokens and webhook secrets
  • πŸ™ˆ Automatic secret redaction in all run logs
  • πŸ›‘ Strict CSP β€” no inline scripts and no eval
  • πŸ”‘ tessera OIDC sign-in with verified-email identity binding
  • 🧭 Same-origin guard on cookie-bound mutations
  • πŸšͺ KV sessions with TTL, carried in a Secure HttpOnly __Host-anvil_session cookie

🀝 Contributing

anvil is under active development. The codebase uses strict TypeScript throughout, with codec-validated boundaries and a clear separation between Workers (stateless) and Durable Objects (stateful, transactional).

If you're diving in, start with the spec and the operator guide.


πŸ“„ License

MIT β€” Rachel Chen, 2026