Files
Jon Chery 289e5cf6e1 docs(P02): CLI reference + jobspec reference + ingress guide
P02 — operator-facing documentation (REQ-091, REQ-092, REQ-093; gate C-22).

docs/cli.md (REQ-091):
- Full CLI command/flag reference: every command/subcommand with synopsis,
  flag tables (name/type/default/description), one-line examples.
- Global flags (--json, --system, --config, --no-deprecation-warnings).
- Output modes (text/json/watch), env vars, exit codes.
- Deprecated surface callout boxes (daemon, cert, node-join-mTLS, HCL
  jobspec) pointing to v0.11 removal.

docs/jobspec.md (REQ-092):
- Markdown frontmatter schema reference: all top-level keys, block
  reference (runtime/ports/env-secrets/volumes/restart/update/service/
  health/lifecycle/constraints/affinity/tasks), kinds matrix
  (Job/Service/DaemonSet required vs allowed), CEL subset grammar, body
  byte-exact preservation (R-015), deprecated HCL callout.

docs/ingress.md (REQ-093):
- Traefik ingress reference: service->Traefik mapping (D-175), R-007
  socket-vs-TCP-bind semantics, generated YAML shape (routers/services/
  healthCheck), atomic reload (C-10), drain (weight:0), TLS (certResolver,
  trust domain, step-ca), worked-example pointer to examples/full-stack/,
  v0.11 forward limitations.

All factual claims grounded in live codebase (gate C-22). Cross-links
verified to resolve.

---ci---
project: orca
phase: 2
milestone: v0.10
status: execute
---/ci---
2026-08-05 20:57:05 +00:00

13 KiB

Orca Jobspec Reference

This document is the complete reference for the Orca jobspec format — the Markdown-with-frontmatter specification that describes workloads.

Canonical format (v0.9): Orca uses Markdown with YAML frontmatter as the canonical jobspec format (R-013/R-014). The legacy HCL format is supported via an adapter during the migration window but is deprecated (see HCL jobspec below).

File formats

The orca job run command dispatches by file extension:

Extension Parser Body
.md ParseMarkdown (canonical) Verbatim after closing --- (R-015 byte-exact)
.yaml / .yml parseYAMLFile Whole file as frontmatter; body empty
.hcl ParseHCL (legacy adapter) Empty (deprecated)

Minimal example

---
kind: Job
name: my-job
runtime:
  one_of: process
  command: /bin/echo hello
---
# My Job

This body is preserved byte-exact and carried to the target node.

Top-level keys

Key Type Default Required Notes
orca-spec-version string "" no Free-form version tag (e.g. "1")
kind enum yes One of Job, Service, DaemonSet
name string yes Workload name (trimmed, non-empty)
count int 1 no Job: must be 1; Service: ≥1; DaemonSet: not allowed
runtime block nil see kinds Runtime block (or per-task runtimes in a task group)
ports block list nil Service: yes Array of port mappings
env block map nil no Environment variables
secrets inline/block list nil no Secret names (resolution in v0.11)
volumes block list nil no Volume mounts
restart block nil Service/DaemonSet: yes Restart policy
update block nil Service: yes Update strategy
service block nil no Traefik route definition (implied for Service; not allowed for Job/DaemonSet)
health block nil Service: yes Health check
lifecycle block nil no Pre-stop / post-start hooks
constraints list nil no CEL expressions (node selection)
affinity block list nil no Co-location / anti-affinity rules
tasks block list nil no Task group (multi-process alloc)
timeout duration string "" no Job timeout
schedule block nil DaemonSet: yes Schedule mode

Kinds

Job

A one-shot batch task. Runs once and exits.

  • count must be 1 (or unset). Use Service for replicas.
  • service block is not allowed (no Traefik route for Jobs).
  • restart optional (defaults to never / on-failure).
  • timeout optional.

Example:

---
kind: Job
name: data-migration
runtime:
  one_of: process
  command: /usr/bin/python3 migrate.py
timeout: 300s
env:
  DB_URL: postgres://localhost/mydb
---

Service

