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.
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1 | import type { User } from "./identity"; |
| 2 | import type { RepoPath } from "./repos"; | |
| 3 | import type { Result } from "./result"; | |
| 4 | ||
| 5 | /** | |
| 6 | * Self-hosted runners: a workspace's or a repository's own machines, which | |
| 7 | * run its workflow jobs (and, when it says so, its agents' work) instead of | |
| 8 | * g1t's sandboxes, at $0. Mirrors `crates/contracts/src/runners.rs`. | |
| 9 | */ | |
| 10 | ||
| 11 | export type RunnerStatus = "online" | "busy" | "offline"; | |
| 12 | ||
| 13 | /** What a runner is doing now. */ | |
| 14 | export type RunnerWork = { | |
| 15 | kind: "workflow" | "agent"; | |
| 16 | id: string; | |
| 17 | name: string; | |
| 18 | repo: string | null; | |
| The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how. | 19 | /** The workflow run the job is in; for `agent` work, the agent run it is, when known. */ |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 20 | runId: string | null; |
| 21 | startedAt: string | null; | |
| 22 | }; | |
| 23 | ||
| 24 | export type Runner = { | |
| 25 | id: string; | |
| 26 | name: string; | |
| 27 | workspace: string; | |
| 28 | /** `owner/name` for a repository's own runner; null for the workspace's. */ | |
| 29 | repo: string | null; | |
| 30 | group: string | null; | |
| 31 | labels: string[]; | |
| 32 | os: "linux" | "macos" | "windows"; | |
| 33 | arch: "x64" | "arm64"; | |
| 34 | version: string; | |
| 35 | ephemeral: boolean; | |
| 36 | status: RunnerStatus; | |
| 37 | work: RunnerWork | null; | |
| 38 | lastSeenAt: string | null; | |
| 39 | createdAt: string; | |
| 40 | createdBy: string | null; | |
| Self-hosted runners on a person's own computer are sandboxes that nothing a job runs can get out of. `g1t-runner sandbox up|down|status|update [--json]` gives a Windows, macOS or Linux computer up to two capabilities, each a runner of its own: Linux jobs in a Linux VM the runner owns, where the runner's image runs under Sysbox and each job is a fresh container inside it (nested-container, three walls from the computer); and jobs native to Windows, each in a Windows Sandbox of its own, discarded after, with one read-only folder holding the harness (a new `vm` isolation level, also `g1t-runner register --native`). On Windows the VM is a WSL 2 distribution imported from the release's signed root filesystem: its wsl.conf turns off drives, Windows programs and Windows' PATH, and the supervisor inside unmounts what WSL shares with every distribution anyway (/mnt/wsl, where Docker Desktop keeps its sockets to Windows, WSLg, the GPU driver folders), refuses to run while anything of the computer's is reachable, and drops containers' traffic to the computer's address but for DNS. It needs no administrator once WSL is installed, and works on Windows 11 Home. On macOS the VM runs under vfkit and on Linux without Sysbox under QEMU with KVM, with no shared folders and nothing forwarded in (built and tested as commands, not yet run on those machines); on Linux with Sysbox no VM is needed. `status --json` reports the state, versions, isolation, the current job and a sentence when something is wrong, and each capability's availability with its cause and fix, for the desktop app. `update` drains the runner, swaps in the newest signed image and starts again without registering again. The sandbox's images are built from deploy/sandbox/Dockerfile by scripts/sandbox-image.mjs and listed with their SHA-256 in the release's signed manifest, beside the runner image's digest. The release workflow now builds its images on g1t's own machines (g1t-4core) rather than waiting forever for a self-hosted runner, registering QEMU for arm64 where the machine allows it and building amd64 alone with a warning where it does not, and signs and publishes once the images are built. The Runners page shows each runner's isolation (Sandboxed, VM per job, Shared Docker, Unsandboxed), which the API now returns as `isolation`. The Self-hosted runners guide has a new "Sandbox on your computer" section, with what it cannot stop. | 41 | /** How it keeps work from its machine, as it last said; null for a runner from before runners said. */ |
| 42 | isolation?: RunnerIsolation | null; | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 43 | }; |
| 44 | ||
| Self-hosted runners on a person's own computer are sandboxes that nothing a job runs can get out of. `g1t-runner sandbox up|down|status|update [--json]` gives a Windows, macOS or Linux computer up to two capabilities, each a runner of its own: Linux jobs in a Linux VM the runner owns, where the runner's image runs under Sysbox and each job is a fresh container inside it (nested-container, three walls from the computer); and jobs native to Windows, each in a Windows Sandbox of its own, discarded after, with one read-only folder holding the harness (a new `vm` isolation level, also `g1t-runner register --native`). On Windows the VM is a WSL 2 distribution imported from the release's signed root filesystem: its wsl.conf turns off drives, Windows programs and Windows' PATH, and the supervisor inside unmounts what WSL shares with every distribution anyway (/mnt/wsl, where Docker Desktop keeps its sockets to Windows, WSLg, the GPU driver folders), refuses to run while anything of the computer's is reachable, and drops containers' traffic to the computer's address but for DNS. It needs no administrator once WSL is installed, and works on Windows 11 Home. On macOS the VM runs under vfkit and on Linux without Sysbox under QEMU with KVM, with no shared folders and nothing forwarded in (built and tested as commands, not yet run on those machines); on Linux with Sysbox no VM is needed. `status --json` reports the state, versions, isolation, the current job and a sentence when something is wrong, and each capability's availability with its cause and fix, for the desktop app. `update` drains the runner, swaps in the newest signed image and starts again without registering again. The sandbox's images are built from deploy/sandbox/Dockerfile by scripts/sandbox-image.mjs and listed with their SHA-256 in the release's signed manifest, beside the runner image's digest. The release workflow now builds its images on g1t's own machines (g1t-4core) rather than waiting forever for a self-hosted runner, registering QEMU for arm64 where the machine allows it and building amd64 alone with a warning where it does not, and signs and publishes once the images are built. The Runners page shows each runner's isolation (Sandboxed, VM per job, Shared Docker, Unsandboxed), which the API now returns as `isolation`. The Self-hosted runners guide has a new "Sandbox on your computer" section, with what it cannot stop. | 45 | /** |
| 46 | * `nested-container`: a container per job inside the runner's own sandboxed container (or VM). | |
| 47 | * `vm`: a throwaway VM per job, native to the machine's OS (Windows Sandbox). | |
| 48 | * `container`: a container per job on a Docker Engine shared with the machine. | |
| 49 | * `host`: directly on the machine, unsandboxed. | |
| 50 | */ | |
| 51 | export type RunnerIsolation = "nested-container" | "vm" | "container" | "host"; | |
| 52 | ||
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 53 | export type RunnerGroup = { |
| 54 | id: string; | |
| 55 | name: string; | |
| 56 | default: boolean; | |
| 57 | /** Repository names; empty is every repository in the workspace. */ | |
| 58 | repositories: string[]; | |
| 59 | runners: number; | |
| 60 | updatedAt: string; | |
| 61 | }; | |
| 62 | ||
| 63 | /** Shown once. */ | |
| 64 | export type RegistrationToken = { | |
| 65 | token: string; | |
| 66 | expiresAt: string; | |
| 67 | workspace: string; | |
| 68 | repo: string | null; | |
| 69 | group: string | null; | |
| 70 | url: string; | |
| 71 | }; | |
| 72 | ||
| 73 | export type RunnerSettings = { | |
| 74 | agentsOnSelfHosted: boolean; | |
| 75 | agentLabels: string[]; | |
| 76 | forkPullRequests: boolean; | |
| 77 | /** For a repository: these are its workspace's. */ | |
| 78 | inherited: boolean; | |
| 79 | }; | |
| 80 | ||
| 81 | export type RunnersOwner = { repo: RepoPath } | { workspace: string }; | |
| 82 | ||
| 83 | export type RunnerSettingsChange = { | |
| 84 | agents_on_self_hosted?: boolean; | |
| 85 | agent_labels?: string[]; | |
| 86 | fork_pull_requests?: boolean; | |
| 87 | inherit?: boolean; | |
| 88 | }; | |
| 89 | ||
| The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how. | 90 | /** A workflow job running in one of g1t's sandboxes. Snake case, as `runner_activity` answers. */ |
| 91 | export type CloudJob = { id: string; name: string; run_id: string; repo: string; started_at: string | null }; | |
| 92 | ||
| 93 | /** Agent work handed to one of the workspace's own runners, waiting for one or taken. */ | |
| 94 | export type HandedOverTask = { | |
| 95 | id: string; | |
| 96 | /** The agent run it is; null for work handed over before runs were noted. */ | |
| 97 | run_id: string | null; | |
| 98 | kind: string; | |
| 99 | title: string; | |
| 100 | repo: string; | |
| 101 | status: "queued" | "in_progress"; | |
| 102 | runner_name: string | null; | |
| 103 | created_at: string; | |
| 104 | started_at: string | null; | |
| 105 | }; | |
| 106 | ||
| 107 | /** What runs for a workspace now, in g1t's sandboxes and on its own runners (`runner_activity`). */ | |
| 108 | export type RunnerActivity = { | |
| 109 | /** At most `RUNNER_ACTIVITY_LIMIT`, newest first. */ | |
| 110 | cloud_jobs: CloudJob[]; | |
| 111 | cloud_jobs_queued: number; | |
| 112 | handed_over: HandedOverTask[]; | |
| 113 | self_hosted_jobs_queued: number; | |
| 114 | }; | |
| 115 | ||
| 116 | /** The most jobs and tasks `runner_activity` lists of each: `ACTIVITY_LIMIT` in the actions service. */ | |
| 117 | export const RUNNER_ACTIVITY_LIMIT = 100; | |
| 118 | ||
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 119 | /** A job that has waited ten minutes or more with no matching runner online. */ |
| 120 | export type StuckJob = { id: string; name: string; runId: string; repo: string; labels: string; queuedAt: string }; | |
| 121 | ||
| 122 | export interface RunnersApi { | |
| 123 | /** The viewer's workspaces' jobs stuck waiting for a self-hosted runner, for Mission control. */ | |
| 124 | stuck(viewer: User): Promise<StuckJob[]>; | |
| The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how. | 125 | /** What runs for the workspace now, in g1t's sandboxes and handed to its own runners. Owners. */ |
| 126 | activity(actor: User, workspace: string): Promise<Result<RunnerActivity>>; | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 127 | list(actor: User, owner: RunnersOwner): Promise<Result<Runner[]>>; |
| 128 | createToken(actor: User, owner: RunnersOwner, group?: string): Promise<Result<RegistrationToken>>; | |
| 129 | remove(actor: User, owner: RunnersOwner, id: string): Promise<Result<boolean>>; | |
| 130 | groups(actor: User, workspace: string): Promise<Result<RunnerGroup[]>>; | |
| 131 | /** Without `id`, a new group. */ | |
| 132 | setGroup(actor: User, workspace: string, group: { id?: string; name?: string; repositories?: string[] }): Promise<Result<RunnerGroup>>; | |
| 133 | deleteGroup(actor: User, workspace: string, id: string): Promise<Result<boolean>>; | |
| 134 | settings(actor: User, owner: RunnersOwner): Promise<Result<RunnerSettings>>; | |
| 135 | setSettings(actor: User, owner: RunnersOwner, change: RunnerSettingsChange): Promise<Result<RunnerSettings>>; | |
| 136 | } | |
| 137 | ||
| 138 | /** | |
| 139 | * The runner's container image: `g1t-runner` and the Docker CLI, for | |
| 140 | * running it in Docker or Kubernetes. Published with each release. | |
| 141 | */ | |
| The runner's image is g1t.sh/flagon-io/g1t-runner, on g1t's own registry, public | 142 | export const RUNNER_IMAGE = "g1t.sh/flagon-io/g1t-runner"; |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 143 | |
| 144 | /** Where the runner binary is published: `g1t.sh/downloads/runner/<version>/<file>`. */ | |
| 145 | export const RUNNER_DOWNLOADS = "https://g1t.sh/downloads/runner"; | |
| 146 | ||
| 147 | /** The file for each platform, as published. */ | |
| 148 | export const RUNNER_FILES: Record<"linux-x64" | "linux-arm64" | "macos-arm64" | "macos-x64" | "windows-x64", string> = { | |
| 149 | "linux-x64": "g1t-runner-linux-x64", | |
| 150 | "linux-arm64": "g1t-runner-linux-arm64", | |
| 151 | "macos-arm64": "g1t-runner-macos-arm64", | |
| 152 | "macos-x64": "g1t-runner-macos-x64", | |
| 153 | "windows-x64": "g1t-runner-windows-x64.exe", | |
| 154 | }; |