Skip to main content

kind: Crew

What it is

kind: Crew is the full-CRUD authoring surface for one crew: Create on first apply, Update on drift, Unchanged when the declared spec already matches the server. Where CrewTemplate is a one-shot deploy of a server-side blueprint (no in-place updates), kind: Crew owns every field of the crew row directly — runtime image, devcontainer overlay, mise toolchain, and sidecar services. Both kinds write to the same crews table; the difference is provenance and lifecycle. An exported workspace round-trips through kind: Crew for crews whose slug doesn’t match a known template (the common case, since operators usually rename on deploy). The kind is implemented in internal/manifest/kinds/crew.go.
Agents are separate documents. A standalone kind: Crew document defines only the crew row (and its sidecars / container config). Its agents are authored either as standalone kind: Agent documents that reference this crew by crew_slug, or inline under a kind: Workspace bundle’s nested crew shape. The standalone Crew document does not carry an agents: list.

YAML schema

Top-level spec fields

spec.devcontainer

Models the subset of devcontainer.json operators commonly tweak; unmodeled keys pass through via raw: (typed fields win on collision).
spec.runtime_image is the canonical image. The legacy spec.devcontainer.image is the same thing — set one only; a divergent pair is rejected as a paste mistake.

spec.mise

spec.services[]

Each entry is one sidecar on the crew’s private bridge network. The wire shape mirrors the server’s serviceWire.

spec.files[]

Local files (scripts, fixtures, configs) delivered into the crew’s shared volume at crewship apply time — visible in-container under /crew/shared. This is how a routine’s deterministic scripts travel with the manifest instead of a manual crewship crew files save after every rebuild. Re-applied on every apply (idempotent PUT through the same /files/save endpoint).
A type: script routine step (see docs/manifest/routine.md “Script steps”) can then run them token-zero: path: scripts/parse_vypis.py. A missing or oversized local file fails the plan (and --dry-run), not mid-apply. Bundled files are written to the crew’s /crew/shared bind source on the host, so they are present the moment the crew container comes up — including crews that have no agents and whose container is provisioned lazily on the first routine run. Inspect them with crewship crew files list <crew> --path shared.

Examples

Minimal

With devcontainer sizing + mise toolchain

With a Postgres sidecar

CLI reference

REST endpoint mapping

How each manifest field maps onto the create/update request body: Endpoints used:

Validation rules

CrewDocument.Validate (offline) enforces:
  • apiVersion / kind, when set, equal crewship/v1 / Crew.
  • metadata.name and metadata.slug are non-empty; slug is kebab-case.
  • spec.color, when it starts with #, is a valid 6-digit hex code.
  • spec.runtime_image is required (no sane default; the server tolerates NULL but falls back to a wrong-for-you built-in image).
  • spec.devcontainer.image, when set, must equal spec.runtime_image.
  • devcontainer.memory_mb / cpus are non-negative.
  • Each service: DNS-label name, unique within the crew, image present, numeric ports only, parseable healthcheck durations, named (not bind) volumes with unique mounts.
The assembled devcontainer / mise JSON is not round-tripped through the server’s parser at Validate time (Validate runs offline); the server re-validates on every write, so any deeper schema error surfaces at Apply.

Apply behavior

ApplyUpsert (default)

  • Remote missing → ActionCreate: POST the full body.
  • Remote present, fields drift → ActionUpdate: a sparse PATCH of only the drifted fields. Empty declared scalars and nil devcontainer/mise/services are skipped — so the manifest never overwrites a value the operator set via the UI.
  • Remote present, no drift → ActionUnchanged.
devcontainer_config / mise_config / services_json are compared after JSON normalisation, so server-side key reordering doesn’t trigger phantom drift. services: [] (empty array) clears all sidecars; an absent services: key leaves them alone.

Round-trip via export

crewship export workspace calls ExportCrews, which renders one kind: Crew per crew (sorted by slug). The devcontainer / mise JSON is decoded back into the typed sub-fields where possible, with anything unmodeled stashed under raw: so the round-trip stays byte-stable. Columns the manifest doesn’t model (cached_image, config_hash, container_ttl_hours, network_mode) are dropped.

See also

  • Agent — references this crew via spec.crew_slug.
  • CrewTemplate — one-shot deploy of a server blueprint.
  • Integration — crew-scoped MCP servers.
  • Workspace — the top-level bundle that nests crews + agents.
  • Backend: internal/api/crews_create.go, internal/api/crews_update.go, internal/api/crew_services.go.
  • This kind’s Go implementation: internal/manifest/kinds/crew.go.