Skip to content

Commit

The docs say how to use a token on the website, with Playwright and curl examples, what still needs a real sign-in, its rate limit and what the audit log records, and the page-timing script signs in with a token read from the file G1T_TOKEN_FILE names, without printing it or putting it on a command line.

syntaqxcommitted Parent31b01b4Browse files
7 files+136−70/7 viewed
+1−1
7676 | Actor | Who did it: a person, an agent, or a workspace token. |
7777 | On behalf of | For an agent, the person it worked for: `g1t on behalf of syntaqx`. |
7878 | Run | The agent run, with its kind: `implement`, `review`, `update` and so on; or the workflow run whose job's token did it, as `workflow_job`. |
79−| Credential | The id of the token used. |
79+| Credential | The id of the token used: on the API, the MCP server or git, or on g1t.sh by a token [used on the website](/guides/authentication/#use-a-token-on-the-website). Empty for a person signed in on g1t.sh. |
8080 | Action | The API or MCP operation, such as `create_issue`, or `git.push` and `git.fetch`. |
8181 | Target | The repository, the issue or pull request number, and for git the refs it moved. |
8282 | Outcome | `allowed` or `denied`. |
+102−4
577577
578578 ## Access tokens
579579
580−A token stands in for your password everywhere outside the website:
580+A token stands in for your password everywhere outside the website, and on
581+the website too when you turn that on for it:
581582
582583 | Where | How to send it |
583584 | --- | --- |
584585 | git | As the password, with your username. |
585586 | API | `Authorization: Bearer g1t_…` |
586587 | MCP | The same header, set when you add the server. |
588+| The website | The same header on every request, from automation that drives a browser. Only a token with **Use the website as you** turned on. See [use a token on the website](#use-a-token-on-the-website). |
587589
588590 A token is shown once, when it is created; g1t stores only a hash of it.
589591 If you lose one, delete it and create another. Delete a token the moment
627629 5. Under **Permissions**, set each resource the token needs to a level.
628630 **Read only**, **Agent** and **CI** fill in a [preset](#presets);
629631 **Clear** sets everything back to no access.
630−6. Select **Generate token**, and copy it. It is not shown again.
632+6. Leave **Use the website as you**, under **Website**, off unless the
633+ token is for automation that drives a browser. See
634+ [use a token on the website](#use-a-token-on-the-website).
635+7. Select **Generate token**, and copy it. It is not shown again.
631636
632637 When you make a token for one workspace that
633638 [requires approval](#a-workspaces-rules-for-tokens), and you are not one of
641646 The list under Settings → Access tokens shows each token's name, status
642647 (pending, denied or revoked, with the owner's note), where it reaches, its
643648 permissions, and when it was made, last used and expires. Select a token to
644−open its page, where you can change its name, description, repositories
645−and permissions, and select **Save changes**. The token itself stays the
649+open its page, where you can change its name, description, repositories,
650+permissions and **Use the website as you**, and select **Save changes**.
651+A token that can use the website is marked **Uses the website**. The token
652+itself stays the
646653 same; the change applies from its next request. Widening a token made for
647654 a workspace that requires approval asks its owners again. Where it reaches
648655 and when it expires cannot change; make a new token instead.
649656
650657 **Delete token**, at the bottom of its page, stops it working at once.
651658
659+### Use a token on the website
660+
661+Automation that drives a browser, such as end-to-end tests or an agent
662+checking how a page looks, can use g1t.sh as you with an access token, so it
663+never types your password or a two-factor code. Each request it makes
664+carries the token in the `Authorization` header; no cookie is set and no
665+session is started.
666+
667+1. Open [Settings → Access tokens](https://g1t.sh/settings/tokens) and
668+ select **New token**, or open a token you have.
669+2. Give it an expiration, and the permissions it needs for git, the API and
670+ MCP, if any.
671+3. Under **Website**, tick **Use the website as you**. It is off unless you
672+ tick it, and a workspace's own token cannot have it.
673+4. Select **Generate token** (or **Save changes**), and keep the token in a
674+ file only the automation can read.
675+5. Send `Authorization: Bearer g1t_…` on every request to g1t.sh.
676+
677+With [Playwright](https://playwright.dev), set the header on the browser
678+context, reading the token from a file so it is never printed:
679+
680+```js
681+import { readFileSync } from "node:fs";
682+import { chromium } from "playwright";
683+
684+const token = readFileSync(process.env.G1T_TOKEN_FILE, "utf8").trim();
685+const browser = await chromium.launch();
686+const context = await browser.newContext({
687+ extraHTTPHeaders: { authorization: `Bearer ${token}` },
688+});
689+const page = await context.newPage();
690+await page.goto("https://g1t.sh/acme/rocket/pulls");
691+await page.screenshot({ path: "pulls.png", fullPage: true });
692+await browser.close();
693+```
694+
695+`extraHTTPHeaders` sends the header with every request the page makes,
696+including to other addresses it loads files from. To send it to g1t.sh
697+only, add it per request instead:
698+
699+```js
700+const context = await browser.newContext();
701+await context.route("https://g1t.sh/**", (route) =>
702+ route.continue({ headers: { ...route.request().headers(), authorization: `Bearer ${token}` } }),
703+);
704+```
705+
706+Any HTTP client works the same way:
707+
708+```sh
709+curl -H "Authorization: Bearer $(cat ~/.config/g1t/website-token)" https://g1t.sh/acme/rocket/pulls
710+```
711+
712+On the website, the token acts as you in the workspaces it
713+[reaches](#where-a-token-reaches). Its permissions are made for git, the API
714+and MCP, and the website does not hold it to them: treat it as able to do
715+anything there that you can. Keep it as safe as your password, and give it
716+an expiration.
717+
718+- **Only the header counts.** A token in a query string or a cookie is
719+ ignored. A request with the header is the token's, even if it also has a
720+ session cookie.
721+- **Checked on every request.** Deleting the token, its expiry, or a
722+ workspace [revoking it](#a-workspaces-rules-for-tokens) stops it at once.
723+- **A token that is not accepted is no one.** One that is not valid, has
724+ expired, or does not have **Use the website as you** loads pages as
725+ someone signed out, with a `WWW-Authenticate` header saying the token was
726+ refused; the data requests and form posts pages make answer `401`.
727+- **Form posts need nothing more.** Browsers never send the header by
728+ themselves, so a post with it needs no other proof it came from g1t.sh.
729+ A post from another site is still refused.
730+- **Limits follow the token**: 1,000 requests a minute, as on the API. See
731+ [rate limits](/reference/rate-limits/).
732+- **The audit log names it.** A change made this way is recorded as yours,
733+ with the token's id under **Credential**. See [the audit log](/guides/audit-log/).
734+
735+Some things always need you to sign in on g1t.sh yourself. With a token,
736+these pages answer **This needs you to sign in** (`403`):
737+
738+| What | Where |
739+| --- | --- |
740+| Access tokens, yours and a workspace's, and a workspace's rules for and approvals of members' tokens | Settings → Access tokens; a workspace's Settings → Access tokens and Personal access tokens |
741+| Two-factor authentication | Settings → Two-factor authentication |
742+| Your username, and deleting your account | Settings → Account |
743+| Email addresses, which reset your password | Settings → Emails |
744+| SSH keys | Settings → SSH keys |
745+| Applications you signed in to, and signing in with GitHub | Settings → Connected applications, Settings → GitHub |
746+| Letting a device or an application sign in | `g1t.sh/device`, `g1t.sh/oauth/authorize` |
747+| Deleting a workspace, and giving it to another owner | A workspace's Settings and People |
748+| Payment methods: the billing portal, adding a card, subscribing and buying AI credit | A workspace's Billing |
749+
652750 ### Permissions
653751
654752 A permission is a resource and a level. A higher level includes the lower
+2−1
106106 </Card>
107107 <Card title="Everything has an API" icon="book-open">
108108 Repositories, issues, pull requests, agents and workflows are all a [REST endpoint](/reference/api/) and an [MCP tool action](/reference/mcp/) away, with
109− [webhooks](/guides/webhooks/) for every event.
109+ [webhooks](/guides/webhooks/) for every event. Tests that drive a browser
110+ [use the website with a token](/guides/authentication/#use-a-token-on-the-website).
110111 </Card>
111112 </CardGrid>
112113
+1−0
2020 | Git over HTTPS, without credentials | Client IP address | 120 |
2121 | Anonymous clones of one repository that are not cached | Repository | 120 |
2222 | Pages on g1t.sh, signed in | Session | 1,200 |
23+| Pages on g1t.sh, [with a token](/guides/authentication/#use-a-token-on-the-website) | Token | 1,000 |
2324 | Pages on g1t.sh, signed out | Client IP address | 600 |
2425 | Archive downloads, workflow run pages, logs and search, signed out | Client IP address | 30 |
2526 | [Raw files](/guides/git/#raw-files) on g1tusercontent.com | Client IP address, together with pages signed out | 600 |
+4−0
513513 # Signed in: your g1t_session cookie's value, from DevTools; never printed
514514 $env:G1T_SESSION = "<64 hex>"
515515 powershell -File scripts/perf/measure.ps1 -Runs 7 -Pull 12 -Issue 11 -Out before-signed-in.csv
516+# Or signed in with an access token that may use the website: the path of
517+# the file holding it (the token is never printed or put on a command line)
518+$env:G1T_TOKEN_FILE = "$HOME\.config\g1t\website-token"
519+powershell -File scripts/perf/measure.ps1 -Runs 7 -Out before-token.csv
516520 ```
517521
518522 It prints p50 and p90 of the server's share (TLS handshake done to first
+1−0
5858 | `WEB_ADDRESS_LIMIT` | 4204 | 3,000 | address | web: every request that reaches the Worker; stops made-up cookies getting round the signed-out limit |
5959 | `GIT_ANONYMOUS_LIMIT` | 4205 | 120 | address | web: git smart HTTP without `Authorization` (~40 clones) |
6060 | `GIT_SIGNED_LIMIT` | 4206 | 1,200 | `Authorization` hash | web: git smart HTTP with credentials, sandboxes' included |
61+| `WEB_TOKEN_LIMIT` | 4207 | 1,000 | token hash | web: pages and data requests with `Authorization: Bearer` (a token used on the website, not checked there); the same limit as `API_TOKEN_LIMIT`, counted apart |
6162 | `PACK_FILL_LIMIT` | 4301 | 30 | repository id | repos `src/limits.rs`: packs written to `GIT_PACKS`; past it the pack is streamed, not kept |
6263 | `ANONYMOUS_FETCH_LIMIT` | 4302 | 120 | repository id | repos: anonymous fetches the git store answers (cache hits never count) |
6364 | `API_ANONYMOUS_LIMIT` | 4401 | 60 | `rest:`/`mcp:` + address | api `src/limits.rs`: no token, or a wrong one |
+25−1
1616 powershell -File scripts/perf/measure.ps1 -Runs 7 -Out before.csv
1717
1818 .EXAMPLE
19+ # Signed in with an access token that has "Use the website as you" on:
20+ # G1T_TOKEN_FILE names the file holding it. The script never prints it.
21+ $env:G1T_TOKEN_FILE = "$HOME\.config\g1t\website-token"
22+ powershell -File scripts/perf/measure.ps1 -Runs 7
23+
24+.EXAMPLE
1925 # As a browser sees it: React Router streams to browsers and renders the
2026 # whole page first for anything it takes for a crawler, which curl's own
2127 # user agent is. Compare the two to see what streaming saves.
6066 $signedIn = [bool]$env:G1T_SESSION
6167 $cookieArgs = @()
6268 if ($signedIn) { $cookieArgs = @("-H", "Cookie: g1t_session=$($env:G1T_SESSION)") }
69+# Signed in with an access token that may use the website: G1T_TOKEN_FILE
70+# names the file holding it. curl reads the header from a temporary file,
71+# so the token is never on a command line or printed. It wins over
72+# G1T_SESSION, as on the site.
73+$tokenHeaderFile = $null
74+if ($env:G1T_TOKEN_FILE) {
75+ if (-not (Test-Path -LiteralPath $env:G1T_TOKEN_FILE -PathType Leaf)) { throw "G1T_TOKEN_FILE does not name a file." }
76+ $tokenHeaderFile = [System.IO.Path]::GetTempFileName()
77+ $header = "Authorization: Bearer " + (Get-Content -Raw -LiteralPath $env:G1T_TOKEN_FILE).Trim()
78+ [System.IO.File]::WriteAllText($tokenHeaderFile, $header)
79+ Remove-Variable header
80+ $cookieArgs = @("-H", "@$tokenHeaderFile")
81+ $signedIn = $true
82+}
6383 $agentArgs = @()
6484 if ($BrowserUA) {
6585 $agentArgs = @("-A", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36")
116136 return ($shown -join " ")
117137 }
118138
119−Write-Host "Measuring $Base, $Runs runs each, $(if ($signedIn) { 'signed in' } else { 'signed out' }), $(if ($BrowserUA) { 'as a browser (streamed)' } else { 'as curl (a crawler: whole page first)' })."
139+Write-Host "Measuring $Base, $Runs runs each, $(if ($tokenHeaderFile) { 'signed in with a token' } elseif ($signedIn) { 'signed in' } else { 'signed out' }), $(if ($BrowserUA) { 'as a browser (streamed)' } else { 'as curl (a crawler: whole page first)' })."
140+try {
120141 $rows = foreach ($path in $Paths) {
121142 $url = "$Base$path"
122143 $null = Measure-Once $url # warm the connection and the isolate
135156 "Server-Timing (last run)" = Summarize-Timing $last.Timing
136157 }
137158 }
159+} finally {
160+ if ($tokenHeaderFile) { Remove-Item -LiteralPath $tokenHeaderFile -ErrorAction SilentlyContinue }
161+}
138162
139163 $rows | Format-Table -AutoSize -Wrap
140164 if ($Out) {