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

README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise1//! Types and service interfaces shared by every g1t service.
2//!
3//! Each service has a module here holding the data it exchanges and the
4//! arguments of each of its methods. Services and their callers depend on
5//! this crate, never on each other's code.
6
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb977pub mod about;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise8pub mod access;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r99pub mod advisories;
g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other.10pub mod agent_sessions;
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)11pub mod account_deletion;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise12pub mod accounts;
13pub mod actions;
14pub mod agents;
15pub mod audit;
16pub mod backups;
17pub mod billing;
Merge checks: statuses and check runs on every commit18pub mod checks;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar19pub mod codeowners;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r920pub mod coding_agents;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise21pub mod credentials;
Artifacts contracts: folios, their four kinds, dashboard datasets, folio events and the artifacts scopes are typed and validated the same in TypeScript and Rust, with nothing using them yet22pub mod datasets;
Merge branch 'worktree-agent-ab8033ab973f8d99a'23pub mod decisions;
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca24pub mod deploy_keys;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise25pub mod events;
Artifacts contracts: folios, their four kinds, dashboard datasets, folio events and the artifacts scopes are typed and validated the same in TypeScript and Rust, with nothing using them yet26pub mod folios;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise27pub mod github;
28pub mod guardrails;
29pub mod identity;
Inbox: the events service tells people what needs them as events arrive30pub mod inbox;
Merge branch 'worktree-agent-a5c94df323642630e'31pub mod live;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise32pub mod integrations;
Merge main (membership, two-factor, GitHub repo roles) into tokens33pub mod members;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise34mod ids;
35mod names;
36mod outcome;
37pub mod packages;
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.38pub mod people;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise39pub mod projects;
40pub mod repos;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge41pub mod rules;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise42pub mod runners;
43pub mod scopes;
44pub mod search;
45pub mod security;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar46pub mod teams;
47pub mod security_suite;
Merge branch 'worktree-agent-ad8a36dfcd4176015' into spend-guardrails48pub mod subscribers;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise49pub mod time;
Fine-grained personal tokens, workspace token rules and approvals in identity50pub mod tokens;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar51pub mod updates;
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise52pub mod webhooks;
53pub mod work;
54
Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales.55pub use ids::{id_floor, new_id};
Merge branch 'worktree-agent-a8385d293d42c913a'56pub use names::{
A workflow's `uses: actions/setup-node@v4` means GitHub's `actions` again, whoever is on g1t. g1t looked for a repository on g1t before GitHub, and anyone could make a public workspace called `actions`, `docker` or `aws-actions` and have its `setup-node` run in every workflow that names GitHub's, with that job's secrets. Now another workspace's public repository answers only for a name GitHub has no public repository of; the caller's own workspace and g1t's own `g1t/…` still answer from g1t, and when GitHub can't be asked, GitHub's is used. The names of the GitHub accounts whose actions workflows use most can no longer be registered, renamed to, or taken by signing up with GitHub (which suggests the name with `-gh`); an account that has one keeps it, and mentions and invitations still find it. The Actions, authentication and workspaces guides say so.57 Username, aliasable_name, claimable_namespace, is_action_owner_name, is_claimable_name, claimable_username, is_namespace_shaped, is_reserved_name, is_route_name,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers58 is_valid_namespace, is_valid_repo_name,
Merge branch 'worktree-agent-a8385d293d42c913a'59};
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise60pub use outcome::{Failure, FailureCode, Outcome};
61
62use serde::{Deserialize, Serialize};
63
Merge main (membership, two-factor, GitHub repo roles) into tokens64/// What a member may do in a workspace. A member may also hold
65/// [`members::OrgRole`]s, which add to it.
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise66#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "lowercase")]
68pub enum Role {
Merge main (membership, two-factor, GitHub repo roles) into tokens69 /// Everything: Admin on every repository, the workspace's members,
70 /// settings, billing and security.
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise71 Owner,
Merge main (membership, two-factor, GitHub repo roles) into tokens72 /// The workspace's base permission on each repository, and what its
73 /// member privileges allow (see [`members::MemberPrivileges`]).
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise74 Member,
75}
76
Merge main (membership, two-factor, GitHub repo roles) into tokens77pub use members::{MemberPrivileges, OrgRole};
78
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise79/// One workspace a user belongs to.
80#[derive(Clone, Debug, Serialize, Deserialize)]
81pub struct Membership {
82 /// The workspace's name in URLs: `g1t.sh/<slug>`.
83 pub slug: String,
84 pub role: Role,
85 /// The workspace's display name, for showing it to people. Set when a
86 /// user is resolved from credentials; absent on principals made up by
87 /// a service.
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub name: Option<String>,
90 /// The workspace's uploaded icon: the SHA-256 of its bytes, served at
91 /// `/avatars/<avatar>`. Absent means the generated letter avatar.
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub avatar: Option<String>,
94 /// What a member gets on each of the workspace's repositories: the
95 /// workspace's base permission. Set when a user is resolved from
96 /// credentials; absent means the default, Write. Owners have Admin
97 /// whatever it says. See [`access`].
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub base_permission: Option<access::BasePermission>,
Merge branch 'worktree-agent-ad7c6d88d93adc817'100 /// Who may create the workspace's teams. Set when a user is resolved
101 /// from credentials; absent means the default, any member. See
102 /// [`teams::TeamCreation`].
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub team_creation: Option<teams::TeamCreation>,
Merge main (membership, two-factor, GitHub repo roles) into tokens105 /// The roles the member holds besides `role`: billing manager,
106 /// security manager. Set when a user is resolved from credentials.
107 #[serde(default, skip_serializing_if = "Vec::is_empty")]
108 pub org_roles: Vec<OrgRole>,
109 /// What the workspace lets members (and repository admins) do. Set
110 /// when a user is resolved from credentials; absent means the
111 /// defaults. See [`members::MemberPrivileges`].
112 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub privileges: Option<MemberPrivileges>,
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise114}
115
116impl Membership {
117 /// A plain member of `slug`, as services act inside one workspace.
118 pub fn member(slug: impl Into<String>) -> Self {
119 Membership {
120 slug: slug.into(),
121 role: Role::Member,
122 name: None,
123 avatar: None,
124 base_permission: None,
Merge branch 'worktree-agent-ad7c6d88d93adc817'125 team_creation: None,
Merge main (membership, two-factor, GitHub repo roles) into tokens126 org_roles: Vec::new(),
127 privileges: None,
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise128 }
129 }
Merge main (membership, two-factor, GitHub repo roles) into tokens130
131 /// Whether the member holds `role` besides owner or member.
132 pub fn has(&self, role: OrgRole) -> bool {
133 self.org_roles.contains(&role)
134 }
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise135}
136
137/// What a set of credentials resolved to.
138#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
139#[serde(rename_all = "lowercase")]
140pub enum PrincipalKind {
141 /// A person's account.
142 #[default]
143 User,
144 /// A workspace, acting through one of its own access tokens. Its `id`
145 /// is the workspace's, its `username` the workspace's slug, and it is a
146 /// member of that workspace and no other.
147 Workspace,
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.148 /// A run's token: a sandbox g1t started (checks, the merge queue, a
149 /// merge check), acting through a token that lives as long as its run
150 /// and can do only what that token's scope lists, in one repository.
151 /// Its `username` is `g1t`. g1t no longer runs agents of its own; the
152 /// name stays because identity still issues run credentials with it and
153 /// historical rows written by `@g1t`'s agent were recorded as it.
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise154 Agent,
155 /// g1t itself: the platform acting on its own, as when it opens a
156 /// pull request to upgrade a vulnerable dependency or merges from the
157 /// queue. Never resolved from credentials: only services make one,
158 /// with [`User::system`]. Its `username` is `g1t`, which nobody can
159 /// register.
160 System,
161}
162
163/// g1t's own identity, as [`PrincipalKind::System`] work is recorded.
164pub mod system {
165 /// Its id wherever an author or actor id is stored.
166 pub const ID: &str = "g1t";
167 /// Its name, shown as the author of what it does.
168 pub const USERNAME: &str = "g1t";
169 /// The address on the commits it makes, which no mailbox receives.
170 pub const EMAIL: &str = "g1t@users.noreply.g1t.sh";
171 /// Ids that earlier versions stored for g1t's own actions, such as a
172 /// merge its settings made. Read as g1t too.
173 pub const LEGACY_IDS: [&str; 3] = ["g1t_policy", "svc_runner", "g1t_runner"];
174
175 /// Whether `id` is g1t's own.
176 pub fn is_system_id(id: &str) -> bool {
177 id == ID || LEGACY_IDS.contains(&id)
178 }
179}
180
181#[derive(Clone, Debug, Default, Serialize, Deserialize)]
182pub struct User {
183 pub id: String,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers184 /// Lowercased: what the person is found, linked and mentioned by.
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise185 pub username: String,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers186 /// The username as its owner wrote it (`Ana`), when that differs from
187 /// `username`: what pages show. Set on the signed-in person and on
188 /// people looked up by name; absent elsewhere, where `username` is shown.
189 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub display_username: Option<String>,
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise191 #[serde(default)]
192 pub kind: PrincipalKind,
193 /// Whether the account's email address has been confirmed. Unverified
194 /// accounts can sign in but cannot create or change anything.
195 #[serde(default)]
196 pub verified: bool,
197 /// The workspaces this user belongs to. Filled in when a user is
198 /// resolved from credentials, so any service can authorize from it.
199 #[serde(default)]
200 pub workspaces: Vec<Membership>,
201 /// The person's uploaded avatar: the SHA-256 of its bytes, served at
202 /// `/avatars/<avatar>`. Absent means the generated letter avatar.
203 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub avatar: Option<String>,
205 /// Set on an agent resolved from its token: who it acts for, with which
206 /// credential, and what it may do. See [`credentials`].
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub acting: Option<Box<credentials::Acting>>,
209 /// The repositories this user has been given a role on directly,
210 /// whether or not they belong to its workspace. Filled in with
211 /// `workspaces`; see [`access`].
212 #[serde(default, skip_serializing_if = "Vec::is_empty")]
213 pub grants: Vec<access::RepoGrant>,
214 /// Set on a user resolved from an access token: its scopes and the
215 /// workspaces or repositories it is limited to. Absent on a signed-in
216 /// session and on an agent (whose `acting` scope applies instead).
217 /// See [`scopes`].
218 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub token: Option<Box<scopes::TokenAccess>>,
Merge main (membership, two-factor, GitHub repo roles) into tokens220 /// The workspaces this person belongs to but cannot use until they
221 /// meet its policy, such as turning on two-factor authentication.
222 /// They are left out of `workspaces` and `grants` meanwhile. Set when
223 /// a person is resolved from a session.
224 #[serde(default, skip_serializing_if = "Vec::is_empty")]
225 pub held: Vec<members::PolicyHold>,
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise226}
227
228impl User {
229 /// g1t itself, acting in `workspace`: what the platform's own work,
230 /// such as security updates, is done and recorded as.
231 pub fn system(workspace: &str) -> User {
232 User {
233 id: system::ID.to_owned(),
234 username: system::USERNAME.to_owned(),
235 kind: PrincipalKind::System,
236 verified: true,
237 workspaces: vec![Membership::member(workspace.to_lowercase())],
238 ..User::default()
239 }
240 }
241
242 /// Whether this is g1t itself.
243 pub fn is_system(&self) -> bool {
244 self.kind == PrincipalKind::System
245 }
246
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)247 /// Whether this is a person whose account has not confirmed its email
248 /// address. Such an account can only confirm it (or change it, or sign
249 /// out): the site, the API, MCP and git refuse it everything else
250 /// ([`accounts::confirm_email_first`]).
251 pub fn awaits_confirmation(&self) -> bool {
252 self.kind == PrincipalKind::User && !self.verified
253 }
254
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise255 pub fn role_in(&self, slug: &str) -> Option<Role> {
256 self.workspaces
257 .iter()
258 .find(|membership| membership.slug == slug)
259 .map(|membership| membership.role)
260 }
261
262 pub fn is_member(&self, slug: &str) -> bool {
263 self.role_in(slug).is_some()
264 }
Merge main (membership, two-factor, GitHub repo roles) into tokens265
266 /// The membership in `slug`, if any.
267 pub fn membership(&self, slug: &str) -> Option<&Membership> {
268 self.workspaces.iter().find(|membership| membership.slug.eq_ignore_ascii_case(slug))
269 }
270
271 /// Whether this is a person who owns `slug`, or holds `role` in it.
272 pub fn owns_or_has(&self, slug: &str, role: OrgRole) -> bool {
273 self.membership(slug)
274 .is_some_and(|membership| membership.role == Role::Owner || membership.has(role))
275 }
276
277 /// Whether the user may manage `slug`'s billing: an owner or a billing
278 /// manager.
279 pub fn manages_billing(&self, slug: &str) -> bool {
280 self.owns_or_has(slug, OrgRole::BillingManager)
281 }
282
283 /// Whether the user may see and manage security across `slug`: an
284 /// owner or a security manager.
285 pub fn manages_security(&self, slug: &str) -> bool {
286 self.owns_or_has(slug, OrgRole::SecurityManager)
287 }
288
289 /// The workspace's member privileges as this user sees them: the
290 /// defaults when the membership does not say.
291 pub fn privileges_in(&self, slug: &str) -> MemberPrivileges {
292 self.membership(slug).and_then(|membership| membership.privileges).unwrap_or_default()
293 }
README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise294}
295
296/// Who is asking. Every read and write in every service takes one.
297pub type Viewer = Option<User>;

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