Skip to main content

Provisioning

Endpoints that drive the Devcontainers pipeline: discover available features and runtimes, trigger or rebuild a crew’s cached image, and inspect the local cache. Implementation lives in internal/api/crew_provisioning_jobs.go and internal/api/crew_provisioning_cache.go; the underlying provisioner is in internal/devcontainer/.
All routes require authentication and are workspace-scoped where they touch crew rows.

Endpoints


Catalogs

Discover the features and runtimes a crew can pin in its devcontainer_config.

Feature catalog

Auth: Authenticated request. Request: optional search query parameter; no body. Response: JSON object with a features array. Statuses: 200 OK on success; the catalog remains available through the documented fallback when the dynamic fetch fails. Lists devcontainer features Crewship can install. The dynamic fetcher pulls from upstream registries (ghcr.io/devcontainers/features/*, ghcr.io/devcontainers-extra/features/*, ghcr.io/crewship-ai/features/*); when that fails, the handler falls back to internal/devcontainer.FallbackCatalog so the UI is never empty. ?search= filters by name, description, or category (server-side substring). Response: 200 OK
Unknown publishers (anything outside the three allowlisted prefixes) are rejected at provisioning time even if they appear in a custom catalog — see internal/devcontainer/features.go.

Runtime catalog

Auth: Authenticated request. Request: optional search query parameter; no body. Response: JSON object with a runtimes array. Statuses: 200 OK on success; the catalog uses the documented fallback when the dynamic fetch fails. Lists the mise-managed runtimes (Node, Python, Go, Terraform, kubectl, …) the workspace can pin in devcontainer_config.mise. Falls back to FallbackRuntimeCatalog when the dynamic fetcher fails. Not a base-image catalog. The base-image list is CLI-onlycrewship features base-images reads baseImagesCatalog directly from cmd/crewship/cmd_features.go; there is no HTTP endpoint for it today. Response: 200 OK

Crew provisioning lifecycle

Inspect, trigger, rebuild, and recycle a crew’s cached image and runtime container.

Provision status

Auth: Authenticated workspace request; crewId must belong to that workspace. Request: crewId path parameter; no body. Response: JSON provision-status object described below. Statuses: 200 OK; 404 Not Found when the crew is not in the workspace; 503 Service Unavailable when provisioning is not configured. Response: 200 OK. Always-present fields come from the crews row; the step / total / message / steps / log_tail / started_at / completed_at / error fields appear only when an in-memory job is currently running or recently finished for this crew.
status is one of:

Trigger provision

Auth: Authenticated caller with the create role action. Request: crewId path parameter; no body. Response: JSON { "status": "started", "message": "…" }. Statuses: 202 Accepted; 400, 403, 404, 409, 429, 500, or 503 for the conditions listed below. Requires the create role action (OWNER, ADMIN, MANAGER). Returns 202 immediately and runs the pipeline in a background goroutine; poll the status endpoint for progress.
This builds an image. It does not start a container. A completed provision leaves the crew’s cached_image set and its container stopped — the container is created lazily on the crew’s first agent run. On a cache hit the job reaches completed in seconds having started nothing at all.To start the container, call POST /api/v1/crews/{crewId}/container-start, which provisions first if the crew has no image and then starts it.
Response: 202 Accepted
Errors (RFC 7807 Problem Details on every 4xx/5xx — type, title, status, detail, instance):

Rebuild

Auth: Authenticated caller with the create role action. Request: crewId path parameter; no body. Response: the same JSON shape as the trigger endpoint. Statuses: 202 Accepted; 403, 404, 409, 429, 500, or 503 when the corresponding provisioning condition applies. Clears the cached image marker on the crew row and triggers a fresh provision. Use when upstream features publish breaking updates under the same tag.
Discards the existing cache marker before rebuilding — the next agent run waits on a full pipeline pass.
Response: 202 Accepted — same shape as Trigger.

Restart agents

Auth: Authenticated caller with the update role action. Request: crewId path parameter; no body. Response: JSON { "restarted": <count> }. Statuses: 200 OK; 400, 403, 404, 500, or 503 for the conditions listed below. Force-removes the crew’s runtime container so the next agent exec recreates it from the current cached_image — without rebuilding the image. Used when an env-var change should take effect without the cost of a new image. Idempotent: when no container is running it still returns 200 OK with { "restarted": 0 }. Requires the update role action (OWNER, ADMIN, MANAGER). Response: 200 OK with { "restarted": <count> } — the count is the number of non-deleted agents in the crew (they share one container, so all are recreated together).

Image freshness

A crew container is created once and reused until something recreates it. On a long-lived crew that can be weeks, during which the image tag it was built from keeps moving — so the container quietly becomes a snapshot of an older release. These two endpoints report that and fix it. See Keeping crew images current for the full story, including the daily check and the notification it raises.

Image status

Auth: Authenticated workspace request; any workspace role, including VIEWER — noticing that a crew is behind is a read. Request: crewId path parameter; no body. Response: JSON object with crew_id, image, container_id, running, running_digest, resolved_digest, behind and reason. Statuses: 200 OK; 400 on an empty crew ID, 404 when the crew is not in the workspace, 500 on a daemon failure, 503 when no container provider can report image digests. Compares the registry manifest digest the crew’s running container was created from against what its tag resolves to on the registry right now. It never pulls and never restarts anything.
behind: false is not the same as “current”. Read reason alongside it — registry unreachable, no container, image has no registry digest and locally built image all report behind: false because nothing could be compared. Only an empty reason with behind: false means the two digests were compared and agreed.

Refresh image

Auth: Authenticated caller with the update role action (OWNER, ADMIN, MANAGER). Request: crewId path parameter; no body. Response: JSON object with crew_id, image, previous_digest, new_digest and container_removed. Statuses: 200 OK; 400, 403, 404, 500, or 503 for the conditions listed below. Pulls the crew’s configured image, then force-removes its runtime container so the next agent exec recreates it from the fresh copy. The pull happens first: if it fails, the crew is left exactly as it was rather than without a container and without an image. Idempotent — a crew that is already current transfers nothing, and one with no container has nothing to drop (container_removed: false).
Agents executing in the container when it is removed are interrupted.
Not the same as Restart agents. restart-agents drops the container without pulling, which picks up a devcontainer image you have already rebuilt locally. refresh-image pulls first, which is what picks up a base image that has moved on in the registry.

Cache image registry

Inspect and prune the locally built crewship-cache:* images. This registry is per-host, not per-workspace — see Tenancy.

List cached images

Auth: Authenticated workspace request. Request: no body or required query parameters. Response: JSON object with an images array. Statuses: 200 OK; 500 on Docker image-list failure; 503 when Docker is not configured. Response: 200 OK
Requires the read role action (any workspace member). Returns 503 ({ "error": "cache management not available (Docker client not configured)" }) when no Docker client is wired, and 500 on a Docker ImageList failure. Images orphaned from any crew (i.e. referenced_by is empty) are eligible for the GC sweeper. See CREWSHIP_CACHE_GC_AUTODELETE.

Delete cached image

Auth: Authenticated caller with the delete role action (ADMIN or OWNER). Request: tag path parameter and optional force=true query parameter; no body. Response: JSON deletion result with tag and status. Statuses: 200 OK; 400 for an invalid tag, 403 for insufficient role, 409 when a referenced image is not forced, 500 for Docker removal failure, or 503 when Docker is not configured. Removes a cached image. By default refuses to delete an image still referenced by a live crew (returns 409 Conflict). Pass ?force=true to delete anyway.
?force=true removes an image even while live crews reference it — their next agent run rebuilds from scratch.
Requires the delete role action (OWNER, ADMIN only — a MANAGER is rejected with 403). Response: 200 OK
A 409 Conflict body lists the referencing crew slugs under referenced_by. The reference check is host-global — it counts crews in any workspace, so an image another tenant still uses cannot be force-free-deleted by accident:
Errors:

Tenancy

  • crewId on the provision-status / trigger / rebuild / restart-agents routes is validated against the session workspace via the crews.workspace_id SELECT; a crew in another workspace returns 404.
  • The cache image registry is per-host, not per-workspace — operators sharing a Docker daemon will see each other’s tags, and the cache routes do not workspace-scope the {tag} path param. GET /cache/images scopes only the referenced_by annotation to the caller’s workspace; DELETE /cache/images/{tag} deliberately checks references across all workspaces so a tag another tenant depends on cannot be deleted without ?force=true. There is no cross-tenant 404 on a {tag} — the tag namespace is shared by design.