Skip to content
This repository was archived by the owner on Sep 15, 2026. It is now read-only.

Latest commit

 

History

History
174 lines (139 loc) · 10.1 KB

control-api.md

File metadata and controls

174 lines (139 loc) · 10.1 KB

Control API v1

The Autback control plane exposes rtest.v1.ControlService with Connect over HTTPS. The protobuf schema in api/rtest/v1/control.proto is the source of truth. rtest.v1 is the stable v1 wire route; it is not a product alias or CLI name. Generated Go clients and handlers are committed so the CLI and server compile against the same wire contract.

Compatibility

The v1 package is additive: existing field numbers, enum values, messages, and RPCs may not be removed or changed incompatibly. task proto:check compares the current schema with the committed v1 descriptor using Buf's WIRE_JSON breaking-change policy. A deliberate compatible addition updates generated Go code but does not require replacing the baseline. A future incompatible contract must use a new protobuf package such as autback.v2.

Project authorization

Every job and build belongs to one project. The server resolves a project slug or ID at admission and stores the immutable project ID. Get, list, cancel, log, and finish operations authorize the caller against that stored project; knowing a resource ID does not grant access. Page tokens are also bound cryptographically to their project. ListProjects returns only projects available to the authenticated device user or the single project embedded in a temporary GitHub credential; clients use it to validate a repository selection before transferring source bytes.

Project runner images

A project stores an active and previous digest-pinned OCI image. PrepareJob resolves an omitted image to the active project image before hashing and persisting admission, so every job record contains the immutable digest it actually used. An explicit different image is accepted only while project override policy allows it.

Only an administrator authenticated with a device credential can activate, roll back, or change image policy. Activation requires an @sha256: reference and asks the scheduler to pull and inspect it before the store transaction changes the active digest. Rollback validates the previous digest before atomically swapping active and previous. ListProjectImageHistory is project-authorized and returns the activation/rollback actor, digest, replaced digest, and timestamp in newest-first order.

Device enrollment

Normal interactive login uses the HTTPS endpoints below /auth: the CLI creates a short-lived device request, the user signs in to the browser with GitHub and approves it, and the CLI polls until it can exchange the request exactly once for a standard device token. These browser endpoints are deliberately outside the stable Connect v1 API.

BindGitHubIdentity is the administrative Connect command used before that flow. It requires an administrator device credential, resolves the supplied GitHub login through GitHub's API, and stores the returned immutable numeric subject against one Autback user. The mutable login is metadata only. A duplicate GitHub subject or a second GitHub identity for the same Autback user is rejected.

RevokeGitHubIdentity is the corresponding administrator-device operation. It removes the identity binding and atomically revokes that user's browser sessions, device tokens, and approved-but-unexchanged device logins. The user and project memberships remain so an administrator can deliberately bind the account again. Browser-session credentials cannot authorize any authenticated Connect RPC even when copied into an Authorization header; they are an audience-limited console credential, not a CLI credential.

Recovery enrollment

CreateEnrollmentCode requires an administrator's device credential and names the target user and device. Codes expire between one and 30 minutes after issuance, contain 256 bits of entropy, are stored only as a keyed digest, and lock permanently after five failed secret comparisons. The response is the only time the code is returned.

ExchangeEnrollmentCode is intentionally unauthenticated: the one-time code is its credential. A successful SQLite transaction consumes the code and creates the same opaque, independently revocable device-token type used everywhere else. Reuse, expiry, malformed input, an unknown code, or the retry limit returns UNAUTHENTICATED without revealing which condition occurred. The CLI sends the code in the protobuf request body over TLS, never in a URL or required command argument.

Admission idempotency

PrepareJob and PrepareBuild require an idempotency_key containing 8–128 URL-safe characters. A client generates one key per logical admission and reuses it when retrying that admission.

  • The first request atomically persists the key, a deterministic request hash, and the resource.
  • Repeating the same project, key, and normalized request returns the original resource. Short-lived data-plane credentials are included only after that resource owns the FIFO admission lease.
  • Reusing the key with different request data returns ALREADY_EXISTS and never creates another resource.
  • Keys are scoped by project and resource kind, so job and build keys do not collide.

