The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were.
| 1 | 1 | # Deploys g1t.sh from main, with g1t's own Actions. What it does is | |
| 2 | − | # scripts/deploy.mjs, the same tool a person runs; docs/DEPLOYING.md is the | |
| 3 | − | # guide. | |
| 2 | + | # scripts/deploy.mjs, the same tool a person runs; the guide is | |
| 3 | + | # docs.g1t.sh/guides/deploy-to-cloudflare/. | |
| 4 | 4 | # | |
| 5 | 5 | # check the deploy manifest is consistent, and the tool's tests pass | |
| 6 | 6 | # plan what changed since each Worker's live commit, and pending migrations | |
| ⋯ | |||
| 27 | 27 | # so with its own Docker Engine, on a larger machine. Optionally, the | |
| 28 | 28 | # secret STATUS_DEPLOY_TOKEN (the status Worker's secret of the same name) | |
| 29 | 29 | # and status.g1t.sh among the same workflow-only domains, so status.g1t.sh | |
| 30 | − | # hears each deploy start and finish. See docs/DEPLOYING.md. | |
| 30 | + | # hears each deploy start and finish. | |
| 31 | 31 | name: Deploy | |
| 32 | 32 | ||
| 33 | 33 | on: | |
| 15 | 15 | # | |
| 16 | 16 | # node scripts/deploy.mjs build-base | |
| 17 | 17 | # | |
| 18 | − | # and commit services/runner/base.json. docs/DEPLOYING.md explains both. | |
| 18 | + | # and commit services/runner/base.json. "The runner's images" in | |
| 19 | + | # docs.g1t.sh/guides/deploy-to-cloudflare/ explains both. | |
| 19 | 20 | # Once a runner is registered, set the repository variable | |
| 20 | 21 | # RUNNER_BASE_SELF_HOSTED to "true" to turn this workflow's job on. | |
| 21 | 22 | name: Runner base image |
| 5 | 5 | # | |
| 6 | 6 | # A release is a tag `runner-v<version>`, where the version is the one in | |
| 7 | 7 | # crates/runner/Cargo.toml; or run it by hand. scripts/runner-release.mjs | |
| 8 | − | # does the work; docs/DEPLOYING.md, "The self-hosted runner", says how to | |
| 9 | − | # make the release key the first time. | |
| 8 | + | # does the work, and its header says how to make the release key the first | |
| 9 | + | # time; CONTRIBUTING.md ("Deploying") says how a release is made. | |
| 10 | 10 | name: Runner release | |
| 11 | 11 | ||
| 12 | 12 | on: |
| 58 | 58 | Pushes to `main` deploy themselves: `.g1t/workflows/deploy.yml` runs | |
| 59 | 59 | `scripts/deploy.mjs`, which deploys only the parts that changed, migrations | |
| 60 | 60 | first. Every deployable part is listed in `deploy/stack.jsonc`; a new service | |
| 61 | − | or app goes there (`npm run test:deploy` says what is missing). See | |
| 62 | − | [docs/DEPLOYING.md](docs/DEPLOYING.md) for the tool, the workflow, rollbacks | |
| 63 | − | and adding a service. | |
| 61 | + | or app goes there, and in the table on | |
| 62 | + | [How a self-hosted g1t runs](https://docs.g1t.sh/guides/self-hosting-architecture/#each-part) | |
| 63 | + | (`npm run test:deploy` says what is missing). | |
| 64 | + | [Deploy g1t to Cloudflare](https://docs.g1t.sh/guides/deploy-to-cloudflare/) | |
| 65 | + | covers the tool, the workflow, rollbacks, adding a unit and first-time | |
| 66 | + | setup. | |
| 67 | + | ||
| 68 | + | - **Migrations run before the code**, so the old code reads the new schema | |
| 69 | + | for a minute or more. Add tables and columns; change what rows mean in | |
| 70 | + | two deploys (code that reads both forms first); never drop what live code | |
| 71 | + | still reads. | |
| 72 | + | - **The self-hosted runner** is released, not deployed: bump `version` in | |
| 73 | + | `crates/runner/Cargo.toml`, merge, and push a `runner-v<version>` tag. | |
| 74 | + | `.g1t/workflows/runner-release.yml` builds, signs and publishes it, and | |
| 75 | + | runners update themselves to it. `scripts/runner-release.mjs` does the | |
| 76 | + | same by hand. | |
| 77 | + | ||
| 78 | + | ## Speed | |
| 79 | + | ||
| 80 | + | Pages are a few rounds of service calls, and each round costs a trip to | |
| 81 | + | the databases, so start calls together and add no rounds. | |
| 82 | + | ||
| 83 | + | - Every page answers with `Server-Timing`: each loader, each service's | |
| 84 | + | calls and their database time. Look at it in DevTools when a page feels | |
| 85 | + | slow. | |
| 86 | + | - `scripts/perf/measure.ps1` times pages from your machine (`-BrowserUA` | |
| 87 | + | for signed-out pages as a browser sees them). A page that only reads | |
| 88 | + | must not set the `g1t_d1` cookie: a new read-only service method goes in | |
| 89 | + | `READS` in `apps/web/app/lib/perf.ts`. | |
| 90 | + | - Targets from the US: signed-out pages under 200 ms to first byte, | |
| 91 | + | signed-in pages under 400 ms, streamed panels within a second. | |
| 92 | + | - Workers run without Smart Placement; `scripts/perf/placement-probe.mjs` | |
| 93 | + | measures a placement before you pin one. | |
| 94 | + | ||
| 95 | + | ## Rate limits | |
| 96 | + | ||
| 97 | + | `RATE_LIMITS` in `packages/contracts/src/rate-limits.ts` is the table of | |
| 98 | + | record, and the [rate limits page](https://docs.g1t.sh/reference/rate-limits/) | |
| 99 | + | is the public one: change both, and the binding in the Worker's | |
| 100 | + | `wrangler.jsonc`, together (`front-door-limits.test.ts` fails when they | |
| 101 | + | disagree). Limits fail open, keys hash anything secret, and each Worker | |
| 102 | + | takes its namespace ids from its own block of a hundred (41xx packages, | |
| 103 | + | 42xx web, 43xx repos, 44xx api, 45xx og, 46xx status). | |
| 104 | + | ||
| 105 | + | ## Operating g1t.sh | |
| 106 | + | ||
| 107 | + | For Flagon staff. | |
| 108 | + | ||
| 109 | + | - **Incidents** are declared, updated and resolved in sudo, under | |
| 110 | + | **Platform → Incidents**, and appear on status.g1t.sh within 30 seconds. | |
| 111 | + | Declare as soon as people are affected, and pick the higher severity | |
| 112 | + | when unsure. SEV1 (down for most people, or data at risk) gets an update | |
| 113 | + | at least every 30 minutes and SEV2 (a core part broken for many) at least | |
| 114 | + | hourly; both email subscribers and need a blameless postmortem within | |
| 115 | + | five working days. | |
| 116 | + | - **An Artifacts outage:** `scripts/ops/artifacts-namespaces.mjs` shows a | |
| 117 | + | failing namespace. After 15 minutes, serve it read-only from the fallback | |
| 118 | + | git store (`scripts/ops/restore-to-gitstore.mjs restore`, then the repos | |
| 119 | + | Worker's secret `GIT_FALLBACK_NAMESPACES`) and open an incident. To | |
| 120 | + | switch back, `reconcile` anything the fallback took, then delete the | |
| 121 | + | secret. The script's header has every step. | |
| 122 | + | - **Costs and margin**, model prices, credits and the platform pause are | |
| 123 | + | in sudo, under **Costs & margin**. The code is in `services/billing/src` | |
| 124 | + | (`costs.rs`, `margin.rs`, `pricing.rs`, `budget.rs`, `platform.rs`). |
| 19 | 19 | unfinished. | |
| 20 | 20 | - **Coming**: planned, not built. | |
| 21 | 21 | ||
| 22 | − | The plan behind this is [docs/PLAN.md](docs/PLAN.md). | |
| 23 | − | ||
| 24 | 22 | ## Where things are | |
| 25 | 23 | ||
| 26 | 24 | - Site: <https://g1t.sh> | |
| 27 | 25 | - Docs: <https://docs.g1t.sh> | |
| 28 | 26 | - API: <https://api.g1t.sh> · MCP: <https://mcp.g1t.sh> | |
| 29 | − | - Plan: [docs/PLAN.md](docs/PLAN.md) | |
| 30 | 27 | - Limits you can hit today: [docs.g1t.sh/about/limitations](https://docs.g1t.sh/about/limitations/) | |
| 31 | 28 | ||
| 32 | 29 | ## What's in it | |
| ⋯ | |||
| 171 | 168 | http://localhost:8789; packages and container images are kept in the | |
| 172 | 169 | bundled S3-compatible store, RustFS. Agents, deployments and context search | |
| 173 | 170 | are off in this version. | |
| 174 | − | [docs/SELF_HOSTING.md](docs/SELF_HOSTING.md) says what works and what is next. | |
| 171 | + | [Run g1t yourself](https://docs.g1t.sh/guides/self-hosting/) says what works, | |
| 172 | + | and [how a self-hosted g1t runs](https://docs.g1t.sh/guides/self-hosting-architecture/) | |
| 173 | + | what runs inside it. | |
| 175 | 174 | ||
| 176 | 175 | ### On Cloudflare | |
| 177 | 176 | ||
| ⋯ | |||
| 208 | 207 | ||
| 209 | 208 | Create the first account by registering on your site, or with | |
| 210 | 209 | `node services/identity/scripts/create-user.mjs <username>`. | |
| 210 | + | [Deploy g1t to Cloudflare](https://docs.g1t.sh/guides/deploy-to-cloudflare/) | |
| 211 | + | covers the deploy tool, the workflow and first-time setup in full. | |
| 211 | 212 | ||
| 212 | 213 | ## License | |
| 213 | 214 | ||
| 19 | 19 | form_urlencoded = "1" | |
| 20 | 20 | ||
| 21 | 21 | # wasm-opt at -O1: about the same gzipped size as -O in a tenth of the | |
| 22 | − | # time (docs/DEPLOYING.md, "Build speed"). | |
| 22 | + | # time. | |
| 23 | 23 | [package.metadata.wasm-pack.profile.release] | |
| 24 | 24 | wasm-opt = ["-O1"] |
| 1 | 1 | //! Artifacts over REST and MCP: a workspace's docs, slides, designs and | |
| 2 | − | //! dashboards (Artifacts mode, docs/ARTIFACTS_MODE.md), at | |
| 2 | + | //! dashboards (Artifacts mode), at | |
| 3 | 3 | //! `/workspaces/{workspace}/artifacts` and as the `artifact` MCP tool. | |
| 4 | 4 | //! Code calls them folios; people, URLs, the tool and the scopes say | |
| 5 | 5 | //! "artifact". Workflow runs' artifacts are something else (artifacts.rs). |
| 16 | 16 | //! private key in PEM (PKCS#8 or PKCS#1). `ACTIONS_OIDC_KEY_PREVIOUS`, | |
| 17 | 17 | //! while it is set, is published too, so tokens it signed still verify | |
| 18 | 18 | //! while the new key takes over. Each key's `kid` is its RFC 7638 | |
| 19 | − | //! thumbprint. docs/DEPLOYING.md says how to make and rotate them. | |
| 19 | + | //! thumbprint. docs.g1t.sh/guides/deploy-to-cloudflare/ ("OIDC tokens | |
| 20 | + | //! for workflow jobs") says how to make and rotate them. | |
| 20 | 21 | ||
| 21 | 22 | use base64::Engine; | |
| 22 | 23 | use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}; |
| 171 | 171 | { label: 'Audit log', slug: 'guides/audit-log' }, | |
| 172 | 172 | { label: 'Usage and billing', slug: 'guides/usage-and-billing' }, | |
| 173 | 173 | { label: 'Git', slug: 'guides/git' }, | |
| 174 | − | { label: 'Run g1t yourself', slug: 'guides/self-hosting' }, | |
| 175 | 174 | ], | |
| 176 | 175 | }, | |
| 177 | 176 | { | |
| 177 | + | label: 'Run g1t yourself', | |
| 178 | + | items: [ | |
| 179 | + | { label: 'With Docker Compose', slug: 'guides/self-hosting' }, | |
| 180 | + | { label: 'How a self-hosted g1t runs', slug: 'guides/self-hosting-architecture' }, | |
| 181 | + | { label: 'Deploy to Cloudflare', slug: 'guides/deploy-to-cloudflare' }, | |
| 182 | + | ], | |
| 183 | + | }, | |
| 184 | + | { | |
| 178 | 185 | label: 'Reference', | |
| 179 | 186 | items: [ | |
| 180 | 187 | { label: 'API overview', slug: 'reference/api' }, |
| 106 | 106 | fast and still strictly correct. | |
| 107 | 107 | ||
| 108 | 108 | <Aside type="note" title="Where this stands today"> | |
| 109 | − | Rules 1, 2, 3, 5, 6 and 7 hold today. Replies look up code, issues, pull | |
| 110 | − | requests and messages only within the conversation's audience, a withheld | |
| 111 | − | read says nothing about what was withheld, and every lookup is recorded. | |
| 112 | − | Public channels and conversations of more than 50 people get no code reads | |
| 113 | − | at all for now, the strictest reading of rule 2. Memory with its source | |
| 114 | − | (rule 4) comes with agent memory. <Soon /> | |
| 109 | + | Replies and sessions look up code, issues, pull requests, messages and | |
| 110 | + | artifacts only within the conversation's audience, a withheld read says | |
| 111 | + | nothing about what was withheld, and every lookup is recorded. Public | |
| 112 | + | channels and conversations of more than 50 people get no code reads at | |
| 113 | + | all for now, the strictest reading of rule 2. Every fact an agent keeps | |
| 114 | + | records its source and is recalled only where that source allows; see | |
| 115 | + | [agent memory](/guides/agent-memory/#scopes). Files, and the rules for | |
| 116 | + | customer-data files, are <Soon /> | |
| 115 | 117 | </Aside> | |
| 116 | 118 | ||
| 117 | 119 | ## A support lead asks for a change |
| 33 | 33 | | **An issue is opened** | By a person or an agent. | | |
| 34 | 34 | | **A deploy fails** | A production or preview deploy. | | |
| 35 | 35 | ||
| 36 | − | Each run is one session about the one thing that happened. Name the | |
| 36 | + | Each run is one session about the one thing that happened, and a routine | |
| 37 | + | runs at most 20 times an hour on events; events past that are skipped, so | |
| 38 | + | a burst can't run up the agent's budget. Name the | |
| 37 | 39 | **repositories** to follow, such as `acme/web, acme/api` (up to 20), or | |
| 38 | 40 | leave it empty for every repository the routine's sponsor can read. | |
| 39 | 41 |
| 153 | 153 | ||
| 154 | 154 | ## Issues for whoever asked | |
| 155 | 155 | ||
| 156 | − | When a session finds work to do in code, it files issues in a repository | |
| 157 | − | the asker can read, on their behalf, and lists them under **What it | |
| 158 | − | produced**. Someone who can't change code still gets the issue filed; | |
| 159 | − | changes come from people, and agents, who may make them. | |
| 156 | + | When a session finds work to do in code, it drafts an issue for a | |
| 157 | + | repository the asker can read. The draft is posted in the conversation as | |
| 158 | + | a card with **File issue** and **Discard**: whoever presses **File issue** | |
| 159 | + | files it as themselves, if they can read the repository. The agent never | |
| 160 | + | files it on its own. See [cards you can act on](/guides/chat/#cards-you-can-act-on). | |
| 160 | 161 | ||
| 161 | 162 | ## Next | |
| 162 | 163 |
| 49 | 49 | | --- | --- | | |
| 50 | 50 | | **In every workspace** | On every surface: Chat, issues and pull requests, Notifications and MCP. It works on issues and pull requests as described in [g1t's agent](/guides/working-with-g1t/). | | |
| 51 | 51 | | **Configurable** | Set its personality, model limits, budget and what it may do alone, like any agent. Its job is fixed, and you can add instructions to it. | | |
| 52 | − | | **Knows the team** <Soon /> | Every colleague's role, what they are working on and their budget, and which teams own what. | | |
| 52 | + | | **Knows the team** | Every agent you hire: its handle, title, team, responsibilities, whether it is idle, working, paused or out of budget, and its spend this month against its monthly cap. | | |
| 53 | 53 | | **Delegates** | When a specialist's role fits, it [hands the work off](#hand-off) with a brief: here, if the specialist is in the conversation, or in a group message with you and them. At most two hand-offs per message. | | |
| 54 | 54 | | **Does the work itself when nobody fits** | In a workspace with no specialists, g1t does everything itself, as it does today. | | |
| 55 | − | | **Reports** <Soon /> | A daily or weekly summary of what the team's agents did, and answers to "what's everyone working on?" | | |
| 55 | + | | **Reports** | Answers "what's everyone working on?" from what it knows of the team. A daily or weekly summary of what the team's agents did is <Soon /> | | |
| 56 | 56 | ||
| 57 | 57 | `g1t` is never another agent's handle. | |
| 58 | 58 | ||
| ⋯ | |||
| 81 | 81 | ||
| 82 | 82 | </Steps> | |
| 83 | 83 | ||
| 84 | − | The agent appears on the Agents page as **Idle**. Open a DM with it from | |
| 85 | − | Chat's **New message**, or invite it to the channels its team works in. | |
| 84 | + | The agent appears on the Agents page as **Idle**, and a direct message | |
| 85 | + | between you and it opens in Chat, where it says hello in its own voice: who | |
| 86 | + | it is, what it will do for the team, and a thing or two to ask it first. | |
| 87 | + | That hello is a reply like any other, charged to its budget; when the | |
| 88 | + | workspace has no model it can use, it sends a short fixed hello instead. | |
| 89 | + | Invite it to the channels its team works in. | |
| 86 | 90 | ||
| 87 | 91 | ### Example: hire Margo from the QA template | |
| 88 | 92 | ||
| ⋯ | |||
| 140 | 144 | | Responsibilities | What it answers for, one per line. | 2 to 8, each up to 160 characters, or none yet. | | |
| 141 | 145 | | Job | Its instructions: how it works and what good looks like. | Up to 8,000 characters. | | |
| 142 | 146 | | Personality | A preset, plus free text that refines the voice. | Free text up to 1,000 characters. | | |
| 143 | − | | Subagents | Help it keeps for its own work. See [subagents](#subagents). | Run soon. | | |
| 147 | + | | Subagents | Help it keeps for its own work. See [subagents](#subagents). | Up to 8. They run inside its sessions. | | |
| 148 | + | | Required reading | Spaces in [Artifacts](/guides/artifacts/) it checks first, every time it answers or works. It still reads only what the person it works for, and everyone reading its answer, can open. | Up to 10 spaces. | | |
| 144 | 149 | | Models | A floor, a ceiling and the providers it may use. | See [model routing](#model-routing). | | |
| 145 | 150 | | Budget | A monthly cap, a daily cap and a cap per session. | Each optional, up to $100,000. | | |
| 146 | 151 | | What it may do alone | Pull requests, merging, production deploys, doc edits. | See [what it may do alone](#what-it-may-do-alone). | | |
| ⋯ | |||
| 374 | 379 | While it writes, the agent shows as typing. Its answer is charged to its | |
| 375 | 380 | own budget; see [what an agent costs](#what-an-agent-costs). | |
| 376 | 381 | ||
| 377 | − | <Aside type="note" title="What a reply can see today"> | |
| 382 | + | <Aside type="note" title="What a reply can see"> | |
| 378 | 383 | A reply reads the conversation it is in: the thread, or the latest 30 | |
| 379 | 384 | messages of the channel or DM. It knows who asked and whether they can | |
| 380 | − | change code. Looking things up in code, issues, checks and docs while it | |
| 381 | − | answers is <Soon /> | |
| 385 | + | change code, and it is given the facts it [remembers](/guides/agent-memory/) | |
| 386 | + | for that place and the passages of [artifacts](/guides/artifacts/#agents-and-artifacts) | |
| 387 | + | closest to what was asked. While it answers it can look up code, issues, | |
| 388 | + | pull requests with their checks, earlier messages and artifacts, up to 8 | |
| 389 | + | lookups a reply, only within what everyone in the conversation can see. | |
| 390 | + | In public channels and conversations of more than 50 people it reads no | |
| 391 | + | code. See [what agents can do for whom](/guides/agent-access/). | |
| 382 | 392 | </Aside> | |
| 383 | 393 | ||
| 384 | 394 | ## Model routing | |
| 13 | 13 | for can read. | |
| 14 | 14 | ||
| 15 | 15 | Open **Artifacts** in the dock on the left, or go to | |
| 16 | − | `g1t.sh/<workspace>/-/artifacts`. Artifacts is open to every member of a | |
| 17 | − | workspace, on every plan. | |
| 16 | + | `g1t.sh/<workspace>/-/artifacts`. On a phone, it's under **More** in the bar | |
| 17 | + | at the bottom. Artifacts is open to every member of a workspace, on every | |
| 18 | + | plan. | |
| 18 | 19 | ||
| 19 | 20 | ## How it fits together | |
| 20 | 21 | ||
| ⋯ | |||
| 42 | 43 | - **Filters** narrow the list by kind, space, owner and project. On a phone | |
| 43 | 44 | they're under **Filters**. | |
| 44 | 45 | - The list and grid buttons switch between rows and cards with a preview. | |
| 46 | + | On a phone, Home shows the list, and the sidebar's sections follow it. | |
| 45 | 47 | - Each row shows the kind, who can open it (a lock when only you can, a globe | |
| 46 | 48 | when the workspace can, a link when anyone in it with the link can, or how | |
| 47 | 49 | many people it's shared with), where it is, and when it was last edited and | |
| ⋯ | |||
| 201 | 203 | pushed to the default branch, the doc is marked **possibly out of date**: a | |
| 202 | 204 | banner on it, a dot in the sidebar, and **Possibly out of date** in the | |
| 203 | 205 | sidebar listing every such doc you can open. Its owner is told. Read what | |
| 204 | − | changed, update it (or ask an agent to), then press **It's current**. | |
| 206 | + | changed, update it, then press **It's current**. Or ask an agent to keep the | |
| 207 | + | docs current: it finds the docs marked out of date, and its update clears | |
| 208 | + | the mark when it's applied or accepted. | |
| 205 | 209 | ||
| 206 | 210 | ## Agents and artifacts | |
| 207 | 211 | ||
| 85 | 85 | ||
| 86 | 86 | <Aside type="note" title="What a reply can see today"> | |
| 87 | 87 | A reply reads the conversation it is in, and looks things up while it | |
| 88 | − | answers: code, issues, pull requests and earlier messages. It only reads | |
| 88 | + | answers: code, issues, pull requests and their checks, earlier messages | |
| 89 | + | and [artifacts](/guides/artifacts/#agents-and-artifacts). It only reads | |
| 89 | 90 | what everyone in the conversation can see; see | |
| 90 | 91 | [what agents can do for whom](/guides/agent-access/). In public channels | |
| 91 | − | and conversations of more than 50 people, agents don't read code yet, so | |
| 92 | − | ask in a DM or a private channel. Docs and checks come later. | |
| 92 | + | and conversations of more than 50 people, agents don't read code, so ask | |
| 93 | + | in a DM or a private channel. | |
| 93 | 94 | </Aside> | |
| 94 | 95 | ||
| 95 | 96 | ## Channels | |
| ⋯ | |||
| 241 | 242 | | A person | It is counted as a mention in their sidebar. | | |
| 242 | 243 | | An agent that is in the channel | The agent answers in the thread. | | |
| 243 | 244 | | An agent that is not in the channel | Nothing. Invite the agent first. | | |
| 244 | − | | `@g1t` | Nothing in Chat yet. `@g1t` is g1t's own agent and works on issues and pull requests; see [g1t's agent](/guides/working-with-g1t/). | | |
| 245 | + | | `@g1t` | In a channel, g1t joins the first time it is mentioned, with no invite, and answers in the thread. A DM never gains a member, so to talk to g1t alone, open a DM with it. See [g1t, the orchestrator](/guides/agents/#g1t-the-orchestrator). | | |
| 245 | 246 | ||
| 246 | 247 | An address such as `me@example.com` is never read as a mention. | |
| 247 | 248 | ||
| ⋯ | |||
| 615 | 616 | your notifications still update, and everyone sees your amber dot. **Resume | |
| 616 | 617 | notifications** ends it early. | |
| 617 | 618 | ||
| 619 | + | At most three pop-ups show at once. Each stays six seconds (one with a | |
| 620 | + | card's buttons, twelve), and stays while your pointer or keyboard is on | |
| 621 | + | it. Nothing pops up for the conversation you have open. | |
| 622 | + | ||
| 623 | + | g1t never asks to show browser notifications when a page loads. After | |
| 624 | + | your first pop-up for a DM or a mention, it offers once: **Get notified | |
| 625 | + | when someone messages you**. If you say no, it doesn't ask again; turn | |
| 626 | + | them on later under **Settings → Notifications**. | |
| 627 | + | ||
| 618 | 628 | [Notifications](/guides/notifications/) are separate: they keep every item until you | |
| 619 | 629 | deal with it, whatever these settings say. | |
| 620 | 630 | ||
| 12 | 12 | https://g1t.sh/-/composer/<workspace>/ | |
| 13 | 13 | ``` | |
| 14 | 14 | ||
| 15 | + | Packages from Packagist still install from Packagist: add the workspace's | |
| 16 | + | repository beside it, as [below](#install). | |
| 17 | + | ||
| 15 | 18 | ## Make a repository a package | |
| 16 | 19 | ||
| 17 | 20 | Give the repository a `composer.json` at its root with a `name`: |
| 1 | + | --- | |
| 2 | + | title: Deploy g1t to Cloudflare | |
| 3 | + | description: How g1t.sh is deployed to Cloudflare, and how to deploy the same code to an account of your own. | |
| 4 | + | --- | |
| 5 | + | ||
| 6 | + | g1t.sh runs on Cloudflare, and the tool that deploys it is in the | |
| 7 | + | repository. You can deploy the same code to a Cloudflare account of your | |
| 8 | + | own. To run g1t on one machine without Cloudflare, see | |
| 9 | + | [Run g1t yourself](/guides/self-hosting/) instead. | |
| 10 | + | ||
| 11 | + | You need a Cloudflare account on the Workers Paid plan (Cloudflare | |
| 12 | + | Artifacts, where repositories are kept, requires it), Node 22.22 or newer, | |
| 13 | + | Rust with the `wasm32-unknown-unknown` target, and Docker to build the | |
| 14 | + | runner's image. | |
| 15 | + | ||
| 16 | + | Every `wrangler.jsonc` names g1t.sh's account, domains and resource ids. | |
| 17 | + | Before a first deploy to another account, change those to your own (see | |
| 18 | + | [A first deploy to a new account](#a-first-deploy-to-a-new-account)). | |
| 19 | + | ||
| 20 | + | ## The pieces | |
| 21 | + | ||
| 22 | + | | Piece | Where | | |
| 23 | + | | --- | --- | | |
| 24 | + | | The manifest: every deployable part | `deploy/stack.jsonc` | | |
| 25 | + | | The tool | `scripts/deploy.mjs`, with its library and tests in `scripts/deploy/` | | |
| 26 | + | | Rust Worker builds | `scripts/build-rust-worker.mjs` | | |
| 27 | + | | The runner's images | `services/runner/base/Dockerfile`, `services/runner/Dockerfile`, `services/runner/base.json` | | |
| 28 | + | | The workflows | `.g1t/workflows/deploy.yml`, `.g1t/workflows/runner-base.yml` | | |
| 29 | + | ||
| 30 | + | ## The manifest | |
| 31 | + | ||
| 32 | + | `deploy/stack.jsonc` lists every unit: each folder with a `wrangler.jsonc`. | |
| 33 | + | ||
| 34 | + | | Field | What it is | | |
| 35 | + | | --- | --- | | |
| 36 | + | | `path` | The unit's folder | | |
| 37 | + | | `kind` | `rust-worker`, `ts-worker`, `react-router` or `astro` | | |
| 38 | + | | `worker` | The Worker's name. It must match its `wrangler.jsonc`. | | |
| 39 | + | | `d1` | `{ database, migrations }`, when it has a database | | |
| 40 | + | | `stage` | `core`, `edge` or `front` (below) | | |
| 41 | + | | `secrets` | The Wrangler secrets it needs, by name | | |
| 42 | + | | `setup` | One-time steps a first deploy needs that no config can say | | |
| 43 | + | | `inputs` | Files outside its folder it is built from that no package names | | |
| 44 | + | | `image` | A Containers image, for the runner | | |
| 45 | + | | `self_host` | `run`, `off`, `separate` or `none`: what a [self-hosted installation](/guides/self-hosting-architecture/) does with it | | |
| 46 | + | ||
| 47 | + | What a unit is built from is read, never listed by hand: Rust path | |
| 48 | + | dependencies from `cargo metadata`, workspace packages from each | |
| 49 | + | `package.json`. A lockfile change counts for a unit only when a package | |
| 50 | + | it uses changed. `node scripts/deploy.mjs manifest` prints the result. | |
| 51 | + | ||
| 52 | + | Units deploy in stages. A stage starts only when the one before it | |
| 53 | + | succeeded, and units inside a stage deploy in parallel: | |
| 54 | + | ||
| 55 | + | | Stage | What | | |
| 56 | + | | --- | --- | | |
| 57 | + | | `migrations` | Every pending D1 migration, before any code | | |
| 58 | + | | `core` | The services, reached through bindings | | |
| 59 | + | | `edge` | Public endpoints other than the site: `api`, `models`, `pages`, `status` | | |
| 60 | + | | `front` | The site, sudo and the docs | | |
| 61 | + | ||
| 62 | + | A unit binds only to units in its own stage or an earlier one, so new code | |
| 63 | + | never calls a service that has not shipped. `npm run test:deploy` checks | |
| 64 | + | this, and that every `wrangler.jsonc` has a unit whose names match it. | |
| 65 | + | ||
| 66 | + | ## The tool | |
| 67 | + | ||
| 68 | + | ```sh | |
| 69 | + | node scripts/deploy.mjs plan # what would deploy, and why (read-only) | |
| 70 | + | node scripts/deploy.mjs deploy # migrations, then every changed unit | |
| 71 | + | node scripts/deploy.mjs deploy --only web,api # just these, if they changed | |
| 72 | + | node scripts/deploy.mjs deploy --only web --force # just this, changed or not | |
| 73 | + | node scripts/deploy.mjs deploy --all # everything | |
| 74 | + | node scripts/deploy.mjs build --only events # build as a deploy would; upload nothing | |
| 75 | + | node scripts/deploy.mjs migrate # pending migrations only | |
| 76 | + | node scripts/deploy.mjs manifest --check # the manifest's problems | |
| 77 | + | node scripts/deploy.mjs doctor # secrets each unit lacks | |
| 78 | + | node scripts/deploy.mjs build-base # build and push the runner's base image (Docker) | |
| 79 | + | node scripts/deploy.mjs image # build and push the runner's image (Docker) | |
| 80 | + | ``` | |
| 81 | + | ||
| 82 | + | | Flag | What it does | | |
| 83 | + | | --- | --- | | |
| 84 | + | | `--only a,b`, `--skip a,b` | Units by short name, folder or Worker name | | |
| 85 | + | | `--all` | Every unit, changed or not | | |
| 86 | + | | `--force` | The selected units even if unchanged | | |
| 87 | + | | `--concurrency N` | Units at once inside a stage, and migrations at once (default 4) | | |
| 88 | + | | `--stage core` | One stage only | | |
| 89 | + | | `--no-migrations` | Skip the migrations step | | |
| 90 | + | | `--allow-dirty` | Deploy with uncommitted changes. The version records no commit, so the next plan deploys it again. | | |
| 91 | + | | `--rebuild-image`, `--rebuild-base` | Build the runner's image, or its base, even if one exists | | |
| 92 | + | | `--rollback` | Allow deploying a commit older than the one live | | |
| 93 | + | | `--since REV` | Treat Workers with no recorded commit as running `REV` | | |
| 94 | + | ||
| 95 | + | **Only what changed deploys.** Each deploy records its commit on the | |
| 96 | + | Worker's version (`wrangler deploy --message "g1t-deploy <sha> ..." --tag | |
| 97 | + | g1t-<sha>`). `plan` reads the live version of each Worker, diffs from its | |
| 98 | + | commit to `HEAD`, and deploys the units the change touches. A change only | |
| 99 | + | to a crate's tests deploys nothing. A version deployed any other way has | |
| 100 | + | no commit, so the unit is deployed again. | |
| 101 | + | ||
| 102 | + | With `CLOUDFLARE_API_TOKEN` (in CI) or `CLOUDFLARE_DEPLOY_TOKEN` (on your | |
| 103 | + | machine) and `CLOUDFLARE_ACCOUNT_ID`, `plan` reads Cloudflare's API itself | |
| 104 | + | and takes a few seconds. With only `wrangler login`, it asks Wrangler and | |
| 105 | + | takes minutes. Each unit's output is kept in `$TMPDIR/g1t-deploy/<unit>.log`. | |
| 106 | + | ||
| 107 | + | ### Migrations run while the old code is live | |
| 108 | + | ||
| 109 | + | Migrations apply before any code, so for a minute or more the old code | |
| 110 | + | reads the new schema. Keep it working: | |
| 111 | + | ||
| 112 | + | - **Add, never change meaning.** New tables and columns with defaults are | |
| 113 | + | safe. | |
| 114 | + | - **Change meaning in two deploys.** First ship code that reads both forms; | |
| 115 | + | then the migration that rewrites the rows. | |
| 116 | + | - **Never drop or rename** a column or table in the same deploy that stops | |
| 117 | + | reading it. | |
| 118 | + | ||
| 119 | + | Migrations never run backwards: undoing one is a new migration. | |
| 120 | + | ||
| 121 | + | ## The workflow | |
| 122 | + | ||
| 123 | + | `.g1t/workflows/deploy.yml` runs the tool on every push to `main`, and by | |
| 124 | + | hand (**Actions → Deploy → Run workflow**) with `units`, `all` and | |
| 125 | + | `dry_run`. | |
| 126 | + | ||
| 127 | + | | Job | Does | | |
| 128 | + | | --- | --- | | |
| 129 | + | | `check` | `manifest --check` and `npm run test:deploy` | | |
| 130 | + | | `plan` | The plan, shown in the run's summary | | |
| 131 | + | | `migrate` | Pending migrations; skipped when there are none | | |
| 132 | + | | `core`, `edge`, `front` | Each stage's units, one job per build group | | |
| 133 | + | | `smoke` | Loads the landing page, sign-in, sign-up and pricing | | |
| 134 | + | ||
| 135 | + | Deploys run one at a time; a second push waits. Rust units build on | |
| 136 | + | `g1t-4core` machines, at most four to a job, with Cargo's target and | |
| 137 | + | sccache kept in the Actions cache. | |
| 138 | + | ||
| 139 | + | The deploy jobs use `environment: production`, so only they can read the | |
| 140 | + | Cloudflare token. They reach Cloudflare through the project's | |
| 141 | + | [workflow-only domains](/guides/guardrails/), under **Settings → | |
| 142 | + | Guardrails**: | |
| 143 | + | ||
| 144 | + | ```text | |
| 145 | + | api.cloudflare.com | deploy.yml | production | |
| 146 | + | registry.cloudflare.com | deploy.yml, runner-base.yml | production | |
| 147 | + | ``` | |
| 148 | + | ||
| 149 | + | Every deploy shows on the repository's Deployments page. A deploy run by | |
| 150 | + | hand reports one too when `G1T_DEPLOY_TOKEN` holds a g1t token with | |
| 151 | + | `deployments:write`. | |
| 152 | + | ||
| 153 | + | ### The Cloudflare API token | |
| 154 | + | ||
| 155 | + | Make a custom token at **dash.cloudflare.com → My Profile → API Tokens**, | |
| 156 | + | restricted to your account and zones: | |
| 157 | + | ||
| 158 | + | | Scope | Permission | | |
| 159 | + | | --- | --- | | |
| 160 | + | | Account | Workers Scripts: Edit | | |
| 161 | + | | Account | D1: Edit | | |
| 162 | + | | Account | Queues: Edit | | |
| 163 | + | | Account | Workers R2 Storage: Read | | |
| 164 | + | | Account | Account Settings: Read | | |
| 165 | + | | Account | Containers: Edit | | |
| 166 | + | | Zone | Workers Routes: Edit | | |
| 167 | + | | Zone | DNS: Edit | | |
| 168 | + | | Zone | Zone: Read | | |
| 169 | + | ||
| 170 | + | Store it on the repository under **Settings → Secrets and variables** as | |
| 171 | + | the secret `CLOUDFLARE_API_TOKEN`, for workflows in the `production` | |
| 172 | + | environment, and your account id as the variable `CLOUDFLARE_ACCOUNT_ID`. | |
| 173 | + | A missing permission fails with `Authentication error [code: 10000]` and | |
| 174 | + | the route it was refused on. | |
| 175 | + | ||
| 176 | + | ## A first deploy to a new account | |
| 177 | + | ||
| 178 | + | What the configs refer to must exist first. | |
| 179 | + | `node scripts/deploy.mjs manifest --json` lists, for each unit, its | |
| 180 | + | queues, KV namespaces, R2 buckets, Vectorize indexes and dispatch | |
| 181 | + | namespaces; its `setup` and `secrets` say the rest. | |
| 182 | + | ||
| 183 | + | 1. Set `account_id`, routes and custom domains in each `wrangler.jsonc` to | |
| 184 | + | your own. | |
| 185 | + | 2. D1: `npx wrangler d1 create <database>` for each, and put its id in the | |
| 186 | + | unit's `wrangler.jsonc`. | |
| 187 | + | 3. KV: `npx wrangler kv namespace create <name>` for each name under | |
| 188 | + | `resources.kv` in the manifest, and put the ids in the configs. | |
| 189 | + | 4. Queues: `npx wrangler queues create <queue>` for each queue in the | |
| 190 | + | manifest, including the dead-letter queue `g1t-events-dlq`. | |
| 191 | + | 5. R2: create `g1t-screenshots`, `g1t-actions-cache` and `g1t-git-packs`, | |
| 192 | + | with lifecycle rules: | |
| 193 | + | ||
| 194 | + | ```sh | |
| 195 | + | npx wrangler r2 bucket lifecycle add g1t-actions-cache expire-cache c/ --expire-days 30 --abort-multipart-days 1 | |
| 196 | + | npx wrangler r2 bucket lifecycle add g1t-actions-cache expire-artifacts a/ --expire-days 91 --abort-multipart-days 1 | |
| 197 | + | npx wrangler r2 bucket lifecycle add g1t-git-packs expire-packs packs/ --expire-days 7 --abort-multipart-days 1 | |
| 198 | + | ``` | |
| 199 | + | ||
| 200 | + | 6. The runner's base image: `node scripts/deploy.mjs build-base`, then | |
| 201 | + | commit `services/runner/base.json`. | |
| 202 | + | 7. Everything else each unit's `setup` names: Vectorize, the dispatch | |
| 203 | + | namespace, DNS, Access, Email Sending and Artifacts. | |
| 204 | + | 8. Secrets: `npx wrangler secret put <NAME>` in the unit's folder. | |
| 205 | + | `node scripts/deploy.mjs doctor` lists what is missing. | |
| 206 | + | 9. For [Deployments](/guides/deployments/), which need the Workers for | |
| 207 | + | Platforms add-on and a zone for apps: `scripts/setup-deployments.sh`. | |
| 208 | + | 10. `node scripts/deploy.mjs deploy --all`. A Worker bound to a service | |
| 209 | + | that does not exist yet may be refused: deploy that service first with | |
| 210 | + | `--only`. | |
| 211 | + | 11. Register on your site to make the first account, or run | |
| 212 | + | `node services/identity/scripts/create-user.mjs <username>`. | |
| 213 | + | ||
| 214 | + | ### Adding a unit | |
| 215 | + | ||
| 216 | + | 1. Make its folder with a `wrangler.jsonc`. A Rust Worker is a member of | |
| 217 | + | the root `Cargo.toml`, with `node ../../scripts/build-rust-worker.mjs` | |
| 218 | + | as its build command and `wasm-opt = ["-O1"]`; a TypeScript one is an | |
| 219 | + | npm workspace. | |
| 220 | + | 2. Add it to `deploy/stack.jsonc` in the earliest stage after everything | |
| 221 | + | it binds to, with its secrets, setup and `self_host`. | |
| 222 | + | 3. Add a row for it to the table in | |
| 223 | + | [How a self-hosted g1t runs](/guides/self-hosting-architecture/#each-part). | |
| 224 | + | 4. `npm run test:deploy` and `node scripts/deploy.mjs manifest --check` | |
| 225 | + | say what is missing. | |
| 226 | + | ||
| 227 | + | ## The runner's images | |
| 228 | + | ||
| 229 | + | `services/runner` runs every sandbox (agents, checks, the merge queue, | |
| 230 | + | workflow jobs and deploy builds) from one Containers image in two parts: | |
| 231 | + | ||
| 232 | + | | Image | Built from | Rebuilt | | |
| 233 | + | | --- | --- | --- | | |
| 234 | + | | The base | `services/runner/base/Dockerfile`: Debian, Node, Python, Docker, Go, Rust, Java, .NET, Ruby and the Claude Code CLI | When its folder changes, weekly by `runner-base.yml`, or by hand with `build-base` | | |
| 235 | + | | The runner | `services/runner/Dockerfile`: the base plus the `g1t-runner` binary | When the binary or the base changes | | |
| 236 | + | ||
| 237 | + | `services/runner/base.json` records the base that was pushed; | |
| 238 | + | `npm run test:deploy` fails while it and the base's folder disagree. The | |
| 239 | + | runner's image is tagged with a hash of what it is built from, so a deploy | |
| 240 | + | builds it only when the registry does not already have it. When nothing | |
| 241 | + | the image is built from changed, the deploy leaves running sandboxes | |
| 242 | + | alone. | |
| 243 | + | ||
| 244 | + | Jobs choose larger machines with `runs-on: g1t-2core` or `g1t-4core`. Your | |
| 245 | + | account's Containers limits must allow `standard-4`. | |
| 246 | + | ||
| 247 | + | Set the runner Worker's `DOCKER` variable to `off` to give workflow jobs no | |
| 248 | + | Docker Engine. | |
| 249 | + | ||
| 250 | + | `CLOUDFLARE_API_TOKEN=<token> node scripts/ops/runner-errors.mjs` sorts the | |
| 251 | + | sandboxes' errors over the last 7 days into expected ones (a deploy | |
| 252 | + | resetting them, no free capacity, a container that stopped) and ones to | |
| 253 | + | read. | |
| 254 | + | ||
| 255 | + | ## Rolling back | |
| 256 | + | ||
| 257 | + | - **One unit, at once:** `npx wrangler rollback` in its folder. Revert the | |
| 258 | + | commit on `main` too, or the next push brings it back. | |
| 259 | + | - **To a commit:** check it out and run | |
| 260 | + | `node scripts/deploy.mjs deploy --only <units> --rollback`. Without | |
| 261 | + | `--rollback` the tool refuses a unit whose live commit is newer. | |
| 262 | + | - **The runner's image:** redeploy the older commit with | |
| 263 | + | `--only runner --rollback`; its image is still in the registry. | |
| 264 | + | ||
| 265 | + | ## Other one-time setup | |
| 266 | + | ||
| 267 | + | ### The dead-letter queue | |
| 268 | + | ||
| 269 | + | Every queue consumer retries a message a fixed number of times, then puts | |
| 270 | + | it on `g1t-events-dlq`. Nothing consumes that queue: read it with | |
| 271 | + | `npx wrangler queues info g1t-events-dlq`, fix the cause, and replay by | |
| 272 | + | hand. | |
| 273 | + | ||
| 274 | + | ### OIDC tokens for workflow jobs | |
| 275 | + | ||
| 276 | + | The API issues workflow jobs' [OIDC tokens](/guides/actions/), signed with | |
| 277 | + | an RSA key in its secret `ACTIONS_OIDC_KEY`. Without it, jobs get no | |
| 278 | + | tokens. | |
| 279 | + | ||
| 280 | + | ```sh | |
| 281 | + | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out oidc.pem | |
| 282 | + | cd apps/api && npx wrangler secret put ACTIONS_OIDC_KEY < ../../oidc.pem | |
| 283 | + | rm ../../oidc.pem | |
| 284 | + | ``` | |
| 285 | + | ||
| 286 | + | To rotate it, store the current key as `ACTIONS_OIDC_KEY_PREVIOUS`, store | |
| 287 | + | a new one as `ACTIONS_OIDC_KEY`, and delete the previous one a day later. | |
| 288 | + | For a key that may have leaked, skip keeping the previous one. | |
| 289 | + | ||
| 290 | + | ### Telling the status page about deploys | |
| 291 | + | ||
| 292 | + | Restarts during a deploy can look like an outage to the status page's | |
| 293 | + | checks. Set the same value as the status Worker's secret | |
| 294 | + | `STATUS_DEPLOY_TOKEN` and as the repository's Actions secret | |
| 295 | + | `STATUS_DEPLOY_TOKEN`, and add the status page's host to the workflow-only | |
| 296 | + | domains. Deploys then announce their start and finish, and the status page | |
| 297 | + | drafts no incident during them or for 3 minutes after. | |
| 298 | + | ||
| 299 | + | ### Browser push | |
| 300 | + | ||
| 301 | + | `node scripts/ops/vapid-keys.mjs` prints a VAPID key pair. Put the public | |
| 302 | + | half in `VAPID_PUBLIC_KEY` in `services/notify/wrangler.jsonc` and the | |
| 303 | + | private half in the secret `VAPID_PRIVATE_KEY`. | |
| 304 | + | ||
| 305 | + | ## Rate limits | |
| 306 | + | ||
| 307 | + | Every public surface that costs money or sends something is behind a | |
| 308 | + | [Workers Rate Limiting](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/) | |
| 309 | + | binding, with the limits on the [rate limits](/reference/rate-limits/) | |
| 310 | + | page. The binding's `namespace_id` must be unique in your account. A | |
| 311 | + | missing binding, or one that fails, lets requests through. | |
| 312 | + | ||
| 313 | + | ## Spend guardrails | |
| 314 | + | ||
| 315 | + | Cloudflare has no hard spending cap. Besides each workspace's own | |
| 316 | + | [limits](/guides/usage-and-billing/), g1t watches the platform as a whole: | |
| 317 | + | ||
| 318 | + | - **The hourly watch.** At a quarter past each hour, billing reads the | |
| 319 | + | hour before from Cloudflare's GraphQL Analytics API: Workers requests | |
| 320 | + | and CPU, D1 rows, Queue operations, Durable Objects, KV and Artifacts. | |
| 321 | + | It needs `CLOUDFLARE_BILLING_TOKEN` (or `CLOUDFLARE_USAGE_TOKEN`) with | |
| 322 | + | Account Analytics Read. An hour over its threshold, or over ten times | |
| 323 | + | the week's median hour, is emailed to `COSTS_ALERT_EMAIL`. | |
| 324 | + | - **Thresholds** are the `PLATFORM_HOURLY_*` variables in | |
| 325 | + | `services/billing/wrangler.jsonc`; `0` turns one off. A breach at five | |
| 326 | + | times its threshold pauses the kinds of work in `AUTO_PAUSE`. | |
| 327 | + | - **The platform pause** stops a kind of work everywhere: `compute` | |
| 328 | + | (sandboxes), `schedules`, `indexing` or `renders` (social cards). Staff | |
| 329 | + | pause and resume in sudo, under **Costs & margin**. Work already | |
| 330 | + | running finishes, and if the pause cannot be read, nothing is paused. | |
| 331 | + | - **Check the watch's queries** after changing them: | |
| 332 | + | `CLOUDFLARE_API_TOKEN=<token> node scripts/ops/platform-usage.mjs`. It | |
| 333 | + | exits 1 and names any dataset that errored or answered empty. | |
| 334 | + | ||
| 335 | + | In Cloudflare's dashboard, which no code can set, add **Billable Usage** | |
| 336 | + | notifications for each product g1t uses, a monthly **Budget alert**, and a | |
| 337 | + | notification destination that reaches someone. | |
| 338 | + | ||
| 339 | + | ## Logs | |
| 340 | + | ||
| 341 | + | Each Worker keeps a share of its invocations' logs, set by | |
| 342 | + | `head_sampling_rate` in its `wrangler.jsonc`: every one for billing, | |
| 343 | + | identity, runner, actions, deployments, status and sudo, and a tenth for | |
| 344 | + | the rest. Metrics are not sampled. To chase a rare error on a sampled | |
| 345 | + | Worker, raise its rate to 1 while you look. |
| 105 | 105 | places. A workflow deploys when any job names an `environment:`. | |
| 106 | 106 | - Secrets come from the [project on g1t](/guides/secrets-and-variables/), | |
| 107 | 107 | never the remote's. | |
| 108 | + | - A push copied in that changes `.g1t/`, `.github/workflows/` or | |
| 109 | + | `.github/actions/` starts runs that wait for approval before they can | |
| 110 | + | use the repository's secrets and variables. | |
| 108 | 111 | ||
| 109 | 112 | **End CI failover** when the remote runs its workflows again. Runs already | |
| 110 | 113 | started finish. | |
| ⋯ | |||
| 195 | 198 | as the `mirror_*` actions of the MCP `repository` tool. See the | |
| 196 | 199 | [API reference](/reference/api/). Reading a repository's | |
| 197 | 200 | mirroring needs read access; syncing needs push; everything else needs the | |
| 198 | − | Admin role. Agents never move a repository to g1t or handle a remote's | |
| 199 | − | token. | |
| 201 | + | Admin role. Agents never move a repository to g1t, nor add, change or | |
| 202 | + | remove a remote. | |
| 203 | + | ||
| 204 | + | Removing a remote makes a mirror an ordinary repository with what it has, | |
| 205 | + | and stops pushes to a follower. It is refused during a takeover: hand it | |
| 206 | + | back or move it to g1t first. | |
| 200 | 207 | ||
| 201 | 208 | [Webhooks](/guides/webhooks/) can subscribe to: | |
| 202 | 209 | ||
| ⋯ | |||
| 216 | 223 | ||
| 217 | 224 | A takeover in progress keeps going and says why on the link: move it to | |
| 218 | 225 | g1t to keep it. | |
| 226 | + | ||
| 227 | + | ## Not supported yet | |
| 228 | + | ||
| 229 | + | - Workflow results from CI failover stay on g1t; they are not reported | |
| 230 | + | back to the remote as checks. | |
| 231 | + | - CI failover starts only when someone starts it. | |
| 232 | + | - A takeover runs under the repository's own [rules](/guides/rules/) on | |
| 233 | + | g1t, not the remote's branch protection. | |
| 234 | + | - A mirror doesn't show the remote's pull requests. | |
| 235 | + | - GitLab and Bitbucket repositories link as any host over HTTPS, asked | |
| 236 | + | every five minutes. | |
| 182 | 182 | Without the [g1t plan](/guides/usage-and-billing/#the-g1t-plan), public | |
| 183 | 183 | packages may hold 10 GB and private ones 500 MB per workspace; a push past | |
| 184 | 184 | either is refused, with a message saying how much is used. On the plan, | |
| 185 | − | storage past those amounts is charged instead. See | |
| 186 | − | [storage and pull limits](/guides/containers/#storage-and-pull-limits). | |
| 185 | + | nothing is refused and storage past those amounts is charged instead: | |
| 186 | + | ||
| 187 | + | - What the workspace's packages hold is measured each day, public and | |
| 188 | + | private apart, each past its own free amount. | |
| 189 | + | - A month's GB-months are those days added up, divided by 30, charged at | |
| 190 | + | $0.018 a GB-month as **Package storage** on the | |
| 191 | + | [bill](/guides/usage-and-billing/). | |
| 192 | + | - Deleted packages and versions, kept for 30 days, do not count. | |
| 193 | + | - Downloads and pulls cost nothing. Anonymous pulls are rate limited; see | |
| 194 | + | [storage and pull limits](/guides/containers/#storage-and-pull-limits). | |
| 187 | 195 | ||
| 188 | 196 | ## Downloads | |
| 189 | 197 | ||
| ⋯ | |||
| 241 | 249 | pushing a NuGet version's symbols and yanking a gem version), as are | |
| 242 | 250 | restoring them, purging them after 30 days, every change to who has access | |
| 243 | 251 | and to Manage Actions access, changing the visibility, and linking and | |
| 244 | − | unlinking (see the [audit log's list](/guides/audit-log/)). The events | |
| 245 | − | `package.published`, `package.version_deleted`, `package.deleted` and | |
| 246 | − | `package.visibility_changed`, which [webhooks](/guides/webhooks/) can be | |
| 247 | − | sent: a linked package's go to its repository's webhooks and its | |
| 248 | − | workspace's, an unlinked package's to its workspace's webhooks. | |
| 252 | + | unlinking (see the [audit log's list](/guides/audit-log/)). | |
| 253 | + | ||
| 254 | + | [Webhooks](/guides/webhooks/) can subscribe to these events. A linked | |
| 255 | + | package's go to its repository's webhooks and its workspace's; an | |
| 256 | + | unlinked package's go to its workspace's webhooks. | |
| 257 | + | ||
| 258 | + | | Event | Sent when | | |
| 259 | + | | --- | --- | | |
| 260 | + | | `package.published` | A version is published. `data.tags` names the tags that now point to it. | | |
| 261 | + | | `package.version_deleted` | A version is deleted. | | |
| 262 | + | | `package.deleted` | A package is deleted. | | |
| 263 | + | | `package.visibility_changed` | A package is made public or private. `data.visibility` is the new one. | | |
| 264 | + | ||
| 265 | + | Each one's `data` has `package_id`, `workspace`, `ecosystem`, `name` and | |
| 266 | + | `repo_id` (null for an unlinked package); a version's event also has its | |
| 267 | + | `version` and `digest` (for a container image, the manifest's), and | |
| 268 | + | `package.published` its `size`, except for a Composer package. | |
| 269 | + | ||
| 270 | + | ## Not supported yet | |
| 271 | + | ||
| 272 | + | - PyPI packages. | |
| 273 | + | - Installing unscoped npm packages, or other scopes, through g1t.sh, and | |
| 274 | + | a copy of Packagist for Composer: those still come from their public | |
| 275 | + | registries. | |
| 276 | + | - A Go module proxy: Go modules are fetched with git. | |
| 277 | + | - Workflows that run when a package is published. | |
| 278 | + | - Packages in site-wide search. | |
| 1 | + | --- | |
| 2 | + | title: How a self-hosted g1t runs | |
| 3 | + | description: What runs inside the Docker Compose installation, what stands in for each Cloudflare service, and what each part of g1t does when you run it yourself. | |
| 4 | + | --- | |
| 5 | + | ||
| 6 | + | [Run g1t yourself](/guides/self-hosting/) starts g1t with Docker Compose. | |
| 7 | + | This page is for the person who keeps that installation running: what is | |
| 8 | + | inside it, what stands in for each Cloudflare service g1t.sh uses, and | |
| 9 | + | where the limits are. | |
| 10 | + | ||
| 11 | + | ## The shape | |
| 12 | + | ||
| 13 | + | g1t is a set of Workers, one per service, each with its own SQLite | |
| 14 | + | database. They call each other over HTTP (`POST /rpc/<method>` with a JSON | |
| 15 | + | body). A self-hosted installation runs the same Workers, built from the | |
| 16 | + | same source, in workerd, the open-source Workers runtime, under | |
| 17 | + | `wrangler dev`: | |
| 18 | + | ||
| 19 | + | - **The site and every core service** run in one workerd process on | |
| 20 | + | `G1T_PORT` (8787). Their databases, key-value stores and queues are | |
| 21 | + | SQLite files on the `g1t-data` volume. | |
| 22 | + | - **The API and the MCP server** run in a second workerd on `API_PORT` | |
| 23 | + | (8789), started once the first answers. It reaches the other services | |
| 24 | + | through Wrangler's local registry. | |
| 25 | + | - **The status page** runs in a process of its own (`status.sh`), so it | |
| 26 | + | keeps answering when the site does not. | |
| 27 | + | ||
| 28 | + | Three Cloudflare services are replaced by small Workers bound in their | |
| 29 | + | place, so no service's code changes: | |
| 30 | + | ||
| 31 | + | | Binding | Stands in for | Self-hosted | | |
| 32 | + | | --- | --- | --- | | |
| 33 | + | | `ARTIFACTS` | Cloudflare Artifacts, where g1t.sh keeps repositories | `deploy/self-host/workers/artifacts`, in front of the git store: bare repositories on the `g1t-git` volume, served with `git http-backend`. Access tokens are HMAC-signed, scoped and expire. Forks are clones with hard-linked objects. | | |
| 34 | + | | `EMAIL` | Cloudflare Email Sending | `deploy/self-host/workers/mail`: logs each message and hands it to Mailpit, which can relay through your SMTP server | | |
| 35 | + | | The runner, the context hub and the model proxy | Containers, Vectorize, Workers AI and AI Gateway | `deploy/self-host/workers/off`: answers that the feature is off, which every page that uses them shows | | |
| 36 | + | ||
| 37 | + | ## What is in `deploy/self-host` | |
| 38 | + | ||
| 39 | + | | File | What it is | | |
| 40 | + | | --- | --- | | |
| 41 | + | | `docker-compose.yml` | The services: `g1t`, `status`, `gitstore`, `rustfs` (S3-compatible storage), `storage-setup` (makes the buckets once, then exits) and `mailpit`. Volumes: `g1t-data`, `g1t-git`, `g1t-objects`, `g1t-status`, `g1t-secrets`. | | |
| 42 | + | | `Dockerfile` | Compiles the Rust services to WebAssembly and builds the site, as g1t.sh's deploys do. The image has Node, Wrangler, workerd and the built Workers. | | |
| 43 | + | | `start.sh` | Makes the sealing keys on first start, writes the configs, applies database migrations, then starts both workerd processes and the scheduler. | | |
| 44 | + | | `configs.mjs` | Writes each Worker's self-hosted config from its `wrangler.jsonc`: drops routes, the account and placement, binds the stand-ins above, points packages, backups, clone packs and files in pages at the S3 store, and replaces g1t.sh's addresses with yours. What runs, and what is bound to the off stand-in, comes from each part's `self_host` field in `deploy/stack.jsonc`. | | |
| 45 | + | | `scheduler.mjs` | Runs scheduled jobs (below). | | |
| 46 | + | | `gitstore/` | The git store. | | |
| 47 | + | | `workers/` | The three stand-ins. | | |
| 48 | + | | `smoke.sh` | The end-to-end check in [Check an installation](/guides/self-hosting/#check-an-installation). | | |
| 49 | + | ||
| 50 | + | ## Each part | |
| 51 | + | ||
| 52 | + | | Part | Self-hosted | | |
| 53 | + | | --- | --- | | |
| 54 | + | | `apps/web` | Runs: the site, on `G1T_PORT` | | |
| 55 | + | | `apps/api` | Runs in the second workerd, on `API_PORT`. `API_URL` is its OAuth issuer and MCP is at `/mcp` on it. Actions artifacts need the runner, which is off. | | |
| 56 | + | | `apps/status` | Runs in a process of its own; see [the status page](/guides/self-hosting/#the-status-page) | | |
| 57 | + | | `apps/sudo` | Not run. It is g1t.sh's staff console, behind Cloudflare Access. | | |
| 58 | + | | `apps/docs` | Not run: docs.g1t.sh serves these pages | | |
| 59 | + | | `services/identity` | Runs; its email goes to Mailpit | | |
| 60 | + | | `services/repos` | Runs, against the git store. Nightly backups are queued but cut by the runner, which is off, so none are made yet. Clone packs are kept in the `g1t-git-packs` bucket. | | |
| 61 | + | | `services/work` | Runs | | |
| 62 | + | | `services/events` | Runs: the event bus, on local queues | | |
| 63 | + | | `services/projects` | Runs | | |
| 64 | + | | `services/search` | Runs: site search is SQLite full-text search | | |
| 65 | + | | `services/chat` | Runs, with its Durable Objects; custom emoji are kept with avatars | | |
| 66 | + | | `services/docs` | Runs: pages and [artifacts](/guides/artifacts/), with their live rooms. Files people add are kept in the `g1t-docs-files` bucket. Search matches words, not meaning, because there is no embedding model. | | |
| 67 | + | | `services/notify` | Runs. Notifications are live in open tabs; browser push needs a VAPID key pair, which an installation does not make. | | |
| 68 | + | | `services/agents` | Runs, but replies need the model proxy, which is off, so an agent answers with a short apology | | |
| 69 | + | | `services/billing` | Runs with nothing charged and no usage limit | | |
| 70 | + | | `services/integrations` | Runs | | |
| 71 | + | | `services/webhooks` | Runs, retries included | | |
| 72 | + | | `services/actions` | Runs. Jobs need the runner, which is off. | | |
| 73 | + | | `services/packages` | Runs, with files in the `g1t-packages` bucket and no request size limit | | |
| 74 | + | | `services/security` | Runs, with its sweeps on schedule | | |
| 75 | + | | `services/deployments` | Runs with no Cloudflare token, so nothing deploys; its pages and settings still show | | |
| 76 | + | | `services/runner` | Off: agents, checks run by g1t, merge queue testing and workflow jobs | | |
| 77 | + | | `services/context` | Off: the context hub | | |
| 78 | + | | `services/models` | Off: g1t's hosted models | | |
| 79 | + | | `services/pages` | Not run: there is no `g1t.page` | | |
| 80 | + | | `services/og` | Not run: pages have no social cards | | |
| 81 | + | ||
| 82 | + | ## Scheduled jobs | |
| 83 | + | ||
| 84 | + | workerd runs a Worker's scheduled handler only when asked. `scheduler.mjs` | |
| 85 | + | asks once a minute, inside the `g1t` container, through Wrangler's local | |
| 86 | + | API, for each due schedule in the Workers' own configs: repos, events, | |
| 87 | + | identity, security, webhooks, packages and docs. A job still running from | |
| 88 | + | the minute before is not started again, and one is given up on after ten | |
| 89 | + | minutes. | |
| 90 | + | ||
| 91 | + | Not run: Actions schedules (`on: schedule`, which would need the runner), | |
| 92 | + | billing and deployments. | |
| 93 | + | ||
| 94 | + | `node deploy/self-host/scheduler.mjs --once /data/generated/schedules.json` | |
| 95 | + | runs every job once and exits non-zero if one failed. | |
| 96 | + | ||
| 97 | + | ## Storage | |
| 98 | + | ||
| 99 | + | Every bucket is on the bundled RustFS, or on any S3-compatible store you | |
| 100 | + | point `S3_ENDPOINT` at (MinIO, Ceph, Garage or AWS S3). `storage-setup` | |
| 101 | + | makes them on the bundled store; on another store, make them yourself. | |
| 102 | + | ||
| 103 | + | | Bucket | Holds | Setting | | |
| 104 | + | | --- | --- | --- | | |
| 105 | + | | `g1t-packages` | Container image layers and other package files | `S3_BUCKET` | | |
| 106 | + | | `g1t-git-packs` | Packs for fresh clones, a cache. Give it a rule that deletes objects after 7 days and aborts multipart uploads after a day. | `PACK_S3_BUCKET` | | |
| 107 | + | | `g1t-backups` | Nightly repository backups, once the runner cuts them | `BACKUP_S3_BUCKET` | | |
| 108 | + | | `g1t-docs-files` | Images and files added to pages and artifacts | `DOCS_S3_BUCKET` | | |
| 109 | + | ||
| 110 | + | Requests to the store are signed with AWS Signature Version 4, path style. | |
| 111 | + | ||
| 112 | + | ## Limits | |
| 113 | + | ||
| 114 | + | - **Use it on `localhost` or a private network you trust.** `wrangler dev` | |
| 115 | + | is a development server: it answers its own development endpoints | |
| 116 | + | (`/cdn-cgi/...`) on the same port as the site. | |
| 117 | + | - **One machine.** Every database is a SQLite file with one writer. It | |
| 118 | + | suits a team, not a large organisation. | |
| 119 | + | - **HTTPS anywhere but `localhost`.** Sign-in cookies are `Secure`; put a | |
| 120 | + | proxy with a certificate in front and set `PUBLIC_URL` to its | |
| 121 | + | `https://` address. | |
| 122 | + | - **If the API says a binding is `[not connected]`**, restart the | |
| 123 | + | container: the API finds the other services through Wrangler's local | |
| 124 | + | registry. | |
| 125 | + | - **Building takes minutes and several GB**, because every Rust service is | |
| 126 | + | compiled from source. A change that does not compile breaks the image, | |
| 127 | + | so build from a released commit. | |
| 128 | + | - **Some links still leave your installation**: the docs links go to | |
| 129 | + | docs.g1t.sh, and the Status links to status.g1t.sh. |
| 33 | 33 | | Billing | Off. Nothing is charged, and no usage limit stops work. | | |
| 34 | 34 | | Site analytics | Off. Only g1t.sh sends page analytics (to HeyCatch, as its [privacy policy](https://g1t.sh/policies/privacy#how-the-site-is-used) describes); your installation sends none. | | |
| 35 | 35 | | Git over SSH and the `g1t` CLI | Not available yet | | |
| 36 | − | | Scheduled jobs | Run on their schedules inside the g1t container: webhook retries, purging deleted repositories, the packages sweep, security sweeps, audit log retention and access request summaries. Actions schedules (`on: schedule`) are not run. | | |
| 36 | + | | Scheduled jobs | Run on their schedules inside the g1t container: webhook retries, purging deleted repositories, the packages sweep, security sweeps, audit log retention, access request summaries and emptying artifacts left in the trash for 30 days. Actions schedules (`on: schedule`) are not run. | | |
| 37 | 37 | ||
| 38 | 38 | What hosted g1t cannot do yet either is on | |
| 39 | − | [What g1t can't do yet](/about/limitations/). | |
| 39 | + | [What g1t can't do yet](/about/limitations/). What runs inside the | |
| 40 | + | installation, and what stands in for each part of g1t.sh, is in | |
| 41 | + | [How a self-hosted g1t runs](/guides/self-hosting-architecture/). | |
| 40 | 42 | ||
| 41 | 43 | ## Before you start | |
| 42 | 44 | ||
| ⋯ | |||
| 164 | 166 | | `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` | the bundled RustFS, bucket `g1t-packages` | Where packages' files are kept: any S3-compatible store. Change the two keys before first start; RustFS is made with them. | | |
| 165 | 167 | | `S3_PUBLIC_ENDPOINT` | (none) | The store's address as clients reach it. When set, large layers are downloaded from it directly with a signed URL. | | |
| 166 | 168 | | `PACK_S3_BUCKET` | `g1t-git-packs` | The bucket on the same store that packs for fresh clones are kept in, so the next clone of the same commit is not built again. The bundled store deletes packs after 7 days, and uploads left unfinished after a day; on another store, give the bucket a lifecycle rule that does the same. | | |
| 169 | + | | `DOCS_S3_BUCKET` | `g1t-docs-files` | The bucket on the same store that images and files added to pages and [artifacts](/guides/artifacts/) are kept in. | | |
| 167 | 170 | | `RUSTFS_IMAGE` | `rustfs/rustfs:1.0.1` | The image the bundled object store runs: [RustFS](https://rustfs.com), an S3-compatible server. | | |
| 168 | 171 | | `AWS_CLI_IMAGE` | `amazon/aws-cli:2.37.10` | The image `storage-setup` makes the buckets and the packs' lifecycle rule with. | | |
| 169 | 172 | | `BACKUP_S3_BUCKET` | `g1t-backups` | The bucket on the same store that nightly repository backups (a `git bundle` of each repository whose branches or tags changed) are kept in. The bundles are cut by g1t's runner, which this installation does not run yet, so the bucket stays empty for now: copy the volumes, as below. | | |
| ⋯ | |||
| 245 | 248 | | --- | --- | | |
| 246 | 249 | | `g1t_g1t-data` | Accounts, workspaces, issues and every other record, as SQLite files; the keys that seal stored secrets (`keys.env`) | | |
| 247 | 250 | | `g1t_g1t-git` | Your repositories, one bare git repository each | | |
| 248 | − | | `g1t_g1t-objects` | The bundled object store (RustFS): container images' layers and other package files in `g1t-packages`, the `g1t-backups` bucket and the clone packs in `g1t-git-packs` | | |
| 251 | + | | `g1t_g1t-objects` | The bundled object store (RustFS): container images' layers and other package files in `g1t-packages`, the `g1t-backups` bucket, the clone packs in `g1t-git-packs`, and files added to pages and artifacts in `g1t-docs-files` | | |
| 249 | 252 | | `g1t_g1t-secrets` | The key the site and the git store share | | |
| 250 | 253 | ||
| 251 | 254 | To back up, stop g1t and copy the volumes: | |
| 767 | 767 | | [`accept_repository_invitation`](/reference/api/access/accept-repo-invitation/) | Accept one; its role is yours at once. | `id` | `account:write` | | |
| 768 | 768 | | [`decline_repository_invitation`](/reference/api/access/decline-repo-invitation/) | Decline one. | `id` | `account:write` | | |
| 769 | 769 | ||
| 770 | − | ||
| 771 | 770 | ## `artifact` | |
| 772 | 771 | ||
| 773 | 772 | A workspace's artifacts: its docs, and later its slides, designs and | |
| ⋯ | |||
| 792 | 791 | | [`query_data`](/reference/api/artifacts/query-workspace-dataset/) | Run a dataset `query` as you, over what you can read. Answers that dashboards are not here yet until they ship. | `workspace`, `query` | `artifacts:read` | | |
| 793 | 792 | | [`create`](/reference/api/artifacts/create-workspace-artifact/) | Make one from `markdown` or a `template_id`, in a `space`, under a `parent_id`, or in your Private. `kind` is `doc`; the others are not here yet. | `workspace` | `artifacts:write` | | |
| 794 | 793 | | [`update`](/reference/api/artifacts/update-workspace-artifact/) | Change its `title` or `icon`, or move it to a `space` (`private` for your Private) or under a `parent_id`. | `workspace`, `artifact_id` | `artifacts:write` | | |
| 795 | − | | [`edit`](/reference/api/artifacts/edit-workspace-artifact/) | Change its content: `markdown` with a `target` (`append`, `document`, a `section` by `heading`, or `blocks`). Made with the edit role; a suggestion with the comment role or `suggest_only`. | `workspace`, `artifact_id` | `artifacts:write` | | |
| 794 | + | | [`edit`](/reference/api/artifacts/edit-workspace-artifact/) | Change its content: `markdown` with a `target` (`append`, `document`, a `section` by `heading`, or `blocks`). Made with the edit role; a suggestion with the comment role or `suggest_only`. `note` says why; `marks_current` clears the doc's possibly out of date mark when the change is made or accepted. | `workspace`, `artifact_id` | `artifacts:write` | | |
| 796 | 795 | | [`trash`](/reference/api/artifacts/trash-workspace-artifact/) | Move it, and what is under it, to the trash; deleted for good after 30 days. | `workspace`, `artifact_id` | `artifacts:write` | | |
| 797 | 796 | | [`restore`](/reference/api/artifacts/restore-workspace-artifact/) | Bring it back from the trash. | `workspace`, `artifact_id` | `artifacts:write` | | |
| 798 | 797 | | [`restore_version`](/reference/api/artifacts/restore-workspace-artifact-version/) | Make an earlier version its content again, as a new version. | `workspace`, `artifact_id`, `version_id` | `artifacts:write` | | |
| 64 | 64 | /** | |
| 65 | 65 | * The page-speed budget the status page holds the site to: the slower of a | |
| 66 | 66 | * public project page and Explore, to the first byte of the answer, from a | |
| 67 | − | * Cloudflare data centre. docs/PERFORMANCE.md has the targets. | |
| 67 | + | * Cloudflare data centre. CONTRIBUTING.md, "Speed", has the site's targets. | |
| 68 | 68 | */ | |
| 69 | 69 | export const SPEED_BUDGET_MS = 800; | |
| 70 | 70 |
| 211 | 211 | context, prices, typical run and when its provider last listed it, each | |
| 212 | 212 | with **Retire** or **Restore**. **Checks**: the latest checks of each | |
| 213 | 213 | provider. Every change names the staff member and why in the audit log | |
| 214 | − | (account `models`). See docs/BILLING_OPERATIONS.md, "The model | |
| 215 | − | catalogue". | |
| 214 | + | (account `models`). The catalogue itself is billing's | |
| 215 | + | (`services/billing/src/catalogue.rs`). | |
| 216 | 216 | - **Stripe**: whether billing's key is in test or live mode (or off), the | |
| 217 | 217 | webhook Stripe calls (URL, endpoint id, events, who registered it and | |
| 218 | 218 | when), and the events Stripe sent lately with what billing did with |
| 507 | 507 | ||
| 508 | 508 | ||
| 509 | 509 | /** | |
| 510 | − | * The platform pause and the hourly usage watch (billing's platform.rs, | |
| 511 | − | * docs/SPEND-GUARDRAILS.md): four levels staff can pause across g1t, what | |
| 512 | − | * Cloudflare counted in the last hour against each threshold, and the | |
| 513 | − | * last day's breaches. | |
| 510 | + | * The platform pause and the hourly usage watch (billing's platform.rs; | |
| 511 | + | * docs.g1t.sh/guides/deploy-to-cloudflare/#spend-guardrails): four levels | |
| 512 | + | * staff can pause across g1t, what Cloudflare counted in the last hour | |
| 513 | + | * against each threshold, and the last day's breaches. | |
| 514 | 514 | */ | |
| 515 | 515 | function PlatformSection({ | |
| 516 | 516 | guard, |
| 15 | 15 | export const DEPARTMENTS = ["Engineering", "QA", "Operations", "Docs", "Product", "Customer Support", "Sales"]; | |
| 16 | 16 | ||
| 17 | 17 | /** | |
| 18 | − | * An agent's role (docs/WORKSPACE.md, "Roles, not tasks"): its title, the | |
| 19 | − | * team it is on (or a department, when it is on none), and what it is | |
| 20 | − | * responsible for, as a list to add to, edit and reorder. | |
| 18 | + | * An agent's role: its title, the team it is on (or a department, when it | |
| 19 | + | * is on none), and what it is responsible for, as a list to add to, edit | |
| 20 | + | * and reorder. | |
| 21 | 21 | */ | |
| 22 | 22 | export function RoleFields({ | |
| 23 | 23 | title, |
| 47 | 47 | import { sessionChip } from "../../lib/session-card"; | |
| 48 | 48 | import { codeAccessPath } from "../../lib/workspace-nav"; | |
| 49 | 49 | ||
| 50 | − | // A card g1t or an agent posts in chat (docs/WORKSPACE.md, "Cards"): what | |
| 50 | + | // A card g1t or an agent posts in chat: what | |
| 51 | 51 | // it is about, its state, a preview, a few facts, and what people can do | |
| 52 | 52 | // right here. Pressing an action goes to the site (routes/workspace/chat/ | |
| 53 | 53 | // api.ts, `card_action`), then to chat, then to the service that owns the |
| 84 | 84 | return new Date(at).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", timeZone: zone }); | |
| 85 | 85 | } | |
| 86 | 86 | ||
| 87 | − | /** Whether the viewer uses Code in this workspace (docs/WORKSPACE.md, "Members without Code"). */ | |
| 87 | + | /** Whether the viewer uses Code in this workspace. */ | |
| 88 | 88 | export function useCodeAccess(slug: string): boolean { | |
| 89 | 89 | const root = useRouteLoaderData("root") as { user?: { workspaces?: { slug: string; code_access?: boolean }[] } | null } | undefined; | |
| 90 | 90 | return hasCodeAccess(root?.user?.workspaces?.find((m) => m.slug === slug) ?? null); |
| 9 | 9 | import { SelectField } from "../ui/select"; | |
| 10 | 10 | import { ORCHESTRATOR, type WritableSpace, type WriteUpAgent, type WriteUpWhere, defaultWhere, writableSpaces, writeUpMessage } from "../../lib/write-up"; | |
| 11 | 11 | ||
| 12 | − | // "Write this up as an artifact" (docs/ARTIFACTS_MODE.md section 4.4): | |
| 12 | + | // "Write this up as an artifact" (docs.g1t.sh/guides/artifacts/, "Write a | |
| 13 | + | // thread up"): | |
| 13 | 14 | // where it goes, a title if you have one, and which agent writes it. The | |
| 14 | 15 | // kind is a doc for now. Sending posts the ask in the thread, as you, | |
| 15 | 16 | // where everyone sees it; the agent answers it as it answers any mention, |
| 243 | 243 | } | |
| 244 | 244 | ||
| 245 | 245 | /** | |
| 246 | − | * The dock down the left (docs/WORKSPACE.md, "Shell"): g1t's mark, which | |
| 246 | + | * The dock down the left: g1t's mark, which | |
| 247 | 247 | * leads to Today; the built-in apps (Today, Chat, Notifications, Agents, | |
| 248 | 248 | * Code and Artifacts) with their names; the apps you pinned, as icons; | |
| 249 | 249 | * the Apps launcher; and at its foot People, Workspace and your account. |
| 1 | 1 | /** | |
| 2 | 2 | * Docs and code: citing code in a doc (the Cite code dialog), and showing | |
| 3 | − | * a project's docs folder in Artifacts. Plan: docs/WORKSPACE.md, "Agents | |
| 4 | − | * and docs" and "Docs and repository docs". | |
| 3 | + | * a project's docs folder in Artifacts. | |
| 5 | 4 | */ | |
| 6 | 5 | import { DOC_CITATION_KIND_LABELS, type DocCitationKind, type DocRepoSpace } from "@g1t/contracts"; | |
| 7 | 6 | import { FileCode2, FolderGit2 } from "lucide-react"; |
| 1 | 1 | /** | |
| 2 | 2 | * Each kind of artifact as the site shows it: its icon and colour, whether | |
| 3 | 3 | * it can be made yet, and its page body. One line per kind, so each kind's | |
| 4 | − | * phase adds its own (docs/ARTIFACTS_MODE.md section 8: slides, design and | |
| 5 | − | * dashboards are "Coming soon" until theirs ships). Bodies are lazy, so | |
| 4 | + | * phase adds its own (slides, design and dashboards are "Coming soon" | |
| 5 | + | * until theirs ships). Bodies are lazy, so | |
| 6 | 6 | * Home and the sidebar carry no editor code. | |
| 7 | 7 | */ | |
| 8 | 8 | import { FOLIO_KIND_LABELS, type DocRole, type Folio, type FolioKind, type FolioPage, type FolioPreview } from "@g1t/contracts"; |
| 1 | 1 | /** | |
| 2 | − | * Artifacts as lists show them (docs/ARTIFACTS_MODE.md section 6.2): rows | |
| 2 | + | * Artifacts as lists show them: rows | |
| 3 | 3 | * grouped by the day they were last edited, in the viewer's time zone, or | |
| 4 | 4 | * cards with a picture drawn from each one's preview. Each has its kind, | |
| 5 | 5 | * who can see it, its space, when it was edited and by whom, and its ⋯ |
| 1 | 1 | /** | |
| 2 | − | * Share an artifact (docs/ARTIFACTS_MODE.md section 6.4): invite people, | |
| 2 | + | * Share an artifact (docs.g1t.sh/guides/artifacts/, "Sharing"): invite people, | |
| 3 | 3 | * agents and teams with a role, see everyone who has access and where it | |
| 4 | 4 | * comes from, choose whether it follows its space or parent or only | |
| 5 | 5 | * people invited, open it to the workspace or to anyone in it with the |
| 1 | 1 | /** | |
| 2 | − | * The header every artifact's page shares, whatever its kind | |
| 3 | − | * (docs/ARTIFACTS_MODE.md section 6.5): where it is (a space or Private, | |
| 2 | + | * The header every artifact's page shares, whatever its kind: where it | |
| 3 | + | * is (a space or Private, | |
| 4 | 4 | * then the docs it sits under), who is here, "Offline, changes will | |
| 5 | 5 | * sync", and Share, comments, history, favorite and the ⋯ menu. A kind's | |
| 6 | 6 | * own actions go in `actions`. |
| 1 | 1 | /** | |
| 2 | − | * Artifacts mode's sidebar (beside the rail; docs/ARTIFACTS_MODE.md | |
| 3 | − | * section 6.3): search, Home, New and Templates; Favorites; the spaces | |
| 2 | + | * Artifacts mode's sidebar (beside the rail; docs.g1t.sh/guides/artifacts/, | |
| 3 | + | * "The sidebar"): search, Home, New and Templates; Favorites; the spaces | |
| 4 | 4 | * shown (joined open spaces, team spaces, members-only spaces) each with | |
| 5 | 5 | * its tree; Private, everything of yours in no space; Shared, the tops of | |
| 6 | 6 | * what others shared with you; then what's possibly out of date, |
| 1 | 1 | /** | |
| 2 | − | * Chat's cards for artifact links (docs/ARTIFACTS_MODE.md section 4.3 rule | |
| 3 | − | * 3): each viewer asks the site for their own, so someone who can open the | |
| 4 | − | * artifact sees its kind, title, space and last edit, and anyone else sees | |
| 5 | − | * only "An artifact you don't have access to", with no title. Looking is | |
| 6 | − | * not opening: a card never makes a link-shared artifact readable. | |
| 2 | + | * Chat's cards for artifact links (docs.g1t.sh/guides/artifacts/, "Links | |
| 3 | + | * in chat"): each viewer asks the site for their own, so someone who can | |
| 4 | + | * open the artifact sees its kind, title, space and last edit, and anyone | |
| 5 | + | * else sees only "An artifact you don't have access to", with no title. | |
| 6 | + | * Looking is not opening: a card never makes a link-shared artifact readable. | |
| 7 | 7 | */ | |
| 8 | 8 | import type { FolioKind, Result } from "@g1t/contracts"; | |
| 9 | 9 | import { Lock } from "lucide-react"; |
| 10 | 10 | import { currentSink, dismiss, settle, useWaitingCards } from "../../lib/notify-client"; | |
| 11 | 11 | import { cardActionRequest, notificationActions } from "../../lib/notify-store"; | |
| 12 | 12 | ||
| 13 | − | // A chat card's actions on a notification about it (docs/WORKSPACE.md, | |
| 14 | − | // "Cards"): a session at its cap (Approve more with the amount inline, | |
| 15 | − | // Stop, Open), a draft issue (File issue, Discard). Pressing one sends the | |
| 16 | − | // same `card_action` as the card in the conversation; the card there | |
| 17 | − | // changes for everyone, and this notification is put away. | |
| 13 | + | // A chat card's actions on a notification about it: a session at its cap | |
| 14 | + | // (Approve more with the amount inline, Stop, Open), a draft issue (File | |
| 15 | + | // issue, Discard). Pressing one sends the same `card_action` as the card | |
| 16 | + | // in the conversation; the card there changes for everyone, and this | |
| 17 | + | // notification is put away. | |
| 18 | 18 | ||
| 19 | 19 | const BASE = | |
| 20 | 20 | "inline-flex h-7 shrink-0 items-center justify-center gap-1 rounded-md px-2.5 text-xs font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-accent/50 disabled:pointer-events-none disabled:opacity-50 max-sm:h-9 max-sm:px-3"; |
| 3 | 3 | import { Mark } from "./logo"; | |
| 4 | 4 | ||
| 5 | 5 | /** | |
| 6 | − | * Whether an agent is `@g1t`, the orchestrator every workspace has | |
| 7 | − | * (docs/WORKSPACE.md, "g1t, the orchestrator"): built in, first in the | |
| 8 | − | * agents service's list. Its handle is never another agent's. | |
| 6 | + | * Whether an agent is `@g1t`, the orchestrator every workspace has: built | |
| 7 | + | * in, first in the agents service's list. Its handle is never another agent's. | |
| 9 | 8 | */ | |
| 10 | 9 | export function isOrchestrator(agent: Pick<WorkspaceAgent, "handle"> & { builtin?: boolean | null }): boolean { | |
| 11 | 10 | return agent.builtin === true || agent.handle === "g1t"; |
| 1759 | 1759 | } | |
| 1760 | 1760 | ||
| 1761 | 1761 | /** | |
| 1762 | − | * The app (docs/WORKSPACE.md, "Shell"): three surfaces that never blur | |
| 1762 | + | * The app: three surfaces that never blur | |
| 1763 | 1763 | * together. The dock, a floating bar of apps down the left; the mode's | |
| 1764 | 1764 | * sidebar, flat on the background beside it, which folds away with Ctrl B | |
| 1765 | 1765 | * and is a drawer below 1024px; and the page, a rounded panel inset from |
| 1 | 1 | /** | |
| 2 | − | * Cards people can act on in chat (docs/WORKSPACE.md, "Cards"): how a | |
| 2 | + | * Cards people can act on in chat: how a | |
| 3 | 3 | * card's state reads, which of its actions are links and which run, and | |
| 4 | 4 | * the money and text people type into them. Pure, so it is tested on its | |
| 5 | 5 | * own; components/chat/card.tsx draws them. |
| 1 | 1 | /** | |
| 2 | 2 | * Artifacts mode's pure helpers (code says "folio", people see | |
| 3 | − | * "artifact"; docs/ARTIFACTS_MODE.md): sidebar trees, the home list's | |
| 3 | + | * "artifact"): sidebar trees, the home list's | |
| 4 | 4 | * days, cursor colours, covers, roles, how search snippets mark their | |
| 5 | 5 | * matches, and projects' docs folders. No Workers or DOM imports, so it is | |
| 6 | 6 | * tested under Node. |
| 1 | 1 | /** | |
| 2 | 2 | * The front door's rate limits (workers/app.ts), per request before it is | |
| 3 | 3 | * answered. The bindings and their limits are in `RATE_LIMITS` | |
| 4 | − | * (packages/contracts/src/rate-limits.ts); docs/RATE-LIMITS.md says why. | |
| 4 | + | * (packages/contracts/src/rate-limits.ts); CONTRIBUTING.md, "Rate limits", | |
| 5 | + | * says how they are kept. | |
| 5 | 6 | * | |
| 6 | 7 | * - Git over HTTPS: without credentials, by address; with them, by a hash | |
| 7 | 8 | * of the credential, much higher. A clone is about three requests. |
| 40 | 40 | const root = new URL("../../../../", import.meta.url); | |
| 41 | 41 | const SAMPLES: [string, string][] = [ | |
| 42 | 42 | ["README.md", readFileSync(new URL("README.md", root), "utf8")], | |
| 43 | − | ["docs/PERFORMANCE.md", readFileSync(new URL("docs/PERFORMANCE.md", root), "utf8")], | |
| 43 | + | ["apps/docs/src/content/docs/guides/deploy-to-cloudflare.md", readFileSync(new URL("apps/docs/src/content/docs/guides/deploy-to-cloudflare.md", root), "utf8")], | |
| 44 | 44 | ["CONTRIBUTING.md", readFileSync(new URL("CONTRIBUTING.md", root), "utf8")], | |
| 45 | 45 | [ | |
| 46 | 46 | "edge cases", |
| 43 | 43 | // ── The desktop bridge ─────────────────────────────────────────────────── | |
| 44 | 44 | ||
| 45 | 45 | /** | |
| 46 | − | * What the desktop app (an Electron shell loading this web app; see | |
| 47 | − | * docs/WORKSPACE.md, "Desktop app") exposes from its preload script with | |
| 46 | + | * What the desktop app (an Electron shell loading this web app) exposes | |
| 47 | + | * from its preload script with | |
| 48 | 48 | * `contextBridge.exposeInMainWorld("g1tDesktop", …)`. When it is there, | |
| 49 | 49 | * notifications go to it instead of the browser: | |
| 50 | 50 | * | |
| ⋯ | |||
| 233 | 233 | set({ settled: new Set(state.settled).add(id), toasts: dismissToast(state.toasts, id) }); | |
| 234 | 234 | } | |
| 235 | 235 | ||
| 236 | − | /** The chat cards waiting on the person (docs/WORKSPACE.md, "Cards"), newest first, for the panel. */ | |
| 236 | + | /** The chat cards waiting on the person, newest first, for the panel. */ | |
| 237 | 237 | export function useWaitingCards(workspace?: string | null): FeedNotification[] { | |
| 238 | 238 | const s = useNotifyState(); | |
| 239 | 239 | return waitingCards(s.recent, s.settled, Date.now(), workspace); | |
| 96 | 96 | ||
| 97 | 97 | // ── Cards ───────────────────────────────────────────────────────────────── | |
| 98 | 98 | ||
| 99 | − | /** A card's actions on its notification: what the toast and the panel offer (docs/WORKSPACE.md, "Cards"). */ | |
| 99 | + | /** A card's actions on its notification: what the toast and the panel offer. */ | |
| 100 | 100 | export function notificationActions(notification: FeedNotification): CardAction[] { | |
| 101 | 101 | const card = notification.card; | |
| 102 | 102 | if (!card || !notification.workspace || !card.channel_id || !card.message_id) return []; |
| 2 | 2 | * The pure parts of request timing and D1 read consistency for the site: | |
| 3 | 3 | * the `g1t_d1` cookie, which session each service call asks for, which | |
| 4 | 4 | * calls may write, and the `Server-Timing` header. perf.server.ts holds | |
| 5 | − | * the per-request state; docs/PERFORMANCE.md explains the whole. | |
| 5 | + | * the per-request state; CONTRIBUTING.md, "Speed", says how to use it. | |
| 6 | 6 | */ | |
| 7 | 7 | ||
| 8 | 8 | /** The cookie that carries D1 bookmarks between a person's requests. */ |
| 75 | 75 | // from wherever it was going (lib/confirm-gate.ts). | |
| 76 | 76 | const gated = confirmGate(pathname, search, viewer); | |
| 77 | 77 | if (gated) throw redirect(gated); | |
| 78 | − | // A member without Code access in a workspace (docs/WORKSPACE.md, | |
| 79 | − | // "Members without Code"): their Home in place of Mission control, and | |
| 80 | − | // the page that says to ask an owner in place of anything of Code's. | |
| 78 | + | // A member without Code access in a workspace: their Home in place of | |
| 79 | + | // Mission control, and the page that says to ask an owner in place of | |
| 80 | + | // anything of Code's. | |
| 81 | 81 | // The services enforce it too; this keeps the site from offering it. | |
| 82 | 82 | const noCode = (viewer?.workspaces ?? []).filter((m) => !hasCodeAccess(m)).map((m) => m.slug.toLowerCase()); | |
| 83 | 83 | if (request.method === "GET" && noCode.length > 0) { |
| 113 | 113 | } | |
| 114 | 114 | ||
| 115 | 115 | /** | |
| 116 | − | * The dock's modes (docs/WORKSPACE.md, "Shell"). Today is the front page; | |
| 116 | + | * The dock's modes. Today is the front page; | |
| 117 | 117 | * Chat, Agents, Code and Artifacts are where work happens; Notifications | |
| 118 | 118 | * spans them; People is who belongs; Workspace is the workspace itself: | |
| 119 | 119 | * its money, policies and settings; Apps is everything installed that you |
| 1 | 1 | /** | |
| 2 | − | * "Write this up as an artifact" from chat (docs/ARTIFACTS_MODE.md section | |
| 3 | − | * 4.4): where it goes (a space, Private, or shared with the conversation), | |
| 4 | − | * the agents that can write it, and the message that asks one to. Nothing | |
| 5 | − | * is written behind anyone's back: the person posts the ask in the thread, | |
| 6 | − | * as themselves, and the agent answers it the usual way (a session if it | |
| 7 | − | * needs one, then `create_artifact`). The kind is a doc until slides and | |
| 8 | − | * the other kinds ship. Pure, so it is tested on its own; | |
| 2 | + | * "Write this up as an artifact" from chat (docs.g1t.sh/guides/artifacts/, | |
| 3 | + | * "Write a thread up"): where it goes (a space, Private, or shared with | |
| 4 | + | * the conversation), the agents that can write it, and the message that | |
| 5 | + | * asks one to. Nothing is written behind anyone's back: the person posts | |
| 6 | + | * the ask in the thread, as themselves, and the agent answers it the usual | |
| 7 | + | * way (a session if it needs one, then `create_artifact`). The kind is a | |
| 8 | + | * doc until slides and the other kinds ship. Pure, so it is tested on its own; | |
| 9 | 9 | * components/chat/write-up.tsx draws the dialog. | |
| 10 | 10 | */ | |
| 11 | 11 | import type { DocRole, MemberProfile } from "@g1t/contracts"; |
| 383 | 383 | <meta name="apple-mobile-web-app-capable" content="yes" /> | |
| 384 | 384 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> | |
| 385 | 385 | {/* The stylesheet before everything React Router preloads, so a slow | |
| 386 | − | connection paints sooner (docs/research/css-shipping.md). */} | |
| 386 | + | connection paints sooner. */} | |
| 387 | 387 | <link rel="stylesheet" href={appCss} precedence="default" /> | |
| 388 | 388 | {root?.analyticsConsent && <meta name="g1t-analytics" content="consent" />} | |
| 389 | 389 | <Meta /> |
| 141 | 141 | route("-/settings/chat", "routes/workspace/chat-settings.tsx"), | |
| 142 | 142 | route("-/repositories", "routes/workspace/repositories.tsx"), | |
| 143 | 143 | // Agents mode: the overview (budget, sessions, roster, spend) first, then | |
| 144 | − | // each of the workspace's own agents and its sessions (docs/WORKSPACE.md). | |
| 144 | + | // each of the workspace's own agents and its sessions. | |
| 145 | 145 | // `new` is no agent's handle. | |
| 146 | 146 | route("-/agents", "routes/workspace/agents/layout.tsx", [ | |
| 147 | 147 | index("routes/workspace/agents.tsx"), | |
| ⋯ | |||
| 168 | 168 | route("dm/:id", "routes/workspace/chat/channel.tsx", { id: "routes/workspace/chat/dm" }), | |
| 169 | 169 | route(":channel", "routes/workspace/chat/channel.tsx"), | |
| 170 | 170 | ]), | |
| 171 | − | // Artifacts mode (docs/ARTIFACTS_MODE.md; code says "folio"): what its | |
| 171 | + | // Artifacts mode (code says "folio"): what its | |
| 172 | 172 | // pages call as they run (an artifact's live socket, JSON, comments, | |
| 173 | 173 | // uploads, export), then Home, making one, templates, the trash, the | |
| 174 | 174 | // spaces, and each artifact by its address. Addresses are flat and end | |
| ⋯ | |||
| 198 | 198 | route("-/today", "routes/workspace/home.tsx"), | |
| 199 | 199 | route("-/home", "routes/workspace/moved.ts", { id: "routes/workspace/moved-home" }), | |
| 200 | 200 | route("-/apps", "routes/workspace/apps.tsx"), | |
| 201 | − | // What Code's pages say to a member without Code (docs/WORKSPACE.md, "Members without Code"). | |
| 201 | + | // What Code's pages say to a member without Code. | |
| 202 | 202 | route("-/code-access", "routes/workspace/code-access.tsx"), | |
| 203 | 203 | route("-/memory", "routes/workspace/memory.tsx"), | |
| 204 | 204 | route("-/context", "routes/workspace/context.tsx"), | |
| 101 | 101 | overage: "Everything is metered from the first unit at what it costs g1t plus 20%. Unused included usage does not roll over.", | |
| 102 | 102 | }; | |
| 103 | 103 | ||
| 104 | − | /** How each part of a workspace is charged: the plan's Pricing table (docs/WORKSPACE.md). */ | |
| 104 | + | /** How each part of a workspace is charged. */ | |
| 105 | 105 | const WORKSPACE_CHARGES: { what: string; how: string; soon?: boolean }[] = [ | |
| 106 | 106 | { | |
| 107 | 107 | what: "People chatting", |
| 12 | 12 | ||
| 13 | 13 | /** | |
| 14 | 14 | * What a member without Code access sees in place of a repository, an | |
| 15 | − | * issue, a pull request or a project list (docs/WORKSPACE.md, "Members | |
| 16 | − | * without Code"): what it was, and who to ask. | |
| 15 | + | * issue, a pull request or a project list: what it was, and who to ask. | |
| 17 | 16 | */ | |
| 18 | 17 | export async function loader({ params, context, request }: Route.LoaderArgs) { | |
| 19 | 18 | const viewer = requireUser(context, request); |
| 63 | 63 | }; | |
| 64 | 64 | ||
| 65 | 65 | /** | |
| 66 | − | * Artifacts' home (docs/ARTIFACTS_MODE.md section 6.2): search and | |
| 66 | + | * Artifacts' home (docs.g1t.sh/guides/artifacts/, "Home"): search and | |
| 67 | 67 | * filters, All / Yours / Shared with you, tiles to make something new, | |
| 68 | 68 | * and everything you can open, grouped by the day it was last edited, or | |
| 69 | 69 | * as cards. |
| 11 | 11 | import { knownTimeZone } from "../../../lib/time-zone"; | |
| 12 | 12 | ||
| 13 | 13 | /** | |
| 14 | − | * Artifacts mode (docs/ARTIFACTS_MODE.md; code says "folio"): what its | |
| 14 | + | * Artifacts mode (code says "folio"): what its | |
| 15 | 15 | * sidebar shows (the shell draws it from this data: favorites, spaces and | |
| 16 | 16 | * their trees, Private, Shared, projects' docs), who can be mentioned or | |
| 17 | 17 | * shared with, and the viewer's time zone for the home list's days. Each |
| 115 | 115 | * Public pages as someone signed out sees them: the same for every such | |
| 116 | 116 | * visitor, so kept in this data centre's cache. Reserved first segments | |
| 117 | 117 | * (settings, sign-in, invitations and the like) and workspace pages (`-`) | |
| 118 | − | * are never kept; docs/PERFORMANCE.md lists the rules. A repository's kept | |
| 119 | − | * page is served only while repos says the repository is still public: one | |
| 120 | − | * made private or deleted is never served from any data centre's copy. | |
| 118 | + | * are never kept. A repository's kept page is served only while repos | |
| 119 | + | * says the repository is still public: one made private or deleted is | |
| 120 | + | * never served from any data centre's copy. | |
| 121 | 121 | */ | |
| 122 | 122 | const PUBLIC_TOP = /^\/(?:|_root\.data|pricing|explore|security|support|policies(?:\/[a-z-]+)?)(?:\.data)?$/; | |
| 123 | 123 | const PUBLIC_PROJECT = |
| 6 | 6 | // AsyncLocalStorage, for per-request timings and D1 bookmarks | |
| 7 | 7 | // (app/lib/perf.server.ts). | |
| 8 | 8 | "compatibility_flags": ["nodejs_als"], | |
| 9 | − | // Where it runs: docs/PERFORMANCE.md. Off runs it in the data centre | |
| 9 | + | // Where it runs (CONTRIBUTING.md, "Speed"). Off runs it in the data centre | |
| 10 | 10 | // nearest the visitor; scripts/perf/placement-probe.mjs measures the | |
| 11 | 11 | // alternatives and `apply` changes every Worker's at once. | |
| 12 | 12 | "placement": { "mode": "off" }, |
| 1 | 1 | #!/usr/bin/env node | |
| 2 | 2 | // Runs g1t's pull request reviewer over ReviewBench and scores it with the | |
| 3 | − | // benchmark's own judge. docs/research/reviewbench.md explains the design. | |
| 3 | + | // benchmark's own judge. | |
| 4 | 4 | // | |
| 5 | 5 | // node bench/reviewbench/run.mjs setup clone ReviewBench (pinned) and install its judge | |
| 6 | 6 | // node bench/reviewbench/run.mjs build build the agent image from the runner base image | |
| ⋯ | |||
| 114 | 114 | * growing context every turn, mostly from cache: turns and context grow | |
| 115 | 115 | * with the size of the change. Calibrate against real review runs (the | |
| 116 | 116 | * billing ledger records each run's cost) before trusting the absolute | |
| 117 | − | * numbers; see docs/research/reviewbench.md. | |
| 117 | + | * numbers. | |
| 118 | 118 | */ | |
| 119 | 119 | function estimateOne(p, model) { | |
| 120 | 120 | const [, output, cacheRead, cacheWrite] = PRICES[model] ?? PRICES[DEFAULT_MODEL]; | |
| 1 | 1 | //! g1t's own workflows (`.g1t/workflows/*.yml`), read by the same parser | |
| 2 | 2 | //! and expressions the actions service runs them with: each reads, nothing | |
| 3 | 3 | //! in it is unsupported, and the deploy workflow's jobs start, wait and | |
| 4 | − | //! stop as docs/DEPLOYING.md says. | |
| 4 | + | //! stop as docs.g1t.sh/guides/deploy-to-cloudflare/ says. | |
| 5 | 5 | ||
| 6 | 6 | use std::path::PathBuf; | |
| 7 | 7 |
| 1 | 1 | //! Repository backups: a nightly `git bundle` of every repository whose | |
| 2 | − | //! refs changed, kept outside the git store (docs/ARTIFACTS.md, R11). | |
| 2 | + | //! refs changed, kept outside the git store. | |
| 3 | 3 | //! | |
| 4 | 4 | //! The repos service decides what is due and keeps the bundles and their | |
| 5 | 5 | //! manifests (services/repos/src/backups.rs). It cannot run git, so the |
| 3733 | 3733 | pub by: String, | |
| 3734 | 3734 | } | |
| 3735 | 3735 | ||
| 3736 | − | // ---- Platform pauses and the usage watcher (docs/SPEND-GUARDRAILS.md) ---- | |
| 3736 | + | // ---- Platform pauses and the usage watcher ---- | |
| 3737 | + | // docs.g1t.sh/guides/deploy-to-cloudflare/#spend-guardrails | |
| 3737 | 3738 | ||
| 3738 | 3739 | /// A g1t-wide pause, set by staff in sudo or by billing's hourly usage | |
| 3739 | 3740 | /// watcher on a severe breach. Each level is independent. |
| 480 | 480 | "restore_package_version", | |
| 481 | 481 | // Sharing an artifact and deleting one for good are for people: an | |
| 482 | 482 | // agent shares only through the agents service, with the people | |
| 483 | − | // already in its conversation (docs/ARTIFACTS_MODE.md, section 4.3). | |
| 483 | + | // already in its conversation. | |
| 484 | 484 | "set_workspace_artifact_access", | |
| 485 | 485 | "purge_workspace_artifact", | |
| 486 | 486 | ]; |
| 1 | − | //! Datasets: the safe query layer behind dashboards (Artifacts mode, | |
| 2 | − | //! docs/ARTIFACTS_MODE.md section 3.4). Mirrors | |
| 3 | − | //! `packages/contracts/src/datasets.ts`; the tests here keep the catalog the | |
| 4 | − | //! same and run both validators over `datasets.fixtures.json`. | |
| 1 | + | //! Datasets: the safe query layer behind dashboards (Artifacts mode). | |
| 2 | + | //! Mirrors `packages/contracts/src/datasets.ts`; the tests here keep the | |
| 3 | + | //! catalog the same and run both validators over `datasets.fixtures.json`. | |
| 5 | 4 | //! | |
| 6 | 5 | //! Not SQL: a query names a dataset from a declared catalog, one measure, | |
| 7 | 6 | //! at most one dimension, an interval, filters on declared fields and a |
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
Binary or large file; its contents are not shown.
This change is too large to show in full.