# stig A git forge built entirely on Cloudflare: repositories live in [Artifacts](https://developers.cloudflare.com/artifacts/), and everything else runs on Workers, D1, KV, R2, Queues, Workflows, Durable Objects and Containers. ## Features - Repositories: create, import from HTTPS remotes, fork, archive, delete, public/private visibility - Browsing: trees, syntax-highlighted files, raw files, README rendering, commit history, commit diffs, branches, tags, `.tar.gz` archives - Git over HTTPS: clone/fetch/push through a Worker proxy authenticated with personal access tokens - Web editing: create, edit, rename and delete files, optionally on a new branch - Branch protection: required pull requests, approvals and status checks, deletion protection, admin bypass - Issues: labels, assignees, comments, locking, closing keywords (`fixes #12`), full-text search - Pull requests: cross-fork PRs, inline review threads, approvals and change requests, review requests, drafts, merge/squash/rebase, mergeability checks - Actions: GitHub-Actions-style workflows in `.stig/workflows/*.yml` with `needs`, matrices, `if:` expressions, secrets, artifacts and live logs - Runners: Cloudflare Containers by default, plus self-hosted runners (`runner/stig-runner.ts`) - Releases with R2-backed assets, webhooks with HMAC signatures and redelivery, notifications, activity feeds, organizations ## Architecture | Concern | Cloudflare primitive | | ---------------------------------------------- | ---------------------------------------------- | | Git objects and refs | Artifacts (Workers binding + smart HTTP) | | Users, repos, issues, PRs, CI state | D1 (with FTS5 search) | | Sessions, refs/object/diff caches, token cache | KV | | Logs, CI artifacts, release assets | R2 | | Webhook fan-out and delivery, notifications | Queues (with retries and a DLQ) | | Post-push pipeline, CI run orchestration | Workflows (`PushWorkflow`, `CiRunWorkflow`) | | Job execution, log streaming, merges | Durable Object `Runner` with a Container | | Push events from Artifacts | `triggers.events` (`cf.artifacts.repo.pushed`) | Key code paths: - `src/server.ts` — Worker entry: git proxy, runner API, raw/archive/asset routes, queue and cron handlers - `src/server/git/` — pkt-line, refs, packfile writer, receive-pack client, tree diff, merge-base - `src/server/ci/` — workflow schema, expressions, compiler, bash job script, runner DO, scheduler - `src/fns/` — TanStack Start server functions used by the UI - `migrations/` — D1 schema ## Setup Requirements: [Bun](https://bun.sh/), Docker (for building the runner image), a Cloudflare account on the Workers Paid plan with Artifacts enabled. ```sh bun install ``` Create resources and put their IDs in `wrangler.jsonc` (also set `account_id` if your login has several accounts): ```sh bunx wrangler d1 create stig bunx wrangler kv namespace create stig bunx wrangler r2 bucket create stig bunx wrangler queues create stig-events bunx wrangler queues create stig-events-dlq bunx wrangler secret put SECRETS_KEY ``` The Artifacts namespace (`stig`) is referenced by the binding in `wrangler.jsonc`; check it with `bunx wrangler artifacts namespaces get stig`. Set `PUBLIC_URL` in `wrangler.jsonc` to your deployment URL. Optional: `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` secrets enable GitHub login. ### Develop ```sh echo "SECRETS_KEY=dev-secret" > .dev.vars bun run db:migrate bun run dev ``` The Artifacts binding uses `remote: true`, so local development talks to your real Artifacts namespace. ### Deploy ```sh bun run deploy ``` The first account to sign up becomes the instance administrator. Set `SIGNUPS_ENABLED` to `"false"` to close registration afterwards. ## Using git Create a token under **Settings → Access tokens**, then use your username and the token as the password: ```sh git clone https://stig.example.com/alice/project.git ``` ## Workflows ```yaml # .stig/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: cloudflare strategy: matrix: node: [22, 24] steps: - uses: actions/checkout@v4 - run: bun install && bun test - uses: actions/upload-artifact@v4 with: name: coverage-${{ matrix.node }} path: coverage deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: [self-hosted, linux] steps: - run: ./deploy.sh env: TOKEN: ${{ secrets.DEPLOY_TOKEN }} ``` Supported triggers: `push` (branches, tags, paths filters), `pull_request` (types, branches, paths), `release`, `workflow_dispatch` (with inputs). Steps are `run` scripts (bash, sh, python, node, bun) plus `checkout`, `upload-artifact` and setup actions treated as no-ops; third-party actions are not supported. Job status is published as commit statuses named ` / `, which branch protection can require. ### Self-hosted runners Register a runner under **Repository settings → Runners**, then on the host (needs bash, git, tar): ```sh STIG_URL=https://stig.example.com STIG_RUNNER_TOKEN=stig_runner_... STIG_RUNNER_LABELS=linux bun runner/stig-runner.ts ``` ## Pages Static sites are served from `.//` (a repo named `.` is served at the root) and pull request previews from `/_pr//`. `PAGES_DOMAIN` must be a separate registrable domain from the forge so user content can never read forge cookies. - Add Worker routes for `*./*` (with a wildcard DNS record and certificate). - Custom domains use Cloudflare for SaaS: set `PAGES_ZONE_ID` to the SaaS zone (with a fallback origin and a `*/*` Worker route) and add a `CF_API_TOKEN` secret with "SSL and Certificates: Edit" on that zone. - Deploys copy files into content-addressed R2 objects (`pages/objects/`), publish a manifest to KV and flip a KV pointer, so rollbacks are instant. ## Scripts | Command | Purpose | | --------------------------------- | -------------------------------------- | | `bun run dev` | Local dev server | | `bun run build` | Production build and type-check | | `bun run deploy` | Apply D1 migrations and deploy | | `bun run cf-typegen` | Regenerate `worker-configuration.d.ts` | | `bun run lint` / `bun run format` | oxlint / oxfmt | ## License Released under the [0BSD](LICENSE) license.