Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
77 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.

The Packages page names every registry it has1/**
2 * Run credentials: the tokens a sandbox works with. Each is bound to its
The runner no longer runs g1t's own agents. Cloud and self-hosted runners now do what a forge's runner does: Actions jobs, required checks, merge checks and merge-queue test runs, deployments, dependency bumps and backups. Everything that started Claude Code in a sandbox on an issue or pull request is gone, with its prompts, model access, mentions, delegation, plans, reviews, revisions and the workspace agents' computers.3 * run, its repository and what that kind of run needs, acts on behalf of
4 * the person (or g1t itself) the work is for, and is revoked the moment
The Packages page names every registry it has5 * the sandbox stops. See `crates/contracts/src/credentials.rs` for what
6 * each kind of run may do.
7 */
8
The runner no longer runs g1t's own agents. Cloud and self-hosted runners now do what a forge's runner does: Actions jobs, required checks, merge checks and merge-queue test runs, deployments, dependency bumps and backups. Everything that started Claude Code in a sandbox on an issue or pull request is gone, with its prompts, model access, mentions, delegation, plans, reviews, revisions and the workspace agents' computers.9import type { CreateRunCredentialInput, ServiceBinding } from "@g1t/contracts";
The Packages page names every registry it has10
The runner no longer runs g1t's own agents. Cloud and self-hosted runners now do what a forge's runner does: Actions jobs, required checks, merge checks and merge-queue test runs, deployments, dependency bumps and backups. Everything that started Claude Code in a sandbox on an issue or pull request is gone, with its prompts, model access, mentions, delegation, plans, reviews, revisions and the workspace agents' computers.11/** The environment variable a sandbox's g1t token is passed in. */
12export const CREDENTIAL_VARS = ["G1T_TOKEN"] as const;
The Packages page names every registry it has13
14const STORAGE_KEY = "credentials";
15
16/** Identity's JSON protocol, as the contracts' client speaks it. */
17async function call<T>(identity: ServiceBinding, method: string, args: object): Promise<T> {
18 const response = await identity.fetch(`https://service/rpc/${method}`, {
19 method: "POST",
20 headers: { "content-type": "application/json" },
21 body: JSON.stringify(args),
22 });
23 if (!response.ok) throw new Error(`${method} failed with status ${response.status}`);
24 return (await response.json()) as T;
25}
26
27/** A run credential's text. */
28export async function runCredential(identity: ServiceBinding, input: CreateRunCredentialInput): Promise<string> {
29 const { token } = await call<{ token: string }>(identity, "create_run_credential", input);
30 return token;
31}
32
33/** SHA-256 in lowercase hex, as identity stores tokens. */
34export async function sha256Hex(text: string): Promise<string> {
35 const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
36 return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
37}
38
39/** The hashes of the g1t tokens among a sandbox's variables. */
40export async function credentialHashes(envVars: Record<string, string>): Promise<string[]> {
41 const tokens = CREDENTIAL_VARS.map((name) => envVars[name]).filter(
42 (value): value is string => typeof value === "string" && value.startsWith("g1t_"),
43 );
44 return Promise.all(tokens.map(sha256Hex));
45}
46
47type Storage = {
48 put(key: string, value: unknown): Promise<void>;
49 get<T>(key: string): Promise<T | undefined>;
50 delete(key: string): Promise<boolean>;
51};
52
53/**
54 * Remembers a sandbox's credentials, by hash, so they can be revoked when
55 * it stops, and ties them to the run it recorded. Never stops the sandbox
56 * from starting.
57 */
58export async function holdCredentials(
59 identity: ServiceBinding,
60 storage: Storage,
61 envVars: Record<string, string>,
62 runId: string | null,
63): Promise<void> {
64 const hashes = await credentialHashes(envVars);
65 if (hashes.length === 0) return;
66 await storage.put(STORAGE_KEY, hashes);
67 if (!runId) return;
68 await call(identity, "bind_run_credentials", { tokenHashes: hashes, runId }).catch((error: unknown) => console.log("run credentials not bound", runId, String(error)));
69}
70
The runner no longer runs g1t's own agents. Cloud and self-hosted runners now do what a forge's runner does: Actions jobs, required checks, merge checks and merge-queue test runs, deployments, dependency bumps and backups. Everything that started Claude Code in a sandbox on an issue or pull request is gone, with its prompts, model access, mentions, delegation, plans, reviews, revisions and the workspace agents' computers.71/** Ends a sandbox's g1t credentials, once. */
72export async function revokeCredentials(identity: ServiceBinding, storage: Storage): Promise<void> {
The Packages page names every registry it has73 const hashes = await storage.get<string[]>(STORAGE_KEY);
74 if (!hashes?.length) return;
75 await storage.delete(STORAGE_KEY);
76 await call(identity, "revoke_run_credentials", { tokenHashes: hashes }).catch((error: unknown) => console.log("run credentials not revoked", String(error)));
77}

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