Skip to content

Architecture

Every unit g1t deploys, what each one does, what it owns and what it calls, and how a call between two services works, down to the wire.

g1t deploys twenty-five units, listed once in deploy/stack.jsonc. Each is a Worker with its own wrangler.jsonc, which names its database, its queues and the services it calls. This page puts all of them in one place.

Calls lists the services each unit binds to, which are the only ones it can reach. A test, npm run test:deploy, reads every wrangler.jsonc and fails if this table and the bindings disagree.

Unit Language Stage What it does Owns Calls
apps/web TypeScript front The site on g1t.sh: pages, git over HTTPS and the package registries passed to their services, and g1tusercontent.com The downloads bucket actions, artifacts, billing, chat, deployments, events, identity, integrations, notify, og, packages, projects, repos, search, security, telemetry, webhooks, work
apps/api Rust edge REST, MCP, OAuth, the runner protocol, OTLP ingest, Actions toolkit and OIDC Nothing; writes Actions cache and artifact files actions, artifacts, billing, deployments, events, identity, integrations, packages, projects, repos, runner, search, security, telemetry, webhooks, work
apps/status TypeScript edge status.g1t.sh: probes every minute and keeps incidents D1 g1t-status billing, repos
apps/sudo TypeScript front g1t.sh’s staff console, behind Cloudflare Access Nothing actions, billing, events, identity, projects, status, telemetry
apps/docs Astro front docs.g1t.sh, these pages Nothing none
services/identity Rust core Accounts, sessions, workspaces, members, teams, repository roles, tokens, OAuth, device sign-in, 2FA D1 g1t, the avatars KV billing, events, projects, repos
services/repos Rust core Repositories and git: smart HTTP, pre-receive checks, caches, forks, landing merges, releases, backups D1 g1t-repos, the git store, the git cache KV, the packs and backups buckets billing, events, identity, security, work
services/work Rust core Issues, pull requests, comments, reviews, labels, milestones, rulesets, checks, statuses, CODEOWNERS, the merge queue D1 g1t-work actions, events, identity, repos
services/events Rust core The event bus and log, the audit log, the inbox D1 g1t-events, the g1t-events queue and its subscriber queues billing, identity, notify, repos, work
services/actions Rust core Workflows: reading them, triggering runs, planning jobs, secrets, self-hosted runners, logs D1 g1t-actions; lists and expires the Actions cache bucket billing, deployments, events, identity, notify, projects, repos, runner, work
services/runner TypeScript core Sandboxes on Cloudflare Containers, one per job, with their egress The Container classes actions, billing, deployments, events, identity, repos, work
services/deployments TypeScript core Builds and serves apps on g1t.page, previews, custom domains D1 g1t-deployments, the domains KV, the dispatch namespace actions, billing, events, identity, og, projects, repos, runner, telemetry, work
services/pages TypeScript edge The dispatcher for *.g1t.page and custom domains Nothing; reads the domains KV none
services/packages Rust core npm, Cargo, Composer, Maven, NuGet, RubyGems, Go and container images D1 g1t-packages, the packages bucket billing, events, identity, repos
services/security Rust core Secret scanning, dependency alerts and updates, code scanning, the supply chain D1 g1t-security actions, billing, events, identity, repos, runner, work
services/search Rust core Site search over repositories, issues, pull requests, people and code D1 g1t-search, its jobs queue billing, identity, repos, work
services/projects TypeScript core Projects and what they depend on D1 g1t-projects deployments, identity, og, packages, repos
services/chat TypeScript core Channels, direct messages and threads, live D1 g1t-chat, a Durable Object per channel identity, integrations, notify
services/artifacts TypeScript core Docs written together, live, with their spaces, sharing and search D1 g1t-docs, its files bucket, two Vectorize indexes, a Durable Object per open doc billing, events, identity, notify, repos, work
services/notify TypeScript core Notifications, live pages, presence, browser and phone push A Durable Object per person, per workspace and per repository identity
services/telemetry TypeScript core Logs, traces, metrics and errors for projects, at otel.g1t.sh A Durable Object per project billing
services/billing Rust core Usage, holds and settlements, limits, Stripe D1 g1t-billing identity, packages, repos
services/integrations Rust core Model providers, alert sources, issue trackers, the GitHub App, Ask g1t D1 g1t-integrations identity, repos, work
services/webhooks Rust core Webhook subscriptions and signed deliveries with retries D1 g1t-webhooks identity, repos
services/og TypeScript core Social cards and page screenshots, at og.g1t.sh The screenshots bucket billing, identity, projects, repos, work

