Skip to content
114 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.

Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1// Namespaces follow GitHub's rules (no leading, trailing or doubled hyphens),
2// so "--" can never appear inside one.
3const NAMESPACE = /^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,38}$/;
4const REPO_NAME = /^[a-z0-9._-]{1,100}$/;
5
6/** Routes and reserved words that may not be registered as usernames. */
7const RESERVED = new Set([
8 "api", "mcp", "login", "logout", "register", "new", "settings", "search",
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)9 "admin", "auth", "integrations", "pulls", "issues", "verify", "confirm-email", "forgot", "reset", "device", "workspaces", "u", "oauth", "assets", "avatars", "docs", "explore", "about", "pricing",
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent10 // g1t itself, and the name its agent once went by: everything g1t does is
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)11 // shown as `g1t`, so nobody else may be called either. `ghost` wrote what
12 // a deleted account wrote (`GHOST_USERNAME`).
13 "g1t", "g1t-agent", "ghost",
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays14 // Trust pages on g1t.sh, and names kept for them.
15 "policies", "security", "support", "status", "terms", "privacy", "help", "blog",
16 // Invite links, and the waitlist.
17 "invite", "invites", "waitlist",
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)18 // g1t.sh/invitations: the workspace invitations waiting for an answer.
19 "invitations",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member20 // The container registry, at g1t.sh/v2/.
21 "v2",
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all22 // g1t.sh/inbox, and the name it might also go by.
23 "inbox", "notifications",
preparing for cloud work24 // g1t.sh/download, the desktop app; g1t.sh/downloads/, the releases it and the runner come from.
25 "download", "downloads",
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays26]);
27
A workflow's `uses: actions/setup-node@v4` means GitHub's `actions` again, whoever is on g1t. g1t looked for a repository on g1t before GitHub, and anyone could make a public workspace called `actions`, `docker` or `aws-actions` and have its `setup-node` run in every workflow that names GitHub's, with that job's secrets. Now another workspace's public repository answers only for a name GitHub has no public repository of; the caller's own workspace and g1t's own `g1t/…` still answer from g1t, and when GitHub can't be asked, GitHub's is used. The names of the GitHub accounts whose actions workflows use most can no longer be registered, renamed to, or taken by signing up with GitHub (which suggests the name with `-gh`); an account that has one keeps it, and mentions and invitations still find it. The Actions, authentication and workspaces guides say so.28/**
29 * The GitHub accounts whose actions workflows use most. `uses:
30 * actions/setup-node@v4` means GitHub's `actions`, so nobody may register
31 * these on g1t; an account that already has one keeps it. The list is
32 * crates/contracts/src/names.rs's `ACTION_OWNERS`.
33 */
34const ACTION_OWNERS = new Set([
35 "actions", "actions-ecosystem", "actions-rs", "amannn", "anchore", "appleboy", "aquasecurity",
36 "arduino", "astral-sh", "aws-actions", "azure", "browser-actions", "bufbuild", "cachix",
37 "changesets", "chromaui", "cloudflare", "codecov", "conda-incubator", "coursier", "crazy-max",
38 "cypress-io", "dawidd6", "denoland", "dependabot", "dessant", "determinatesystems",
39 "devcontainers", "digitalocean", "docker", "dorny", "dtolnay", "endbug", "erlef",
40 "firebaseextended", "fluxcd", "getsentry", "github", "golangci", "google-github-actions",
41 "googleapis", "goreleaser", "gradle", "gradle-update", "hadolint", "hashicorp",
42 "haskell-actions", "hecrj", "helm", "ilammy", "jamesives", "jetbrains", "julia-actions",
43 "kubernetes-sigs", "lukka", "marocchino", "microsoft", "mikepenz", "mozilla-actions", "msys2",
44 "mxschmitt", "ncipollo", "netlify", "nick-fields", "nwtgck", "octokit", "ossf", "oven-sh",
45 "peaceiris", "peter-evans", "pnpm", "pre-commit", "pypa", "r-lib", "release-drafter",
46 "renovatebot", "reviewdog", "rhysd", "ruby", "rust-lang", "sbt", "shivammathur", "sigstore",
47 "slackapi", "slsa-framework", "snyk", "softprops", "sonarsource", "stefanzweifel",
48 "step-security", "styfle", "subosito", "supabase", "super-linter", "superfly", "svenstaro",
49 "swatinem", "taiki-e", "tj-actions", "treosh", "vercel", "webfactory",
50]);
51
52/** Whether `value`, whatever its case, names a well-known GitHub action owner. */
53export function isActionOwnerName(value: string): boolean {
54 return ACTION_OWNERS.has(value.trim().toLowerCase());
55}
56
57/** Whether a person or workspace may take `value` as its name now: valid, and no well-known action owner's. */
58export function isClaimableName(value: string): boolean {
59 return isValidNamespace(value) && !isActionOwnerName(value);
60}
61
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent62/** Whether `value`, whatever its case, is a name nobody can register: a route, or g1t's own. */
63export function isReservedName(value: string): boolean {
64 return RESERVED.has(value.trim().toLowerCase());
65}
66
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays67export function isValidNamespace(value: string): boolean {
Merge branch 'worktree-agent-a8385d293d42c913a'68 return isNamespaceShaped(value) && !isReservedName(value);
69}
70
71/**
72 * Whether `value` has a namespace's shape, reserved or not: what a
73 * workspace's old name or an alias staff set (such as `g1t`) can be.
74 */
75export function isNamespaceShaped(value: string): boolean {
76 return NAMESPACE.test(value);
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays77}
78
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers79/**
80 * Usernames: letters of either case, digits and single hyphens, not
81 * starting or ending with a hyphen, 1 to 39 characters, never reserved in
82 * any case. The case is kept for showing; everything finds a person by the
83 * name lowercased (`canonicalUsername`), so `Ana` and `ana` are one name.
84 */
85const USERNAME = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
86
87/** The `pattern` a username field takes in a form: the same rule, for the browser. */
88export const USERNAME_PATTERN = "[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9]))*";
89
90export function isValidUsername(value: string): boolean {
91 const name = value.trim();
A workflow's `uses: actions/setup-node@v4` means GitHub's `actions` again, whoever is on g1t. g1t looked for a repository on g1t before GitHub, and anyone could make a public workspace called `actions`, `docker` or `aws-actions` and have its `setup-node` run in every workflow that names GitHub's, with that job's secrets. Now another workspace's public repository answers only for a name GitHub has no public repository of; the caller's own workspace and g1t's own `g1t/…` still answer from g1t, and when GitHub can't be asked, GitHub's is used. The names of the GitHub accounts whose actions workflows use most can no longer be registered, renamed to, or taken by signing up with GitHub (which suggests the name with `-gh`); an account that has one keeps it, and mentions and invitations still find it. The Actions, authentication and workspaces guides say so.92 return USERNAME.test(name) && !isReservedName(name) && !isActionOwnerName(name);
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers93}
94
95/** The name a person is found, linked and mentioned by: lowercased. */
96export function canonicalUsername(value: string): string {
97 return value.trim().toLowerCase();
98}
99
100/** How a person's username shows: as they wrote it, or as it is kept. */
101export function shownUsername(person: { username: string; display_username?: string | null; displayUsername?: string | null }): string {
102 const display = person.display_username ?? person.displayUsername;
103 return display && display.toLowerCase() === person.username.toLowerCase() ? display : person.username;
104}
105
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays106export function isValidRepoName(value: string): boolean {
107 return (
108 REPO_NAME.test(value) &&
109 !value.startsWith(".") &&
110 !value.endsWith(".git") &&
111 // `/<workspace>/-/…` holds the workspace's own pages.
112 value !== "-"
113 );
114}

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