Skip to content

g1t/packages/contracts/src/scopes.ts

331 lines15,650 bytesCodeBlame

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.

Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1/**
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 *
6 * A token reaches whatever its owner can reach (a workspace's token, that
7 * workspace); what a request may do is the intersection of the owner's
8 * role and the token's scopes.
9 */
10
11export type ScopeResource =
12 | "repo"
13 | "code"
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member14 | "packages"
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step15 | "issues"
16 | "pull_requests"
17 | "agents"
18 | "workflows"
19 | "memory"
20 | "account"
API: notifications over REST and MCP, with notifications scopes21 | "notifications"
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step22 | "workspace"
23 | "access"
24 | "webhooks"
Fast pages, required checks on the branch, self-hosted runners, honest incidents25 | "secrets"
26 | "runners";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step27
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member28export type ScopeLevel = "read" | "write" | "run" | "delete" | "admin";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step29
30/** Every scope, grouped by resource, least first. */
31export const SCOPES = [
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily32 { scope: "repo:read", description: "See repositories, their settings, labels, timelines and security alerts, and search" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step33 { scope: "repo:write", description: "Create repositories, rename branches and change how pull requests merge" },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily34 { scope: "repo:admin", description: "Rename, archive, transfer, delete or change who can see a repository, and dismiss security alerts" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step35 { scope: "code:read", description: "Clone and fetch private repositories with git" },
36 { scope: "code:write", description: "Push commits with git" },
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member37 { scope: "packages:read", description: "Pull container images and install private packages" },
38 { scope: "packages:write", description: "Push container images and publish packages" },
39 { scope: "packages:delete", description: "Delete packages and their versions" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step40 { scope: "issues:read", description: "Read issues, comments and plans" },
41 { scope: "issues:write", description: "Open, edit, close and comment on issues" },
42 { scope: "pull_requests:read", description: "Read pull requests, their changes, sessions and merge queues" },
43 { scope: "pull_requests:write", description: "Open, review, close and merge pull requests" },
44 { scope: "agents:run", description: "Put g1t agents to work and message them, which uses the workspace's money" },
45 { scope: "workflows:read", description: "Read workflows, runs and logs" },
46 { scope: "workflows:write", description: "Run, cancel, rerun and turn workflows on or off" },
47 { scope: "memory:read", description: "Recall memory and search the workspace's context" },
48 { scope: "memory:write", description: "Save memory for the next agent" },
49 { scope: "account:read", description: "Read your email addresses, invites and invitations" },
50 { scope: "account:write", description: "Change your email addresses, make invites and answer invitations" },
API: notifications over REST and MCP, with notifications scopes51 { scope: "notifications:read", description: "See your inbox, its threads, and what you subscribe to and watch" },
52 { scope: "notifications:write", description: "Mark notifications read, done, saved or snoozed, subscribe to threads and watch repositories" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step53 { scope: "workspace:read", description: "Read workspace invites, integrations and model routes" },
54 { scope: "workspace:admin", description: "Create and delete workspaces, invite members, connect integrations" },
55 { scope: "access:read", description: "See who has access to repositories" },
56 { scope: "access:admin", description: "Give and take away access to repositories" },
57 { scope: "webhooks:read", description: "See webhooks and their deliveries" },
58 { scope: "webhooks:admin", description: "Create, change and delete webhooks" },
59 { scope: "secrets:read", description: "List secrets (never their values) and read variables" },
60 { scope: "secrets:admin", description: "Set and delete secrets and variables" },
Fast pages, required checks on the branch, self-hosted runners, honest incidents61 { scope: "runners:read", description: "See self-hosted runners, their groups and where agents run" },
62 { scope: "runners:admin", description: "Register and remove self-hosted runners, change their groups and settings" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step63] as const;
64
65export type Scope = (typeof SCOPES)[number]["scope"];
66
67/** Resources in the order settings show them, with their names for people. */
68export const SCOPE_RESOURCES: { resource: ScopeResource; label: string }[] = [
69 { resource: "repo", label: "Repositories" },
70 { resource: "code", label: "Code" },
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member71 { resource: "packages", label: "Packages" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step72 { resource: "issues", label: "Issues" },
73 { resource: "pull_requests", label: "Pull requests" },
74 { resource: "agents", label: "g1t agents" },
75 { resource: "workflows", label: "Workflows" },
76 { resource: "memory", label: "Memory and context" },
77 { resource: "account", label: "Your account" },
API: notifications over REST and MCP, with notifications scopes78 { resource: "notifications", label: "Notifications" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step79 { resource: "workspace", label: "Workspaces" },
80 { resource: "access", label: "Who has access" },
81 { resource: "webhooks", label: "Webhooks" },
82 { resource: "secrets", label: "Secrets and variables" },
Fast pages, required checks on the branch, self-hosted runners, honest incidents83 { resource: "runners", label: "Self-hosted runners" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step84];
85
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member86const LEVEL_ORDER: Record<ScopeLevel, number> = { read: 0, write: 1, run: 2, delete: 3, admin: 4 };
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step87
88export function scopeResource(scope: Scope): ScopeResource {
89 return scope.split(":")[0] as ScopeResource;
90}
91
92export function scopeLevel(scope: Scope): ScopeLevel {
93 return scope.split(":")[1] as ScopeLevel;
94}
95
96export function isScope(text: string): text is Scope {
97 return SCOPES.some((row) => row.scope === text);
98}
99
100/** Changes that are hard to undo, or decide who can reach what. */
101export function isDangerous(scope: Scope): boolean {
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member102 const level = scopeLevel(scope);
103 return level === "admin" || level === "delete";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step104}
105
106export function describeScope(scope: Scope): string {
107 return SCOPES.find((row) => row.scope === scope)?.description ?? scope;
108}
109
110/** Whether holding `held` gives `needed`: the same resource, at its level or lower. */
111export function scopeIncludes(held: Scope, needed: Scope): boolean {
112 return (
113 scopeResource(held) === scopeResource(needed) &&
114 LEVEL_ORDER[scopeLevel(held)] >= LEVEL_ORDER[scopeLevel(needed)]
115 );
116}
117
118/** The levels a resource has, least first. */
119export function levelsOf(resource: ScopeResource): ScopeLevel[] {
120 return SCOPES.filter((row) => scopeResource(row.scope) === resource).map((row) => scopeLevel(row.scope));
121}
122
123/** Scopes from text separated by spaces or commas, in table order; unknown ones are left out. */
124export function parseScopes(text: string): Scope[] {
125 const given = new Set(text.split(/[\s,]+/).map((part) => part.trim().toLowerCase()));
126 return SCOPES.map((row) => row.scope).filter((scope) => given.has(scope));
127}
128
129/** What a token stores for full access. */
130export const FULL_ACCESS = "*";
131
132export type PresetId = "read_only" | "agent" | "ci" | "full";
133
134/** Starting points for choosing scopes. `*` is full access. */
135export const PRESET_SCOPES = {
136 read_only: [
API: notifications over REST and MCP, with notifications scopes137 "repo:read", "code:read", "packages:read", "issues:read", "pull_requests:read", "workflows:read", "memory:read", "account:read", "notifications:read", "workspace:read", "access:read", "webhooks:read", "secrets:read", "runners:read",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step138 ] as const,
139 agent: [
API: notifications over REST and MCP, with notifications scopes140 "repo:read", "code:read", "code:write", "packages:read", "issues:read", "issues:write", "pull_requests:read", "pull_requests:write", "agents:run", "workflows:read", "memory:read", "memory:write", "account:read", "notifications:read", "notifications:write", "workspace:read", "access:read", "webhooks:read", "secrets:read",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step141 ] as const,
142 ci: [
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member143 "repo:read", "code:read", "code:write", "packages:read", "packages:write", "workflows:read", "workflows:write",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step144 ] as const,
145 full: [
146 "*",
147 ] as const,
148};
149
150export const PRESETS: { id: PresetId; label: string; description: string }[] = [
151 { id: "read_only", label: "Read only", description: "Read everything you can read; change nothing." },
152 { id: "agent", label: "Agent", description: "Read everything, work on issues and pull requests, push code and run g1t agents." },
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member153 { id: "ci", label: "CI", description: "Clone and push code, push and pull packages, and run workflows." },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step154 { id: "full", label: "Full access", description: "Everything you can do, including deleting repositories and changing who has access." },
155];
156
157/** The scopes of a preset, or null for full access. */
158export function presetScopes(id: PresetId): Scope[] | null {
159 if (id === "full") return null;
160 return [...PRESET_SCOPES[id]] as Scope[];
161}
162
163/** What an OAuth client gets when it asks for nothing in particular. */
164export const OAUTH_DEFAULT_SCOPES: Scope[] = [...PRESET_SCOPES.agent];
165
166/** The operation each scope gates, by the API's operation names. */
167export const OPERATION_SCOPES = [
168 ["list_emails", "account:read"],
169 ["add_email", "account:write"],
170 ["remove_email", "account:write"],
171 ["update_email_settings", "account:write"],
172 ["list_invites", "account:read"],
173 ["create_invite", "account:write"],
174 ["revoke_invite", "account:write"],
175 ["list_my_repo_invitations", "account:read"],
176 ["accept_repo_invitation", "account:write"],
177 ["decline_repo_invitation", "account:write"],
API: notifications over REST and MCP, with notifications scopes178 // Your inbox: notifications, subscriptions and watching.
179 ["list_notifications", "notifications:read"],
180 ["get_notification_thread", "notifications:read"],
181 ["get_thread_subscription", "notifications:read"],
182 ["get_repo_subscription", "notifications:read"],
183 ["list_watched_repos", "notifications:read"],
184 ["mark_notifications_read", "notifications:write"],
185 ["mark_thread_read", "notifications:write"],
186 ["mark_thread_done", "notifications:write"],
187 ["save_thread", "notifications:write"],
188 ["snooze_thread", "notifications:write"],
189 ["set_thread_subscription", "notifications:write"],
190 ["delete_thread_subscription", "notifications:write"],
191 ["set_repo_subscription", "notifications:write"],
192 ["delete_repo_subscription", "notifications:write"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step193 ["create_workspace", "workspace:admin"],
194 ["delete_workspace", "workspace:admin"],
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily195 ["update_workspace", "workspace:admin"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step196 ["list_workspace_invites", "workspace:read"],
197 ["invite_member", "workspace:admin"],
198 ["revoke_workspace_invite", "workspace:admin"],
199 ["list_integrations", "workspace:read"],
200 ["connect_integration", "workspace:admin"],
201 ["disconnect_integration", "workspace:admin"],
202 ["test_integration", "workspace:admin"],
203 ["get_model_routes", "workspace:read"],
204 ["set_model_routes", "workspace:admin"],
205 ["list_repos", "repo:read"],
206 ["get_repo", "repo:read"],
207 ["search", "repo:read"],
208 ["list_events", "repo:read"],
209 ["list_labels", "repo:read"],
210 ["get_repo_settings", "repo:read"],
Fast pages, required checks on the branch, self-hosted runners, honest incidents211 ["list_check_names", "repo:read"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step212 ["list_deleted_repos", "repo:read"],
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily213 ["list_security_alerts", "repo:read"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step214 ["create_repo", "repo:write"],
215 ["update_repo", "repo:write"],
216 ["update_repo_settings", "repo:write"],
217 ["rename_branch", "repo:write"],
218 ["rename_repo", "repo:admin"],
219 ["transfer_repo", "repo:admin"],
220 ["archive_repo", "repo:admin"],
221 ["unarchive_repo", "repo:admin"],
222 ["set_repo_visibility", "repo:admin"],
223 ["delete_repo", "repo:admin"],
224 ["restore_repo", "repo:admin"],
225 ["purge_repo", "repo:admin"],
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily226 ["dismiss_security_alert", "repo:admin"],
227 ["reopen_security_alert", "repo:admin"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step228 ["list_issues", "issues:read"],
229 ["get_issue", "issues:read"],
230 ["get_plan", "issues:read"],
231 ["create_issue", "issues:write"],
232 ["update_issue", "issues:write"],
233 ["close_issue", "issues:write"],
234 ["reopen_issue", "issues:write"],
235 ["add_comment", "issues:write"],
236 ["import_issue", "issues:write"],
237 ["apply_plan", "issues:write"],
238 ["list_pull_requests", "pull_requests:read"],
239 ["get_pull_request", "pull_requests:read"],
240 ["get_pull_request_changes", "pull_requests:read"],
241 ["read_session", "pull_requests:read"],
242 ["get_merge_queue", "pull_requests:read"],
243 ["create_pull_request", "pull_requests:write"],
244 ["record_session", "pull_requests:write"],
245 ["mark_pull_request_ready", "pull_requests:write"],
246 ["close_pull_request", "pull_requests:write"],
247 ["review_pull_request", "pull_requests:write"],
248 ["merge_pull_request", "pull_requests:write"],
249 ["assign_issue", "agents:run"],
250 ["delegate", "agents:run"],
251 ["plan_work", "agents:run"],
252 ["message_agent", "agents:run"],
253 ["answer_message", "agents:run"],
254 ["take_messages", "agents:run"],
255 ["list_workflows", "workflows:read"],
256 ["list_workflow_runs", "workflows:read"],
257 ["get_workflow_run", "workflows:read"],
258 ["get_job_logs", "workflows:read"],
259 ["dispatch_workflow", "workflows:write"],
260 ["cancel_workflow_run", "workflows:write"],
261 ["rerun_workflow_run", "workflows:write"],
262 ["update_workflow", "workflows:write"],
263 ["recall", "memory:read"],
264 ["search_context", "memory:read"],
265 ["get_entity", "memory:read"],
266 ["get_context", "memory:read"],
267 ["remember", "memory:write"],
268 ["list_collaborators", "access:read"],
269 ["get_collaborator_permission", "access:read"],
270 ["list_repo_invitations", "access:read"],
271 ["list_outside_collaborators", "access:read"],
272 ["add_collaborator", "access:admin"],
273 ["update_collaborator", "access:admin"],
274 ["remove_collaborator", "access:admin"],
275 ["revoke_repo_invitation", "access:admin"],
276 ["set_base_permission", "access:admin"],
277 ["list_webhooks", "webhooks:read"],
278 ["list_webhook_deliveries", "webhooks:read"],
279 ["create_webhook", "webhooks:admin"],
280 ["update_webhook", "webhooks:admin"],
281 ["delete_webhook", "webhooks:admin"],
282 ["ping_webhook", "webhooks:admin"],
283 ["redeliver_webhook", "webhooks:admin"],
284 ["list_actions_secrets", "secrets:read"],
285 ["list_actions_variables", "secrets:read"],
286 ["set_actions_secret", "secrets:admin"],
287 ["delete_actions_secret", "secrets:admin"],
288 ["set_actions_variable", "secrets:admin"],
289 ["delete_actions_variable", "secrets:admin"],
Fast pages, required checks on the branch, self-hosted runners, honest incidents290 // Self-hosted runners.
291 ["list_runners", "runners:read"],
292 ["list_runner_groups", "runners:read"],
293 ["get_runner_settings", "runners:read"],
294 ["create_runner_registration_token", "runners:admin"],
295 ["remove_runner", "runners:admin"],
296 ["create_runner_group", "runners:admin"],
297 ["update_runner_group", "runners:admin"],
298 ["delete_runner_group", "runners:admin"],
299 ["update_runner_settings", "runners:admin"],
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step300] as const;
301
302/**
303 * The scopes a token or grant holds, as stored: null for full access, or
304 * the list. `legacy` marks a token made before scopes, which has full
305 * access until someone narrows it.
306 */
307export type TokenScopes = {
308 scopes: Scope[] | null;
309 legacy: boolean;
310};
311
312/**
313 * How settings group scopes into a checklist: each group's scopes, least
314 * first. Admin scopes are not here; they are under "Dangerous" on their
315 * own (see `DANGEROUS_SCOPES`). Every other scope is in exactly one group.
316 */
317export const SCOPE_GROUPS: { id: string; label: string; scopes: Scope[] }[] = [
318 { id: "code", label: "Repositories & code", scopes: ["repo:read", "repo:write", "code:read", "code:write"] },
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member319 { id: "packages", label: "Packages", scopes: ["packages:read", "packages:write"] },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step320 { id: "work", label: "Issues & pull requests", scopes: ["issues:read", "issues:write", "pull_requests:read", "pull_requests:write"] },
321 { id: "agents", label: "Agents", scopes: ["agents:run"] },
322 { id: "workflows", label: "Workflows", scopes: ["workflows:read", "workflows:write"] },
323 { id: "memory", label: "Memory & search", scopes: ["memory:read", "memory:write"] },
324 { id: "account", label: "Account", scopes: ["account:read", "account:write"] },
API: notifications over REST and MCP, with notifications scopes325 { id: "notifications", label: "Notifications", scopes: ["notifications:read", "notifications:write"] },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step326 { id: "workspace", label: "Workspace", scopes: ["workspace:read", "access:read", "webhooks:read", "secrets:read"] },
Fast pages, required checks on the branch, self-hosted runners, honest incidents327 { id: "runners", label: "Runners", scopes: ["runners:read"] },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step328];
329
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member330/** The admin and delete scopes, shown under "Dangerous" behind a warning. */
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step331export const DANGEROUS_SCOPES: Scope[] = SCOPES.map((row) => row.scope).filter(isDangerous);