Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,146 changes: 3,421 additions & 725 deletions public/openapi.json

Large diffs are not rendered by default.

Binary file added src/assets/shots/snapshots.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/shots/workspace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/shots/workspaces.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/content/docs/admin/hosts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,5 @@ A token is one-time unless `--reusable`, lasts 24 hours unless `--expires` says
most 30 days), and is shown once. `--team` makes a runner that only takes the team's workspaces.
The same flags set what the runner advertises and whether it is preemptible.

Such a machine needs KVM, and must run a release of Spinbox no newer than the control plane's and no
more than <Value name="compatWindow" /> older.
Such a machine needs KVM, and must run the control plane's release of Spinbox: a host of any other is
refused, and the refusal says so.
4 changes: 2 additions & 2 deletions src/content/docs/concepts/runners.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ the answer names the runners' role. There is no token to create, store or rotate

A machine outside the group can join with a one-time registration token from Admin → Runners.

A runner must be of the same release as the control plane or older, and no more than <Value name="compatWindow" />
older. A newer one is refused: it may speak of things the control plane does not know.
A runner must run the control plane's release. One of any other, older or newer, is refused when it
registers, and the refusal says which release to run.

## Placement

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Egress is closed by default, and policies name what a workspace may reach. A tea

## And

- **Built for agents.** [Claude Code and Codex on every workspace's PATH](<feature:Claude Code and Codex on every workspace's PATH, at the versions the release pins, with their own defaults>), API keys with scopes, JSON output.
- **Built for agents.** [Claude Code and Codex on every workspace's PATH](<feature:Claude Code, Codex and T3 Code on every workspace's PATH, at the versions the release pins, with their own defaults>), API keys with scopes, JSON output.
- **Secrets the workspace never holds.** [A brokered rule](<feature:A brokered rule names whose it is, the agent's or the person's: one policy says what each may reach and with what>) adds the team's secret on the way out, one for the person and one for the agent.
- **Moves while it runs.** [A running workspace goes to another host](<feature:Move a running workspace to another host, its processes as they are>) with its processes as they are.
- **Asleep when idle, awake as it was.** [A workspace nobody uses is suspended](<feature:Suspend with the processes where they are; a start resumes them>), after asking first, and a start resumes it.
Expand Down
66 changes: 64 additions & 2 deletions src/content/docs/reference/api/adminservice.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ Add allowed email/domain
|---|---|---|
| `entry` | [AllowedEmail](/reference/api/types/#allowedemail) | |

## CreateHostClass

`POST /spin.v1.AdminService/CreateHostClass`

**Request**

| Field | Type | What it is |
|---|---|---|
| `class` | [HostClass](/reference/api/types/#hostclass) | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `class` | [HostClass](/reference/api/types/#hostclass) | |

## CreateNetworkPolicy

`POST /spin.v1.AdminService/CreateNetworkPolicy`
Expand Down Expand Up @@ -66,8 +82,9 @@ Create a resource preset
| `sortOrder` | integer (int32) | Optional: display order |
| `burstFloorCpuCores` | integer (int32) | Required for a burst mode: what its workspaces start with, at most the preset's size. |
| `burstFloorMemoryMb` | integer (int32) | |
| `bandwidthMbps` | integer (int32) | What a workspace of it may carry each way, in megabits a second; 0 is unshaped. |
| `bandwidthMbps` | integer or null (int32) | What a workspace of it may carry each way, in megabits a second. Present, since 0 - unshaped - is an answer; absent, the installation's default. |
| `rpoSeconds` | integer (int32) | How far behind the object store a workspace of it may fall, in seconds; 0 is the installation's. |
| `hostClass` | string | The host class its workspaces run on; empty is the default. |

**Response**

Expand All @@ -94,6 +111,20 @@ Apply a change the file proposes, or dismiss it. Either is remembered until the

No fields: `{}`.

## DeleteHostClass

`POST /spin.v1.AdminService/DeleteHostClass`

**Request**

| Field | Type | What it is |
|---|---|---|
| `name` | string | |

**Response**

No fields: `{}`.

## DeleteNetworkPolicy

`POST /spin.v1.AdminService/DeleteNetworkPolicy`
Expand Down Expand Up @@ -262,7 +293,7 @@ No fields: `{}`.
| `images` | list of [PlatformImage](/reference/api/types/#platformimage) | Empty until the object store is configured: there is nowhere to publish to. |
| `baseImages` | list of [BaseImage](/reference/api/types/#baseimage) | Every base image published, one per machine release, newest first. |
| `supervisorReleases` | list of [ExtensionRelease](/reference/api/types/#extensionrelease) | Every release of the supervisor, newest first. |
| `agentReleases` | list of [ExtensionRelease](/reference/api/types/#extensionrelease) | Every release of the agent CLIs (Claude Code and Codex, one extension), newest first. |
| `agentReleases` | list of [ExtensionRelease](/reference/api/types/#extensionrelease) | Every release of the agents' programs (Claude Code, Codex and T3 Code, one extension), newest first. |

## GetRegistrationMode

Expand Down Expand Up @@ -430,6 +461,20 @@ No fields: `{}`.
|---|---|---|
| `changes` | list of [ConfigChange](/reference/api/types/#configchange) | |

## ListHostClasses

`POST /spin.v1.AdminService/ListHostClasses`

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `classes` | list of [HostClass](/reference/api/types/#hostclass) | |

## ListJobRuns

`POST /spin.v1.AdminService/ListJobRuns`
Expand Down Expand Up @@ -690,6 +735,22 @@ Take a ref off what it names: no new workspace gets those bytes by it, the disks

No fields: `{}`.

## UpdateHostClass

`POST /spin.v1.AdminService/UpdateHostClass`

**Request**

| Field | Type | What it is |
|---|---|---|
| `class` | [HostClass](/reference/api/types/#hostclass) | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `class` | [HostClass](/reference/api/types/#hostclass) | |

## UpdateInstallationSettings

`POST /spin.v1.AdminService/UpdateInstallationSettings`
Expand Down Expand Up @@ -747,6 +808,7 @@ Update a resource preset
| `burstFloorMemoryMb` | integer (int32) | Optional: new burst floor |
| `bandwidthMbps` | integer or null (int32) | Optional: a new bandwidth. Present, since zero - unshaped - is an answer and not "keep". |
| `rpoSeconds` | integer or null (int32) | Optional: a new RPO. Present, since zero - the installation's - is an answer and not "keep". |
| `hostClass` | string | Optional: a new host class; empty keeps it. Its workspaces made before keep theirs. |

**Response**

Expand Down
239 changes: 239 additions & 0 deletions src/content/docs/reference/api/forgeservice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
---
title: "ForgeService"
description: "spin.v1.ForgeService: each procedure, its request and its response."
source: "spin:internal/controlplane/apidocs/spin.v1.openapi.json"
editUrl: false
sidebar:
order: 3
---

<!-- Generated by tools/refgen from spin:internal/controlplane/apidocs/spin.v1.openapi.json. Edit the source, not this file. -->

ForgeService is a person's account on a forge, connected through the installation's GitHub App. (spin#758): the control plane keeps the token, lists the person's repositories with it, and a. host's egress proxy sets it on their workspaces' brokered requests. No workspace holds it.

## CompleteAppCreation

`POST /spin.v1.ForgeService/CompleteAppCreation`

**Request**

| Field | Type | What it is |
|---|---|---|
| `code` | string | |
| `state` | string | |
| `binding` | string | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `app` | [ForgeApp](/reference/api/types/#forgeapp) | |

## CompleteConnect

`POST /spin.v1.ForgeService/CompleteConnect`

**Request**

| Field | Type | What it is |
|---|---|---|
| `code` | string | |
| `state` | string | |
| `binding` | string | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `connection` | [ForgeConnection](/reference/api/types/#forgeconnection) | |

## DeleteForgeApp

`POST /spin.v1.ForgeService/DeleteForgeApp`

Takes the app out of use at once. Every connection through it is revoked at the forge, and the app is gone when that is done.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |

**Response**

No fields: `{}`.

## Disconnect

`POST /spin.v1.ForgeService/Disconnect`

Forgets the token and revokes it at the forge.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |

**Response**

No fields: `{}`.

## ListConnections

`POST /spin.v1.ForgeService/ListConnections`

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `connections` | list of [ForgeConnection](/reference/api/types/#forgeconnection) | |

## ListForgeApps

`POST /spin.v1.ForgeService/ListForgeApps`

The installation's app on each forge: whether a person can connect, and where. Its secrets are never answered with.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `apps` | list of [ForgeApp](/reference/api/types/#forgeapp) | |

## PollDeviceConnect

`POST /spin.v1.ForgeService/PollDeviceConnect`

**Request**

| Field | Type | What it is |
|---|---|---|
| `handle` | string | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `connection` | [ForgeConnection](/reference/api/types/#forgeconnection) | Set once the person approved: the connection, kept. Unset is pending: poll again after interval_seconds, which the forge may have lengthened. |
| `intervalSeconds` | integer (int32) | |

## SearchRepositories

`POST /spin.v1.ForgeService/SearchRepositories`

The repositories the person's account reaches that match query, as the forge searches them with the person's token: what they can see there is what they can pick here.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |
| `query` | string | Empty lists the account's repositories, most recently pushed first. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `repositories` | list of [ForgeRepository](/reference/api/types/#forgerepository) | |

## SetForgeApp

`POST /spin.v1.ForgeService/SetForgeApp`

Keeping an app that was made elsewhere. Refused while the forge has one: an app is never edited, because another client id could not revoke what this one granted.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |
| `appId` | string (int64) | |
| `slug` | string | |
| `clientId` | string | |
| `clientSecret` | string | |
| `webhookSecret` | string | |
| `apiUrl` | string | As StartAppCreationRequest's. |
| `webUrl` | string | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `app` | [ForgeApp](/reference/api/types/#forgeapp) | |

## StartAppCreation

`POST /spin.v1.ForgeService/StartAppCreation`

Making the app, by an instance admin, through the forge's manifest flow: StartAppCreation answers the form the browser posts to the forge, which makes the app and sends the admin back with a code; CompleteAppCreation exchanges it for the app's credentials. Nobody copies a key.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |
| `organization` | string | The organization the app belongs to; empty is the admin's own account. |
| `apiUrl` | string | Empty is github.com's; a GitHub Enterprise Server's API is https://&lt;host>/api/v3, and its web origin https://&lt;host>. |
| `webUrl` | string | |
| `public` | boolean | Whether any account can install it. A private app installs only on the account that owns it, so only that account's repositories are reached. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `formUrl` | string | Where to send the browser: a page of this control plane's that posts the manifest to the forge, because the dashboard's own policy lets it post nowhere else. |
| `state` | string | |
| `binding` | string | A secret for the client that began this, kept where only it reads it and never in a URL: CompleteAppCreation requires it with the state. |

## StartConnect

`POST /spin.v1.ForgeService/StartConnect`

Connecting: StartConnect answers where the person authorizes the app and a binding only this client keeps; the forge sends the browser back to redirect_uri with a code and the state, and CompleteConnect exchanges them, with the binding, for the person's token.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |
| `redirectUri` | string | The dashboard's /git/callback, over https; anything else is refused. Empty is the installation's own. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `authorizeUrl` | string | |
| `state` | string | |
| `binding` | string | A secret for the client that began this, kept where only it reads it and never in a URL: CompleteConnect requires it with the state. |

## StartDeviceConnect

`POST /spin.v1.ForgeService/StartDeviceConnect`

Connecting from somewhere with no browser to send back to, the CLI: the control plane asks the forge for a device code (RFC 8628) and keeps it, and answers with the code the person types on the forge's page and a handle only the caller can poll with. PollDeviceConnect asks the forge, at most once an interval, and on the person's approval keeps their token as CompleteConnect does: once, for the person who began it.

**Request**

| Field | Type | What it is |
|---|---|---|
| `forge` | string | |

**Response**

| Field | Type | What it is |
|---|---|---|
| `handle` | string | What PollDeviceConnect is asked with: a secret of the caller's, polled by them alone. The forge's device code stays on the control plane. |
| `userCode` | string | What the person types at verification_uri, signed in to the forge. |
| `verificationUri` | string | |
| `expiresAt` | `Timestamp` | |
| `intervalSeconds` | integer (int32) | How long to wait before each poll. |

Loading
Loading