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

Fixes from the page sweep1//! 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;
Fixes from the page sweep8pub mod access;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r99pub mod advisories;
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)10pub mod account_deletion;
Fixes from the page sweep11pub mod accounts;
12pub mod actions;
13pub mod agents;
14pub mod audit;
Merge branch 'worktree-agent-ac5b181a013e54348'15pub mod backups;
Fixes from the page sweep16pub mod billing;
Merge checks: statuses and check runs on every commit17pub mod checks;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar18pub mod codeowners;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r919pub mod coding_agents;
Fixes from the page sweep20pub 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 yet21pub mod datasets;
Merge branch 'worktree-agent-ab8033ab973f8d99a'22pub mod decisions;
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca23pub mod deploy_keys;
Fixes from the page sweep24pub 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 yet25pub mod folios;
Fixes from the page sweep26pub mod github;
27pub mod guardrails;
28pub mod identity;
Inbox: the events service tells people what needs them as events arrive29pub mod inbox;
Merge branch 'worktree-agent-a5c94df323642630e'30pub mod live;
Fixes from the page sweep31pub mod integrations;
Merge main (membership, two-factor, GitHub repo roles) into tokens32pub mod members;
Fixes from the page sweep33mod ids;
34mod names;
35mod outcome;
36pub 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.37pub mod people;
Fixes from the page sweep38pub mod projects;
39pub mod repos;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge40pub mod rules;
Fixes from the page sweep41pub mod runners;
42pub mod scopes;
43pub mod search;
44pub mod security;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar45pub mod teams;
46pub mod security_suite;
Merge branch 'worktree-agent-ad8a36dfcd4176015' into spend-guardrails47pub mod subscribers;
Fixes from the page sweep48pub mod time;
Fine-grained personal tokens, workspace token rules and approvals in identity49pub mod tokens;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar50pub mod updates;
Fixes from the page sweep51pub mod webhooks;
52pub mod work;
53
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.54pub use ids::{id_floor, new_id};
Merge branch 'worktree-agent-a8385d293d42c913a'55pub 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.56 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 managers57 is_valid_namespace, is_valid_repo_name,
Merge branch 'worktree-agent-a8385d293d42c913a'58};
Fixes from the page sweep59pub use outcome::{Failure, FailureCode, Outcome};
60
61use serde::{Deserialize, Serialize};
62
Merge main (membership, two-factor, GitHub repo roles) into tokens63/// What a member may do in a workspace. A member may also hold
64/// [`members::OrgRole`]s, which add to it.
Fixes from the page sweep65#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(rename_all = "lowercase")]
67pub enum Role {
Merge main (membership, two-factor, GitHub repo roles) into tokens68 /// Everything: Admin on every repository, the workspace's members,
69 /// settings, billing and security.
Fixes from the page sweep70 Owner,
Merge main (membership, two-factor, GitHub repo roles) into tokens71 /// The workspace's base permission on each repository, and what its
72 /// member privileges allow (see [`members::MemberPrivileges`]).
Fixes from the page sweep73 Member,
74}
75
Merge main (membership, two-factor, GitHub repo roles) into tokens76pub use members::{MemberPrivileges, OrgRole};
77
Fixes from the page sweep78/// One workspace a user belongs to.
79#[derive(Clone, Debug, Serialize, Deserialize)]
80pub struct Membership {
81 /// The workspace's name in URLs: `g1t.sh/<slug>`.
82 pub slug: String,
83 pub role: Role,
84 /// The workspace's display name, for showing it to people. Set when a
85 /// user is resolved from credentials; absent on principals made up by
86 /// a service.
87 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub name: Option<String>,
89 /// The workspace's uploaded icon: the SHA-256 of its bytes, served at
90 /// `/avatars/<avatar>`. Absent means the generated letter avatar.
91 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub avatar: Option<String>,
93 /// What a member gets on each of the workspace's repositories: the
94 /// workspace's base permission. Set when a user is resolved from
95 /// credentials; absent means the default, Write. Owners have Admin
96 /// whatever it says. See [`access`].
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub base_permission: Option<access::BasePermission>,
Merge branch 'worktree-agent-ad7c6d88d93adc817'99 /// Who may create the workspace's teams. Set when a user is resolved
100 /// from credentials; absent means the default, any member. See
101 /// [`teams::TeamCreation`].
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub team_creation: Option<teams::TeamCreation>,
Merge main (membership, two-factor, GitHub repo roles) into tokens104 /// The roles the member holds besides `role`: billing manager,
105 /// security manager. Set when a user is resolved from credentials.
106 #[serde(default, skip_serializing_if = "Vec::is_empty")]
107 pub org_roles: Vec<OrgRole>,
108 /// What the workspace lets members (and repository admins) do. Set
109 /// when a user is resolved from credentials; absent means the
110 /// defaults. See [`members::MemberPrivileges`].
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub privileges: Option<MemberPrivileges>,
Fixes from the page sweep113}
114
115impl Membership {
116 /// A plain member of `slug`, as services act inside one workspace.
117 pub fn member(slug: impl Into<String>) -> Self {
118 Membership {
119 slug: slug.into(),
120 role: Role::Member,
121 name: None,
122 avatar: None,
123 base_permission: None,
Merge branch 'worktree-agent-ad7c6d88d93adc817'124 team_creation: None,
Merge main (membership, two-factor, GitHub repo roles) into tokens125 org_roles: Vec::new(),
126 privileges: None,
Fixes from the page sweep127 }
128 }
Merge main (membership, two-factor, GitHub repo roles) into tokens129
130 /// Whether the member holds `role` besides owner or member.
131 pub fn has(&self, role: OrgRole) -> bool {
132 self.org_roles.contains(&role)
133 }
Fixes from the page sweep134}
135
136/// What a set of credentials resolved to.
137#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "lowercase")]
139pub enum PrincipalKind {
140 /// A person's account.
141 #[default]
142 User,
143 /// A workspace, acting through one of its own access tokens. Its `id`
144 /// is the workspace's, its `username` the workspace's slug, and it is a
145 /// member of that workspace and no other.
146 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.147 /// A run's token: a sandbox g1t started (checks, the merge queue, a
148 /// merge check), acting through a token that lives as long as its run
149 /// and can do only what that token's scope lists, in one repository.
150 /// Its `username` is `g1t`. g1t no longer runs agents of its own; the
151 /// name stays because identity still issues run credentials with it and
152 /// historical rows written by `@g1t`'s agent were recorded as it.
Fixes from the page sweep153 Agent,
154 /// g1t itself: the platform acting on its own, as when it opens a
155 /// pull request to upgrade a vulnerable dependency or merges from the
156 /// queue. Never resolved from credentials: only services make one,
157 /// with [`User::system`]. Its `username` is `g1t`, which nobody can
158 /// register.
159 System,
160}
161
162/// g1t's own identity, as [`PrincipalKind::System`] work is recorded.
163pub mod system {
164 /// Its id wherever an author or actor id is stored.
165 pub const ID: &str = "g1t";
166 /// Its name, shown as the author of what it does.
167 pub const USERNAME: &str = "g1t";
168 /// The address on the commits it makes, which no mailbox receives.
169 pub const EMAIL: &str = "g1t@users.noreply.g1t.sh";
170 /// Ids that earlier versions stored for g1t's own actions, such as a
171 /// merge its settings made. Read as g1t too.
172 pub const LEGACY_IDS: [&str; 3] = ["g1t_policy", "svc_runner", "g1t_runner"];
173
174 /// Whether `id` is g1t's own.
175 pub fn is_system_id(id: &str) -> bool {
176 id == ID || LEGACY_IDS.contains(&id)
177 }
178}
179
180#[derive(Clone, Debug, Default, Serialize, Deserialize)]
181pub struct User {
182 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 managers183 /// Lowercased: what the person is found, linked and mentioned by.
Fixes from the page sweep184 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 managers185 /// The username as its owner wrote it (`Ana`), when that differs from
186 /// `username`: what pages show. Set on the signed-in person and on
187 /// people looked up by name; absent elsewhere, where `username` is shown.
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub display_username: Option<String>,
Fixes from the page sweep190 #[serde(default)]
191 pub kind: PrincipalKind,
192 /// Whether the account's email address has been confirmed. Unverified
193 /// accounts can sign in but cannot create or change anything.
194 #[serde(default)]
195 pub verified: bool,
196 /// The workspaces this user belongs to. Filled in when a user is
197 /// resolved from credentials, so any service can authorize from it.
198 #[serde(default)]
199 pub workspaces: Vec<Membership>,
200 /// The person's uploaded avatar: the SHA-256 of its bytes, served at
201 /// `/avatars/<avatar>`. Absent means the generated letter avatar.
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub avatar: Option<String>,
204 /// Set on an agent resolved from its token: who it acts for, with which
205 /// credential, and what it may do. See [`credentials`].
206 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub acting: Option<Box<credentials::Acting>>,
208 /// The repositories this user has been given a role on directly,
209 /// whether or not they belong to its workspace. Filled in with
210 /// `workspaces`; see [`access`].
211 #[serde(default, skip_serializing_if = "Vec::is_empty")]
212 pub grants: Vec<access::RepoGrant>,
213 /// Set on a user resolved from an access token: its scopes and the
214 /// workspaces or repositories it is limited to. Absent on a signed-in
215 /// session and on an agent (whose `acting` scope applies instead).
216 /// See [`scopes`].
217 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub token: Option<Box<scopes::TokenAccess>>,
Merge main (membership, two-factor, GitHub repo roles) into tokens219 /// The workspaces this person belongs to but cannot use until they
220 /// meet its policy, such as turning on two-factor authentication.
221 /// They are left out of `workspaces` and `grants` meanwhile. Set when
222 /// a person is resolved from a session.
223 #[serde(default, skip_serializing_if = "Vec::is_empty")]
224 pub held: Vec<members::PolicyHold>,
Fixes from the page sweep225}
226
227impl User {
228 /// g1t itself, acting in `workspace`: what the platform's own work,
229 /// such as security updates, is done and recorded as.
230 pub fn system(workspace: &str) -> User {
231 User {
232 id: system::ID.to_owned(),
233 username: system::USERNAME.to_owned(),
234 kind: PrincipalKind::System,
235 verified: true,
236 workspaces: vec![Membership::member(workspace.to_lowercase())],
237 ..User::default()
238 }
239 }
240
241 /// Whether this is g1t itself.
242 pub fn is_system(&self) -> bool {
243 self.kind == PrincipalKind::System
244 }
245
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)246 /// Whether this is a person whose account has not confirmed its email
247 /// address. Such an account can only confirm it (or change it, or sign
248 /// out): the site, the API, MCP and git refuse it everything else
249 /// ([`accounts::confirm_email_first`]).
250 pub fn awaits_confirmation(&self) -> bool {
251 self.kind == PrincipalKind::User && !self.verified
252 }
253
Fixes from the page sweep254 pub fn role_in(&self, slug: &str) -> Option<Role> {
255 self.workspaces
256 .iter()
257 .find(|membership| membership.slug == slug)
258 .map(|membership| membership.role)
259 }
260
261 pub fn is_member(&self, slug: &str) -> bool {
262 self.role_in(slug).is_some()
263 }
Merge main (membership, two-factor, GitHub repo roles) into tokens264
265 /// The membership in `slug`, if any.
266 pub fn membership(&self, slug: &str) -> Option<&Membership> {
267 self.workspaces.iter().find(|membership| membership.slug.eq_ignore_ascii_case(slug))
268 }
269
270 /// Whether this is a person who owns `slug`, or holds `role` in it.
271 pub fn owns_or_has(&self, slug: &str, role: OrgRole) -> bool {
272 self.membership(slug)
273 .is_some_and(|membership| membership.role == Role::Owner || membership.has(role))
274 }
275
276 /// Whether the user may manage `slug`'s billing: an owner or a billing
277 /// manager.
278 pub fn manages_billing(&self, slug: &str) -> bool {
279 self.owns_or_has(slug, OrgRole::BillingManager)
280 }
281
282 /// Whether the user may see and manage security across `slug`: an
283 /// owner or a security manager.
284 pub fn manages_security(&self, slug: &str) -> bool {
285 self.owns_or_has(slug, OrgRole::SecurityManager)
286 }
287
288 /// The workspace's member privileges as this user sees them: the
289 /// defaults when the membership does not say.
290 pub fn privileges_in(&self, slug: &str) -> MemberPrivileges {
291 self.membership(slug).and_then(|membership| membership.privileges).unwrap_or_default()
292 }
Fixes from the page sweep293}
294
295/// Who is asking. Every read and write in every service takes one.
296pub type Viewer = Option<User>;

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