A long-running, load-balanced workload with a Traefik route.

  • count ≥ 1 (number of replicas).
  • ports required (at least one).
  • restart required; mode one of service, on-failure, never.
  • update required; strategy one of rolling, canary, blue-green.
  • runtime required (or a task group with per-task runtimes).
  • health required (Traefik routing requires health checks).
  • service block optional (implied for Service; use for bind override).
  • service.bind if present must be a valid IP (127.0.0.1 = TCP opt-in; default = Unix socket).

Example:

---
kind: Service
name: web
count: 3
runtime:
  one_of: process
  command: /usr/bin/httpd
ports:
  - name: http
    port: 8080
restart:
  mode: service
  attempts: 5
  delay: 2s
update:
  strategy: rolling
  max_parallel: 1
health:
  check_type: http
  interval: 5s
  timeout: 1s
  unhealthy_threshold: 2
constraints:
  - node.role == "web"
---

DaemonSet

A workload that runs on every matching node.

  • schedule required; mode one of every-node, matching, mandatory.
  • ports not allowed (no Traefik route by default).
  • count not allowed (implicit = matching nodes).
  • restart required.

Example:

---
kind: DaemonSet
name: log-shipper
schedule:
  mode: every-node
runtime:
  one_of: process
  command: /usr/bin/fluent-bit
restart:
  mode: service
---

Block reference

runtime

The runtime backend for the workload.

Field Key Type Default Notes
one_of one_of string Runtime type (see below)
image image string "" Container image (for podman)
command command string ExecStart command

Supported runtime types (one_of):

Type Description Requires
process Direct process execution via systemd (default) systemd on target
wasm / wasmtime WASM via wasmtime CLI (apt-installed on peer, SSH exec) wasmtime on target
podman Container via podman podman on target
pve-vm Proxmox VM via qm Proxmox node
pve-ct Proxmox container via pct Proxmox node
proxmox Alias for Proxmox runtime Proxmox node

An empty/missing Runtime or OneOf is runtime-agnostic (always fits the runtime axis in the scheduler).

ports

Array of port mappings. Required for Service.

Field Key Type Default Notes
name name string Port name (used in socket path)
port port int Container port
host_port host_port int 0 Host port
protocol protocol string "" Protocol (e.g. tcp)
host_ip host_ip string "" Host IP

Example:

ports:
  - name: http
    port: 8080
    host_port: 80
    protocol: tcp
  - name: https
    port: 8443
    host_port: 443

env

Environment variables. Scalar values or secret references.

env:
  FOO: bar
  BAZ: "qux"
  SECRET_REF:
    from: "secret:db-password"
  INLINE: {from: "secret:token"}

Secret resolution (from: "secret:...") lands in v0.11-P03. The parser stores the reference; the emitter will emit EnvironmentFile=/LoadCredential= in v0.11.

secrets

List of secret names. Inline array or block list.

secrets: ["db-password", "api-token"]
# or
secrets:
  - db-password
  - api-token

volumes

Array of volume mounts.

Field Key Type Default Notes
name name string Volume name
type type string Volume type (e.g. host)
source source string Source path (or replicate:<peer>,<peer> for Syncthing)
target target string Mount target
read_only read_only bool false Read-only mount (true/yes/on/1)

Example:

volumes:
  - name: data
    type: host
    source: /data
    target: /data
    read_only: true

restart

Restart policy.

Field Key Type Default Notes
mode mode enum never, on-failure, service
attempts / max_retries attempts or max_retries int 0 Max retries (both keys accepted)
delay delay duration string "" Retry delay (e.g. 2s)

update

Update strategy. Required for Service.

Field Key Type Default Notes
strategy strategy enum rolling, canary, blue-green
max_surge max_surge int 0 Max surge
max_parallel max_parallel int 1 (clamped to count) Max parallel updates
min_healthy_time min_healthy_time duration "" Min time healthy before next batch
healthy_deadline healthy_deadline duration "" Deadline for health
canary canary int or "<n>%" Canary size (int count or percentage)
auto_promote auto_promote bool false Auto-promote canary (true/yes/on/1)

Strategies:

  • rolling: batches of max_parallel, each batch waits for healthy.
  • canary: canary batch first, then promote (manual or auto_promote), then remaining in max_parallel batches.
  • blue-green: all new allocs start in parallel, wait healthy, then cutover.