StartJob, CancelJob, CancelBuild, and FinishBuild mutate a named resource and are naturally retryable to the extent allowed by its state transition. Clients should read the resource after an ambiguous transport failure before attempting a different operation.

FIFO admission

PrepareJob and PrepareBuild append operations to one durable FIFO shared by all projects and users before capacity is checked. At most one operation is admitted on the initial worker. A preparing job has no CAS connection until it owns the FIFO head; clients poll GetJob by stable ID, then upload source through the returned connection and call StartJob. Starting commits the root digest without changing the operation's original FIFO sequence. A queued build has BUILD_STATUS_QUEUED and no BuildKit connection; clients poll GetBuild by stable ID until it becomes running, then receive a fresh operation-scoped connection. CancelBuild removes a waiting build or terminates the active build record and schedules durable cleanup. The next FIFO entry is admitted only after that cleanup releases the worker reservation. GetBuild renews the lease while the authenticated client is waiting or building. GetJob likewise renews a job preparation while it waits or uploads. The service expires an abandoned job preparation or queued/running build after two minutes by default; this is a worker safety bound, not a scheduler priority.

PrepareBuild also requires the build-lease-heartbeat token in the Autback-Client-Capabilities request header. The released CLI supplies this metadata with its Autback-Client-Version. A client without the capability receives FAILED_PRECONDITION before Autback creates a build record or issues a BuildKit credential. This prevents a server lease-policy upgrade from silently admitting a client that cannot keep its lease. PrepareJob requires the analogous durable-job-preparation capability so an older CLI cannot mistake a durable queued response for an immediately usable CAS connection.

The API exposes no priorities, resource sizes, or task graph. Parallelism is part of the single admitted command, not separate dispatcher policy. The deprecated v1 cpus and memory fields remain wire-compatible but are ignored.

Job secrets

PrepareJob.secrets is the only credential-bearing job declaration. Each item contains a project-scoped reference name and either an environment key or a file path below /run/secrets; the message never carries the value. Job.secrets returns the same references for inspection and idempotent replay. Environment targets cannot overlap public environment fields or server-owned AUTBACK_* keys.

The server resolves references only after the FIFO lease is acquired. A revoked or missing reference fails the job permanently and advances after durable cleanup. Generic command or environment values using secret:// or ${{ secrets.* }} syntax are rejected so older workflows migrate to --secret-env NAME=KEY or --secret-file NAME=/run/secrets/PATH instead of persisting an interpolation token.

Pagination

ListJobs accepts page_size from 1 through 100 and an optional opaque page_token. The default page size is 20. The deprecated limit field remains readable for older v1 clients. Responses contain next_page_token only when another page exists.

Tokens are signed keyset cursors over creation time and resource ID. They are not offsets, must not be parsed or persisted as application data, and cannot be reused for another project. Malformed, modified, or cross-project tokens return INVALID_ARGUMENT.

Reconnectable logs

StreamJobLogs uses byte positions. The initial request uses offset zero. Every response sets next_offset to the position immediately after its data, and the terminal frame also contains the final offset and terminal job. After an unavailable connection, the client opens a new stream with its last acknowledged next_offset; the server discards the already delivered prefix so output is not duplicated. Negative offsets return INVALID_ARGUMENT.

Log bytes are opaque and may split UTF-8 code points or lines. Clients must write them in order without text transformations.

Error contract

Connect code Meaning
UNAUTHENTICATED The bearer credential is missing, malformed, expired, or revoked.
PERMISSION_DENIED The principal is not authorized for the resource's project.
INVALID_ARGUMENT Input validation, page-token, or log-offset failure.
NOT_FOUND The addressed resource does not exist.
FAILED_PRECONDITION The project has no active image/default or no image to roll back to.
ALREADY_EXISTS A unique resource exists or an idempotency key conflicts.
UNAVAILABLE A scheduler or log dependency is temporarily unavailable.
CANCELED The caller canceled the request.
INTERNAL An unexpected server failure whose details are intentionally hidden.

Clients may retry UNAVAILABLE with backoff. They must reuse the admission idempotency key and the most recent log offset when doing so. Validation, authorization, and conflict errors are not transient.