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

Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version1/**
2 * Scopes: what an access token may do on its owner's behalf. Mirrors
3 * `crates/contracts/src/scopes.rs`, which is the source of truth; a Rust
4 * test keeps the tables here the same.
5 *
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers6 * A token's permissions are its scopes read per resource: each resource
7 * at none or one level, such as issues: write. What a request may do is
8 * the intersection of the owner's role, the token's reach (the workspace
9 * it is made for, and its repositories) and its permissions.
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version10 */
11
12export type ScopeResource =
13 | "repo"
14 | "code"
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar15 | "security"
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version16 | "packages"
17 | "issues"
18 | "pull_requests"
19 | "agents"
20 | "workflows"
Token reach: workflow_files scope, fine-grained reach, workspace token cap21 | "workflow_files"
Merge checks: statuses and check runs on every commit22 | "checks"
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9723 | "deployments"
Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry.24 | "observability"
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version25 | "memory"
26 | "account"
API: notifications over REST and MCP, with notifications scopes27 | "notifications"
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)28 | "chat"
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version29 | "workspace"
Usage, Billing settings and prepaid AI credit; fixes from the UX audit30 | "billing"
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version31 | "access"
32 | "webhooks"
33 | "secrets"
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens34 | "runners"
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 yet35 | "models"
36 | "artifacts";
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version37
38export type ScopeLevel = "read" | "write" | "run" | "delete" | "admin";
39
40/** Every scope, grouped by resource, least first. */
41export const SCOPES = [
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9742 { scope: "repo:read", description: "See repositories, their settings, labels, timelines, releases, languages, contributors and security alerts, and search" },
The Branches page does things to branches now. It has tabs (Overview, Yours, Active, Stale, All, kept in the address as ?tab=) and search; Overview shows the default branch, then the first five of your branches, the active ones and the stale ones, each linking to the rest. Each branch is a row with its name in a code chip with a copy button, who made its latest commit and when, that commit's checks, behind and ahead counts with bars drawn against the largest shown, and its pull request (the open one, else the newest merged or closed) with its state; on a phone the columns fold under the name. Yours is branches whose latest commit is the viewer's; stale is no commit in 90 days.43 { scope: "repo:write", description: "Create repositories, create, rename and delete branches, change how pull requests merge and publish releases" },
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge44 { scope: "repo:admin", description: "Rename, archive, transfer, delete or change who can see a repository, change its rulesets, and dismiss security alerts" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version45 { scope: "code:read", description: "Clone and fetch private repositories with git" },
46 { scope: "code:write", description: "Push commits with git" },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar47 { scope: "security:read", description: "See secret scanning, code scanning and vulnerability alerts, custom patterns, the dependency graph and SBOM, and security settings" },
48 { scope: "security:write", description: "Dismiss and reopen alerts, bypass push protection, review bypass requests, manage custom patterns, upload SARIF and change security settings" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version49 { scope: "packages:read", description: "Pull container images and install private packages" },
50 { scope: "packages:write", description: "Push container images and publish packages" },
Merge packages: roles, Actions access, source label, soft delete, API51 { scope: "packages:delete", description: "Delete and restore packages and their versions" },
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.52 { scope: "issues:read", description: "Read issues and comments" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version53 { scope: "issues:write", description: "Open, edit, close and comment on issues" },
54 { scope: "pull_requests:read", description: "Read pull requests, their changes, sessions and merge queues" },
55 { scope: "pull_requests:write", description: "Open, review, close and merge pull requests" },
56 { scope: "workflows:read", description: "Read workflows, runs and logs" },
57 { scope: "workflows:write", description: "Run, cancel, rerun and turn workflows on or off" },
Token reach: workflow_files scope, fine-grained reach, workspace token cap58 { scope: "workflow_files:write", description: "Add, change and delete workflow files under .g1t/workflows and .github/workflows, with git or the API" },
Merge checks: statuses and check runs on every commit59 { scope: "checks:read", description: "Read commits' statuses, check runs, check suites and annotations" },
60 { scope: "checks:write", description: "Report statuses and check runs on commits, and ask for checks to run again" },
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9761 { scope: "deployments:read", description: "See deployments, their statuses and environments" },
62 { scope: "deployments:write", description: "Report deployments and their statuses, from any CI" },
Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry.63 { scope: "observability:read", description: "See projects' requests, logs, traces, errors and metrics, and their observability settings, ingest tokens and exports" },
64 { scope: "observability:write", description: "Resolve and ignore errors, and change projects' observability settings, ingest tokens and exports" },
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.65 { scope: "memory:read", description: "Recall memory" },
66 { scope: "memory:write", description: "Save memory for whoever works on the project next" },
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9767 { scope: "account:read", description: "Read your email addresses, invites, invitations, pinned projects and stars" },
68 { scope: "account:write", description: "Change your email addresses, make invites, answer invitations, pin projects and star repositories" },
API: notifications over REST and MCP, with notifications scopes69 { scope: "notifications:read", description: "See your inbox, its threads, and what you subscribe to and watch" },
70 { scope: "notifications:write", description: "Mark notifications read, done, saved or snoozed, subscribe to threads and watch repositories" },
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)71 { scope: "chat:read", description: "Read the conversations you are in: channels, direct messages, threads and reactions, live" },
72 { scope: "chat:write", description: "Send, edit and delete your messages, react, mark conversations read, and answer cards in chat" },
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.73 { scope: "workspace:read", description: "Read workspace settings, invites, integrations, teams and rulesets" },
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge74 { scope: "workspace:admin", description: "Create and delete workspaces, invite members, connect integrations, create, change and delete teams, and change the workspace's rulesets" },
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.75 { scope: "billing:read", description: "See a workspace's usage, budget and invoices" },
76 { scope: "billing:write", description: "Change a workspace's budget" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version77 { scope: "access:read", description: "See who has access to repositories" },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar78 { scope: "access:admin", description: "Give and take away access to repositories, a team's included" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version79 { scope: "webhooks:read", description: "See webhooks and their deliveries" },
80 { scope: "webhooks:admin", description: "Create, change and delete webhooks" },
81 { scope: "secrets:read", description: "List secrets (never their values) and read variables" },
82 { scope: "secrets:admin", description: "Set and delete secrets and variables" },
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.83 { scope: "runners:read", description: "See self-hosted runners and their groups" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version84 { scope: "runners:admin", description: "Register and remove self-hosted runners, change their groups and settings" },
Merge main into Artifacts Phase 285 { scope: "artifacts:read", description: "List, read and search artifacts you can see, their versions, and the numbers their dashboards show" },
86 { scope: "artifacts:write", description: "Create, rename, move, edit, trash and restore artifacts, and propose changes to them" },
87 { scope: "artifacts:admin", description: "Share artifacts, change who can open them, and delete them for good" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version88] as const;
89
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 yet90/**
91 * Scopes of resources being built that tokens are not offered yet (Rust:
92 * `Resource::offered`). They parse in Rust and are typed here, but no
93 * preset, full access, OAuth request or token form hands them out, and no
94 * operation needs them. When one ships, its rows move to the end of
Merge main into Artifacts Phase 295 * `SCOPES` and `SCOPE_RESOURCES`. Empty now: Artifacts shipped last.
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 yet96 */
Merge main into Artifacts Phase 297export const UPCOMING_SCOPES: readonly { scope: Scope; description: string }[] = [
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 yet98] as const;
99
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.100/**
101 * Scopes of what g1t no longer has (Rust: `Resource::retired`): `agents`
102 * (g1t's own agents) and `models` (its model proxy and AI Gateway). Tokens
103 * and OAuth grants that stored them still read, but they are never offered
104 * or granted and no operation needs them: they give nothing. `isScope` and
105 * `parseScopes` leave them out.
106 */
107export const RETIRED_SCOPES = [
108 { scope: "agents:run", description: "Retired: put g1t's own agents to work. Grants nothing now" },
109 { scope: "models:read", description: "Retired: see the AI Gateway's requests. Grants nothing now" },
110 { scope: "models:write", description: "Retired: send requests through the AI Gateway. Grants nothing now" },
111] as const;
112
Merge main into Artifacts Phase 2113export type Scope = (typeof SCOPES)[number]["scope"];
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version114
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers115/** Where a resource sits on the token form; only a person's token may hold account ones. */
116export type ResourceGroup = "repository" | "workspace" | "account";
117
118/** Resources in the order settings show them, with their names for people and their group. */
119export const SCOPE_RESOURCES: { resource: ScopeResource; label: string; group: ResourceGroup }[] = [
120 { resource: "repo", label: "Repositories", group: "repository" },
121 { resource: "code", label: "Code", group: "repository" },
122 { resource: "security", label: "Security", group: "repository" },
123 { resource: "packages", label: "Packages", group: "repository" },
124 { resource: "issues", label: "Issues", group: "repository" },
125 { resource: "pull_requests", label: "Pull requests", group: "repository" },
126 { resource: "workflows", label: "Workflows", group: "repository" },
127 { resource: "workflow_files", label: "Workflow files", group: "repository" },
128 { resource: "checks", label: "Checks and statuses", group: "repository" },
129 { resource: "deployments", label: "Deployments", group: "repository" },
Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry.130 { resource: "observability", label: "Observability", group: "repository" },
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.131 { resource: "memory", label: "Memory", group: "repository" },
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers132 { resource: "account", label: "Your account", group: "account" },
133 { resource: "notifications", label: "Notifications", group: "account" },
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)134 { resource: "chat", label: "Chat", group: "account" },
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers135 { resource: "workspace", label: "Workspaces", group: "workspace" },
136 { resource: "billing", label: "Billing", group: "workspace" },
137 { resource: "access", label: "Who has access", group: "repository" },
138 { resource: "webhooks", label: "Webhooks", group: "repository" },
139 { resource: "secrets", label: "Secrets and variables", group: "repository" },
140 { resource: "runners", label: "Self-hosted runners", group: "workspace" },
Merge main into Artifacts Phase 2141 { resource: "artifacts", label: "Artifacts", group: "workspace" },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version142];
143
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 yet144/** Resources not offered yet, as `UPCOMING_SCOPES`: settings never show them. */
145export const UPCOMING_RESOURCES: { resource: ScopeResource; label: string; group: ResourceGroup }[] = [
146];
147
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.148/** Resources of what g1t no longer has, as `RETIRED_SCOPES`: settings never show them. */
149export const RETIRED_RESOURCES: { resource: ScopeResource; label: string; group: ResourceGroup }[] = [
150 { resource: "agents", label: "g1t agents", group: "repository" },
151 { resource: "models", label: "AI Gateway", group: "workspace" },
152];
153
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version154const LEVEL_ORDER: Record<ScopeLevel, number> = { read: 0, write: 1, run: 2, delete: 3, admin: 4 };
155
156export function scopeResource(scope: Scope): ScopeResource {
157 return scope.split(":")[0] as ScopeResource;
158}
159
160export function scopeLevel(scope: Scope): ScopeLevel {
161 return scope.split(":")[1] as ScopeLevel;
162}
163
164export function isScope(text: string): text is Scope {
165 return SCOPES.some((row) => row.scope === text);
166}
167
168/** Changes that are hard to undo, or decide who can reach what. */
169export function isDangerous(scope: Scope): boolean {
170 const level = scopeLevel(scope);
171 return level === "admin" || level === "delete";
172}
173
174export function describeScope(scope: Scope): string {
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 yet175 return [...SCOPES, ...UPCOMING_SCOPES].find((row) => row.scope === scope)?.description ?? scope;
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version176}
177
178/** Whether holding `held` gives `needed`: the same resource, at its level or lower. */
179export function scopeIncludes(held: Scope, needed: Scope): boolean {
180 return (
181 scopeResource(held) === scopeResource(needed) &&
182 LEVEL_ORDER[scopeLevel(held)] >= LEVEL_ORDER[scopeLevel(needed)]
183 );
184}
185
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)186/**
187 * Whether a token holding `scopes` may do what `needed` gates (Rust:
188 * `TokenAccess::allows`). Null or absent scopes are full access, which
189 * holds every scope; unknown names give nothing.
190 */
191export function tokenAllows(scopes: readonly string[] | null | undefined, needed: Scope): boolean {
192 if (scopes === null || scopes === undefined) return true;
193 return scopes.some((held) => isScope(held) && scopeIncludes(held, needed));
194}
195
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version196/** The levels a resource has, least first. */
197export function levelsOf(resource: ScopeResource): ScopeLevel[] {
198 return SCOPES.filter((row) => scopeResource(row.scope) === resource).map((row) => scopeLevel(row.scope));
199}
200
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers201/** The token form's groups, in order. */
202export const RESOURCE_GROUPS: { group: ResourceGroup; label: string; about: string }[] = [
203 { group: "repository", label: "Repository permissions", about: "What it may do in the repositories it reaches." },
204 { group: "workspace", label: "Workspace permissions", about: "What it may do with the workspaces it reaches themselves." },
205 { group: "account", label: "Account permissions", about: "What it may do with your own account. Personal tokens only." },
206];
207
208/** A token's permissions: each resource held, at its highest level; left out is none. */
209export type Permissions = Partial<Record<ScopeResource, ScopeLevel>>;
210
211/** Scopes as permissions. Null scopes (full access) are every resource at its highest. */
212export function permissionsOf(scopes: readonly string[] | null): Permissions {
213 const permissions: Permissions = {};
214 const held = scopes === null ? SCOPES.map((row) => row.scope) : parseScopes(scopes.join(" "));
215 for (const scope of held) {
216 const resource = scopeResource(scope);
217 const now = permissions[resource];
218 if (!now || LEVEL_ORDER[scopeLevel(scope)] > LEVEL_ORDER[now]) permissions[resource] = scopeLevel(scope);
219 }
220 return permissions;
221}
222
223/** Permissions as the scopes a token stores: the highest of each resource, in table order. */
224export function scopesOfPermissions(permissions: Permissions): Scope[] {
225 const wanted = new Set(
226 Object.entries(permissions)
227 .filter(([, level]) => level)
228 .map(([resource, level]) => `${resource}:${level}`),
229 );
230 return SCOPES.map((row) => row.scope).filter((scope) => wanted.has(scope));
231}
232
233/** The longest a token with an expiry may last, in days. */
234export const MAX_TOKEN_LIFETIME_DAYS = 366;
235
236/** The most repositories a token may select. */
237export const MAX_SELECTED_REPOSITORIES = 50;
238
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version239/** Scopes from text separated by spaces or commas, in table order; unknown ones are left out. */
240export function parseScopes(text: string): Scope[] {
241 const given = new Set(text.split(/[\s,]+/).map((part) => part.trim().toLowerCase()));
242 return SCOPES.map((row) => row.scope).filter((scope) => given.has(scope));
243}
244
245/** What a token stores for full access. */
246export const FULL_ACCESS = "*";
247
248export type PresetId = "read_only" | "agent" | "ci" | "full";
249
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)250/** Starting points for choosing scopes. `*` is full access. No preset gives chat: an application gets it only by asking for it. */
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version251export const PRESET_SCOPES = {
252 read_only: [
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.253 "repo:read", "code:read", "security:read", "packages:read", "issues:read", "pull_requests:read", "workflows:read", "checks:read", "deployments:read", "observability:read", "memory:read", "account:read", "notifications:read", "workspace:read", "billing:read", "access:read", "webhooks:read", "secrets:read", "runners:read", "artifacts:read",
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version254 ] as const,
255 agent: [
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.256 "repo:read", "code:read", "code:write", "security:read", "packages:read", "issues:read", "issues:write", "pull_requests:read", "pull_requests:write", "workflows:read", "checks:read", "deployments:read", "observability:read", "memory:read", "memory:write", "account:read", "notifications:read", "notifications:write", "workspace:read", "billing:read", "access:read", "webhooks:read", "secrets:read", "artifacts:read",
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version257 ] as const,
258 ci: [
Merge checks: statuses and check runs on every commit259 "repo:read", "code:read", "code:write", "packages:read", "packages:write", "workflows:read", "workflows:write", "checks:read", "checks:write", "deployments:read", "deployments:write",
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version260 ] as const,
261 full: [
262 "*",
263 ] as const,
264};
265
266export const PRESETS: { id: PresetId; label: string; description: string }[] = [
267 { id: "read_only", label: "Read only", description: "Read everything you can read; change nothing." },
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.268 { id: "agent", label: "Agent", description: "Read everything, work on issues and pull requests, and push code." },
Merge checks: statuses and check runs on every commit269 { id: "ci", label: "CI", description: "Clone and push code, push and pull packages, run workflows, and report checks and deployments." },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version270 { id: "full", label: "Full access", description: "Everything you can do, including deleting repositories and changing who has access." },
271];
272
273/** The scopes of a preset, or null for full access. */
274export function presetScopes(id: PresetId): Scope[] | null {
275 if (id === "full") return null;
276 return [...PRESET_SCOPES[id]] as Scope[];
277}
278
279/** What an OAuth client gets when it asks for nothing in particular. */
280export const OAUTH_DEFAULT_SCOPES: Scope[] = [...PRESET_SCOPES.agent];
281
282/** The operation each scope gates, by the API's operation names. */
283export const OPERATION_SCOPES = [
284 ["list_emails", "account:read"],
285 ["add_email", "account:write"],
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)286 ["confirm_email", "account:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version287 ["remove_email", "account:write"],
288 ["update_email_settings", "account:write"],
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)289 ["list_invitations", "account:read"],
290 ["accept_invitation", "account:write"],
291 ["decline_invitation", "account:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version292 ["list_my_repo_invitations", "account:read"],
293 ["accept_repo_invitation", "account:write"],
294 ["decline_repo_invitation", "account:write"],
API: pinned projects over REST and MCP295 // Your pinned projects: a preference of your account.
296 ["list_pinned_projects", "account:read"],
297 ["pin_project", "account:write"],
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97298 // Your stars: a preference of your account.
299 ["list_starred", "account:read"],
300 ["check_starred", "account:read"],
301 ["star_repo", "account:write"],
302 ["unstar_repo", "account:write"],
API: pinned projects over REST and MCP303 ["unpin_project", "account:write"],
304 ["reorder_pinned_projects", "account:write"],
API: notifications over REST and MCP, with notifications scopes305 // Your inbox: notifications, subscriptions and watching.
306 ["list_notifications", "notifications:read"],
307 ["get_notification_thread", "notifications:read"],
308 ["get_thread_subscription", "notifications:read"],
309 ["get_repo_subscription", "notifications:read"],
310 ["list_watched_repos", "notifications:read"],
311 ["mark_notifications_read", "notifications:write"],
312 ["mark_thread_read", "notifications:write"],
313 ["mark_thread_done", "notifications:write"],
314 ["save_thread", "notifications:write"],
315 ["snooze_thread", "notifications:write"],
316 ["set_thread_subscription", "notifications:write"],
317 ["delete_thread_subscription", "notifications:write"],
318 ["set_repo_subscription", "notifications:write"],
319 ["delete_repo_subscription", "notifications:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version320 ["create_workspace", "workspace:admin"],
321 ["delete_workspace", "workspace:admin"],
Merge branch 'worktree-agent-ad7c6d88d93adc817'322 ["get_workspace", "workspace:read"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version323 ["update_workspace", "workspace:admin"],
Merge main (membership, two-factor, GitHub repo roles) into tokens324 ["list_members", "workspace:read"],
325 ["update_member", "workspace:admin"],
326 ["remove_member", "workspace:admin"],
327 ["transfer_ownership", "workspace:admin"],
328 ["leave_workspace", "account:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version329 ["list_workspace_invites", "workspace:read"],
330 ["invite_member", "workspace:admin"],
331 ["revoke_workspace_invite", "workspace:admin"],
332 ["list_integrations", "workspace:read"],
333 ["connect_integration", "workspace:admin"],
AI Gateway: OpenAI's format, open models, and your own providers334 ["update_integration", "workspace:admin"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version335 ["disconnect_integration", "workspace:admin"],
336 ["test_integration", "workspace:admin"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar337 ["list_teams", "workspace:read"],
338 ["get_team", "workspace:read"],
339 ["list_team_members", "workspace:read"],
340 ["list_child_teams", "workspace:read"],
341 ["list_team_repos", "workspace:read"],
342 ["list_user_teams", "workspace:read"],
343 ["create_team", "workspace:admin"],
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge344 ["list_workspace_rulesets", "workspace:read"],
345 ["get_workspace_ruleset", "workspace:read"],
346 ["list_workspace_rule_evaluations", "workspace:read"],
347 ["create_workspace_ruleset", "workspace:admin"],
348 ["update_workspace_ruleset", "workspace:admin"],
349 ["delete_workspace_ruleset", "workspace:admin"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar350 ["update_team", "workspace:admin"],
351 ["delete_team", "workspace:admin"],
352 ["set_team_member", "workspace:admin"],
353 ["remove_team_member", "workspace:admin"],
354 ["set_team_review_assignment", "workspace:admin"],
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.355 // A workspace's billing: usage, budget and invoices.
Usage, Billing settings and prepaid AI credit; fixes from the UX audit356 ["get_usage", "billing:read"],
357 ["get_budget", "billing:read"],
358 ["list_invoices", "billing:read"],
359 ["get_billing_details", "billing:read"],
360 ["set_budget", "billing:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version361 ["list_repos", "repo:read"],
362 ["get_repo", "repo:read"],
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.363 ["download_tarball_archive", "code:read"],
364 ["download_zipball_archive", "code:read"],
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97365 // Projects follow their repositories.
366 ["list_projects", "repo:read"],
367 ["get_project", "repo:read"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version368 ["search", "repo:read"],
369 ["list_events", "repo:read"],
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97370 // What the default branch says about a repository, who starred it, and
371 // its releases.
372 ["get_languages", "repo:read"],
373 ["list_contributors", "repo:read"],
374 ["get_license", "repo:read"],
375 ["list_stargazers", "repo:read"],
376 ["list_releases", "repo:read"],
377 ["get_latest_release", "repo:read"],
378 ["get_release_by_tag", "repo:read"],
379 ["get_release", "repo:read"],
380 ["create_release", "repo:write"],
381 ["update_release", "repo:write"],
382 ["delete_release", "repo:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version383 ["list_labels", "repo:read"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar384 ["list_milestones", "repo:read"],
385 ["get_milestone", "repo:read"],
386 ["create_label", "issues:write"],
387 ["update_label", "issues:write"],
388 ["delete_label", "issues:write"],
389 ["add_default_labels", "issues:write"],
390 ["create_milestone", "issues:write"],
391 ["update_milestone", "issues:write"],
392 ["delete_milestone", "issues:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version393 ["get_repo_settings", "repo:read"],
394 ["list_check_names", "repo:read"],
395 ["list_deleted_repos", "repo:read"],
396 ["list_security_alerts", "repo:read"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar397 ["get_codeowners_errors", "repo:read"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version398 ["create_repo", "repo:write"],
399 ["update_repo", "repo:write"],
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97400 ["update_project", "repo:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version401 ["update_repo_settings", "repo:write"],
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge402 ["list_repo_rulesets", "repo:read"],
403 ["get_repo_ruleset", "repo:read"],
404 ["get_branch_rules", "repo:read"],
405 ["list_rule_evaluations", "repo:read"],
406 ["create_repo_ruleset", "repo:admin"],
407 ["update_repo_ruleset", "repo:admin"],
408 ["delete_repo_ruleset", "repo:admin"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version409 ["rename_branch", "repo:write"],
The Branches page does things to branches now. It has tabs (Overview, Yours, Active, Stale, All, kept in the address as ?tab=) and search; Overview shows the default branch, then the first five of your branches, the active ones and the stale ones, each linking to the rest. Each branch is a row with its name in a code chip with a copy button, who made its latest commit and when, that commit's checks, behind and ahead counts with bars drawn against the largest shown, and its pull request (the open one, else the newest merged or closed) with its state; on a phone the columns fold under the name. Yours is branches whose latest commit is the viewer's; stale is no commit in 90 days.410 ["create_branch", "repo:write"],
411 ["delete_branch", "repo:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version412 ["rename_repo", "repo:admin"],
413 ["transfer_repo", "repo:admin"],
414 ["archive_repo", "repo:admin"],
415 ["unarchive_repo", "repo:admin"],
416 ["set_repo_visibility", "repo:admin"],
417 ["delete_repo", "repo:admin"],
418 ["restore_repo", "repo:admin"],
419 ["purge_repo", "repo:admin"],
420 ["dismiss_security_alert", "repo:admin"],
421 ["reopen_security_alert", "repo:admin"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar422 // The security suite.
423 ["list_secret_scanning_alerts", "security:read"],
424 ["get_secret_scanning_alert", "security:read"],
425 ["list_secret_scanning_locations", "security:read"],
426 ["list_bypass_requests", "security:read"],
427 ["list_custom_patterns", "security:read"],
428 ["list_code_scanning_alerts", "security:read"],
429 ["get_code_scanning_alert", "security:read"],
430 ["list_code_scanning_analyses", "security:read"],
431 ["get_sarif_upload", "security:read"],
432 ["list_vulnerability_alerts", "security:read"],
433 ["get_vulnerability_alert", "security:read"],
434 ["get_dependency_graph", "security:read"],
435 ["get_sbom", "security:read"],
436 ["compare_dependencies", "security:read"],
437 ["get_security_settings", "security:read"],
438 ["get_workspace_security_settings", "security:read"],
439 ["get_security_overview", "security:read"],
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9440 ["list_security_advisories", "security:read"],
441 ["get_security_advisory", "security:read"],
442 ["list_vulnerability_reports", "security:read"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar443 ["update_secret_scanning_alert", "security:write"],
444 ["bypass_push_protection", "security:write"],
445 ["check_secret_validity", "security:write"],
446 ["review_bypass_request", "security:write"],
447 ["create_custom_pattern", "security:write"],
448 ["update_custom_pattern", "security:write"],
449 ["delete_custom_pattern", "security:write"],
450 ["dry_run_custom_pattern", "security:write"],
451 ["update_code_scanning_alert", "security:write"],
452 ["upload_sarif", "security:write"],
453 ["update_vulnerability_alert", "security:write"],
454 ["update_security_settings", "security:write"],
455 ["update_workspace_security_settings", "security:write"],
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9456 ["create_security_advisory", "security:write"],
457 ["update_security_advisory", "security:write"],
458 ["set_security_advisory_state", "security:write"],
459 ["report_vulnerability", "security:write"],
460 ["review_vulnerability_report", "security:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version461 ["list_issues", "issues:read"],
462 ["get_issue", "issues:read"],
463 ["create_issue", "issues:write"],
464 ["update_issue", "issues:write"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar465 ["list_issue_labels", "issues:read"],
466 ["add_issue_labels", "issues:write"],
467 ["set_issue_labels", "issues:write"],
468 ["remove_issue_labels", "issues:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version469 ["close_issue", "issues:write"],
470 ["reopen_issue", "issues:write"],
471 ["add_comment", "issues:write"],
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts472 ["edit_comment", "issues:write"],
473 ["delete_comment", "issues:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version474 ["import_issue", "issues:write"],
The work service coordinates work on a repository: sessions of people's own harnesses (start is idempotent on the harness's session id, every update or batch of entries is a heartbeat that renews the session's claims, a session reads as idle after 15 minutes and ends as timed_out after 2 hours), session entries recorded once per key and shown in the pull request's session when the session is on one (session_entries is rebuilt so an entry can belong to a session alone), advisory expiring claims on issues that are never locks (claiming tells you who else is on it and what the issue is blocked by; release, hand-off with a note, and expiry noticed on the next read), a writer for blocked_by on update_issue that refuses other repositories' numbers, the issue itself and cycles, and get_work, which gathers an issue's definition of done, claims, sessions, pull requests with their checks, mergeability, overlaps and queue place, blockers, the issues it blocks, decisions and recent coordination events. It publishes session.started, session.ended, work.claimed, work.released, work.claim_expired, work.handoff_requested, dependency.changed (when blocked_by changes, and when a blocker closes or has its pull request merged) and overlap.detected (once for each side when a push or a new pull request starts an overlap, again only after it cleared), and the contracts and the TypeScript mirror list them.475 ["list_claims", "issues:read"],
476 ["get_work", "issues:read"],
477 ["claim_issue", "issues:write"],
478 ["release_claim", "issues:write"],
479 ["hand_off_issue", "issues:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version480 ["list_pull_requests", "pull_requests:read"],
481 ["get_pull_request", "pull_requests:read"],
482 ["get_pull_request_changes", "pull_requests:read"],
483 ["read_session", "pull_requests:read"],
484 ["get_merge_queue", "pull_requests:read"],
485 ["create_pull_request", "pull_requests:write"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar486 ["update_pull_request", "pull_requests:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version487 ["record_session", "pull_requests:write"],
488 ["mark_pull_request_ready", "pull_requests:write"],
489 ["close_pull_request", "pull_requests:write"],
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts490 ["reopen_pull_request", "pull_requests:write"],
491 ["convert_pull_request_to_draft", "pull_requests:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version492 ["review_pull_request", "pull_requests:write"],
493 ["merge_pull_request", "pull_requests:write"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar494 ["request_reviewers", "pull_requests:write"],
495 ["remove_requested_reviewers", "pull_requests:write"],
The work service coordinates work on a repository: sessions of people's own harnesses (start is idempotent on the harness's session id, every update or batch of entries is a heartbeat that renews the session's claims, a session reads as idle after 15 minutes and ends as timed_out after 2 hours), session entries recorded once per key and shown in the pull request's session when the session is on one (session_entries is rebuilt so an entry can belong to a session alone), advisory expiring claims on issues that are never locks (claiming tells you who else is on it and what the issue is blocked by; release, hand-off with a note, and expiry noticed on the next read), a writer for blocked_by on update_issue that refuses other repositories' numbers, the issue itself and cycles, and get_work, which gathers an issue's definition of done, claims, sessions, pull requests with their checks, mergeability, overlaps and queue place, blockers, the issues it blocks, decisions and recent coordination events. It publishes session.started, session.ended, work.claimed, work.released, work.claim_expired, work.handoff_requested, dependency.changed (when blocked_by changes, and when a blocker closes or has its pull request merged) and overlap.detected (once for each side when a push or a new pull request starts an overlap, again only after it cleared), and the contracts and the TypeScript mirror list them.496 ["get_session", "pull_requests:read"],
497 ["list_sessions", "pull_requests:read"],
498 ["start_session", "pull_requests:write"],
499 ["update_session", "pull_requests:write"],
500 ["end_session", "pull_requests:write"],
501 ["record_session_entries", "pull_requests:write"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version502 ["list_workflows", "workflows:read"],
503 ["list_workflow_runs", "workflows:read"],
504 ["get_workflow_run", "workflows:read"],
505 ["get_job_logs", "workflows:read"],
506 ["dispatch_workflow", "workflows:write"],
507 ["cancel_workflow_run", "workflows:write"],
508 ["rerun_workflow_run", "workflows:write"],
509 ["update_workflow", "workflows:write"],
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2510 ["list_artifacts", "workflows:read"],
511 ["list_workflow_run_artifacts", "workflows:read"],
512 ["get_artifact", "workflows:read"],
513 ["download_artifact", "workflows:read"],
514 ["get_artifact_retention", "workflows:read"],
515 ["delete_artifact", "workflows:write"],
516 ["set_artifact_retention", "workflows:write"],
Merge checks: statuses and check runs on every commit517 ["list_commit_statuses", "checks:read"],
518 ["get_combined_status", "checks:read"],
519 ["list_check_runs_for_ref", "checks:read"],
520 ["get_check_run", "checks:read"],
521 ["list_check_run_annotations", "checks:read"],
522 ["list_check_suites_for_ref", "checks:read"],
523 ["get_check_suite", "checks:read"],
524 ["create_commit_status", "checks:write"],
525 ["create_check_run", "checks:write"],
526 ["update_check_run", "checks:write"],
527 ["rerequest_check_run", "checks:write"],
528 ["rerequest_check_suite", "checks:write"],
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97529 // Deployments, wherever they run: reading them, and reporting them.
530 ["list_deployments", "deployments:read"],
531 ["get_deployment", "deployments:read"],
532 ["list_deployment_statuses", "deployments:read"],
533 ["list_environments", "deployments:read"],
534 ["get_environment", "deployments:read"],
535 ["create_deployment", "deployments:write"],
536 ["create_deployment_status", "deployments:write"],
Merge branch 'worktree-agent-a4d35ee70c50683d0' into claude/cool-newton-7nm8r9537 ["start_deployment_build", "deployments:write"],
Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry.538 // Observability: what a project's software did, and how it is collected.
539 ["get_observability_overview", "observability:read"],
540 ["list_requests", "observability:read"],
541 ["get_request", "observability:read"],
542 ["list_logs", "observability:read"],
543 ["list_traces", "observability:read"],
544 ["get_trace", "observability:read"],
545 ["list_errors", "observability:read"],
546 ["list_metrics", "observability:read"],
547 ["get_metric", "observability:read"],
548 ["get_observability_settings", "observability:read"],
549 ["list_ingest_tokens", "observability:read"],
550 ["list_telemetry_exports", "observability:read"],
551 ["get_observability_usage", "observability:read"],
552 ["update_error", "observability:write"],
553 ["update_observability_settings", "observability:write"],
554 ["create_ingest_token", "observability:write"],
555 ["delete_ingest_token", "observability:write"],
556 ["create_telemetry_export", "observability:write"],
557 ["update_telemetry_export", "observability:write"],
558 ["delete_telemetry_export", "observability:write"],
Merge branch 'worktree-agent-a3abfcce648e87dca'559 // What keeps runs safe: the runs environments hold and reviewing them,
560 // approving a pull request's run, and a repository's own rules for its
561 // environments and tokens, which are an admin's.
562 ["get_pending_deployments", "workflows:read"],
563 ["review_pending_deployments", "workflows:write"],
564 ["approve_workflow_run", "workflows:write"],
565 ["get_workflow_permissions", "repo:read"],
566 ["get_fork_pr_approval", "repo:read"],
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts567 ["get_actions_access", "repo:read"],
Merge branch 'worktree-agent-a3abfcce648e87dca'568 ["update_environment", "repo:admin"],
569 ["delete_environment", "repo:admin"],
570 ["set_workflow_permissions", "repo:admin"],
571 ["set_fork_pr_approval", "repo:admin"],
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts572 ["set_actions_access", "repo:admin"],
Merge branch 'worktree-agent-a3abfcce648e87dca'573 // Starting workflows from outside, as a push would.
574 ["create_repository_dispatch", "code:write"],
575 // A workspace's policy for its repositories' tokens.
576 ["get_workspace_workflow_permissions", "workspace:read"],
577 ["set_workspace_workflow_permissions", "workspace:admin"],
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.578 ["list_workspace_managed_workflows", "workspace:read"],
579 ["create_workspace_managed_workflow", "workspace:admin"],
580 ["update_workspace_managed_workflow", "workspace:admin"],
581 ["delete_workspace_managed_workflow", "workspace:admin"],
API and MCP for a workspace's personal access token rules, members' tokens and approvals582 // A workspace's rules for personal access tokens, and its members' tokens.
583 ["get_token_policy", "workspace:read"],
584 ["set_token_policy", "workspace:admin"],
585 ["list_member_tokens", "access:read"],
586 ["list_token_requests", "access:read"],
587 ["review_token_request", "access:admin"],
588 ["revoke_member_token", "access:admin"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version589 ["recall", "memory:read"],
590 ["get_context", "memory:read"],
591 ["remember", "memory:write"],
592 ["list_collaborators", "access:read"],
593 ["get_collaborator_permission", "access:read"],
594 ["list_repo_invitations", "access:read"],
595 ["list_outside_collaborators", "access:read"],
596 ["add_collaborator", "access:admin"],
597 ["update_collaborator", "access:admin"],
598 ["remove_collaborator", "access:admin"],
599 ["revoke_repo_invitation", "access:admin"],
600 ["set_base_permission", "access:admin"],
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar601 ["set_team_repo", "access:admin"],
602 ["remove_team_repo", "access:admin"],
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca603 ["list_deploy_keys", "access:read"],
604 ["get_deploy_key", "access:read"],
605 ["create_deploy_key", "access:admin"],
606 ["delete_deploy_key", "access:admin"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version607 ["list_webhooks", "webhooks:read"],
608 ["list_webhook_deliveries", "webhooks:read"],
609 ["create_webhook", "webhooks:admin"],
610 ["update_webhook", "webhooks:admin"],
611 ["delete_webhook", "webhooks:admin"],
612 ["ping_webhook", "webhooks:admin"],
613 ["redeliver_webhook", "webhooks:admin"],
614 ["list_actions_secrets", "secrets:read"],
615 ["list_actions_variables", "secrets:read"],
616 ["set_actions_secret", "secrets:admin"],
617 ["delete_actions_secret", "secrets:admin"],
618 ["set_actions_variable", "secrets:admin"],
619 ["delete_actions_variable", "secrets:admin"],
620 // Self-hosted runners.
621 ["list_runners", "runners:read"],
622 ["list_runner_groups", "runners:read"],
623 ["get_runner_settings", "runners:read"],
624 ["create_runner_registration_token", "runners:admin"],
625 ["remove_runner", "runners:admin"],
626 ["create_runner_group", "runners:admin"],
627 ["update_runner_group", "runners:admin"],
628 ["delete_runner_group", "runners:admin"],
629 ["update_runner_settings", "runners:admin"],
Merge packages: roles, Actions access, source label, soft delete, API630 // Packages: reading them, their versions and who may use them needs
631 // `packages:read`; changing their settings, access and Manage Actions
632 // access `packages:write` (and the Admin role on the package, which the
633 // packages service checks); deleting and restoring packages and
634 // versions `packages:delete`, as the registries' own deletes do.
635 ["list_packages", "packages:read"],
636 ["get_package", "packages:read"],
637 ["list_package_versions", "packages:read"],
638 ["get_package_version", "packages:read"],
639 ["list_package_access", "packages:read"],
640 ["list_package_actions_access", "packages:read"],
641 ["update_package", "packages:write"],
642 ["link_package", "packages:write"],
643 ["unlink_package", "packages:write"],
644 ["set_package_access", "packages:write"],
645 ["remove_package_access", "packages:write"],
646 ["set_package_actions_access", "packages:write"],
647 ["remove_package_actions_access", "packages:write"],
648 ["delete_package", "packages:delete"],
649 ["restore_package", "packages:delete"],
650 ["delete_package_version", "packages:delete"],
651 ["restore_package_version", "packages:delete"],
Merge main into Artifacts Phase 2652 // Artifacts mode's docs, slides, designs and dashboards (the `artifact`
The artifacts service is services/artifacts, the Worker g1t-artifacts, bound as ARTIFACTS by the API, the site and the agents; its live rooms move to it with a Durable Object transfer from g1t-docs-service, and its database, bucket, indexes and queue keep their names. The git store's binding and settings are GITSTORE, its ops scripts gitstore-*, and workflow run artifacts keep their compatible API under run_artifacts modules. The deploy tool puts a Worker that has never deployed before the Workers in its stage that bind to it, and the deploy guide gives the cutover runbook.653 // MCP tool). The artifacts service then checks the person's role on each.
Merge main into Artifacts Phase 2654 ["list_workspace_artifacts", "artifacts:read"],
655 ["search_workspace_artifacts", "artifacts:read"],
656 ["get_workspace_artifact", "artifacts:read"],
657 ["get_workspace_artifact_content", "artifacts:read"],
658 ["list_workspace_artifact_versions", "artifacts:read"],
659 ["get_workspace_artifact_access", "artifacts:read"],
660 ["list_workspace_artifact_templates", "artifacts:read"],
661 ["list_workspace_artifact_spaces", "artifacts:read"],
662 ["query_workspace_dataset", "artifacts:read"],
663 ["create_workspace_artifact", "artifacts:write"],
664 ["update_workspace_artifact", "artifacts:write"],
665 ["edit_workspace_artifact", "artifacts:write"],
666 ["trash_workspace_artifact", "artifacts:write"],
667 ["restore_workspace_artifact", "artifacts:write"],
668 ["restore_workspace_artifact_version", "artifacts:write"],
669 ["set_workspace_artifact_access", "artifacts:admin"],
670 ["purge_workspace_artifact", "artifacts:admin"],
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version671] as const;
672
673/**
674 * The scopes a token or grant holds, as stored: null for full access, or
675 * the list. `legacy` marks a token made before scopes, which has full
676 * access until someone narrows it.
677 */
678export type TokenScopes = {
679 scopes: Scope[] | null;
680 legacy: boolean;
681};
682
683/**
684 * How settings group scopes into a checklist: each group's scopes, least
685 * first. Admin scopes are not here; they are under "Dangerous" on their
686 * own (see `DANGEROUS_SCOPES`). Every other scope is in exactly one group.
687 */
688export const SCOPE_GROUPS: { id: string; label: string; scopes: Scope[] }[] = [
689 { id: "code", label: "Repositories & code", scopes: ["repo:read", "repo:write", "code:read", "code:write"] },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar690 { id: "security", label: "Security", scopes: ["security:read", "security:write"] },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version691 { id: "packages", label: "Packages", scopes: ["packages:read", "packages:write"] },
692 { id: "work", label: "Issues & pull requests", scopes: ["issues:read", "issues:write", "pull_requests:read", "pull_requests:write"] },
Token reach: workflow_files scope, fine-grained reach, workspace token cap693 { id: "workflows", label: "Workflows", scopes: ["workflows:read", "workflows:write", "workflow_files:write"] },
Merge checks: statuses and check runs on every commit694 { id: "checks", label: "Checks", scopes: ["checks:read", "checks:write"] },
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97695 { id: "deployments", label: "Deployments", scopes: ["deployments:read", "deployments:write"] },
Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry.696 { id: "observability", label: "Observability", scopes: ["observability:read", "observability:write"] },
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.697 { id: "memory", label: "Memory", scopes: ["memory:read", "memory:write"] },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version698 { id: "account", label: "Account", scopes: ["account:read", "account:write"] },
API: notifications over REST and MCP, with notifications scopes699 { id: "notifications", label: "Notifications", scopes: ["notifications:read", "notifications:write"] },
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)700 { id: "chat", label: "Chat", scopes: ["chat:read", "chat:write"] },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version701 { id: "workspace", label: "Workspace", scopes: ["workspace:read", "access:read", "webhooks:read", "secrets:read"] },
Usage, Billing settings and prepaid AI credit; fixes from the UX audit702 { id: "billing", label: "Billing", scopes: ["billing:read", "billing:write"] },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version703 { id: "runners", label: "Runners", scopes: ["runners:read"] },
Merge main into Artifacts Phase 2704 { id: "artifacts", label: "Artifacts", scopes: ["artifacts:read", "artifacts:write"] },
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version705];
706
707/** The admin and delete scopes, shown under "Dangerous" behind a warning. */
708export const DANGEROUS_SCOPES: Scope[] = SCOPES.map((row) => row.scope).filter(isDangerous);

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