Skip to content
232 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Fast pages, required checks on the branch, self-hosted runners, honest incidents1/**
2 * The pure parts of request timing and D1 read consistency for the site:
3 * the `g1t_d1` cookie, which session each service call asks for, which
4 * calls may write, and the `Server-Timing` header. perf.server.ts holds
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.5 * the per-request state; CONTRIBUTING.md, "Speed", says how to use it.
Fast pages, required checks on the branch, self-hosted runners, honest incidents6 */
7
8/** The cookie that carries D1 bookmarks between a person's requests. */
9export const D1_COOKIE = "g1t_d1";
10
11/**
12 * How long after a write the services a request did not get a bookmark
13 * from read their primary. A write can reach a service the site did not
14 * call itself (work writing to repos, say), whose bookmark the site never
15 * sees; D1 replicas trail the primary by well under a second, so 30
16 * seconds covers that with room to spare.
17 */
18export const PRIMARY_WINDOW_SECONDS = 30;
19
20/** How long the bookmarks are kept: far longer than any replica trails. */
21export const D1_COOKIE_MAX_AGE = 300;
22
23/**
24 * The services whose RPCs open a D1 session (crates/kit/src/d1.rs and
25 * @g1t/contracts d1.ts), by the name the site gives their binding.
26 */
27export const SESSION_SERVICES = new Set(["identity", "repos", "work", "search", "billing", "projects", "deployments"]);
28
29/** What the site remembers from a person's last writes. */
30export type Bookmarks = {
31 /** Unix seconds of the last request that may have written, if recent. */
32 at: number | null;
33 /** The latest bookmark each service returned after it. */
34 services: Record<string, string>;
35};
36
37const BOOKMARK = /^[0-9A-Za-z-]{1,256}$/;
38const SERVICE = /^[a-z]{1,32}$/;
39
40/** Reads the `g1t_d1` cookie; anything malformed is dropped. */
41export function readBookmarks(cookieHeader: string | null): Bookmarks {
42 const empty: Bookmarks = { at: null, services: {} };
43 if (!cookieHeader) return empty;
44 const match = new RegExp(`(?:^|;\\s*)${D1_COOKIE}=([^;]*)`).exec(cookieHeader);
45 if (!match) return empty;
46 const found: Bookmarks = { at: null, services: {} };
47 for (const entry of match[1].split("~")) {
48 const colon = entry.indexOf(":");
49 if (colon < 1) continue;
50 const key = entry.slice(0, colon);
51 const value = entry.slice(colon + 1);
52 if (key === "at") {
53 const at = Number(value);
54 if (Number.isSafeInteger(at) && at > 0) found.at = at;
55 } else if (SERVICE.test(key) && SESSION_SERVICES.has(key) && BOOKMARK.test(value)) {
56 found.services[key] = value;
57 }
58 }
59 return found;
60}
61
62/** The `g1t_d1` cookie's value. */
63export function writeBookmarks(bookmarks: Bookmarks): string {
64 const entries = bookmarks.at ? [`at:${bookmarks.at}`] : [];
65 for (const [service, bookmark] of Object.entries(bookmarks.services).sort()) {
66 if (SESSION_SERVICES.has(service) && BOOKMARK.test(bookmark)) entries.push(`${service}:${bookmark}`);
67 }
68 return entries.join("~");
69}
70
71/** The `Set-Cookie` value for `bookmarks`. */
72export function bookmarkCookie(bookmarks: Bookmarks, secure: boolean): string {
73 return `${D1_COOKIE}=${writeBookmarks(bookmarks)}; Path=/; HttpOnly;${secure ? " Secure;" : ""} SameSite=Lax; Max-Age=${D1_COOKIE_MAX_AGE}`;
74}
75
76/**
77 * What a call to `service` sends as `x-d1-bookmark`, or null for none
78 * (that service reads its primary, as before sessions):
79 *
80 * - A request that writes (any method but GET and HEAD) starts every
81 * session on the primary, so what it checks before writing is current.
82 * - Within `PRIMARY_WINDOW_SECONDS` of the person's last write, the
83 * primary, for every service: that write may have reached a service
84 * through another one, whose bookmark the site never saw.
85 * - Otherwise the bookmark that service returned after that write: never
86 * older than what they did, however far a replica trails.
87 * - Otherwise the nearest copy.
88 */
89export function sessionFor(service: string, bookmarks: Bookmarks, writing: boolean, nowSeconds: number): string | null {
90 if (!SESSION_SERVICES.has(service)) return null;
91 if (writing) return "first-primary";
92 if (bookmarks.at && nowSeconds - bookmarks.at < PRIMARY_WINDOW_SECONDS) return "first-primary";
93 return bookmarks.services[service] ?? "first-unconstrained";
94}
95
96/**
97 * Methods that only read. Anything not listed is taken to write, so a new
98 * method errs towards a cookie and a primary read, never a stale page.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily99 * `get`, `list`, `queue` and `pulls_for_repos` were missing: every project
100 * page called `get` (repos, projects), so every one set the cookie, was
101 * never kept in the public cache, and sent the next 30 s of the person's
Public pages cacheable again: a signed-out GET never sets g1t_d1; stars, about and public_links are reads102 * reads to the primary. `stars`, `about` and `public_links` (2026-10-08)
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25103 * did the same to every project page and Explore for 13 hours. `tags`,
104 * `commit_checks` and `shortcuts` (every overview), and the Files page's
105 * `last_commits`, `languages`, `contributors` and `license`, still made a
106 * signed-in view count as a write until 2026-10-08: the cookie, 30 s of
107 * primary reads, and no sidebar cache after every overview.
Fast pages, required checks on the branch, self-hosted runners, honest incidents108 */
109const READS = new Set(
110 (
Chat controls, public profiles, shadcn selects, and no Docs tab in a project111 "account active_agents all_ids get list queue pulls_for_repos blame blob branches by_author by_repo contributions catalog check_invite check_limit " +
Fast pages, required checks on the branch, self-hosted runners, honest incidents112 "check_workspace_deletion check_workspace_rename collaborator_permission compare counts deleted deliveries " +
Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy113 "domains entitlements entity explore features git_access has_feature invoices ledger limit " +
Fast pages, required checks on the branch, self-hosted runners, honest incidents114 "limit_requests links log logs managed_pulls memories_by_id memory_context my_repo_invitations " +
115 "outside_collaborators overview path_by_id prices profile profile_workspaces public_namespaces read_session " +
116 "readable ready_issues references registration repo_access resolve resolve_branch resolve_path resolve_slug " +
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.117 "routes run run_context run_cost runner_activity runner_groups runner_settings runners runs scorecards search search_memories usage_report " +
Fast pages, required checks on the branch, self-hosted runners, honest incidents118 "settings statement statement_entries status status_by_id suggest tree usage usage_meters user_by_username " +
Merge main into Artifacts Phase 2119 "user_for_session user_for_access_token usernames waiting_workspaces workflows workspace workspace_invites github_enabled " +
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25120 "stars about public_links branch_drift tags last_commits languages contributors license releases release " +
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.121 "stargazers starred commit_checks shortcuts spend person_budgets usage_report templates install_requests extension_installs " +
Agents → Skills is the workspace's skill library in the open SKILL.md format: write a skill in g1t, import a SKILL.md or zip, link a repository whose .g1t/skills folders publish on every push, or save a finished session as a skill once a person reviews it; attach a skill to an agent, a team or the whole workspace, each attachment pinned to a version. Agents see each skill's name and when to use it, and read the rest with use_skill when a request needs it; a skill never adds a permission, and scripts wait for an agent's own computer. The agent skills guide says how.122 "people_directory team_agents team_context team_members github_visible_installations skill_library skill agent_skills"
Fast pages, required checks on the branch, self-hosted runners, honest incidents123 ).split(" "),
124);
125
Public pages cacheable again: a signed-out GET never sets g1t_d1; stars, about and public_links are reads126/**
127 * Whether a response sets the `g1t_d1` cookie. A signed-out GET never
128 * does, whatever it called: nobody signed out can write anything their
129 * next page must read, and a cookie keeps the page out of the public
130 * cache. Signing in on a GET (GitHub) starts a session, so it does.
131 */
132export function setsBookmark(request: { writing: boolean; wrote: boolean; hasSession: boolean; signedIn: boolean }): boolean {
133 if (request.writing || request.signedIn) return true;
134 return request.wrote && request.hasSession;
135}
136
Fast pages, required checks on the branch, self-hosted runners, honest incidents137/** Whether an RPC to `method` may write. */
138export function mayWrite(method: string): boolean {
139 if (READS.has(method)) return false;
140 return !(method.startsWith("get_") || method.startsWith("list_"));
141}
142
143/** The method name of an RPC URL (`https://service/rpc/<method>`). */
144export function rpcMethodOf(input: string): string {
145 const at = input.indexOf("/rpc/");
146 return at < 0 ? "" : input.slice(at + 5).split(/[?#]/)[0];
147}
148
149/** One service's calls during a request. */
150export type ServiceTiming = {
151 calls: number;
152 /** Summed wall time of its calls, from here. */
153 wallMs: number;
154 /** Summed time its own `server-timing: svc;dur` reported. */
155 serviceMs: number;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily156 /**
157 * Of that, summed time it reported waiting on its database
158 * (`db;dur`, crates/kit/src/d1.rs `Timing`) and in how many round trips.
159 * Absent for services that do not time them.
160 */
161 dbMs?: number;
162 dbTrips?: number;
Fast pages, required checks on the branch, self-hosted runners, honest incidents163};
164
165/** The `svc;dur=N` a service reports, or null. */
166export function serviceDuration(header: string | null): number | null {
167 if (!header) return null;
168 const match = /(?:^|,)\s*svc;dur=([0-9.]+)/.exec(header);
169 return match ? Number(match[1]) : null;
170}
171
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily172/**
173 * The `db;dur=N;desc="T round trips, …"` a service reports: its summed
174 * database time and round trips, or null.
175 */
176export function databaseTime(header: string | null): { ms: number; trips: number } | null {
177 if (!header) return null;
178 const match = /(?:^|,)\s*db;dur=([0-9.]+)(?:;desc="(\d+) round trips?)?/.exec(header);
179 return match ? { ms: Number(match[1]), trips: Number(match[2] ?? 0) } : null;
180}
181
Fast pages, required checks on the branch, self-hosted runners, honest incidents182/** Total time covered by overlapping [start, end] intervals. */
183export function coveredMs(intervals: [number, number][]): number {
184 const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
185 let total = 0;
186 let end = -Infinity;
187 let start = -Infinity;
188 for (const [from, to] of sorted) {
189 if (from > end) {
190 if (end > start) total += end - start;
191 start = from;
192 end = to;
193 } else if (to > end) {
194 end = to;
195 }
196 }
197 if (end > start) total += end - start;
198 return total;
199}
200
201/** A Server-Timing metric name: a token, so `/` and spaces become `.`. */
202export function metricName(name: string): string {
203 return name.replace(/^routes\//, "").replace(/[^A-Za-z0-9_.-]+/g, ".");
204}
205
206/**
207 * The `Server-Timing` header for a request: the whole, the loaders, the
208 * time spent waiting on services (overlap counted once), then each
209 * service. DevTools shows them in this order under Network → Timing.
210 */
211export function serverTiming(input: {
212 totalMs: number;
213 loaders: { id: string; ms: number; kind: "loader" | "action" }[];
214 rpcMs: number;
215 services: Record<string, ServiceTiming>;
216 sessions: string;
217}): string {
218 const parts = [`total;dur=${input.totalMs};desc="web to first byte"`];
219 for (const loader of input.loaders) {
220 parts.push(`${loader.kind}.${metricName(loader.id)};dur=${loader.ms}`);
221 }
222 const calls = Object.values(input.services).reduce((sum, timing) => sum + timing.calls, 0);
223 if (calls > 0) parts.push(`rpc;dur=${input.rpcMs};desc="${calls} service calls, overlap counted once"`);
224 const ranked = Object.entries(input.services).sort((a, b) => b[1].wallMs - a[1].wallMs);
225 for (const [name, timing] of ranked) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily226 const db = timing.dbTrips ? `, db ${timing.dbMs ?? 0}ms in ${timing.dbTrips} round trip${timing.dbTrips === 1 ? "" : "s"}` : "";
227 const inside = timing.serviceMs > 0 ? `, ${timing.serviceMs}ms inside${db}` : "";
Fast pages, required checks on the branch, self-hosted runners, honest incidents228 parts.push(`${metricName(name)};dur=${timing.wallMs};desc="${timing.calls} call${timing.calls === 1 ? "" : "s"}${inside}"`);
229 }
230 if (input.sessions) parts.push(`d1;desc="${input.sessions}"`);
231 return parts.join(", ");
232}

This file's history is long; its oldest lines are credited to the oldest commit read.