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.
| Sidebar: the panels really slide | 1 | //! 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-a69aeabc4b0deeb97 | 7 | pub mod about; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 8 | pub mod access; |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 9 | pub mod account_deletion; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 10 | pub mod accounts; |
| Sidebar: the panels really slide | 11 | pub mod actions; |
| Agents and memory, checks and conflicts, profiles, slug renames, custom domains | 12 | pub mod agents; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 13 | pub mod audit; |
| Merge branch 'worktree-agent-ac5b181a013e54348' | 14 | pub mod backups; |
| Sidebar: the panels really slide | 15 | pub mod billing; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 16 | pub mod capture; |
| Merge checks: statuses and check runs on every commit | 17 | pub mod checks; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 18 | pub mod codeowners; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 19 | pub 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 yet | 20 | pub mod datasets; |
| Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca | 21 | pub mod deploy_keys; |
| Sidebar: the panels really slide | 22 | pub 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 yet | 23 | pub mod folios; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 24 | pub mod github; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 25 | pub mod guardrails; |
| Sidebar: the panels really slide | 26 | pub mod identity; |
| Inbox: the events service tells people what needs them as events arrive | 27 | pub mod inbox; |
| Sidebar: the panels really slide | 28 | pub mod integrations; |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 29 | pub mod members; |
| Merge branch 'mirroring' into artifacts-mode | 30 | pub mod mirrors; |
| Sidebar: the panels really slide | 31 | mod ids; |
| 32 | mod names; | |
| 33 | mod outcome; | |
| Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member | 34 | pub 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. | 35 | pub mod people; |
| Projects: what a workspace builds and runs, first on every page | 36 | pub mod projects; |
| Sidebar: the panels really slide | 37 | pub mod repos; |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 38 | pub mod rules; |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 39 | pub mod runners; |
| Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step | 40 | pub mod scopes; |
| Search across all of g1t, Explore, and a command palette | 41 | pub mod search; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 42 | pub mod security; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 43 | pub mod teams; |
| 44 | pub mod security_suite; | |
| Merge branch 'worktree-agent-ad8a36dfcd4176015' into spend-guardrails | 45 | pub mod subscribers; |
| Sidebar: the panels really slide | 46 | pub mod time; |
| Fine-grained personal tokens, workspace token rules and approvals in identity | 47 | pub mod tokens; |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 48 | pub mod updates; |
| Sidebar: the panels really slide | 49 | pub mod webhooks; |
| 50 | pub mod work; | |
| 51 | ||
| 52 | pub use ids::new_id; | |
| Merge branch 'worktree-agent-a8385d293d42c913a' | 53 | pub use names::{ |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 54 | Username, aliasable_name, claimable_namespace, claimable_username, is_namespace_shaped, is_reserved_name, is_route_name, |
| 55 | is_valid_namespace, is_valid_repo_name, | |
| Merge branch 'worktree-agent-a8385d293d42c913a' | 56 | }; |
| Sidebar: the panels really slide | 57 | pub use outcome::{Failure, FailureCode, Outcome}; |
| 58 | ||
| 59 | use serde::{Deserialize, Serialize}; | |
| 60 | ||
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 61 | /// What a member may do in a workspace. A member may also hold |
| 62 | /// [`members::OrgRole`]s, which add to it. | |
| Sidebar: the panels really slide | 63 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
| 64 | #[serde(rename_all = "lowercase")] | |
| 65 | pub enum Role { | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 66 | /// Everything: Admin on every repository, the workspace's members, |
| 67 | /// settings, billing and security. | |
| Sidebar: the panels really slide | 68 | Owner, |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 69 | /// The workspace's base permission on each repository, and what its |
| 70 | /// member privileges allow (see [`members::MemberPrivileges`]). | |
| Sidebar: the panels really slide | 71 | Member, |
| 72 | } | |
| 73 | ||
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 74 | pub use members::{MemberPrivileges, OrgRole}; |
| 75 | ||
| Sidebar: the panels really slide | 76 | /// One workspace a user belongs to. |
| 77 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
| 78 | pub struct Membership { | |
| 79 | /// The workspace's name in URLs: `g1t.sh/<slug>`. | |
| 80 | pub slug: String, | |
| 81 | pub role: Role, | |
| Workspace names and icons, and a component kit for every control | 82 | /// The workspace's display name, for showing it to people. Set when a |
| 83 | /// user is resolved from credentials; absent on principals made up by | |
| 84 | /// a service. | |
| 85 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 86 | pub name: Option<String>, | |
| 87 | /// The workspace's uploaded icon: the SHA-256 of its bytes, served at | |
| 88 | /// `/avatars/<avatar>`. Absent means the generated letter avatar. | |
| 89 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 90 | pub avatar: Option<String>, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 91 | /// What a member gets on each of the workspace's repositories: the |
| 92 | /// workspace's base permission. Set when a user is resolved from | |
| 93 | /// credentials; absent means the default, Write. Owners have Admin | |
| 94 | /// whatever it says. See [`access`]. | |
| 95 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 96 | pub base_permission: Option<access::BasePermission>, | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 97 | /// Who may create the workspace's teams. Set when a user is resolved |
| 98 | /// from credentials; absent means the default, any member. See | |
| 99 | /// [`teams::TeamCreation`]. | |
| 100 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 101 | pub team_creation: Option<teams::TeamCreation>, | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 102 | /// The roles the member holds besides `role`: billing manager, |
| 103 | /// security manager. Set when a user is resolved from credentials. | |
| 104 | #[serde(default, skip_serializing_if = "Vec::is_empty")] | |
| 105 | pub org_roles: Vec<OrgRole>, | |
| 106 | /// What the workspace lets members (and repository admins) do. Set | |
| 107 | /// when a user is resolved from credentials; absent means the | |
| 108 | /// defaults. See [`members::MemberPrivileges`]. | |
| 109 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 110 | pub privileges: Option<MemberPrivileges>, | |
| Sidebar: the panels really slide | 111 | } |
| 112 | ||
| Workspace names and icons, and a component kit for every control | 113 | impl Membership { |
| 114 | /// A plain member of `slug`, as services act inside one workspace. | |
| 115 | pub fn member(slug: impl Into<String>) -> Self { | |
| 116 | Membership { | |
| 117 | slug: slug.into(), | |
| 118 | role: Role::Member, | |
| 119 | name: None, | |
| 120 | avatar: None, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 121 | base_permission: None, |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 122 | team_creation: None, |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 123 | org_roles: Vec::new(), |
| 124 | privileges: None, | |
| Workspace names and icons, and a component kit for every control | 125 | } |
| 126 | } | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 127 | |
| 128 | /// Whether the member holds `role` besides owner or member. | |
| 129 | pub fn has(&self, role: OrgRole) -> bool { | |
| 130 | self.org_roles.contains(&role) | |
| 131 | } | |
| Workspace names and icons, and a component kit for every control | 132 | } |
| 133 | ||
| Sidebar: the panels really slide | 134 | /// What a set of credentials resolved to. |
| 135 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] | |
| 136 | #[serde(rename_all = "lowercase")] | |
| 137 | pub enum PrincipalKind { | |
| 138 | /// A person's account. | |
| 139 | #[default] | |
| 140 | User, | |
| 141 | /// A workspace, acting through one of its own access tokens. Its `id` | |
| 142 | /// is the workspace's, its `username` the workspace's slug, and it is a | |
| 143 | /// member of that workspace and no other. | |
| 144 | Workspace, | |
| 145 | /// A g1t agent at work in a sandbox, acting through a token that lives | |
| 146 | /// as long as its run and can do only what that token's scope lists, in | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 147 | /// one repository. Its `username` is `g1t`. |
| Sidebar: the panels really slide | 148 | Agent, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 149 | /// g1t itself: the platform acting on its own, as when it opens a |
| 150 | /// pull request to upgrade a vulnerable dependency or merges from the | |
| 151 | /// queue. Never resolved from credentials: only services make one, | |
| 152 | /// with [`User::system`]. Its `username` is `g1t`, which nobody can | |
| 153 | /// register. | |
| 154 | System, | |
| 155 | } | |
| 156 | ||
| 157 | /// g1t's own identity, as [`PrincipalKind::System`] work is recorded. | |
| 158 | pub mod system { | |
| 159 | /// Its id wherever an author or actor id is stored. | |
| 160 | pub const ID: &str = "g1t"; | |
| 161 | /// Its name, shown as the author of what it does. | |
| 162 | pub const USERNAME: &str = "g1t"; | |
| 163 | /// The address on the commits it makes, which no mailbox receives. | |
| 164 | pub const EMAIL: &str = "g1t@users.noreply.g1t.sh"; | |
| 165 | /// Ids that earlier versions stored for g1t's own actions, such as a | |
| 166 | /// merge its settings made. Read as g1t too. | |
| 167 | pub const LEGACY_IDS: [&str; 3] = ["g1t_policy", "svc_runner", "g1t_runner"]; | |
| 168 | ||
| 169 | /// Whether `id` is g1t's own. | |
| 170 | pub fn is_system_id(id: &str) -> bool { | |
| 171 | id == ID || LEGACY_IDS.contains(&id) | |
| 172 | } | |
| Sidebar: the panels really slide | 173 | } |
| 174 | ||
| 175 | #[derive(Clone, Debug, Default, Serialize, Deserialize)] | |
| 176 | pub struct User { | |
| 177 | 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 managers | 178 | /// Lowercased: what the person is found, linked and mentioned by. |
| Sidebar: the panels really slide | 179 | 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 managers | 180 | /// The username as its owner wrote it (`Ana`), when that differs from |
| 181 | /// `username`: what pages show. Set on the signed-in person and on | |
| 182 | /// people looked up by name; absent elsewhere, where `username` is shown. | |
| 183 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 184 | pub display_username: Option<String>, | |
| Sidebar: the panels really slide | 185 | #[serde(default)] |
| 186 | pub kind: PrincipalKind, | |
| 187 | /// Whether the account's email address has been confirmed. Unverified | |
| 188 | /// accounts can sign in but cannot create or change anything. | |
| 189 | #[serde(default)] | |
| 190 | pub verified: bool, | |
| 191 | /// The workspaces this user belongs to. Filled in when a user is | |
| 192 | /// resolved from credentials, so any service can authorize from it. | |
| 193 | #[serde(default)] | |
| 194 | pub workspaces: Vec<Membership>, | |
| Workspace names and icons, and a component kit for every control | 195 | /// The person's uploaded avatar: the SHA-256 of its bytes, served at |
| 196 | /// `/avatars/<avatar>`. Absent means the generated letter avatar. | |
| 197 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 198 | pub avatar: Option<String>, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 199 | /// Set on an agent resolved from its token: who it acts for, with which |
| 200 | /// credential, and what it may do. See [`credentials`]. | |
| 201 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 202 | pub acting: Option<Box<credentials::Acting>>, | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 203 | /// The repositories this user has been given a role on directly, |
| 204 | /// whether or not they belong to its workspace. Filled in with | |
| 205 | /// `workspaces`; see [`access`]. | |
| 206 | #[serde(default, skip_serializing_if = "Vec::is_empty")] | |
| 207 | pub grants: Vec<access::RepoGrant>, | |
| Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step | 208 | /// Set on a user resolved from an access token: its scopes and the |
| 209 | /// workspaces or repositories it is limited to. Absent on a signed-in | |
| 210 | /// session and on an agent (whose `acting` scope applies instead). | |
| 211 | /// See [`scopes`]. | |
| 212 | #[serde(default, skip_serializing_if = "Option::is_none")] | |
| 213 | pub token: Option<Box<scopes::TokenAccess>>, | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 214 | /// The workspaces this person belongs to but cannot use until they |
| 215 | /// meet its policy, such as turning on two-factor authentication. | |
| 216 | /// They are left out of `workspaces` and `grants` meanwhile. Set when | |
| 217 | /// a person is resolved from a session. | |
| 218 | #[serde(default, skip_serializing_if = "Vec::is_empty")] | |
| 219 | pub held: Vec<members::PolicyHold>, | |
| Sidebar: the panels really slide | 220 | } |
| 221 | ||
| 222 | impl User { | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 223 | /// g1t itself, acting in `workspace`: what the platform's own work, |
| 224 | /// such as security updates, is done and recorded as. | |
| 225 | pub fn system(workspace: &str) -> User { | |
| 226 | User { | |
| 227 | id: system::ID.to_owned(), | |
| 228 | username: system::USERNAME.to_owned(), | |
| 229 | kind: PrincipalKind::System, | |
| 230 | verified: true, | |
| 231 | workspaces: vec![Membership::member(workspace.to_lowercase())], | |
| 232 | ..User::default() | |
| 233 | } | |
| 234 | } | |
| 235 | ||
| 236 | /// Whether this is g1t itself. | |
| 237 | pub fn is_system(&self) -> bool { | |
| 238 | self.kind == PrincipalKind::System | |
| 239 | } | |
| 240 | ||
| Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036) | 241 | /// Whether this is a person whose account has not confirmed its email |
| 242 | /// address. Such an account can only confirm it (or change it, or sign | |
| 243 | /// out): the site, the API, MCP and git refuse it everything else | |
| 244 | /// ([`accounts::confirm_email_first`]). | |
| 245 | pub fn awaits_confirmation(&self) -> bool { | |
| 246 | self.kind == PrincipalKind::User && !self.verified | |
| 247 | } | |
| 248 | ||
| Sidebar: the panels really slide | 249 | pub fn role_in(&self, slug: &str) -> Option<Role> { |
| 250 | self.workspaces | |
| 251 | .iter() | |
| 252 | .find(|membership| membership.slug == slug) | |
| 253 | .map(|membership| membership.role) | |
| 254 | } | |
| 255 | ||
| 256 | pub fn is_member(&self, slug: &str) -> bool { | |
| 257 | self.role_in(slug).is_some() | |
| 258 | } | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 259 | |
| 260 | /// The membership in `slug`, if any. | |
| 261 | pub fn membership(&self, slug: &str) -> Option<&Membership> { | |
| 262 | self.workspaces.iter().find(|membership| membership.slug.eq_ignore_ascii_case(slug)) | |
| 263 | } | |
| 264 | ||
| 265 | /// Whether this is a person who owns `slug`, or holds `role` in it. | |
| 266 | pub fn owns_or_has(&self, slug: &str, role: OrgRole) -> bool { | |
| 267 | self.membership(slug) | |
| 268 | .is_some_and(|membership| membership.role == Role::Owner || membership.has(role)) | |
| 269 | } | |
| 270 | ||
| 271 | /// Whether the user may manage `slug`'s billing: an owner or a billing | |
| 272 | /// manager. | |
| 273 | pub fn manages_billing(&self, slug: &str) -> bool { | |
| 274 | self.owns_or_has(slug, OrgRole::BillingManager) | |
| 275 | } | |
| 276 | ||
| 277 | /// Whether the user may see and manage security across `slug`: an | |
| 278 | /// owner or a security manager. | |
| 279 | pub fn manages_security(&self, slug: &str) -> bool { | |
| 280 | self.owns_or_has(slug, OrgRole::SecurityManager) | |
| 281 | } | |
| 282 | ||
| 283 | /// The workspace's member privileges as this user sees them: the | |
| 284 | /// defaults when the membership does not say. | |
| 285 | pub fn privileges_in(&self, slug: &str) -> MemberPrivileges { | |
| 286 | self.membership(slug).and_then(|membership| membership.privileges).unwrap_or_default() | |
| 287 | } | |
| Sidebar: the panels really slide | 288 | } |
| 289 | ||
| 290 | /// Who is asking. Every read and write in every service takes one. | |
| 291 | pub type Viewer = Option<User>; |
This file's history is long; its oldest lines are credited to the oldest commit read.