| 1 | --- |
| 2 | title: Identity and access |
| 3 | description: How every kind of credential becomes one viewer, what the viewer carries, and how each service decides access with the same compiled-in rules instead of asking identity. |
| 4 | --- |
| 5 | |
| 6 | import Aside from '../../../components/Aside.astro'; |
| 7 | import Diagram from '../../../components/Diagram.astro'; |
| 8 | |
| 9 | `identity` (`services/identity`, Rust, D1 `g1t`) is the only service that |
| 10 | knows about credentials. It holds accounts, sessions, workspaces and their |
| 11 | members, teams, repository roles and invitations, access tokens, OAuth, |
| 12 | device sign-in and two-factor authentication. Every other service learns |
| 13 | who is asking from the viewer it is handed. |
| 14 | |
| 15 | <Diagram name="identity-flow" /> |
| 16 | |
| 17 | ## Credentials |
| 18 | |
| 19 | | Credential | Looks like | Used by | Resolved with | |
| 20 | | --- | --- | --- | --- | |
| 21 | | Session cookie | `g1t_session`, 64 hex characters, 30 days | The site | `user_for_session` | |
| 22 | | Access token | `g1t_…`, personal or a workspace's, with scopes | The API, MCP, git, the CLI | `user_for_access_token` | |
| 23 | | OAuth access token | Issued by `POST /oauth/token`, refreshed with `g1r_…` | MCP clients, the phone app, apps you authorise | `user_for_access_token` | |
| 24 | | Job token | `G1T_TOKEN` in a workflow job | Actions, for that job alone | `user_for_access_token` | |
| 25 | | Run credential | Made per sandbox run, up to 6 hours, or per [session](/guides/sessions/) g1t starts on Claude Managed Agents, up to 12 | Sandboxes; a session's agent, for the API, MCP, git and the CLI | `user_for_access_token` | |
| 26 | | git password | Your account's password | git, only without two-factor authentication | `user_for_git_credentials` | |
| 27 | |
| 28 | The CLI signs in with the device flow of RFC 8628 (`/device/code`, then |
| 29 | `/device/token`) and keeps its token in the system keychain. Session |
| 30 | cookies and access tokens are stored as their SHA-256, so neither can be |
| 31 | read back. |
| 32 | |
| 33 | ## The viewer |
| 34 | |
| 35 | `identity` answers with a `User` (`crates/contracts/src/lib.rs`), and the |
| 36 | edge hands it to every service it calls, as `viewer` in the call's |
| 37 | arguments. It is `null` for someone signed out. |
| 38 | |
| 39 | | Field | Holds | |
| 40 | | --- | --- | |
| 41 | | `kind` | `User`, `Workspace`, `Agent` or `System`: what kind of principal this is | |
| 42 | | `workspaces` | Each membership: the role, the workspace's base permission, the roles that add to a member, such as billing or security manager, and privileges | |
| 43 | | `grants` | Roles on repositories, directly or through teams | |
| 44 | | `acting` | For a job or run token: the narrower scope it acts within. A session's run credential resolves to g1t acting on behalf of the person who started it; past the API it is that person with `acting` kept, so their role decides and what it writes is recorded as g1t's for them | |
| 45 | |
| 46 | A service never asks `identity` whether someone may do something. Each |
| 47 | calls `can` in `g1t_contracts::access`, the same rules compiled into every |
| 48 | Rust service, with `packages/contracts/src/access.ts` as the TypeScript |
| 49 | mirror, which a test keeps identical. That takes `identity` out of every |
| 50 | request after the first call, and means access is decided by the service |
| 51 | that owns the data, next to it. |
| 52 | |
| 53 | <Aside title="Why the viewer can be trusted"> |
| 54 | The viewer is passed as plain JSON, not signed. That is safe because |
| 55 | services can be reached only through service bindings: none is on |
| 56 | `workers.dev`, and the only routes in the core are `telemetry`'s and |
| 57 | `og`'s public endpoints, which take no viewer. A Worker can only be |
| 58 | called by the Workers bound to it, and those are the edge Workers and |
| 59 | other services, which resolved the viewer themselves. |
| 60 | </Aside> |
| 61 | |
| 62 | ## Roles |
| 63 | |
| 64 | | Level | Roles | |
| 65 | | --- | --- | |
| 66 | | Workspace | Owner and member, plus the roles that add to a member (billing manager, security manager) and member privileges | |
| 67 | | Repository | Read, Triage, Write, Maintain and Admin, given to a person, a team, or everyone in the workspace as its base permission | |
| 68 | | Outside collaborators | A repository role for someone who is not a member | |
| 69 | |
| 70 | A [work item](/guides/work-items/) belongs to the workspace, so it has no |
| 71 | repository role of its own. `item_role` gives the viewer the higher of |
| 72 | their role through workspace membership and their permission on the work |
| 73 | item's origin repository, if it has one. `can_item` and `check_item` judge |
| 74 | it with the same table as `can` and `check`. So members see every work |
| 75 | item, anyone who can read the origin repository sees the ones filed there, |
| 76 | and a work item with no origin is seen only by members. Managing the |
| 77 | workspace's labels and milestones takes the role through membership. |
| 78 | |
| 79 | The rules live in `crates/contracts/src/access.rs`; the grants live in |
| 80 | `identity`. [Access and roles](/guides/access-and-roles/) explains each |
| 81 | role from a person's side. |
| 82 | |
| 83 | ## Scopes |
| 84 | |
| 85 | A token can do no more than its owner, and often less. Scopes are written |
| 86 | `resource:level`, where `admin` includes `write` and `write` includes |
| 87 | `read` (`crates/contracts/src/scopes.rs`). A token's reach is every |
| 88 | workspace its owner belongs to, one of them, or none. The API checks the |
| 89 | scopes before it calls any service, and the MCP server lists only the |
| 90 | tools they allow ([How a request is served](/architecture/requests/#scopes)). |
| 91 | |
| 92 | ## Sign-in hardening |
| 93 | |
| 94 | | | | |
| 95 | | --- | --- | |
| 96 | | Two-factor | TOTP (RFC 6238), with ten recovery codes stored as SHA-256. A workspace can require it. | |
| 97 | | Sudo mode | Sensitive changes ask you to prove it is you again (`Identity::proof`) | |
| 98 | | Lockouts | Wrong passwords are throttled per account and per address | |
| 99 | | Unconfirmed email | Can sign in, but cannot push | |
| 100 | | GitHub sign-in | A GitHub App's OAuth web flow with PKCE, keyed on your GitHub account's numeric id | |
| 101 | | Staff | `sudo.g1t.sh` checks the Cloudflare Access token against a staff list, and is the only caller of `identity`'s admin methods | |
| 102 | |
| 103 | ## Events |
| 104 | |
| 105 | `identity` publishes `user.*`, `workspace.*`, `team.*` and `invite.*` |
| 106 | events. Lifecycle events, such as a workspace being |
| 107 | deleted or renamed, go to every subscriber, so each service cleans up or |
| 108 | renames its own data ([Events](/architecture/events/#subscriber-queues)). |