Merge branch 'open-signups'
136 files+98−356120 files not counted0/136 viewed
| 8 | 8 | # migrate pending D1 migrations, before any code, once check and plan pass | |
| 9 | 9 | # core, edge, front the units of each stage, in jobs that share a build; | |
| 10 | 10 | # a stage starts only when the one before it succeeded | |
| 11 | − | # smoke sign-in, sign-up and the waitlist still work on g1t.sh | |
| 11 | + | # smoke sign-in and sign-up still work on g1t.sh | |
| 12 | 12 | # | |
| 13 | 13 | # Each run that deploys is one production deployment of g1t.sh, made by the | |
| 14 | 14 | # jobs that name `environment: production` (one per run, however many jobs): | |
| ⋯ | |||
| 301 | 301 | steps: *deploy | |
| 302 | 302 | ||
| 303 | 303 | # Once everything has deployed: the ways in for someone new still work. | |
| 304 | − | # The pages a visitor lands on load, and the waitlist form reaches | |
| 305 | − | # identity, sent an address it refuses before keeping anything, so the | |
| 306 | − | # real waitlist is never touched. scripts/ops/smoke.mjs. | |
| 304 | + | # The pages a visitor lands on load, and the sign-up form reaches | |
| 305 | + | # identity, sent an address it refuses before keeping or counting | |
| 306 | + | # anything, so no account is made. scripts/ops/smoke.mjs. | |
| 307 | 307 | smoke: | |
| 308 | 308 | name: Smoke | |
| 309 | 309 | needs: [check, plan, core, edge, front] | |
| ⋯ | |||
| 312 | 312 | timeout-minutes: 5 | |
| 313 | 313 | steps: | |
| 314 | 314 | - uses: actions/checkout@v5 | |
| 315 | − | - name: Sign-in, sign-up and the waitlist work | |
| 315 | + | - name: Sign-in and sign-up work | |
| 316 | 316 | run: node scripts/ops/smoke.mjs | |
| 86 | 86 | ), | |
| 87 | 87 | ( | |
| 88 | 88 | "Invites", | |
| 89 | − | "While g1t is invite-only, every new account needs an invite. Your invites, inviting people into a workspace by username or email, and answering the invitations to workspaces sent to you.", | |
| 89 | + | "Inviting people into a workspace by username or email, and answering the invitations to workspaces sent to you.", | |
| 90 | 90 | &[ | |
| 91 | − | Op::ListInvites, | |
| 92 | − | Op::CreateInvite, | |
| 93 | − | Op::RevokeInvite, | |
| 94 | 91 | Op::ListWorkspaceInvites, | |
| 95 | 92 | Op::InviteMember, | |
| 96 | 93 | Op::RevokeWorkspaceInvite, | |
| ⋯ | |||
| 661 | 658 | Op::ConfirmEmail => "Confirm an email address", | |
| 662 | 659 | Op::RemoveEmail => "Remove an email address", | |
| 663 | 660 | Op::UpdateEmailSettings => "Change your email settings", | |
| 664 | − | Op::ListInvites => "List your invites", | |
| 665 | − | Op::CreateInvite => "Create an invite", | |
| 666 | − | Op::RevokeInvite => "Revoke an invite", | |
| 667 | 661 | Op::ListWorkspaceInvites => "List a workspace's invites", | |
| 668 | 662 | Op::InviteMember => "Invite someone to a workspace", | |
| 669 | 663 | Op::ListInvitations => "List your workspace invitations", | |
| 132 | 132 | ConfirmEmail, | |
| 133 | 133 | RemoveEmail, | |
| 134 | 134 | UpdateEmailSettings, | |
| 135 | − | ListInvites, | |
| 136 | − | CreateInvite, | |
| 137 | − | RevokeInvite, | |
| 138 | 135 | ListWorkspaceInvites, | |
| 139 | 136 | InviteMember, | |
| 140 | 137 | RevokeWorkspaceInvite, | |
| ⋯ | |||
| 720 | 717 | } | |
| 721 | 718 | ||
| 722 | 719 | impl Op { | |
| 723 | − | pub const ALL: [Op; 398] = [ | |
| 720 | + | pub const ALL: [Op; 395] = [ | |
| 724 | 721 | Op::Whoami, | |
| 725 | 722 | Op::GetWorkspace, | |
| 726 | 723 | Op::CreateWorkspace, | |
| ⋯ | |||
| 736 | 733 | Op::ConfirmEmail, | |
| 737 | 734 | Op::RemoveEmail, | |
| 738 | 735 | Op::UpdateEmailSettings, | |
| 739 | − | Op::ListInvites, | |
| 740 | − | Op::CreateInvite, | |
| 741 | − | Op::RevokeInvite, | |
| 742 | 736 | Op::ListWorkspaceInvites, | |
| 743 | 737 | Op::InviteMember, | |
| 744 | 738 | Op::RevokeWorkspaceInvite, | |
| ⋯ | |||
| 1143 | 1137 | Op::ConfirmEmail => "confirm_email", | |
| 1144 | 1138 | Op::RemoveEmail => "remove_email", | |
| 1145 | 1139 | Op::UpdateEmailSettings => "update_email_settings", | |
| 1146 | − | Op::ListInvites => "list_invites", | |
| 1147 | − | Op::CreateInvite => "create_invite", | |
| 1148 | − | Op::RevokeInvite => "revoke_invite", | |
| 1149 | 1140 | Op::ListWorkspaceInvites => "list_workspace_invites", | |
| 1150 | 1141 | Op::InviteMember => "invite_member", | |
| 1151 | 1142 | Op::RevokeWorkspaceInvite => "revoke_workspace_invite", | |
| ⋯ | |||
| 1366 | 1357 | } | |
| 1367 | 1358 | Op::UpdateEmailSettings => { | |
| 1368 | 1359 | "Change what your addresses do; only the fields given change. `primary` is a confirmed address to make primary: account mail and password resets go there. `backup` is a confirmed address that also gets security notices, or an empty string for the primary only. Changing either needs your account `password`, and every confirmed address is told. `private_email` keeps your address off commits g1t makes for you (merges and changes made on the web, and agents' commits for you), which use your noreply address instead; `block_private_pushes` refuses pushes whose commits carry one of your addresses while it is private. People only." | |
| 1369 | − | } | |
| 1370 | − | Op::ListInvites => { | |
| 1371 | − | "Your invites, newest first, and how many you have left. While g1t is invite-only, every new account needs an invite code. You may have 5 invites out at once: pending and used ones count, and one revoked or expired before it was used comes back. `allowance.limit` is null when you have no limit. `workspaces` lists the workspaces you own that were granted invites to share. A pending invite's `code` is shown to you. `status` is `pending`; `awaiting_confirmation` (used to make an account that has not confirmed its address yet); `awaiting_answer` (used to make an account that has yet to accept or decline the workspace it was invited to); `redeemed`; `declined` (its person declined the workspace); `expired`; or `revoked`. An invite that brings someone into a workspace names it in `workspace`, with the `role` it joins with and, once known, the account it is for in `invitee`." | |
| 1372 | 1360 | } | |
| 1373 | − | Op::CreateInvite => { | |
| 1374 | − | "Make an invite. With `email`, it is sent there and only that address can use it; without, anyone with the code can, once. It works for 30 days. With `workspace`, the new account is brought into that workspace: once it confirms its address it gets an invitation to join as a member, which it accepts or declines, and no workspace of its own is made for it. That must be a workspace you own on the g1t plan; a free workspace is refused with `payment_required` (402). Without `workspace`, the new account gets a free workspace of its own. It uses one of your invites, or with `charge_workspace`, one of the invites g1t granted that workspace (its owners only). Returns the invite with its `code`; the link is https://g1t.sh/invite/<code>. People only: an agent's token or a workspace's token cannot make invites." | |
| 1375 | − | } | |
| 1376 | − | Op::RevokeInvite => { | |
| 1377 | − | "Revoke a pending invite you made, or one made for a workspace you own. It stops working at once, and the invite comes back to whoever it was charged to." | |
| 1378 | − | } | |
| 1379 | 1361 | Op::ListWorkspaceInvites => { | |
| 1380 | 1362 | "The invites made for a workspace, newest first, with each pending one's `code`. Owners only." | |
| 1381 | 1363 | } | |
| 1382 | 1364 | Op::InviteMember => { | |
| 1383 | − | "Invite someone into a workspace, by `username` or by `email`. Nobody joins without saying yes: they get an invitation to accept or decline, and join with `role` (`member` unless you give `owner`) when they accept. By `username`, the account gets the invitation in its inbox and by email, and it costs nothing. By `email`, it always makes an invite bound to that address and emails it the link, so the answer never says whether the address has a g1t account. Without one, the link makes the account, which is invited once it confirms its address; while g1t is invite-only that uses one of the workspace's granted invites, or else one of yours, and once anyone can sign up it costs nothing. With one, it costs nothing. Refused with `409` when the person is already a member or already has a pending invitation to the workspace. Owners only. A free workspace cannot invite anyone: this is refused with `payment_required` (402) until it starts the g1t plan, and an invite sent before cannot be accepted until then." | |
| 1365 | + | "Invite someone into a workspace, by `username` or by `email`. Nobody joins without saying yes: they get an invitation to accept or decline, and join with `role` (`member` unless you give `owner`) when they accept. By `username`, the account gets the invitation in its inbox and by email, and it costs nothing. By `email`, it always makes an invite bound to that address and emails it the link, so the answer never says whether the address has a g1t account. Without one, the link makes the account, which is invited once it confirms its address. Refused with `409` when the person is already a member or already has a pending invitation to the workspace. Owners only. A free workspace cannot invite anyone: this is refused with `payment_required` (402) until it starts the g1t plan, and an invite sent before cannot be accepted until then." | |
| 1384 | 1366 | } | |
| 1385 | 1367 | Op::RevokeWorkspaceInvite => "Revoke a workspace's pending invite. Owners only.", | |
| 1386 | 1368 | Op::ListInvitations => { | |
| ⋯ | |||
| 1991 | 1973 | }), | |
| 1992 | 1974 | &[], | |
| 1993 | 1975 | ), | |
| 1994 | − | Op::ListInvites => object(json!({}), &[]), | |
| 1995 | − | Op::CreateInvite => object( | |
| 1996 | − | json!({ | |
| 1997 | − | "email": { | |
| 1998 | − | "type": "string", | |
| 1999 | − | "description": "Only this address can use it, and it is emailed there. Left out, anyone with the code can.", | |
| 2000 | − | }, | |
| 2001 | − | "workspace": { | |
| 2002 | − | "type": "string", | |
| 2003 | − | "description": "The workspace the new account is invited to, by slug. Once it confirms its address it gets an invitation to join as a member, and no workspace of its own. One you own, on the g1t plan.", | |
| 2004 | − | }, | |
| 2005 | − | "charge_workspace": { | |
| 2006 | − | "type": "string", | |
| 2007 | − | "description": "Use one of the invites g1t granted this workspace instead of yours, by slug. Owners only.", | |
| 2008 | − | }, | |
| 2009 | − | }), | |
| 2010 | − | &[], | |
| 2011 | − | ), | |
| 2012 | − | Op::RevokeInvite => object( | |
| 2013 | − | json!({ "id": { "type": "string", "description": "The invite's id, such as inv_01k…" } }), | |
| 2014 | − | &["id"], | |
| 2015 | − | ), | |
| 2016 | 1976 | Op::ListWorkspaceInvites => object(json!({ "workspace": workspace_schema() }), &["workspace"]), | |
| 2017 | 1977 | Op::InviteMember => object( | |
| 2018 | 1978 | json!({ | |
| ⋯ | |||
| 3584 | 3544 | | Op::ConfirmEmail | |
| 3585 | 3545 | | Op::RemoveEmail | |
| 3586 | 3546 | | Op::UpdateEmailSettings | |
| 3587 | − | | Op::ListInvites | |
| 3588 | − | | Op::CreateInvite | |
| 3589 | − | | Op::RevokeInvite | |
| 3590 | 3547 | | Op::ListWorkspaceInvites | |
| 3591 | 3548 | | Op::InviteMember | |
| 3592 | 3549 | | Op::RevokeWorkspaceInvite | |
| ⋯ | |||
| 3898 | 3855 | "privateEmail": input["private_email"].as_bool(), | |
| 3899 | 3856 | "blockPrivatePushes": input["block_private_pushes"].as_bool(), | |
| 3900 | 3857 | "reauth": { "password": optional_text(input, "password") }, | |
| 3901 | − | }), | |
| 3902 | − | ) | |
| 3903 | − | .await | |
| 3904 | − | } | |
| 3905 | − | Op::ListInvites => { | |
| 3906 | − | let overview: g1t_contracts::identity::InvitesOverview = | |
| 3907 | − | g1t_kit::call(identity, "list_invites", &json!({ "user": actor() })).await?; | |
| 3908 | − | ok(&overview) | |
| 3909 | − | } | |
| 3910 | − | Op::CreateInvite => { | |
| 3911 | − | pass( | |
| 3912 | − | identity, | |
| 3913 | − | "create_invite", | |
| 3914 | − | &json!({ | |
| 3915 | − | "user": actor(), | |
| 3916 | − | "email": optional_text(input, "email"), | |
| 3917 | − | "workspace": optional_text(input, "charge_workspace"), | |
| 3918 | − | "join": optional_text(input, "workspace"), | |
| 3919 | − | "surface": services.audit.surface, | |
| 3920 | 3858 | }), | |
| 3921 | 3859 | ) | |
| 3922 | 3860 | .await | |
| 3923 | − | } | |
| 3924 | − | Op::RevokeInvite => { | |
| 3925 | − | pass(identity, "revoke_invite", &json!({ "user": actor(), "id": text(input, "id") })).await | |
| 3926 | 3861 | } | |
| 3927 | 3862 | Op::ListWorkspaceInvites => { | |
| 3928 | 3863 | pass(identity, "workspace_invites", &json!({ "slug": workspace(), "viewer": viewer })).await | |
| 383 | 383 | }, | |
| 384 | 384 | "notes": "Only the fields given change. `primary` and `backup` need `password`; `private_email` and `block_private_pushes` do not." | |
| 385 | 385 | }, | |
| 386 | − | "list_invites": { | |
| 387 | − | "response": { | |
| 388 | − | "mode": "invite", | |
| 389 | − | "allowance": { | |
| 390 | − | "limit": 5, | |
| 391 | − | "used": 2, | |
| 392 | − | "remaining": 3 | |
| 393 | − | }, | |
| 394 | − | "workspaces": [ | |
| 395 | − | { | |
| 396 | − | "slug": "acme-labs", | |
| 397 | − | "allowance": { | |
| 398 | − | "limit": 20, | |
| 399 | − | "used": 4, | |
| 400 | − | "remaining": 16 | |
| 401 | − | } | |
| 402 | − | } | |
| 403 | − | ], | |
| 404 | − | "invites": [ | |
| 405 | − | { | |
| 406 | − | "id": "inv_01kp2x7m8n9q0r1s2t3v4w5x6y", | |
| 407 | − | "code": "g1t-k7m2-q9xd-4hpw-…", | |
| 408 | − | "hint": "g1t-k7m2", | |
| 409 | − | "email": "ada@example.com", | |
| 410 | − | "kind": "account", | |
| 411 | − | "workspace": null, | |
| 412 | − | "status": "pending", | |
| 413 | − | "charged_to": "user", | |
| 414 | − | "invited_by": "syntaqx", | |
| 415 | − | "redeemed_by": null, | |
| 416 | − | "created_at": "2026-10-05T17:00:00.000Z", | |
| 417 | − | "expires_at": "2026-11-04T17:00:00.000Z", | |
| 418 | − | "redeemed_at": null, | |
| 419 | − | "revoked_at": null, | |
| 420 | − | "invitee": null, | |
| 421 | − | "role": null | |
| 422 | − | }, | |
| 423 | − | { | |
| 424 | − | "id": "inv_01kp1v3c4d5e6f7g8h9j0k1m2n", | |
| 425 | − | "code": null, | |
| 426 | − | "hint": "g1t-p3rt", | |
| 427 | − | "email": null, | |
| 428 | − | "kind": "account", | |
| 429 | − | "workspace": null, | |
| 430 | − | "status": "redeemed", | |
| 431 | − | "charged_to": "user", | |
| 432 | − | "invited_by": "syntaqx", | |
| 433 | − | "redeemed_by": "grace", | |
| 434 | − | "created_at": "2026-10-02T09:12:40.000Z", | |
| 435 | − | "expires_at": "2026-11-01T09:12:40.000Z", | |
| 436 | − | "redeemed_at": "2026-10-02T11:30:05.000Z", | |
| 437 | − | "revoked_at": null, | |
| 438 | − | "invitee": "grace", | |
| 439 | − | "role": null | |
| 440 | − | } | |
| 441 | − | ] | |
| 442 | − | }, | |
| 443 | − | "notes": "`allowance.limit` and `allowance.remaining` are null when you have no limit. A revoked or expired invite that was never used is not counted. `status` is `pending`, `awaiting_confirmation`, `awaiting_answer` (the account it made has yet to accept or decline the workspace in `workspace`), `redeemed`, `declined`, `expired` or `revoked`. Ask for more at hey@flagon.io with the subject `[g1t Invites]`. See [Invites](/guides/authentication/#invites)." | |
| 444 | − | }, | |
| 445 | − | "create_invite": { | |
| 446 | − | "request": { | |
| 447 | − | "email": "ada@example.com", | |
| 448 | − | "workspace": "acme-labs" | |
| 449 | − | }, | |
| 450 | − | "response": { | |
| 451 | − | "id": "inv_01kp2x7m8n9q0r1s2t3v4w5x6y", | |
| 452 | − | "code": "g1t-k7m2-q9xd-4hpw-…", | |
| 453 | − | "hint": "g1t-k7m2", | |
| 454 | − | "email": "ada@example.com", | |
| 455 | − | "kind": "account", | |
| 456 | − | "workspace": "acme-labs", | |
| 457 | − | "status": "pending", | |
| 458 | − | "charged_to": "user", | |
| 459 | − | "invited_by": "syntaqx", | |
| 460 | − | "redeemed_by": null, | |
| 461 | − | "created_at": "2026-10-05T17:00:00.000Z", | |
| 462 | − | "expires_at": "2026-11-04T17:00:00.000Z", | |
| 463 | − | "redeemed_at": null, | |
| 464 | − | "revoked_at": null, | |
| 465 | − | "invitee": null, | |
| 466 | − | "role": "member" | |
| 467 | − | }, | |
| 468 | − | "notes": "`workspace` brings the new account into a workspace you own: once it confirms its address it gets an invitation to join as a member, which it accepts or declines, and no workspace of its own is made for it. Leave `workspace` out and the new account gets a free workspace of its own. A workspace on the free plan cannot bring anyone in: `402` with `payment_required`, until it starts the g1t plan. `charge_workspace` is separate: it uses one of the invites g1t granted that workspace instead of one of yours, and works with or without `workspace`. Refused with `402` (`limit`) when you have no invites left, with `409` when you already have a pending invite for that address or it already has a g1t account, and with `403` for an agent's or a workspace's token. The code is returned in full; send people the link `https://g1t.sh/invite/<code>`." | |
| 469 | − | }, | |
| 470 | − | "revoke_invite": { | |
| 471 | − | "response": { | |
| 472 | − | "id": "inv_01kp2x7m8n9q0r1s2t3v4w5x6y", | |
| 473 | − | "code": null, | |
| 474 | − | "hint": "g1t-k7m2", | |
| 475 | − | "email": "ada@example.com", | |
| 476 | − | "kind": "account", | |
| 477 | − | "workspace": null, | |
| 478 | − | "status": "revoked", | |
| 479 | − | "charged_to": "user", | |
| 480 | − | "invited_by": "syntaqx", | |
| 481 | − | "redeemed_by": null, | |
| 482 | − | "created_at": "2026-10-05T17:00:00.000Z", | |
| 483 | − | "expires_at": "2026-11-04T17:00:00.000Z", | |
| 484 | − | "redeemed_at": null, | |
| 485 | − | "revoked_at": "2026-10-06T08:00:00.000Z", | |
| 486 | − | "invitee": null, | |
| 487 | − | "role": null | |
| 488 | − | } | |
| 489 | − | }, | |
| 490 | 386 | "list_workspace_invites": { | |
| 491 | 387 | "response": [ | |
| 492 | 388 | { | |
| ⋯ | |||
| 497 | 393 | "kind": "account", | |
| 498 | 394 | "workspace": "acme-labs", | |
| 499 | 395 | "status": "pending", | |
| 500 | − | "charged_to": "workspace", | |
| 501 | 396 | "invited_by": "syntaqx", | |
| 502 | 397 | "redeemed_by": null, | |
| 503 | 398 | "created_at": "2026-10-05T17:00:00.000Z", | |
| ⋯ | |||
| 515 | 410 | "kind": "workspace", | |
| 516 | 411 | "workspace": "acme-labs", | |
| 517 | 412 | "status": "pending", | |
| 518 | − | "charged_to": "none", | |
| 519 | 413 | "invited_by": "syntaqx", | |
| 520 | 414 | "redeemed_by": null, | |
| 521 | 415 | "created_at": "2026-10-05T17:00:00.000Z", | |
| ⋯ | |||
| 540 | 434 | "kind": "workspace", | |
| 541 | 435 | "workspace": "acme-labs", | |
| 542 | 436 | "status": "pending", | |
| 543 | − | "charged_to": "none", | |
| 544 | 437 | "invited_by": "syntaqx", | |
| 545 | 438 | "redeemed_by": null, | |
| 546 | 439 | "created_at": "2026-10-05T17:00:00.000Z", | |
| ⋯ | |||
| 561 | 454 | "kind": "account", | |
| 562 | 455 | "workspace": "acme-labs", | |
| 563 | 456 | "status": "revoked", | |
| 564 | − | "charged_to": "workspace", | |
| 565 | 457 | "invited_by": "syntaqx", | |
| 566 | 458 | "redeemed_by": null, | |
| 567 | 459 | "created_at": "2026-10-05T17:00:00.000Z", | |
| 214 | 214 | through::<g1t_contracts::accounts::AccountEmails>(op, sent) | |
| 215 | 215 | } | |
| 216 | 216 | Op::ConfirmEmail => through::<g1t_contracts::accounts::EmailConfirmed>(op, sent), | |
| 217 | − | Op::ListInvites => through::<g1t_contracts::identity::InvitesOverview>(op, sent), | |
| 218 | − | Op::CreateInvite | Op::RevokeInvite | Op::InviteMember | Op::RevokeWorkspaceInvite => { | |
| 217 | + | Op::InviteMember | Op::RevokeWorkspaceInvite => { | |
| 219 | 218 | through::<g1t_contracts::identity::Invite>(op, sent) | |
| 220 | 219 | } | |
| 221 | 220 | Op::ListWorkspaceInvites => through::<Vec<g1t_contracts::identity::Invite>>(op, sent), |
| 51 | 51 | route("POST", "/user/emails/confirm", Op::ConfirmEmail, &[]), | |
| 52 | 52 | route("DELETE", "/user/emails/:email", Op::RemoveEmail, &[]), | |
| 53 | 53 | route("PATCH", "/user/email-settings", Op::UpdateEmailSettings, &[]), | |
| 54 | − | route("GET", "/user/invites", Op::ListInvites, &[]), | |
| 55 | − | route("POST", "/user/invites", Op::CreateInvite, &[]), | |
| 56 | − | route("DELETE", "/user/invites/:id", Op::RevokeInvite, &[]), | |
| 57 | 54 | // Invitations to workspaces waiting for your answer. | |
| 58 | 55 | route("GET", "/user/invitations", Op::ListInvitations, &[]), | |
| 59 | 56 | route("POST", "/user/invitations/:id/accept", Op::AcceptInvitation, &[]), |
| 567 | 567 | Tool { | |
| 568 | 568 | name: "account", | |
| 569 | 569 | title: "Your account", | |
| 570 | − | description: "Who this token acts as and its workspaces (`whoami`), your email addresses, your invites, and invitations to workspaces and repositories waiting for you.", | |
| 570 | + | description: "Who this token acts as and its workspaces (`whoami`), your email addresses, and invitations to workspaces and repositories waiting for you.", | |
| 571 | 571 | default_action: Some("whoami"), | |
| 572 | 572 | actions: &[ | |
| 573 | 573 | a("whoami", Op::Whoami, "Who the token acts as, and its workspaces"), | |
| ⋯ | |||
| 576 | 576 | a("confirm_email", Op::ConfirmEmail, "Confirm an address with the code from its email"), | |
| 577 | 577 | a("remove_email", Op::RemoveEmail, "Remove an address"), | |
| 578 | 578 | a("update_email_settings", Op::UpdateEmailSettings, "Primary, backup and privacy"), | |
| 579 | − | a("list_invites", Op::ListInvites, "Your invites to g1t"), | |
| 580 | − | a("create_invite", Op::CreateInvite, "Make an invite"), | |
| 581 | − | a("revoke_invite", Op::RevokeInvite, "Revoke one"), | |
| 582 | 579 | a("list_workspace_invitations", Op::ListInvitations, "Invitations to workspaces for you"), | |
| 583 | 580 | a("accept_workspace_invitation", Op::AcceptInvitation, "Accept one and join"), | |
| 584 | 581 | a("decline_workspace_invitation", Op::DeclineInvitation, "Decline one"), | |
| 489 | 489 | ||
| 490 | 490 | ## Accounts, status and self-hosting | |
| 491 | 491 | ||
| 492 | − | ### Sign-up needs an invite | |
| 493 | − | ||
| 494 | − | g1t is invite-only for now. See [Invites](/guides/authentication/#invites). | |
| 495 | − | **Status.** Opening sign-up is planned. | |
| 496 | − | ||
| 497 | 492 | ### Tokens for one workspace you belong to | |
| 498 | 493 | ||
| 499 | 494 | A personal access token can be made for one workspace only when you are a |
| 782 | 782 | | --- | --- | | |
| 783 | 783 | | One entry | Up to 2 GiB, compressed. A larger one is not saved, and the job goes on. | | |
| 784 | 784 | | A repository's entries | Up to 10 GiB together. Saving past it removes the entries restored longest ago. | | |
| 785 | + | | A free workspace | Its cache and [artifacts](#artifacts), in every repository, up to 2 GiB together. Past it, a save is refused (the job goes on) until old entries expire or artifacts are deleted; on the g1t plan there is no such cap. | | |
| 785 | 786 | | How long | Until it has not been restored for 7 days, and at most 28 days after it was saved. | | |
| 786 | 787 | | Which branch | An entry belongs to the branch, tag or pull request whose run saved it. A run restores from its own, then from the branch its pull request merges into, then from the default branch. | | |
| 787 | 788 | | Keys | Written once on each branch: saving under a key that exists there does nothing. On each branch in turn, a restore finds its `key` exactly, else the newest entry whose key starts with one of its `restore-keys`. | | |
| ⋯ | |||
| 821 | 822 | | --- | --- | | |
| 822 | 823 | | One artifact | Up to 5 GiB, zipped. | | |
| 823 | 824 | | A run's artifacts | Up to 10 GiB together. | | |
| 825 | + | | A free workspace | Its artifacts and [cache](#the-cache), in every repository, up to 2 GiB together. Past it, an upload fails and says so, until some expire or are deleted; on the g1t plan there is no such cap. | | |
| 824 | 826 | | How long | The repository's setting: 14 days unless someone with the Maintain role changes it under **Settings → Repository → Artifacts**, from 1 to 90 days. `retention-days` asks for fewer days, never more. | | |
| 825 | 827 | | Names | One artifact per name in a run. Uploading a name again fails, unless the upload says `overwrite: true`, which replaces it. A name is up to 256 characters, none of `" : < > \| * ? \ /`. | | |
| 826 | 828 | | `path` | Files, folders and globs, `**` included; a line starting with `!` leaves matching paths out. Files and folders whose names start with `.` are left out unless `include-hidden-files: true`. | | |
| ⋯ | |||
| 1258 | 1260 | fold, errors and warnings are marked, and secrets are replaced with | |
| 1259 | 1261 | `***`. The page keeps itself up to date while the run goes on. | |
| 1260 | 1262 | ||
| 1263 | + | A job's log is kept for **90 days** after the job finished. After that its | |
| 1264 | + | lines are deleted; the run, its jobs, their steps and conclusions, | |
| 1265 | + | annotations, job summaries and artifacts (for their own retention) stay. | |
| 1266 | + | Download a log you need to keep longer. On an installation of your own, | |
| 1267 | + | `LOG_RETENTION_DAYS` in the actions service's settings changes the 90 days, | |
| 1268 | + | and `0` keeps logs for good. | |
| 1269 | + | ||
| 1261 | 1270 | ### Live logs | |
| 1262 | 1271 | ||
| 1263 | 1272 | While a job runs, its log reaches the page within about two seconds of | |
| ⋯ | |||
| 2009 | 2018 | | `get_artifact` | `GET …/actions/artifacts/{artifact_id}` | | |
| 2010 | 2019 | | `download_artifact` | `GET …/actions/artifacts/{artifact_id}/zip`: a `302` to a link good for 10 minutes | | |
| 2011 | 2020 | | `delete_artifact` | `DELETE …/actions/artifacts/{artifact_id}` | | |
| 2012 | − | | `artifact_retention`, `set_artifact_retention` | `GET` and `PUT …/actions/permissions/artifact-and-log-retention` with `days` (it sets artifacts' days only; logs are kept with their run) | | |
| 2021 | + | | `artifact_retention`, `set_artifact_retention` | `GET` and `PUT …/actions/permissions/artifact-and-log-retention` with `days` (it sets artifacts' days only; logs are kept for 90 days) | | |
| 2013 | 2022 | ||
| 2014 | 2023 | Secrets and variables have a tool of their own, `secret`: | |
| 2015 | 2024 | ||
| 204 | 204 | by title; it lists yours under **Linked from**. | |
| 205 | 205 | - **Add icon** above the title gives the doc an emoji. | |
| 206 | 206 | - Images and files are served from `g1tusercontent.com`, never from `g1t.sh`, | |
| 207 | − | up to 25 MB each. | |
| 207 | + | up to 25 MB each. A workspace on the free plan keeps up to 1 GB of them, | |
| 208 | + | across all its docs and artifacts; past it, a new file is refused and the | |
| 209 | + | upload says so. Files already there stay. On the g1t plan there is no | |
| 210 | + | such cap. | |
| 208 | 211 | ||
| 209 | 212 | Docs are live: everyone on one sees each other's changes and cursors, and the | |
| 210 | 213 | faces at the top are who's there now, agents included. When your connection |
| 1 | 1 | --- | |
| 2 | 2 | title: Accounts and authentication | |
| 3 | − | description: Accounts, invites, email addresses, confirming them, two-factor authentication and recovery codes, personal access tokens and their permissions and scopes, a workspace's rules for tokens, OAuth, signing in from a tool, password reset, your security log and deleting your account. | |
| 3 | + | description: Creating an account, invitations, email addresses, confirming them, two-factor authentication and recovery codes, personal access tokens and their permissions and scopes, a workspace's rules for tokens, OAuth, signing in from a tool, password reset, your security log and deleting your account. | |
| 4 | 4 | --- | |
| 5 | 5 | ||
| 6 | 6 | ## Creating an account | |
| 7 | 7 | ||
| 8 | − | g1t is invite-only for now: to make an account you need an | |
| 9 | − | [invite](#invites). Open the link in your invite, or enter its code at | |
| 10 | − | [g1t.sh/register](https://g1t.sh/register). Without one, ask for access | |
| 11 | − | on the same page. | |
| 8 | + | Anyone can make an account at [g1t.sh/register](https://g1t.sh/register), | |
| 9 | + | with an email address and a password or with **Continue with GitHub**. No | |
| 10 | + | invite is needed. If someone invited you into a workspace or onto a | |
| 11 | + | repository, open the link in their invitation instead: it signs you up and | |
| 12 | + | takes you to what you were invited to (see [invites](#invites)). | |
| 12 | 13 | ||
| 13 | 14 | Usernames are letters, digits and single hyphens, 1 to 39 characters, not | |
| 14 | 15 | starting or ending with a hyphen. They keep the case you type: choose | |
| ⋯ | |||
| 44 | 45 | | Profile | [`/settings/profile`](https://g1t.sh/settings/profile) | Your picture, and your [public profile](/guides/workspaces/#profiles): name, pronouns, bio, location, website and time zone. | | |
| 45 | 46 | | Emails | [`/settings/emails`](https://g1t.sh/settings/emails) | Your [email addresses](#email-addresses), the backup address, and [keeping your address private](#keeping-your-address-private). | | |
| 46 | 47 | | Notifications | [`/settings/notifications`](https://g1t.sh/settings/notifications) | [Sounds and do not disturb](/guides/chat/#sounds-and-do-not-disturb), [pop-ups and browser notifications](/guides/chat/#notifications), and [what you are emailed for and how you watch repositories](/guides/notifications/#settings). | | |
| 47 | − | | Invites to g1t | [`/settings/invites`](https://g1t.sh/settings/invites) | [Making, copying and revoking invites to g1t](#making-invites), while g1t is invite-only; after that, the invites you made. | | |
| 48 | 48 | | SSH keys | [`/settings/keys`](https://g1t.sh/settings/keys) | Public keys for [git over SSH](/guides/git/#ssh), each with when it was added and last used. | | |
| 49 | 49 | | Access tokens | [`/settings/tokens`](https://g1t.sh/settings/tokens) | Your [personal access tokens](#access-tokens): their permissions, where they reach, and when they expire. | | |
| 50 | 50 | | Integrations | [`/settings/integrations`](https://g1t.sh/settings/integrations) | [Your own integrations](/guides/integrations/#workspace-and-personal): what you have connected for yourself, in every workspace, and what is coming. | | |
| ⋯ | |||
| 129 | 129 | ||
| 130 | 130 | ## Invites | |
| 131 | 131 | ||
| 132 | − | There are two kinds of invite, and they do different things: | |
| 132 | + | Anyone can sign up without an invite. Invites are how people already on | |
| 133 | + | g1t bring someone in: | |
| 133 | 134 | ||
| 134 | − | | | Invite to g1t | Invite to a workspace | | |
| 135 | − | | --- | --- | --- | | |
| 136 | − | | Made from | [Settings → Invites](https://g1t.sh/settings/invites), your own | **Invite** on the workspace's **Members** or **Invitations** page, by its owners | | |
| 137 | − | | What it gives | One new account. It adds them to no workspace: the account gets a workspace of its own | An invitation to join that workspace, which they accept or decline | | |
| 138 | − | | Someone without an account | Makes their account with it | Makes their account with it too, while g1t is invite-only, then answers the invitation | | |
| 139 | − | | Someone already on g1t | Nothing: they have an account | The invitation, in their notifications and by email | | |
| 140 | − | | Exists | Only while g1t is invite-only | Always | | |
| 141 | − | | What its page and email say | "@syntaqx invited you to g1t" | "@syntaqx invited you to join Flagon, Inc. on g1t" | | |
| 135 | + | - an owner's [invitation into a workspace](#inviting-someone-into-a-workspace), | |
| 136 | + | by username or by email address; | |
| 137 | + | - an [invitation to collaborate on a repository](/guides/access-and-roles/), | |
| 138 | + | by username or by email address. | |
| 142 | 139 | ||
| 143 | − | So you can invite someone to g1t without inviting them into any | |
| 144 | − | workspace; an invite to g1t only adds them to a workspace when you tick | |
| 145 | − | **Also invite them to a workspace** (see [making invites](#making-invites)). | |
| 140 | + | Invited by username, the account gets the invitation in its notifications | |
| 141 | + | and by email. Invited by an address that has no g1t account yet, the email | |
| 142 | + | carries an invite code, such as `g1t-k7m2-q9xd-…`, in a link to | |
| 143 | + | `g1t.sh/invite/<code>`. That invite: | |
| 146 | 144 | ||
| 147 | − | While g1t is invite-only, every new account needs an invite code, such as | |
| 148 | − | `g1t-k7m2-q9xd-…`. People already on g1t make them, and g1t sends them to | |
| 149 | − | people who [asked for access](#asking-for-access). An invite: | |
| 150 | − | ||
| 151 | − | - works once, for one new account; | |
| 145 | + | - works once, for the address it was sent to; | |
| 152 | 146 | - works for 30 days; | |
| 153 | − | - when it was made for an email address, works only with that address; | |
| 154 | − | - can be revoked by whoever made it until it is used, and after that until | |
| 155 | − | the new account [confirms its email address](#confirming-your-email-address). | |
| 147 | + | - can be revoked by whoever sent it (or the workspace's owners) until it is | |
| 148 | + | used, and after that until the new account | |
| 149 | + | [confirms its email address](#confirming-your-email-address). | |
| 156 | 150 | ||
| 157 | 151 | ### Using an invite | |
| 158 | 152 | ||
| ⋯ | |||
| 197 | 191 | invite is revoked or expires, or its workspace is deleted, before you | |
| 198 | 192 | confirm, your address is confirmed all the same and g1t tells you the | |
| 199 | 193 | invite no longer applies: ask whoever invited you to invite you again. A | |
| 200 | − | code typed at [g1t.sh/register](https://g1t.sh/register) goes to the | |
| 201 | − | same page. | |
| 194 | + | link to [g1t.sh/register](https://g1t.sh/register) with a good invite code | |
| 195 | + | goes to the same page. | |
| 202 | 196 | ||
| 203 | 197 | An expired, revoked or used invite says which, and who sent it, so you | |
| 204 | − | can ask them for a new one; or ask for access from the same page. | |
| 198 | + | can ask them for a new one; or [sign up](https://g1t.sh/register) without | |
| 199 | + | it. | |
| 205 | 200 | ||
| 206 | 201 | ### Invites from your inbox | |
| 207 | 202 | ||
| 208 | − | When g1t emails an invite to an address (an invite you make for someone, | |
| 209 | − | an owner's invite into a workspace or a repository, or an approved | |
| 210 | − | [request for access](#asking-for-access)), the link in that email carries a | |
| 203 | + | When g1t emails an invite to an address (an owner's invitation into a | |
| 204 | + | workspace, or an invitation onto a repository), the link in that email carries a | |
| 211 | 205 | `proof` that only the email has: `g1t.sh/invite/<code>?proof=…`. Opening | |
| 212 | 206 | the link shows that you can read that inbox, so: | |
| 213 | 207 | ||
| ⋯ | |||
| 218 | 212 | names is a [workspace invitation](#workspace-invitations) you accept or | |
| 219 | 213 | decline straight away, since nobody joins a workspace without saying yes. | |
| 220 | 214 | ||
| 221 | − | Anything else confirms the address the usual way, after you sign up: the | |
| 222 | − | code typed at [g1t.sh/register](https://g1t.sh/register), an invite link | |
| 223 | − | copied from **Settings → Invites** (whoever made the invite sees the code, | |
| 224 | − | never the proof), an invite made for anyone with the link, or an invite | |
| 225 | − | email sent before this existed. The proof is tied to one invite and its | |
| 215 | + | Anything else confirms the address the usual way, after you sign up: an | |
| 216 | + | invite link copied from the workspace's **Invitations** page (whoever sent | |
| 217 | + | the invite sees the code, never the proof), or an invite email sent before | |
| 218 | + | this existed. The proof is tied to one invite and its | |
| 226 | 219 | address, and stops working when the invite is used, revoked or expires. | |
| 227 | − | ||
| 228 | − | ### Invite links for a group | |
| 229 | − | ||
| 230 | − | g1t sometimes hands one link to a group: an event's judges, readers of a | |
| 231 | − | post, a community. It looks like | |
| 232 | − | `https://g1t.sh/register?invite=g1t-k7m2-…` and opens sign-up with the | |
| 233 | − | code filled in and the group named above the form, such as **Invited as | |
| 234 | − | part of Launch week judges**. | |
| 235 | − | ||
| 236 | − | - **It makes your own account.** Each person who uses it gets a new | |
| 237 | − | account, and then makes their own workspace. It does not add you to | |
| 238 | − | anyone else's workspace; once you are in, a workspace's owners can add | |
| 239 | − | you from its **Members** page. | |
| 240 | − | - **It may be for some email domains only.** When it is, the email field | |
| 241 | − | says which, such as `example.com`, and sign-up takes only an address | |
| 242 | − | there. Use your address at that organization; you confirm it like any | |
| 243 | − | other. | |
| 244 | − | - **It works a set number of times, until a set day.** Once every place | |
| 245 | − | is taken, or the day has passed, or g1t has stopped it, the link gets | |
| 246 | − | the same answer as any invite that cannot be used. Ask whoever shared | |
| 247 | − | it, or [ask for access](#asking-for-access). | |
| 248 | − | ||
| 249 | − | Using the link spends one place in the same step that makes your account, | |
| 250 | − | so two people signing up at the same moment can never take more places | |
| 251 | − | than it has. Anyone with an account can still [make invites](#making-invites) | |
| 252 | − | of their own; group links are made by g1t staff only. | |
| 253 | 220 | ||
| 254 | − | ### Making invites | |
| 255 | − | ||
| 256 | − | These are invites to g1t. They let one person make an account, and add | |
| 257 | − | them to no workspace unless you say so. | |
| 258 | − | ||
| 259 | − | 1. Open [Settings → Invites](https://g1t.sh/settings/invites) (**Invites | |
| 260 | − | to g1t** in your settings and account menu). | |
| 261 | − | 2. Optionally enter the email address of the person you are inviting. | |
| 262 | − | With one, g1t emails them the invite, and only that address can use it. | |
| 263 | − | Without one, anyone with the link can, once. | |
| 264 | − | 3. Optionally tick **Also invite them to a workspace**, then choose the | |
| 265 | − | workspace and the role (**Member** or **Owner**) they are invited with. | |
| 266 | − | It is off to start with, and no workspace is chosen for you, not even | |
| 267 | − | the one you are in. | |
| 268 | − | 4. Select **Create invite**, then copy the link. | |
| 269 | − | ||
| 270 | − | Left unticked, the invite is to g1t only: the new account gets a | |
| 271 | − | [workspace of its own](#your-first-workspace). Ticked, the new account | |
| 272 | − | gets a [workspace invitation](#workspace-invitations) to the workspace you | |
| 273 | − | chose once its address is confirmed, and is not given a workspace of its | |
| 274 | − | own. The list of workspaces holds the ones you can add members to: the ones | |
| 275 | − | you own that are on the g1t plan. A workspace on the free plan cannot add | |
| 276 | − | people, so it is not offered, and the form says so when it is the one you | |
| 277 | − | are in. | |
| 278 | − | ||
| 279 | − | To bring someone into a workspace, you do not need an invite to g1t: | |
| 280 | − | invite them from the workspace's **Members** page, | |
| 281 | − | `g1t.sh/<workspace>/-/members`, instead (see | |
| 282 | − | [inviting someone into a workspace](#inviting-someone-into-a-workspace)). | |
| 283 | − | Settings → Invites links to the Members pages of the workspaces you own. | |
| 284 | − | ||
| 285 | − | Once anyone can sign up for g1t, there are no invites to g1t to make: | |
| 286 | − | Settings → Invites keeps only the list of invites you already made, and | |
| 287 | − | says that anyone can sign up now and that workspace invitations live on | |
| 288 | − | each workspace's Invitations page. With no invites made, the page is not listed | |
| 289 | − | in your settings or account menu. | |
| 290 | − | ||
| 291 | − | Each person can have **5** invites out at a time. Pending and used invites | |
| 292 | − | count; an invite you revoke, or one that expires before anyone uses it, | |
| 293 | − | comes back to you. The list under the form shows each invite's state: | |
| 294 | − | pending, confirming their email (used to sign up by someone who has not | |
| 295 | − | confirmed their address yet), waiting for them to accept (the account is | |
| 296 | − | made and confirmed, and the workspace invitation waits for its answer), | |
| 297 | − | joined (with the username of who joined), declined, expired or revoked. You | |
| 298 | − | must confirm your email before you can make invites. An agent's token and | |
| 299 | − | a workspace's token cannot make them. | |
| 300 | − | ||
| 301 | 221 | ### Inviting someone into a workspace | |
| 302 | 222 | ||
| 303 | 223 | An owner can invite someone into a workspace from its **Members and | |
| ⋯ | |||
| 308 | 228 | to accept or decline. When an address has no g1t account, the invitation | |
| 309 | 229 | also lets it make one first, and the invitation is answered once the | |
| 310 | 230 | account's address is confirmed (at once when it was made from the invite | |
| 311 | − | email's link). While g1t is invite-only, that uses one invite: one of the | |
| 312 | − | workspace's shared invites when it has any, otherwise one of yours. Once | |
| 313 | − | anyone can sign up, it costs nothing. Inviting someone who is already on | |
| 314 | − | g1t never costs anything. | |
| 231 | + | email's link). Inviting someone costs nothing either way. | |
| 315 | 232 | ||
| 316 | 233 | ### Workspace invitations | |
| 317 | 234 | ||
| ⋯ | |||
| 336 | 253 | ||
| 337 | 254 | - An account whose invite brings it into a workspace gets the invitation | |
| 338 | 255 | to it, and no workspace of its own. | |
| 339 | − | - Every other account (signed up with a password, with GitHub, from a | |
| 340 | − | shared invite link or an invite from g1t staff, or with an invite that | |
| 341 | − | names no workspace) gets a workspace of its own, named for its username, | |
| 342 | − | on the free plan. Rename it or start the plan on it whenever you like. | |
| 256 | + | - Every other account (signed up with a password or with GitHub, or with | |
| 257 | + | an invite that names no workspace, such as one onto a repository) gets a | |
| 258 | + | workspace of its own, named for its username, on the free plan. Rename it or start the plan on it whenever you like. | |
| 343 | 259 | ||
| 344 | 260 | Signed in without any workspace (you declined an invitation, or left the | |
| 345 | 261 | only workspace you were in), g1t shows **Create your workspace or ask to | |
| 346 | 262 | join one** in place of Mission control: the invitations waiting for you, if | |
| 347 | 263 | any, and the form to create a workspace. To join a team already on g1t, | |
| 348 | 264 | ask one of its owners to invite you by your username. | |
| 349 | − | ||
| 350 | − | ### Need more invites? | |
| 351 | − | ||
| 352 | − | Write to [hey@flagon.io](mailto:hey@flagon.io?subject=%5Bg1t%20Invites%5D%20) | |
| 353 | − | with the subject `[g1t Invites]` and say who you would like to bring. g1t | |
| 354 | − | can give more invites to you, or to a workspace, whose owners then share | |
| 355 | − | them. Invites given to a workspace appear under | |
| 356 | − | [Settings → Invites](https://g1t.sh/settings/invites) for | |
| 357 | − | each of its owners, as a choice of whose invites to use. | |
| 358 | − | ||
| 359 | − | ### Asking for access | |
| 360 | 265 | ||
| 361 | − | Without an invite, [g1t.sh/register](https://g1t.sh/register) asks for your | |
| 362 | − | email address and, if you like, what you will build. g1t emails that | |
| 363 | − | address once to confirm you are on the list, and staff see the request | |
| 364 | − | straight away. When they approve it, the invite comes to the same address, | |
| 365 | − | sometimes with a note, and its link opens sign-up with the address filled | |
| 366 | − | in. There is no fixed date: g1t opens up a few people at a time. Asking | |
| 367 | − | again with the same address updates your request without another email; it | |
| 368 | − | does not move you down the list. | |
| 369 | − | ||
| 370 | 266 | ### Invites through the API | |
| 371 | 267 | ||
| 372 | 268 | | Route | MCP tool and action | What it does | | |
| 373 | 269 | | --- | --- | --- | | |
| 374 | − | | [`GET /user/invites`](/reference/api/invites/list-invites/) | `account` `list_invites` | Your invites and how many you have left | | |
| 375 | − | | [`POST /user/invites`](/reference/api/invites/create-invite/) | `account` `create_invite` | Make an invite, optionally for one `email`; with `workspace` (a slug), the new account is invited to that workspace | | |
| 376 | − | | [`DELETE /user/invites/{id}`](/reference/api/invites/revoke-invite/) | `account` `revoke_invite` | Revoke a pending invite, or one whose new account has not confirmed its address | | |
| 377 | 270 | | [`POST /workspaces/{workspace}/invitations`](/reference/api/invites/invite-member/) | `workspace` `invite_member` | Invite a `username` or an `email` into a workspace, with a `role`. Owners only. | | |
| 378 | 271 | | [`GET /user/invitations`](/reference/api/invites/list-invitations/) | `account` `list_workspace_invitations` | The workspace invitations waiting for your answer | | |
| 379 | 272 | | [`POST /user/invitations/{id}/accept`](/reference/api/invites/accept-invitation/) | `account` `accept_workspace_invitation` | Join the invitation's workspace with its role | | |
| ⋯ | |||
| 420 | 313 | - **The MCP server** answers `403` with the same message. | |
| 421 | 314 | - **Git** over HTTPS refuses pushes and fetches with your credentials, with | |
| 422 | 315 | the same message. Package registries treat them as wrong credentials. | |
| 423 | − | - You cannot create a workspace, answer the invitation your invite brought, make | |
| 424 | − | invites or tokens, or approve a tool's sign-in. | |
| 316 | + | - You cannot create a workspace, answer the invitation your invite brought, | |
| 317 | + | invite anyone, make tokens, or approve a tool's sign-in. | |
| 425 | 318 | ||
| 426 | 319 | You cannot make a token before you confirm, so the API and MCP refusals | |
| 427 | 320 | matter only for an account that made one before this rule existed. | |
| ⋯ | |||
| 1242 | 1135 | ||
| 1243 | 1136 | ## Too many attempts | |
| 1244 | 1137 | ||
| 1245 | − | g1t counts wrong passwords, on the sign-in page, for git over HTTPS and | |
| 1138 | + | g1t counts sign-ups with a password from each network: after **5** in an | |
| 1139 | + | hour, [g1t.sh/register](https://g1t.sh/register) answers "Too many accounts | |
| 1140 | + | were made from your network in the last hour" until the hour is up. A form | |
| 1141 | + | with a mistake in it (a username that cannot be used, an address that is | |
| 1142 | + | not one, a short password) is not counted. Signing up with GitHub is not | |
| 1143 | + | counted this way: it needs a GitHub account with a verified address. The | |
| 1144 | + | sign-up form also has a Cloudflare Turnstile check above its button, which | |
| 1145 | + | usually passes without you doing anything; wait for it to finish before | |
| 1146 | + | you select **Create account**. | |
| 1147 | + | ||
| 1148 | + | g1t also counts wrong passwords, on the sign-in page, for git over HTTPS and | |
| 1246 | 1149 | when confirming it is you, against the account and against where they come | |
| 1247 | 1150 | from. After 10 wrong passwords for one account in an hour, or 30 from one | |
| 1248 | 1151 | place, g1t stops checking passwords for it for a minute, then twice as long | |
| 326 | 326 | | --- | --- | --- | | |
| 327 | 327 | | A file | 32 MB | The push is declined, naming the file's size. | | |
| 328 | 328 | | A repository, with its pull requests' forks | 950 MB, as g1t counts what was pushed (the store holds 1 GB) | The push is declined; once full, pushes are refused with the reason before any data is sent. | | |
| 329 | + | | A free workspace's repositories | 1 GB private and 5 GB public, together; none on the g1t plan | Pushes to the full kind are refused with the reason before any data is sent; see [usage and billing](/guides/usage-and-billing/). | | |
| 329 | 330 | | A push that push protection can scan before it lands | Most pushes; very large ones are scanned after they land | A very large push goes through and is scanned after it lands; secrets found are open alerts. To have it checked first, push in parts, oldest commits first. | | |
| 330 | 331 | | A push | 100 MB | Refused by the network with HTTP `413` before g1t sees it. | | |
| 331 | 332 |
| 28 | 28 | - **Password**: none. Sign in with GitHub, or set a password later with | |
| 29 | 29 | [Forgot your password](https://g1t.sh/forgot). | |
| 30 | 30 | ||
| 31 | − | g1t is invite-only for now. To create an account with GitHub, open the | |
| 32 | − | invite link you were given and choose **Continue with GitHub** there, or | |
| 33 | − | enter your invite code when g1t asks for it after GitHub. Signing in to an | |
| 34 | − | account you already have needs no invite. | |
| 31 | + | Anyone can create an account with GitHub: choose **Continue with GitHub** | |
| 32 | + | on [g1t.sh/register](https://g1t.sh/register). If someone invited you into | |
| 33 | + | a workspace or onto a repository, open their invite link and choose | |
| 34 | + | **Continue with GitHub** there instead, so the new account gets the | |
| 35 | + | invitation. | |
| 35 | 36 | ||
| 36 | 37 | Signing in with GitHub does not start a trial or give your workspace | |
| 37 | 38 | compute. That works as it does for any account: see |
| 238 | 238 | charts reach back further than its requests do. A project's store holds up | |
| 239 | 239 | to 4 GB; past that, its oldest day goes first. | |
| 240 | 240 | ||
| 241 | + | ## On the free plan | |
| 242 | + | ||
| 243 | + | A project in a workspace on the free plan can send **1 GB a month**, held a | |
| 244 | + | day at a time: about 34 MB a day (UTC), a thirtieth of the month, so a burst | |
| 245 | + | cannot use the month up in an hour. What counts is what arrives: an OTLP | |
| 246 | + | request's body once unzipped, and the rows kept from an app's tail events. | |
| 247 | + | **Settings** shows what arrived this month and today. | |
| 248 | + | ||
| 249 | + | Past the day's share, OTLP requests are answered `429 Too Many Requests` | |
| 250 | + | with the reason, until 00:00 UTC; OpenTelemetry SDKs retry or drop the | |
| 251 | + | batch. Requests from an app on g1t.page still arrive, so its charts and | |
| 252 | + | request list keep going, but without their logs. On the g1t plan there is | |
| 253 | + | no such cap. OTLP is also limited to 600 requests a minute from one | |
| 254 | + | address; see [rate limits](/reference/rate-limits/). | |
| 255 | + | ||
| 241 | 256 | ## Who can do what | |
| 242 | 257 | ||
| 243 | 258 | | | Who | |
| 701 | 701 | so you can see how much ran there. | |
| 702 | 702 | ||
| 703 | 703 | - Workflow jobs on your runners need no plan and no card: they work on the | |
| 704 | − | free tier. | |
| 704 | + | free tier. A workspace on the free plan runs **4** of its workflow jobs | |
| 705 | + | on its runners at once, however many runners it has; the rest stay | |
| 706 | + | queued until one finishes. On the g1t plan there is no such cap. | |
| 707 | + | - A workspace g1t has put a hold on cannot register new runners: making a | |
| 708 | + | registration token answers that it is held. | |
| 705 | 709 | - Agent work on your runners still needs its model paid for: the g1t plan, | |
| 706 | 710 | the trial, the open-source pool on a public repository, or your own | |
| 707 | 711 | model provider, which makes the run free. |
| 162 | 162 | | `MAILPIT_PORT` | `8025` | The port of the Mailpit inbox | | |
| 163 | 163 | | `MAIL_FROM` | `g1t <noreply@localhost>` | The sender of g1t's email | | |
| 164 | 164 | | `MAIL_URL` | `http://mailpit:8025` | The Mailpit server g1t sends mail through | | |
| 165 | − | | `REGISTRATION_MODE` | `open` | `open`: anyone can make an account. `invite`: every new account needs an [invite](/guides/authentication/#invites), as on g1t.sh. | | |
| 166 | − | | `INVITES_PER_USER` | `5` | How many invites each person can have out, while `REGISTRATION_MODE` is `invite` | | |
| 167 | − | | `WAITLIST_NOTIFY_EMAIL` | (none) | Where a summary of new access requests goes, at most every 15 minutes. Empty sends none; requests still wait for you in the database. | | |
| 168 | 165 | | `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` | the bundled RustFS, bucket `g1t-packages` | Where packages' files are kept: any S3-compatible store. Change the two keys before first start; RustFS is made with them. | | |
| 169 | 166 | | `S3_PUBLIC_ENDPOINT` | (none) | The store's address as clients reach it. When set, large layers are downloaded from it directly with a signed URL. | | |
| 170 | 167 | | `PACK_S3_BUCKET` | `g1t-git-packs` | The bucket on the same store that packs for fresh clones are kept in, so the next clone of the same commit is not built again. The bundled store deletes packs after 7 days, and uploads left unfinished after a day; on another store, give the bucket a lifecycle rule that does the same. | | |
| ⋯ | |||
| 174 | 171 | | `BACKUP_S3_BUCKET` | `g1t-backups` | The bucket on the same store that nightly repository backups (a `git bundle` of each repository whose branches or tags changed) are kept in. The bundles are cut by g1t's runner, which this installation does not run yet, so the bucket stays empty for now: copy the volumes, as below. | | |
| 175 | 172 | | `STATUS_PORT` | `8788` | The port the status page is published on | | |
| 176 | 173 | | `STATUS_PROBE_REPO` | (none) | A public repository, `workspace/repo`, whose branches the status page lists every minute as a clone would. Empty: git is not checked. | | |
| 177 | − | | `INVITE_STAFF_WORKSPACES` | (none) | Workspace slugs, comma separated, whose owners can make invites without a limit. Set it to your own workspace before you switch to `invite`, so someone can invite the first people. | | |
| 174 | + | | `TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET` | (none) | A [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/) widget's site key and secret key, to check that a person fills in the sign-up form. With either unset, sign-up has no such check. | | |
| 178 | 175 | | `PHONE_PUSH` | `on` | `off`: send [the phone app](#the-phone-app) no notifications, so their words never pass through Expo's push service. Browser and desktop notifications are not affected. | | |
| 179 | 176 | ||
| 180 | 177 | ## The phone app | |
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…
Loading the diff…