Calls go both ways in places, repos and work for one. That is allowed: bindings only have to respect the deploy stages, and every service is in the same one (How g1t ships).

A service binding is a direct connection from one Worker to another in the same data centre, with no trip over the public internet. g1t uses it as plain HTTP:

POST https://work/rpc/get_pull
content-type: application/json
{"repo": {"namespace": "acme", "name": "app"}, "number": 12, "viewer": {"id": "usr_…", "workspaces": […], …}}

The hostname is ignored; the method is the path. The arguments are that method’s struct in the contracts, here ViewArgs in crates/contracts/src/work.rs. viewer is the person or token asking, or null for someone signed out. The answer is always JSON:

{ "ok": true, "value": { … } }

or, for anything the caller should handle, still with status 200:

{ "ok": false, "error": { "code": "not_found", "message": "No pull request #12." } }
Status Means The caller gets
200 with ok: true It worked The value
200 with ok: false An expected failure, with a code from FailureCode A value to handle, such as Result in TypeScript or Outcome in Rust
404 No such method An error
503 A store is busy, with Retry-After An error
500 Something threw An error

On the Rust side, g1t_kit::call(&binding, "method", &args) sends a call and rpc_method, args and reply serve one (crates/kit/src/lib.rs). On the TypeScript side, the clients in packages/contracts/src/clients.ts do both. Two Workers expose Worker RPC entrypoints instead: og’s Screenshots and status’s StatusAdmin.

There is no secret between services. A service can be called only through a binding, because none of them is published on workers.dev and only og and telemetry have routes, for their own public endpoints. That makes the binding list in each wrangler.jsonc the complete list of who can call it.

Services share types, never code. Each one depends on the contracts, and none on another service’s crate or package.

Package Holds
crates/contracts (g1t-contracts) A module per service with its data and its methods’ arguments; Outcome and Failure; the event envelope and every event’s data; ROUTES, which says which events each subscriber queue gets; access, the access rules every service compiles in; scopes; ids
packages/contracts (@g1t/contracts) The same for TypeScript: each service’s API type (IdentityApi, ReposApi, …), typed clients, Result and FailureCode, events, scopes, rate limits, the D1 session helper. TypeScript source, with no build step.
crates/kit (g1t-kit) Plumbing for Rust Workers: the call protocol, D1 sessions and Server-Timing (d1.rs), rate limits that fail open, billing’s platform pause, the snake_case converter, and handlers every service shares for renames, transfers and deletions

Where the two languages must agree exactly, a Rust test reads the TypeScript file and compares: the access table (access.rs), scopes, and the shapes that have shared fixtures in JSON. Keep both in the same pull request.

Ids are TypeIDs: a prefix that says what the thing is, then a UUIDv7 in Crockford base32, such as evt_01jb… for an event or usr_… for a person. They sort by the time they were made, and are monotonic within one process and millisecond (crates/contracts/src/ids.rs).

Crate What it is
crates/rules The rulesets engine, with no I/O: which rules apply to a ref, and whether a push or a merge passes them
crates/scan Detectors for secrets, a git pack reader, lockfiles, OSV, the dependency graph, SPDX and SARIF
crates/secrets Sealer, AES-256-GCM with the row’s id as associated data, and HMAC signatures
crates/blobstore The BlobStore port, with R2 and S3 adapters
crates/actions Workflow syntax, expressions, filters, matrices and cron, with no I/O, used by both the actions service and the sandbox
crates/runner g1t-runner, the binary every sandbox and self-hosted runner runs
crates/g1t The g1t CLI
crates/sshd An SSH front end for git, not deployed yet
  1. Make it under services/<name>, with its wrangler.jsonc: its D1 database if it keeps data, and "workers_dev": false.
  2. Put its types in crates/contracts or packages/contracts, and its client in packages/contracts/src/clients.ts if the site calls it.
  3. Add it to deploy/stack.jsonc with its kind, stage, secrets and self_host, then to the table above and to How a self-hosted g1t runs.
  4. If it reacts to events, give it a g1t-events-<name> queue and a SUBSCRIBER_* binding on events, and add its event types to ROUTES (Events).
  5. Run npm run test:deploy. It says what is missing.

Deploy g1t to Cloudflare covers the manifest’s fields and a unit’s first deploy.