Transactional update execution lands in v0.11-P10. The current emitter computes the plan; execution is a v0.11 deliverable.

service

Traefik route definition. Implied for Service; not allowed for Job/DaemonSet. See docs/ingress.md for details.

Field Key Type Default Notes
name name string Service name
port port int Service port
bind bind string (IP) "" Bind mode: empty = Unix socket (default); 127.0.0.1 = TCP opt-in (R-007)
host host string "" Host (stored, not yet consumed by emitter)
route_id route_id string "" Route ID (stored, not yet consumed by emitter)

health

Health check. Required for Service.

Field Key Type Default Notes
check_type check_type string Check type (e.g. http)
interval interval duration string Check interval (e.g. 5s)
timeout timeout duration string Check timeout
unhealthy_threshold unhealthy_threshold int 0 Failures before unhealthy

Maps to Traefik healthCheck stanza (path: /healthz).

lifecycle

Lifecycle hooks. Maps to systemd ExecStartPost / ExecStop.

Field Key Type Default systemd mapping
post_start post_start string list nil ExecStartPost= (runs after main starts)
pre_stop pre_stop string list nil ExecStop= (runs before kill)

Example:

lifecycle:
  pre_stop:
    - /bin/sh -c 'sleep 5'
    - /usr/local/bin/drain.sh
  post_start:
    - /usr/local/bin/warm-cache.sh

constraints

CEL-subset expressions for node selection. Inline array or block list.

constraints:
  - node.role == "web"
  - region == "us"
# or inline
constraints: ['node.role == "web"', 'region == "us"']

CEL subset grammar (hand-rolled, no CEL dependency):

  • Node attributes: node.hostname, node.kind, node.cpus, node.memory, node.tags, node.runtimes
  • Bare identifiers: equivalent to node.<name>
  • Literals: string ("..."), int
  • Comparisons: ==, !=, >=, <=, >, <
  • Membership: in, not in
  • Boolean: and, or, not, parentheses
  • Anything outside the subset returns an error (node skipped, not silently mis-evaluated)

affinity

Co-location / anti-affinity rules.

affinity:
  - target: zone == "a"
    weight: 80
  - target: web
    weight: -50    # anti-affinity (negative weight)
  • target: CEL expression or bare workload name (for name-based co-location).
  • weight: positive = co-locate, negative = anti-affinity.
  • Affinity is a hint (not a gate); evaluation failures are ignored.

tasks (task group)

Multi-process alloc (P06). When tasks is non-empty, the alloc runs multiple processes, each as its own systemd unit, grouped under a systemd target.

tasks:
  - name: app
    runtime:
      one_of: process
      command: /usr/bin/httpd -f
    env:
      LOG_LEVEL: debug
  - name: sidecar
    runtime:
      one_of: wasm
      command: /bin/wasm-runner sidecar.wasm
  • A task with no runtime: inherits the top-level spec.Runtime.
  • Each task can have its own env: overlay.
  • command falls back: task.Commandtask.Runtime.Commandspec.Runtime.Command.
  • Task names must be unique within the group.

Kinds matrix

Feature Job Service DaemonSet
count must be 1 ≥ 1 not allowed
ports optional required not allowed
service block not allowed optional (implied) not allowed
restart optional required required
update optional required optional
health optional required optional
runtime optional required (or task group) optional
schedule optional optional required
tasks optional optional optional
Traefik route no yes (implied) no (by default)

Body semantics

The body after the closing --- is preserved byte-exact (R-015) — including trailing newlines, CRLF, BOM in body, and --- inside code fences. The body is carried verbatim to the target node. It is not interpreted as commands/scripts by the parser today.

Deprecated: HCL jobspec

The legacy HCL jobspec format is supported via an adapter during the migration window. It is deprecated in v0.9 and will be removed in v0.11.

job "hello-orca" {
}

task "greet" {
  command = "/bin/echo"
  args    = ["hello", "from", "orca"]
}

The adapter converts this to a *WorkloadSpec{Kind: "Job", Name: "hello-orca", Count: 1, Runtime: {OneOf: "process", Command: "/bin/echo"}}. Use .md for all new jobspecs.

See also