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
165 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.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1/**
2 * Run credentials and the audit log. Mirrors `crates/contracts/src/credentials.rs`
3 * and `crates/contracts/src/audit.rs`.
4 *
5 * Every sandbox run gets its own tokens, bound to the run, its repository
6 * and what its kind of work needs, and acting as an agent on behalf of the
7 * person who started the work. Everything done with them is recorded in
8 * the workspace's audit log, with the rule that allowed or refused it.
9 */
10
11import type { ServiceBinding } from "./clients";
12import type { RepoPath } from "./repos";
13import type { User } from "./identity";
14
15/** What a run does, as far as its credentials are concerned. */
16export type RunCredentialKind =
17 | "implement"
18 | "revise"
19 | "review"
20 | "answer"
21 | "update"
22 | "plan"
23 | "checks"
24 | "queue"
25 | "mergecheck"
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily26 | "deploy"
27 | "bump";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas28
29/**
30 * `runner`: g1t's runner in the sandbox, which clones, pushes and records
31 * the session, acting downstream as the person. `tools`: the agent's own
32 * MCP tools, acting as the agent.
33 */
34export type CredentialUse = "runner" | "tools";
35
36/** A repository a run may push to; `branch` null means any branch of it. */
37export type GitGrant = { repo: RepoPath; branch?: string | null };
38
39/** What binds an agent's token to one run. */
40export type RunBinding = {
41 kind: RunCredentialKind;
42 use: CredentialUse;
43 runId?: string | null;
44 number?: number | null;
45 agent: string;
46 read: RepoPath[];
47 push: GitGrant[];
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily48 /** g1t's own run: the workspace's credential, acting on behalf of g1t. */
49 system?: boolean;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas50};
51
52/** Set on an agent resolved from its token: the composite identity. */
53export type Acting = {
54 credentialId: string;
55 agent: string;
56 onBehalfOf: { id: string; username: string };
57 scope: { repo: RepoPath; operations: string[]; run?: RunBinding };
58};
59
60export type CreateRunCredentialInput = {
61 onBehalfOf: User;
62 repo: RepoPath;
63 kind: RunCredentialKind;
64 use: CredentialUse;
65 /** The pull request the run works on. */
66 number?: number | null;
67 /** Repositories it may clone besides those it may push to. */
68 read?: RepoPath[];
69 push?: GitGrant[];
70 /** The run's timeout. */
71 ttlSeconds: number;
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent72 /** Defaults to `g1t`. */
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas73 agent?: string | null;
74};
75
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily76export type ActorKind = "person" | "agent" | "workspace" | "runner" | "system";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas77export type AuditOutcome = "allowed" | "denied";
78/** Where it came in: the API, MCP, git, or g1t.sh's own pages. */
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member79export type AuditSurface = "rest" | "mcp" | "git" | "web" | "registry";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas80
81export type AuditEntry = {
82 id: string;
83 /** RFC 3339. */
84 time: string;
85 actorKind: ActorKind;
86 /** A person's username, the agent's name, or a workspace's slug. */
87 actor: string;
88 actorId: string;
89 agent: string | null;
90 /** The person an agent acted for. */
91 onBehalfOf: string | null;
92 runId: string | null;
93 runKind: string | null;
94 credentialId: string | null;
95 /** An operation such as `create_issue`, or `git.push` and `git.fetch`. */
96 action: string;
97 surface: AuditSurface;
98 workspace: string;
99 /** `owner/name`. */
100 repo: string | null;
101 number: number | null;
102 gitRef: string | null;
103 path: string | null;
104 outcome: AuditOutcome;
105 /** The rule that allowed or refused it, such as `run:implement/tools` or `scope:repository`. */
106 rule: string;
107 /** `ok`, or the failure's code. */
108 result: string | null;
109 message: string | null;
110 requestId: string;
111};
112
113/**
114 * Which of a workspace's entries the viewer may see: an owner everything;
115 * a member what was done to its projects, and what they did or had done
116 * on their behalf.
117 */
118export type AuditVisibility = { kind: "all" } | { kind: "projects"; username: string };
119
120export type AuditQuery = {
121 workspace: string;
122 visibility: AuditVisibility;
123 actor?: string | null;
124 agent?: string | null;
125 action?: string | null;
126 repo?: string | null;
127 number?: number | null;
128 outcome?: AuditOutcome | null;
129 actorKind?: ActorKind | null;
130 runIds?: string[];
131 /** RFC 3339, inclusive. */
132 since?: string | null;
133 /** RFC 3339, exclusive. */
134 until?: string | null;
135 before?: string | null;
136 /** At most 500. */
137 limit?: number | null;
138};
139
140export type AuditPage = { entries: AuditEntry[]; next: string | null };
141
142export interface AuditApi {
143 /** Newest first. The caller has checked who may see the workspace's log. */
144 list(query: AuditQuery): Promise<AuditPage>;
145}
146
147/** The audit log, which the events service keeps. */
148export function auditClient(events: ServiceBinding): AuditApi {
149 return {
150 list: async (query) => {
151 const response = await events.fetch("https://service/rpc/audit_list", {
152 method: "POST",
153 headers: { "content-type": "application/json" },
154 body: JSON.stringify(query),
155 });
156 if (!response.ok) throw new Error(`audit_list failed with status ${response.status}`);
157 return (await response.json()) as AuditPage;
158 },
159 };
160}
161
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent162/** How an actor is shown: "g1t on behalf of syntaqx". */
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas163export function describeActor(entry: Pick<AuditEntry, "actor" | "agent" | "onBehalfOf">): string {
164 return entry.onBehalfOf ? `${entry.agent ?? entry.actor} on behalf of ${entry.onBehalfOf}` : entry.actor;
165}

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