Skip to content
1,081 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.

Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1import type { AccessClient, BasePermission, RepoGrant } from "./access";
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules2import type { PermissionAccess, RepositorySelection } from "./fine-grained";
Merge main (membership, two-factor, GitHub repo roles) into tokens3import type { MemberPrivileges, OrgRole, PolicyHold } from "./members";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API4import type { Acting, CreateRunCredentialInput, RunBinding } from "./audit";
Agents as a team: lifecycle, merge queue, billing and a new shell5import type { RepoPath } from "./repos";
Initial g1t: services, event bus, intents and attempts6import type { Result } from "./result";
Merge branch 'worktree-agent-ad7c6d88d93adc817'7import type { TeamCreation, TeamsClient } from "./teams";
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca8import type { DeployKeysClient } from "./deploy-keys";
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)9import type { EmailConfirmed } from "./accounts";
Initial g1t: services, event bus, intents and attempts10
Email verification, password reset, and Git for AI scale positioning11export type User = {
12 id: string;
13 username: string;
14 /**
Agents as a team: lifecycle, merge queue, billing and a new shell15 * `workspace` when a workspace is acting through one of its own access
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily16 * tokens: `id` is then the workspace's and `username` its slug. `system`
17 * is g1t itself doing platform work, such as a security update
18 * (`username` `g1t`). Absent means `user`.
Agents as a team: lifecycle, merge queue, billing and a new shell19 */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily20 kind?: "user" | "workspace" | "agent" | "system";
Agents as a team: lifecycle, merge queue, billing and a new shell21 /**
Email verification, password reset, and Git for AI scale positioning22 * Whether the account's email address is confirmed. Only set on users
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)23 * resolved from credentials. An account that has not confirmed it can
24 * only confirm it: see `awaitsConfirmation`.
Email verification, password reset, and Git for AI scale positioning25 */
26 verified?: boolean;
Workspaces own repositories27 /**
28 * The workspaces this user belongs to. Set on users resolved from
29 * credentials, so any service can authorize from it.
30 */
31 workspaces?: Membership[];
Workspace names and icons, and a component kit for every control32 /**
33 * The person's uploaded avatar: the SHA-256 of its bytes, served at
34 * `/avatars/<avatar>`. Only set on the signed-in person; absent means
35 * the generated letter avatar.
36 */
37 avatar?: string;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API38 /**
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent39 * Set on an agent resolved from its token: who it acts for ("g1t
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API40 * on behalf of syntaqx"), with which credential, and what it may do.
41 */
42 acting?: Acting;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look43 /**
44 * The repositories this user has a role on directly, whether or not they
45 * belong to its workspace. Set with `workspaces`; see `access.ts`.
46 */
47 grants?: RepoGrant[];
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step48 /**
49 * Set on a user resolved from an access token: its scopes (null for full
50 * access) and the workspaces or repositories it reaches. See scopes.ts.
51 */
52 token?: {
53 token_id: string;
54 scopes?: string[] | null;
55 legacy?: boolean;
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens56 /** The token's name, as its owner gave it. */
57 name?: string;
TS access mirrors the workspace token cap and fine-grained reach58 /** A fine-grained token's reach: its resource owner and repositories. */
59 fine_grained?: {
60 workspace?: string | null;
61 repositories?: "all" | "selected" | "public";
62 repo_ids?: string[];
63 };
64 /** A workspace's own token an owner gave Admin. */
65 admin?: boolean;
66 /** Set on what a deploy key resolves to. */
67 deploy_key?: string;
68 /** The one repository a job's token or a deploy key reaches. */
69 repo?: string;
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts70 /** Set on a workflow job's token (`G1T_TOKEN`): the run and job it was made for. */
71 job?: { run_id: string; job_id: string; pull_requests?: boolean };
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step72 };
Merge main (membership, two-factor, GitHub repo roles) into tokens73 /**
74 * Workspaces the person belongs to but cannot use until they meet its
75 * policy, such as turning on two-factor authentication. Left out of
76 * `workspaces` and `grants` meanwhile.
77 */
78 held?: PolicyHold[];
Email verification, password reset, and Git for AI scale positioning79};
Initial g1t: services, event bus, intents and attempts80
Workspaces own repositories81/** What a member may do: an owner also manages the workspace's members. */
82export type Role = "owner" | "member";
83
Workspace names and icons, and a component kit for every control84export type Membership = {
85 /** The workspace's name in URLs: `g1t.sh/<slug>`. */
86 slug: string;
87 role: Role;
88 /** Its display name. Set on users resolved from credentials. */
89 name?: string;
90 /** Its uploaded icon, as `Workspace.avatar`. */
91 avatar?: string;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look92 /** The workspace's base permission: what members get on every repository. Absent means `write`. */
93 base_permission?: BasePermission;
Merge branch 'worktree-agent-ad7c6d88d93adc817'94 /** Who may create its teams. Absent means any member. */
95 team_creation?: TeamCreation;
Merge main (membership, two-factor, GitHub repo roles) into tokens96 /** The roles held besides owner or member. */
97 org_roles?: OrgRole[];
98 /** What the workspace lets its members do. Absent means the defaults. */
99 privileges?: MemberPrivileges;
Chat and workspace agents: channels, DMs and named agents you talk to100 /**
101 * Whether this member uses Code: repositories, issues, pull requests,
102 * checks, deploys. False for people who only use Chat, Docs and agents
103 * (support, sales, finance): they see no repository, whatever the base
104 * permission, and agents treat them as unable to change code. Absent
105 * means true.
106 */
107 code_access?: boolean;
Workspace names and icons, and a component kit for every control108};
Workspaces own repositories109
Chat and workspace agents: channels, DMs and named agents you talk to110/** Whether a member uses Code. See `Membership.code_access`. */
111export function hasCodeAccess(membership: Pick<Membership, "code_access"> | null | undefined): boolean {
112 return membership?.code_access !== false;
113}
114
Agents and memory, checks and conflicts, profiles, slug renames, custom domains115/** How long an old workspace slug redirects, and stays reserved for it, after a rename. */
116export const SLUG_HOLD_DAYS = 90;
117
118/** How long a workspace must wait between renames. */
119export const RENAME_COOLDOWN_HOURS = 24;
120
Workspace names and icons, and a component kit for every control121/** The largest avatar that can be uploaded, in bytes. */
122export const MAX_AVATAR_BYTES = 1024 * 1024;
123
Workspaces own repositories124/**
Merge branch 'worktree-agent-a2013627e5ea4ab13'125 * Where a workspace keeps its repositories' git data: anywhere g1t stores
126 * it (the default), or in the EU only. It applies to repositories made
127 * after it is set.
128 */
129export type DataResidency = "anywhere" | "eu";
130
131/**
Workspaces own repositories132 * A workspace: the owner of repositories, and the first segment of their
133 * URLs. A person's own space and a team's are the same thing.
134 */
135export type Workspace = {
136 id: string;
137 slug: string;
138 name: string;
Agents as a team: lifecycle, merge queue, billing and a new shell139 /** One line saying what the workspace is for. */
140 description: string | null;
Workspaces own repositories141 /** RFC 3339. */
142 createdAt: string;
143 memberCount: number;
Workspace names and icons, and a component kit for every control144 /**
145 * The workspace's uploaded icon: the SHA-256 of its bytes, served at
146 * `/avatars/<avatar>`. Null means the generated letter avatar.
147 */
148 avatar: string | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look149 /** What every member gets on each repository; owners have Admin. */
150 basePermission?: BasePermission;
Merge branch 'worktree-agent-ad7c6d88d93adc817'151 /** Who may create its teams. Absent means any member. */
152 teamCreation?: TeamCreation;
Merge main (membership, two-factor, GitHub repo roles) into tokens153 /** Whether members and outside collaborators need two-factor authentication. */
154 twoFactorRequirementEnabled?: boolean;
155} & Partial<MemberPrivileges>;
Workspaces own repositories156
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look157export type Member = {
158 username: string;
159 role: Role;
Merge main (membership, two-factor, GitHub repo roles) into tokens160 /** The roles they hold besides `role`. */
161 org_roles?: OrgRole[];
162 /** Whether two-factor authentication is on; owners only, null for anyone else. */
163 two_factor?: boolean | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look164 /** Their display name, when they set one. */
165 name?: string | null;
166 /** Their uploaded avatar's hash, served at `/avatars/<avatar>`; null for the generated letter avatar. */
167 avatar?: string | null;
168};
Workspaces own repositories169
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace170/** An owner of a workspace, as staff see them. */
171export type AdminOwner = { username: string; email: string | null };
172
173/** A workspace as staff see it. Mirrors `AdminWorkspace` in `crates/contracts/src/identity.rs`. */
174export type AdminWorkspace = {
175 slug: string;
176 name: string;
177 /** RFC 3339. */
178 createdAt: string;
179 owners: AdminOwner[];
180 memberCount: number;
181};
182
183/** A member of a workspace, as staff see them. */
184export type AdminMember = { username: string; email: string | null; role: Role; /** RFC 3339. */ joined: string };
185
186export type AdminWorkspaceDetail = {
187 slug: string;
188 name: string;
189 description: string | null;
190 /** RFC 3339. */
191 createdAt: string;
192 /** Owners first, then by username. */
193 members: AdminMember[];
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member194 /** It can never be deleted, by anyone (identity's `PROTECTED_WORKSPACES`). */
195 protected: boolean;
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace196};
197
198/** The most workspaces one `workspaces` call returns. */
199export const ADMIN_WORKSPACES_LIMIT = 500;
200
201/**
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look202 * Whether anyone may make an account, or only someone with an invite.
203 * Identity's `REGISTRATION_MODE`; unset means `invite`.
204 */
205export type RegistrationMode = "invite" | "open";
206
207/** How many invites a person may have out at once, unless identity's `INVITES_PER_USER` says otherwise. */
208export const INVITES_PER_USER = 5;
209/** How long an invite works, unless identity's `INVITE_TTL_DAYS` says otherwise. */
210export const INVITE_TTL_DAYS = 30;
211
212/** Only a pending invite can be used or revoked. Revoked and expired ones never used give the invite back. */
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)213/**
214 * `awaiting_confirmation`: used to make an account that has not confirmed its
215 * email address yet; what it gives is joined when the address is confirmed,
216 * unless it is revoked first.
217 */
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)218/**
219 * `awaiting_answer`: the account it made is confirmed, and the workspace it
220 * names waits for the person to accept or decline. `declined`: they said no.
221 */
222export type InviteStatus =
223 | "pending"
224 | "awaiting_confirmation"
225 | "awaiting_answer"
226 | "redeemed"
227 | "declined"
228 | "expired"
229 | "revoked";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look230
231/** One invite. Mirrors `Invite` in `crates/contracts/src/identity.rs`. */
232export type Invite = {
233 id: string;
234 /** `g1t-k7m2-…`: returned when it is made, and to its maker while pending. */
235 code: string | null;
236 /** The code's first group, such as `g1t-k7m2`. */
237 hint: string;
238 /** Only this address can use it. */
239 email: string | null;
240 /** `account` makes an account; `workspace` joins an existing one to `workspace`. */
241 kind: "account" | "workspace";
242 /** The workspace using it joins. */
243 workspace: string | null;
244 status: InviteStatus;
245 /** Whose allowance it used. */
246 chargedTo: "user" | "workspace" | "none";
247 /** Its maker's username; null when g1t staff made it. */
248 invitedBy: string | null;
249 /** The account that used it. */
250 redeemedBy: string | null;
251 /** RFC 3339. */
252 createdAt: string;
253 /** RFC 3339. */
254 expiresAt: string;
255 redeemedAt: string | null;
256 revokedAt: string | null;
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)257 /** The account a workspace invitation is for, by username: someone invited by username, or the account the invite made. */
258 invitee?: string | null;
259 /** The role `workspace` is joined with; null when it names none. */
260 role?: Role | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look261 /** The staff member who minted it; only in staff views. */
262 staff?: string | null;
263};
264
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)265/**
266 * A workspace invitation waiting for its person's answer, as they see it.
267 * Mirrors `WorkspaceInvitation` in `crates/contracts/src/identity.rs`.
268 */
269export type WorkspaceInvitation = {
270 id: string;
271 workspace: ProfileWorkspace;
272 /** The role accepting joins with. */
273 role: Role;
274 /** Null when g1t staff sent it. */
275 invitedBy: { username: string; name: string | null; avatar: string | null } | null;
276 createdAt: string;
277 expiresAt: string;
278};
279
280/** Someone to invite, as `findPeople` finds them: never an email address. */
281export type PersonMatch = { username: string; name: string | null; avatar: string | null };
282
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look283/** How many invites someone may have out. `limit` and `remaining` are null for no limit. */
284export type Allowance = { limit: number | null; used: number; remaining: number | null };
285
286export type InvitesOverview = {
287 mode: RegistrationMode;
288 allowance: Allowance;
289 /** Workspaces the person owns that were granted invites to share. */
290 workspaces: { slug: string; allowance: Allowance }[];
291 invites: Invite[];
292};
293
294/** What a valid code is for, before it is used. */
295export type InvitePreview = {
296 kind: "account" | "workspace";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas297 /** Pending, unless `anyStatus` asked about a code that is spent. */
298 status: InviteStatus;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look299 /** Null when g1t staff sent it. */
300 invitedBy: { username: string; name: string | null; avatar: string | null } | null;
301 workspace: ProfileWorkspace | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas302 /** The repository it accepts an invitation to, such as `{ name: "flagon-io/g1t", role: "write" }`. */
303 repository: { name: string; role: string } | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look304 /** Partly hidden, such as `a•••@example.com`. */
305 email: string | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas306 /** The bound address in full, while the invite is pending: it fills in and locks the sign-up form. */
307 address: string | null;
308 /** Whether the bound address has a g1t account already: sign in to accept. */
309 hasAccount: boolean;
310 /** With a viewer: whether it is theirs (for one of their confirmed addresses, or used by them). */
311 forViewer: boolean | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look312 expiresAt: string;
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)313 /** A shared invite link's group, such as `Cloudflare judges`; null for a one-person invite. Not secret. */
314 sharedLabel: string | null;
315 /** The email domains a shared invite link is limited to; empty for any address. */
316 sharedDomains: string[];
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm317 /**
318 * Whether the page was opened from this pending invite's own email (its
319 * `proof` checked out): the account made with it starts with `address`
320 * confirmed. False without a proof, with a wrong one, or for an invite
321 * bound to no address.
322 */
323 emailProven: boolean;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look324};
325
326export type WaitlistStatus = "waiting" | "invited" | "dismissed";
327
328export type WaitlistEntry = {
329 id: string;
330 email: string;
331 about: string | null;
332 status: WaitlistStatus;
333 inviteId: string | null;
334 decidedBy: string | null;
335 decidedAt: string | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas336 /** What staff wrote when approving; it went in the invite email. */
337 note: string | null;
338 /** The account made with the invite, once it was used. */
339 joinedAs: string | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look340 /** When they first asked. */
341 createdAt: string;
342 /** When they last asked. */
343 updatedAt: string;
344};
345
346export type InviteGrant = { amount: number; note: string | null; grantedBy: string; createdAt: string };
347export type InviteTreeNode = { username: string; joinedAt: string; invited: InviteTreeNode[] };
348
349/** Where a person came from and whom they brought. For a workspace, `username` is its slug. */
350export type InviteTree = {
351 username: string;
352 /** Who invited them, then who invited that person, and so on. */
353 invitedBy: string[];
354 /** The staff member who minted their invite, when staff did. */
355 staff: string | null;
356 allowance: Allowance;
357 grants: InviteGrant[];
358 invites: Invite[];
359 /** Whom they invited, three levels down. */
360 invited: InviteTreeNode[];
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)361 /** The shared invite link the account was made with, if it was. */
362 shared: SharedInviteSource | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look363};
364
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)365// --- Shared invite links, staff only ---------------------------------------------------
366//
367// One link for a group (a conference's judges, a post, a community): up to
368// `maxUses` new accounts, until it expires or staff revoke it, optionally only
369// for addresses at some domains. Each use makes a new account, which makes its
370// own workspace; it never joins an existing one and uses nobody's allowance.
371// The link is `https://g1t.sh/register?invite=<code>`. Mirrors the shared
372// invite types in `crates/contracts/src/identity.rs`.
373
374/** How long a shared invite link works when staff give no date. */
375export const SHARED_INVITE_TTL_DAYS = 14;
376/** The furthest ahead a shared invite link's last day may be set. */
377export const SHARED_INVITE_MAX_DAYS = 365;
378/** The most accounts one shared invite link makes. */
379export const MAX_SHARED_INVITE_USES = 1000;
380/** The most characters a shared invite link's label keeps. */
381export const MAX_SHARED_INVITE_LABEL = 80;
382/** The most email domains one shared invite link may be limited to. */
383export const MAX_SHARED_INVITE_DOMAINS = 10;
384
385/** Only a live link makes accounts; `used_up`: every use is taken. */
386export type SharedInviteStatus = "live" | "used_up" | "expired" | "revoked";
387
388/** The shared invite link an account was made with. */
389export type SharedInviteSource = { id: string; label: string };
390
391/** One shared invite link, as staff see it. */
392export type SharedInvite = {
393 /** `sinv_…`. */
394 id: string;
395 /** Whom it is for, such as `Cloudflare judges`. */
396 label: string;
397 /** The code, while it is live. */
398 code: string | null;
399 /** The code's first group, such as `g1t-k7m2`. */
400 hint: string;
401 maxUses: number;
402 /** Accounts made with it so far. */
403 uses: number;
404 /** Only addresses at these domains may use it; empty for any. */
405 domains: string[];
406 status: SharedInviteStatus;
407 /** The staff member who made it, by email. */
408 staff: string;
409 createdAt: string;
410 expiresAt: string;
411 revokedAt: string | null;
412 revokedBy: string | null;
413 /** The accounts made with it, oldest first; `username` is null once one is purged. */
414 accounts: { username: string | null; joinedAt: string }[];
415};
416
417/** What staff make a shared invite link from. */
418export type NewSharedInvite = {
419 label: string;
420 /** 1 to 1000. */
421 maxUses: number;
422 /** The last day it works, `YYYY-MM-DD` (UTC); null for 14 days from now. */
423 expiresOn: string | null;
424 /** Email domains it is limited to, such as `cloudflare.com`; empty for any address. */
425 domains: string[];
426};
427
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look428/** The most rows one staff listing of invites or the waitlist returns. */
429export const ADMIN_INVITES_LIMIT = 500;
430
431/**
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace432 * Staff-only identity, for sudo.g1t.sh. It takes no viewer and checks no
433 * membership: only sudo calls it, over its service binding, once Cloudflare
434 * Access and its staff list have let someone in. Never call it on behalf of
435 * a customer.
436 */
437export interface IdentityAdminApi {
438 /** Every workspace, newest first, at most 500; `query` matches slug, name, or an owner's username or email. */
439 workspaces(query?: string): Promise<AdminWorkspace[]>;
440 /** One workspace with all its members, or null. */
441 workspace(slug: string): Promise<AdminWorkspaceDetail | null>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look442
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas443 /** The waitlist, newest first; `query` matches the address or what they said. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look444 waitlist(query?: string | null, status?: WaitlistStatus | null): Promise<WaitlistEntry[]>;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas445 /** How many requests are waiting, for the navigation's badge. */
446 waitlistPending(): Promise<number>;
447 /**
448 * Approving mints an invite bound to the address and emails it, with
449 * `note` (up to 500 characters) if given; dismissing only marks it.
450 */
451 decideWaitlist(id: string, approve: boolean, staff: string, note?: string | null): Promise<Result<WaitlistEntry>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look452 /** Invites, newest first; `query` is a code's start, or part of an email, inviter or redeemer. */
453 invites(query?: string | null): Promise<Invite[]>;
454 revokeInvite(id: string, staff: string): Promise<Result<Invite>>;
455 /** An invite that uses nobody's allowance, optionally bound to (and emailed to) `email`. */
456 mintInvite(email: string | null, staff: string): Promise<Result<Invite>>;
457 /** More invites (or fewer, with a negative amount) for a person or a workspace. */
458 grantInvites(
459 target: "user" | "workspace",
460 name: string,
461 amount: number,
462 note: string,
463 staff: string,
464 ): Promise<Result<Allowance>>;
465 /** Where a person came from and whom they brought, or null. */
466 inviteTree(username: string): Promise<InviteTree | null>;
467 /** A workspace's granted invites and the invites made for it, or null. */
468 workspaceInvites(slug: string): Promise<InviteTree | null>;
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)469 /** Shared invite links, newest first, each with the accounts it made. */
470 sharedInvites(): Promise<SharedInvite[]>;
471 /** Makes a shared invite link; the result carries its code. Recorded in the audit log. */
472 createSharedInvite(link: NewSharedInvite, staff: string): Promise<Result<SharedInvite>>;
473 /** Stops a shared invite link making more accounts; those it made stay. Recorded in the audit log. */
474 revokeSharedInvite(id: string, staff: string): Promise<Result<SharedInvite>>;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member475
476 /** Workspaces owners deleted that are not purged yet, newest first. */
477 deletedWorkspaces(): Promise<DeletedWorkspace[]>;
478 /**
479 * Brings a deleted workspace back, with its members, tokens and what went
480 * with it, while it is still restorable. Publishes `workspace.restored`.
481 */
482 restoreWorkspace(workspaceId: string, staff: string): Promise<Result<boolean>>;
483 /**
484 * Purges a deleted workspace now rather than at `purgeAfter`. `confirm` is
485 * its slug, typed out. Refused for a protected workspace. Publishes
486 * `workspace.deleted`.
487 */
488 purgeWorkspace(workspaceId: string, staff: string, confirm: string): Promise<Result<boolean>>;
Merge branch 'worktree-agent-a8385d293d42c913a'489
490 /** Every workspace alias, by name. */
491 aliases(): Promise<WorkspaceAlias[]>;
492 /**
493 * Points `alias` at the workspace whose slug is `workspace`. Refused for
494 * one of the site's routes, anyone's username, a workspace's slug (deleted
495 * or held after a rename) and an existing alias. `note` says why.
496 */
497 setAlias(alias: string, workspace: string, note: string, staff: string): Promise<Result<WorkspaceAlias>>;
498 /** Removes an alias; `reason` goes in sudo's audit log. */
499 removeAlias(alias: string, reason: string, staff: string): Promise<Result<boolean>>;
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace500}
501
Merge branch 'worktree-agent-a8385d293d42c913a'502/**
503 * A name g1t's staff point at a workspace, so its addresses lead there under
504 * the workspace's own name: `g1t`, the product, leads to `flagon-io`, Flagon,
505 * Inc. Staff-managed only; it follows the workspace through renames.
506 */
507export type WorkspaceAlias = {
508 alias: string;
509 workspaceId: string;
510 /** The workspace's slug and name now. */
511 workspace: string;
512 workspaceName: string;
513 /** Why it exists. */
514 note: string;
515 /** The staff member who set it, or `migration`. */
516 createdBy: string;
517 /** RFC 3339. */
518 createdAt: string;
519};
520
Initial g1t: services, event bus, intents and attempts521/** Who is asking. Every read and write in every service takes one. */
522export type Viewer = User | null;
523
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)524/**
525 * Whether this is a person whose account has not confirmed its email
526 * address. Such an account can only confirm it, change it, or sign out.
527 */
528export function awaitsConfirmation(user: Pick<User, "kind" | "verified"> | null | undefined): boolean {
529 return !!user && (user.kind ?? "user") === "user" && !user.verified;
530}
531
Initial g1t: services, event bus, intents and attempts532export type SshKey = {
533 id: string;
534 title: string;
535 fingerprint: string;
RFC 3339 timestamps in identity and repos536 /** RFC 3339. */
537 createdAt: string;
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca538 /** When it last signed in over SSH, RFC 3339, to within 5 minutes; null when it never has. */
539 lastUsedAt: string | null;
Initial g1t: services, event bus, intents and attempts540};
541
Agents as a team: lifecycle, merge queue, billing and a new shell542export type AccessToken = {
543 id: string;
544 name: string;
545 /** RFC 3339. */
546 createdAt: string;
547 /** RFC 3339, to within a few minutes. Null until it is first used. */
548 lastUsedAt: string | null;
549 /**
550 * For a workspace's token, the username of the member who made it. Null
551 * once that account is gone, and on personal tokens.
552 */
553 createdBy: string | null;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step554 /** Its scopes, as `resource:level`. Null: full access. */
555 scopes: string[] | null;
556 /** Made before tokens had scopes: full access until someone narrows it. */
557 legacy: boolean;
558 /** RFC 3339. Null: it does not expire. */
559 expiresAt: string | null;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules560 /** Classic, fine-grained, or a workspace's own. */
561 kind?: TokenKind;
562 /** What it is for, as its owner wrote it. */
563 description?: string | null;
564 /** A fine-grained token's resource owner, repositories, permissions and status. */
565 fineGrained?: FineGrainedDetails | null;
566 /** A workspace's own token an owner gave Admin when making it. */
567 admin?: boolean;
568};
569
570export type TokenKind = "classic" | "fine_grained" | "workspace";
571
572/** Whether a fine-grained token may be used on its resource owner. */
573export type TokenStatus = "active" | "pending" | "denied" | "revoked";
574
575export type FineGrainedDetails = {
576 /** The resource owner's slug; null for your own account. */
577 workspace: string | null;
578 repositorySelection: RepositorySelection;
579 /** With `selected`: the repositories, as `owner/name`, that you can see. */
580 repositories: string[];
581 permissions: Partial<Record<string, PermissionAccess>>;
582 status: TokenStatus;
583 /** Why an owner denied or revoked it. */
584 reviewReason?: string | null;
585};
586
587/** What a new fine-grained token is. */
588export type FineGrainedTokenInput = {
589 name: string;
590 description?: string | null;
591 /** Between 1 and 366 days, and no more than the workspace allows. */
592 ttlSeconds: number;
593 /** The resource owner: a workspace's slug, or null for your own account. */
594 workspace: string | null;
595 repositorySelection: RepositorySelection;
596 /** With `selected`: `owner/name` or names in the workspace. */
597 repositories: string[];
598 /** Each permission's level by name; names left out are none. */
599 permissions: Record<string, PermissionAccess>;
600};
601
602/** A workspace's rules for personal access tokens. */
603export type TokenPolicy = {
604 allowClassic: boolean;
605 allowFineGrained: boolean;
606 requireApproval: boolean;
607 /** Null: no limit. */
608 maxLifetimeDays: number | null;
609 forbidNoExpiry: boolean;
610 updatedBy?: string | null;
611 updatedAt?: string | null;
612};
613
614/** A member's personal token that reaches a workspace, as its owners see it. */
615export type MemberToken = {
616 owner: string;
617 token: AccessToken;
618 /** Whether it reaches the workspace now. */
619 reaches: boolean;
620 /** Why not: pending approval, denied, revoked, classic tokens not allowed, lasts too long, never expires. */
621 blockedBy?: string | null;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step622};
623
624/** What a new or changed token may do. */
625export type TokenGrant = {
626 /** Null: full access. */
627 scopes: string[] | null;
Agents as a team: lifecycle, merge queue, billing and a new shell628};
Initial g1t: services, event bus, intents and attempts629
Device sign-in replaces registering and minting tokens over the API630export type DeviceStart = {
631 /** Secret held by the tool and exchanged for a token once approved. */
632 deviceCode: string;
633 /** Short code shown to the person, e.g. `WDJB-MJHT`. */
634 userCode: string;
635 /** Seconds until both codes stop working. */
636 expiresIn: number;
637 /** Seconds the tool should wait between polls. */
638 interval: number;
639};
640
641export type DeviceRequest = { userCode: string; clientName: string };
642
643export type DeviceClaim =
644 | { status: "pending" | "denied" | "expired" }
645 | { status: "approved"; token: string; user: User };
646
OAuth 2.1 sign-in for MCP clients and other applications647/** What the site passes on once a person has approved an application. */
648export type OAuthApproval = {
649 clientId: string;
650 /** Shown wherever the application's access is listed. */
651 clientName: string;
652 redirectUri: string;
653 /** PKCE challenge, method S256. */
654 codeChallenge: string;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step655 /** What the person granted. Null: full access. */
656 scopes: string[] | null;
OAuth 2.1 sign-in for MCP clients and other applications657};
658
659export type OAuthTokens = {
660 accessToken: string;
661 /** Works once; using it returns the next one. */
662 refreshToken: string;
663 /** Seconds until the access token stops working. */
664 expiresIn: number;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step665 /** The scopes granted, space-separated, or `*` for full access. */
666 scope?: string | null;
OAuth 2.1 sign-in for MCP clients and other applications667};
668
669/** An application a person has signed in to. */
670export type OAuthGrant = {
671 id: string;
672 clientName: string;
673 /** RFC 3339. */
674 createdAt: string;
675 /** RFC 3339. */
676 lastUsedAt: string;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step677 /** What the person granted. Null: full access. */
678 scopes: string[] | null;
679 /** Signed in before applications had scopes: full access until narrowed. */
680 legacy: boolean;
OAuth 2.1 sign-in for MCP clients and other applications681};
682
Initial g1t: services, event bus, intents and attempts683/** Accounts, credentials and sessions. */
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member684/**
685 * What deleting a workspace takes with it, and what stands in the way:
686 * nothing does while `billing` is null and it is not `protected`.
687 */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look688export type WorkspaceDeletion = {
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member689 /** Its live repositories, deleted with it. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look690 repositories: number;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member691 /** Its projects, hidden with it. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look692 projects: number;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member693 members: number;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look694 /** Why billing cannot close it yet, in words for its owner. */
695 billing: string | null;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member696 /** It can never be deleted, by anyone. */
697 protected: boolean;
698};
699
700/** How long a deleted workspace is kept, for g1t's staff to restore, before it is purged. */
701export const WORKSPACE_RESTORE_DAYS = 30;
702
703/**
704 * Workspaces nobody can delete, whatever identity's `PROTECTED_WORKSPACES`
705 * says: Flagon's, which runs g1t. Services that act on `workspace.deleting`
706 * check it too, so one published for it by mistake changes nothing.
707 */
708export const ALWAYS_PROTECTED_WORKSPACES: readonly string[] = ["flagon-io"];
709
710/** Whether `slug` is one of `ALWAYS_PROTECTED_WORKSPACES`, in any case. */
711export function isProtectedWorkspace(slug: string): boolean {
712 return ALWAYS_PROTECTED_WORKSPACES.includes(slug.trim().toLowerCase());
713}
714
715/** A workspace an owner deleted, kept until `purgeAfter` for staff to restore. */
716export type DeletedWorkspace = {
717 workspaceId: string;
718 slug: string;
719 name: string;
720 /** RFC 3339. */
721 deletedAt: string;
Merge sudo: delete an account with the workspaces it alone owns, purge each722 /** The username of the owner who deleted it, or the staff member who deleted it with the account that alone owned it. */
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member723 deletedBy: string;
724 /** RFC 3339: when it is purged unless restored first. */
725 purgeAfter: string;
726 /** What went with it, counted when it was deleted. */
727 went: WorkspaceDeletion;
728 /** Whether staff can still restore it. */
729 restorable: boolean;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look730};
731
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca732export interface IdentityApi extends AccessClient, TeamsClient, DeployKeysClient {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look733 /**
734 * Creates an account and signs it in. While registration is invite-only,
735 * `inviteCode` must be an unused, unexpired invite (and, when it names an
736 * email, that address); it is ignored while registration is open.
737 */
738 register(
739 username: string,
740 email: string,
741 password: string,
742 inviteCode?: string | null,
743 /** Who is asking, such as the visitor's IP address, for rate limits. */
744 client?: string | null,
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm745 /**
746 * The `proof` from the invite email's link. When it is the invite's own
747 * and `email` is the address it was sent to, the account starts with that
748 * address confirmed; otherwise it is ignored.
749 */
750 emailProof?: string | null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look751 ): Promise<Result<{ user: User; sessionToken: string }>>;
Initial g1t: services, event bus, intents and attempts752 /** Verifies a username and password for website sign-in. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look753 /**
754 * Verifies a username, or any confirmed address of the account, and its
755 * password. Wrong passwords are counted against the account and `client`
756 * (the visitor's IP address); past a limit nothing is checked for a while.
757 */
Merge main (membership, two-factor, GitHub repo roles) into tokens758 signIn(
759 username: string,
760 password: string,
761 client?: string | null,
762 ): Promise<Result<{ user: User; sessionToken: string; twoFactorChallenge?: string | null }>>;
763 /**
764 * The second step of signing in, for an account with two-factor
765 * authentication: the challenge `signIn` returned, and a code from the
766 * app or a recovery code.
767 */
768 twoFactorSignIn(challenge: string, code: string, client?: string | null): Promise<Result<{ user: User; sessionToken: string }>>;
Initial g1t: services, event bus, intents and attempts769 signOut(sessionToken: string): Promise<void>;
Email verification, password reset, and Git for AI scale positioning770
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)771 /**
772 * Sends a new confirmation code and link to the primary of an account
773 * that has not confirmed it, at most once a minute.
774 */
Email verification, password reset, and Git for AI scale positioning775 resendVerification(user: User): Promise<Result<boolean>>;
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)776 /** Confirms the address the emailed link was sent to, signed in or not; ends the code sent with it. */
777 verifyEmail(token: string): Promise<Result<EmailConfirmed>>;
Email verification, password reset, and Git for AI scale positioning778 /** Emails a reset link if the address has an account. Always resolves. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look779 /**
780 * Any confirmed address of an account works; the link goes to it, and the
781 * primary and backup are told. A few an hour per address and per `client`.
782 */
783 requestPasswordReset(email: string, client?: string | null): Promise<boolean>;
Email verification, password reset, and Git for AI scale positioning784 /** Sets a new password from an emailed token and ends every session. */
785 resetPassword(token: string, password: string): Promise<Result<User>>;
786
Device sign-in replaces registering and minting tokens over the API787 /**
788 * Device sign-in (RFC 8628). A tool starts a request, a person approves
789 * its short code in a browser, and the tool claims an access token.
790 */
791 deviceStart(clientName: string): Promise<DeviceStart>;
792 /** What a user code is asking for, or null if it is not valid. */
793 deviceLookup(userCode: string): Promise<DeviceRequest | null>;
794 deviceResolve(userCode: string, user: User, approve: boolean): Promise<Result<boolean>>;
795 deviceClaim(deviceCode: string): Promise<DeviceClaim>;
796
OAuth 2.1 sign-in for MCP clients and other applications797 /**
798 * OAuth 2.1 for applications that sign a person in through the browser.
799 * The caller has checked the client and its redirect address; this
800 * returns the one-time code the application exchanges for tokens.
801 */
802 oauthAuthorize(user: User, approval: OAuthApproval): Promise<{ code: string }>;
803 /** Redeems a code. It works once, for that client, with the PKCE verifier. */
804 oauthExchange(code: string, codeVerifier: string, clientId: string, redirectUri: string): Promise<Result<OAuthTokens>>;
805 /** Trades a refresh token for new tokens; the old ones stop working. */
806 oauthRefresh(refreshToken: string, clientId: string): Promise<Result<OAuthTokens>>;
807 /** Applications the user has signed in to, most recently used first. */
808 listOAuthGrants(user: User): Promise<OAuthGrant[]>;
809 /** Signs an application out. */
810 revokeOAuthGrant(user: User, id: string): Promise<void>;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step811 /** Changes what an application may do, at once and when it refreshes. */
812 updateOAuthGrant(user: User, id: string, grant: TokenGrant): Promise<Result<OAuthGrant>>;
OAuth 2.1 sign-in for MCP clients and other applications813
Workspaces own repositories814 createWorkspace(user: User, slug: string, name: string): Promise<Result<Workspace>>;
815 /** Public details of a workspace, or null. */
816 getWorkspace(slug: string): Promise<Workspace | null>;
Merge branch 'worktree-agent-a2013627e5ea4ab13'817 /** Where a workspace keeps its repositories' git data; null when there is no such workspace. */
818 workspaceResidency(slug: string): Promise<DataResidency | null>;
819 /**
820 * Owners only. Applies to repositories made from then on. Offer `eu`
821 * only when the repos service's `storageOptions()` says it is available.
822 */
823 setWorkspaceResidency(actor: User, slug: string, residency: DataResidency): Promise<Result<DataResidency>>;
Workspaces own repositories824 /** Members only. */
825 listMembers(slug: string, viewer: Viewer): Promise<Result<Member[]>>;
826 /** Owners only. */
827 addMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
Merge main (membership, two-factor, GitHub repo roles) into tokens828 /** Owners only; your own username is leaving. Never the last owner. */
Workspaces own repositories829 removeMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
Merge main (membership, two-factor, GitHub repo roles) into tokens830 /** Owners only: owner or member, and the roles held besides it. Never leaves no owner. */
831 updateMember(actor: User, slug: string, username: string, change: { role?: Role; org_roles?: OrgRole[] }): Promise<Result<Member>>;
832 /** Owners only: `username` becomes an owner, and you a member. */
833 transferOwnership(actor: User, slug: string, username: string): Promise<Result<boolean>>;
834 /** You leave the workspace. Never the last owner. */
835 leaveWorkspace(user: User, slug: string): Promise<Result<boolean>>;
836 /** Owners only: change some member privileges; returns all of them. */
837 setMemberPrivileges(actor: User, slug: string, change: Partial<MemberPrivileges>): Promise<Result<MemberPrivileges>>;
838 /** Owners only, with two-factor on themselves: require it of everyone. */
839 setTwoFactorRequirement(actor: User, slug: string, required: boolean): Promise<Result<boolean>>;
Agents as a team: lifecycle, merge queue, billing and a new shell840 /** Owners only. An empty name falls back to the slug. */
841 updateWorkspace(actor: User, slug: string, details: { name: string; description: string }): Promise<Result<Workspace>>;
Workspace names and icons, and a component kit for every control842 /**
Agents and memory, checks and conflicts, profiles, slug renames, custom domains843 * Owners only. Changes the slug, the first segment of the workspace's
844 * URLs; the display name is untouched. The old slug redirects to the new
845 * one, and stays reserved for this workspace, for `SLUG_HOLD_DAYS`.
846 * Publishes `workspace.renamed`.
847 */
848 renameWorkspace(actor: User, slug: string, newSlug: string): Promise<Result<Workspace>>;
849 /** Whether `renameWorkspace` would be allowed, changing nothing. */
850 checkWorkspaceRename(actor: User, slug: string, newSlug: string): Promise<Result<boolean>>;
851 /**
852 * The workspace's current slug when `slug` is one it was renamed from
Merge branch 'worktree-agent-a8385d293d42c913a'853 * within `SLUG_HOLD_DAYS`, or when `slug` is an alias staff set for it
854 * (`WorkspaceAlias`); null otherwise, including for a slug in use.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains855 */
856 resolveSlug(slug: string): Promise<string | null>;
857 /**
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look858 * Owners only, a person only. `confirm` is the slug, typed out. Refused
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member859 * for a protected workspace, and while billing cannot settle it. Its
860 * repositories, projects and apps go with it; it is kept for
861 * `WORKSPACE_RESTORE_DAYS`, when g1t's staff can restore it, then purged.
862 * Its slug is never given to anyone else; the person whose username it is
863 * may make it again once it is purged. Publishes `workspace.deleting`.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look864 */
865 deleteWorkspace(actor: User, slug: string, confirm: string): Promise<Result<boolean>>;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member866 /** What `deleteWorkspace` would take with it, and what stands in its way, changing nothing. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look867 checkWorkspaceDeletion(actor: User, slug: string): Promise<Result<WorkspaceDeletion>>;
868 /**
Workspace names and icons, and a component kit for every control869 * Owners only. `image` is the file in base64: PNG, JPEG, WebP or GIF, at
870 * most `MAX_AVATAR_BYTES`, checked by its bytes. Null removes the icon.
871 */
872 setWorkspaceAvatar(actor: User, slug: string, image: string | null): Promise<Result<Workspace>>;
873 /** A person's own avatar, as `setWorkspaceAvatar`: the new one, or null. */
874 setUserAvatar(user: User, image: string | null): Promise<Result<string | null>>;
Workspaces own repositories875
Agents as a team: lifecycle, merge queue, billing and a new shell876 /**
877 * A workspace's own access tokens. They belong to the workspace, act as
878 * it, and keep working when the member who made one leaves. Members only.
879 */
880 listWorkspaceTokens(slug: string, viewer: Viewer): Promise<Result<AccessToken[]>>;
881 /** Owners only. The plaintext token is returned once and never stored. */
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step882 createWorkspaceToken(
883 actor: User,
884 slug: string,
885 name: string,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules886 grant?: TokenGrant & { ttlSeconds?: number; admin?: boolean },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step887 ): Promise<Result<{ token: string; info: AccessToken }>>;
Agents as a team: lifecycle, merge queue, billing and a new shell888 /** Owners only. */
889 removeWorkspaceToken(actor: User, slug: string, id: string): Promise<Result<boolean>>;
890
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules891 /**
892 * A fine-grained personal access token: one resource owner, some of its
893 * repositories, a level for each permission. People only. It starts
894 * pending when its workspace asks for approval and you are not an owner.
895 */
896 createFineGrainedToken(user: User, input: FineGrainedTokenInput): Promise<Result<{ token: string; info: AccessToken }>>;
897 /** Its owner changes it; what is left out stays. Widening it asks for approval again. */
898 updateFineGrainedToken(
899 user: User,
900 id: string,
901 change: Partial<Omit<FineGrainedTokenInput, "ttlSeconds" | "workspace">>,
902 ): Promise<Result<AccessToken>>;
903 /** A workspace's rules for personal access tokens. Members only. */
904 getTokenPolicy(slug: string, viewer: Viewer): Promise<Result<TokenPolicy>>;
905 /** Owners only, as people. `maxLifetimeDays` of 0 removes the limit. */
906 setTokenPolicy(
907 actor: User,
908 slug: string,
909 change: Partial<Omit<TokenPolicy, "updatedBy" | "updatedAt">>,
910 ): Promise<Result<TokenPolicy>>;
911 /** The members' tokens that can reach a workspace. Owners only. */
912 listMemberTokens(
913 actor: User,
914 slug: string,
915 filter?: { status?: TokenStatus; kind?: TokenKind },
916 ): Promise<Result<MemberToken[]>>;
917 /** Approve or deny a fine-grained token waiting for approval. Owners only. */
918 reviewTokenRequest(actor: User, slug: string, id: string, approve: boolean, reason?: string | null): Promise<Result<MemberToken>>;
919 /** Take a member's token out of the workspace. Owners only. */
920 revokeMemberToken(actor: User, slug: string, id: string, reason?: string | null): Promise<Result<boolean>>;
921
Initial g1t: services, event bus, intents and attempts922 userForSession(sessionToken: string): Promise<Viewer>;
923
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look924 /** Whether registration is invite-only. */
925 registration(): Promise<RegistrationMode>;
926 /** A person's invites and what they have left. */
927 listInvites(user: User): Promise<InvitesOverview>;
928 /**
929 * A person makes an invite, optionally for one address (emailed to it),
930 * using one of theirs or, with `workspace`, one the workspace was granted.
931 * People only: never an agent or a workspace's token.
932 */
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)933 createInvite(
934 user: User,
935 options?: { email?: string | null; workspace?: string | null; join?: string | null },
936 ): Promise<Result<Invite>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look937 /** Its maker, or an owner of its workspace, revokes a pending invite; the invite comes back. */
938 revokeInvite(user: User, id: string): Promise<Result<Invite>>;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas939 /**
940 * What a code is for. Unknown, used, revoked and expired codes all get the
941 * same answer, unless `anyStatus`: then a real code that is spent is
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm942 * described, with its `status`. `viewer` sets `forViewer`; `emailProof`,
943 * the `proof` from the invite email's link, sets `emailProven`.
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas944 */
945 checkInvite(
946 code: string,
947 client?: string | null,
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm948 options?: { viewer?: User | null; anyStatus?: boolean; emailProof?: string | null },
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas949 ): Promise<Result<InvitePreview>>;
950 /**
951 * A signed-in person uses a workspace invite sent to their address, or one
952 * sent with a repository invitation; returns the workspace's slug, or
953 * `workspace/repo`.
954 */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look955 acceptInvite(user: User, code: string): Promise<Result<string>>;
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)956 /**
957 * Owners only. Invites someone into a workspace by address (always with an
958 * invite bound to it) or by `username`: a workspace invitation they accept
959 * or decline. Nobody joins without saying yes. `role` is what they join as.
960 */
961 inviteMember(
962 actor: User,
963 slug: string,
964 who: { email?: string | null; username?: string | null; role?: Role | null },
965 ): Promise<Result<Invite>>;
966 /** The workspace invitations waiting for the person's answer, newest first. */
967 listInvitations(user: User): Promise<WorkspaceInvitation[]>;
968 /** Joins the invitation's workspace with its role; returns the workspace's slug. */
969 acceptInvitation(user: User, id: string): Promise<Result<string>>;
970 /** Declines it; whoever sent it is told in their inbox. */
971 declineInvitation(user: User, id: string): Promise<Result<boolean>>;
972 /** People to invite, by username prefix or name: a username, a name and an avatar each. */
973 findPeople(query: string, limit?: number): Promise<PersonMatch[]>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look974 /** Owners only: the workspace's invites, newest first. */
975 workspaceInvites(slug: string, viewer: Viewer): Promise<Result<Invite[]>>;
976 /** Owners only. */
977 revokeWorkspaceInvite(actor: User, slug: string, id: string): Promise<Result<Invite>>;
978 /** Someone without an invite asks for one. Always the same answer for a valid address. */
979 requestAccess(email: string, about: string, client?: string | null): Promise<Result<boolean>>;
980
Initial g1t: services, event bus, intents and attempts981 /** Verifies git credentials: the account password or an access token. */
982 userForGitCredentials(username: string, secret: string): Promise<Viewer>;
API and MCP server, Rust identity service, registration, site redesign983 /** Resolves a `g1t_…` access token, as sent to the API and MCP server. */
984 userForAccessToken(token: string): Promise<Viewer>;
Initial g1t: services, event bus, intents and attempts985 userForSshKey(fingerprint: string): Promise<Viewer>;
986 userByUsername(username: string): Promise<Viewer>;
What happened across an outcome, as a feed beside its graph987 /** The names behind account and workspace ids; unknown ids are left out. */
988 usernames(ids: string[]): Promise<Record<string, string>>;
Initial g1t: services, event bus, intents and attempts989
Agents and memory, checks and conflicts, profiles, slug renames, custom domains990 /** A person's public profile, or null if there is no such account. Never an email address. */
991 profile(username: string): Promise<Profile | null>;
992 /** A person changes their own profile. Every field is replaced; an empty one is cleared. */
993 updateProfile(actor: User, fields: ProfileFields): Promise<Result<Profile>>;
994 /**
995 * The workspaces a profile shows `viewer`: those the viewer belongs to
996 * as well, and those of `publicIn` (where the person made a public
997 * project) that the person really belongs to. Nothing else.
998 */
999 profileWorkspaces(username: string, viewer: Viewer, publicIn: string[]): Promise<ProfileWorkspace[]>;
1000
Initial g1t: services, event bus, intents and attempts1001 listSshKeys(user: User): Promise<SshKey[]>;
1002 /** Takes one line in OpenSSH public key format. */
1003 addSshKey(user: User, title: string, publicKey: string): Promise<Result<SshKey>>;
1004 removeSshKey(user: User, id: string): Promise<void>;
1005
1006 listAccessTokens(user: User): Promise<AccessToken[]>;
Account dropdown, llms.txt onboarding, hosted agent runner (not yet deployed)1007 /**
Agents as a team: lifecycle, merge queue, billing and a new shell1008 * The plaintext token is returned once and never stored. With
1009 * `ttlSeconds` the token expires and is left out of token lists; that
1010 * form is used for hosted agents. A token made for a workspace acting
1011 * through a token of its own belongs to that workspace too.
Account dropdown, llms.txt onboarding, hosted agent runner (not yet deployed)1012 */
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1013 createAccessToken(
1014 user: User,
1015 name: string,
1016 ttlSeconds?: number,
1017 grant?: TokenGrant & { listed?: boolean },
1018 ): Promise<{ token: string; info: AccessToken }>;
1019 /** Changes what one of a person's tokens may do; the token is unchanged. */
1020 updateAccessToken(user: User, id: string, grant: TokenGrant): Promise<Result<AccessToken>>;
Agents as a team: lifecycle, merge queue, billing and a new shell1021 /**
1022 * A token for a g1t agent working for `onBehalfOf`: it acts as
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1023 * `g1t`, in `scope.repo` only, and only for `scope.operations`.
Agents as a team: lifecycle, merge queue, billing and a new shell1024 */
1025 createAgentToken(
1026 onBehalfOf: User,
1027 scope: AgentScope,
1028 ttlSeconds: number,
1029 ): Promise<{ token: string; info: AccessToken }>;
Initial g1t: services, event bus, intents and attempts1030 removeAccessToken(user: User, id: string): Promise<void>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1031 /**
1032 * A token for one sandbox run: it acts as the agent on behalf of
1033 * `onBehalfOf`, can do only what the run's kind needs in `repo`, and
1034 * expires after `ttlSeconds`. See `audit.ts`.
1035 */
1036 createRunCredential(input: CreateRunCredentialInput): Promise<{ token: string; info: AccessToken }>;
1037 /** Ties tokens, by the SHA-256 of their text in hex, to the agent run their sandbox recorded. */
1038 bindRunCredentials(tokenHashes: string[], runId: string): Promise<boolean>;
1039 /** Ends a sandbox's run credentials, by hash or by run. Never touches another token. */
1040 revokeRunCredentials(target: { tokenHashes?: string[]; runId?: string | null }): Promise<boolean>;
Initial g1t: services, event bus, intents and attempts1041}
Agents as a team: lifecycle, merge queue, billing and a new shell1042
1043
1044/** What an agent's token may do: these operations, in this repository. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1045export type AgentScope = { repo: RepoPath; operations: string[]; run?: RunBinding };
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1046
1047/** The most characters each profile field takes. Mirrors `crates/contracts/src/identity.rs`. */
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1048export const PROFILE_LIMITS = { name: 80, bio: 160, location: 80, website: 200, pronouns: 40, timezone: 64 } as const;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1049
1050/** What anyone may see about a person, at `g1t.sh/u/<username>`. */
1051export type Profile = {
1052 username: string;
1053 /** The name they go by, if they gave one. */
1054 name: string | null;
1055 bio: string | null;
1056 location: string | null;
1057 /** Always an `https://` address. */
1058 website: string | null;
1059 pronouns: string | null;
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1060 /** The time zone they are in, an IANA name such as `America/Denver`. */
1061 timezone: string | null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1062 /** The uploaded avatar's hash, served at `/avatars/<avatar>`. */
1063 avatar: string | null;
1064 /** When the account was made. RFC 3339. */
1065 createdAt: string;
1066};
1067
1068/** What a person may change on their profile. Empty clears a field. */
1069export type ProfileFields = {
1070 name: string;
1071 bio: string;
1072 location: string;
1073 /** `https://…`; a bare `example.com` is taken as `https://example.com`. */
1074 website: string;
1075 pronouns: string;
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1076 /** An IANA time zone name, such as `America/Denver`; empty clears it. */
1077 timezone: string;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1078};
1079
1080/** A workspace on a person's profile. */
1081export type ProfileWorkspace = { slug: string; name: string; avatar: string | null };

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