Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.strait.dev/llms.txt

Use this file to discover all available pages before exploring further.

Every job and workflow in Strait is versioned. When you update a job’s configuration, the previous version is snapshotted so that in-flight runs continue to execute with the config they were enqueued under.

Dual Versioning

Each job and workflow has two version identifiers:
FieldTypePurpose
versionIntegerAuto-incrementing counter (1, 2, 3, …). Used for ordering and internal references.
version_idStringGlobally unique nanoid (ver_k8f2m9x1p3). Used as the public-facing identifier.
The integer version is predictable and easy to reason about. The version_id is a 16-character string (4-char prefix + 12-char nanoid using 0-9a-z alphabet) that’s safe to expose in URLs and APIs without leaking ordering information.

Version Lifecycle

CreateJob → version=1, version_id=ver_abc123def456
UpdateJob → version=2, version_id=ver_xyz789ghi012  (old config snapshotted)
UpdateJob → version=3, version_id=ver_mno345pqr678  (old config snapshotted)
On each UpdateJob, the current configuration is snapshotted to the job_versions table before the update is applied. This means:
  • job_versions contains the full config for versions 1, 2, … (N-1)
  • The live jobs table always has the current (latest) version

Atomic Versioning

When a run is enqueued, it captures the job’s current version and version_id:
TriggerJob(job_id=X) → run.job_version=2, run.job_version_id=ver_xyz789ghi012
When the executor picks up the run, it reads the job config at that version from the snapshot table:
job, err := store.GetJobAtVersion(ctx, run.JobID, run.JobVersion)
This prevents a common race condition: if you update a job’s endpoint URL while runs are queued, those runs still execute against the old endpoint.

Fallback Behavior

If no snapshot exists for the requested version (e.g., version 1 before snapshotting was implemented), the executor falls back to reading the live jobs table. This ensures backwards compatibility.

Version Policy

Each job and workflow has a version_policy that controls how queued runs interact with version updates:
PolicyBehaviorUse Case
pin (default)Runs execute with the version they were enqueued underSafety-first deployments
latestQueued runs upgrade to the latest version at dequeue timeFast rollouts, non-breaking changes
minorUpgrade only if the new version is marked backwards_compatibleControlled rollouts

pin (Default)

The safest option. Runs always execute with the exact configuration they were enqueued under. A job update never affects already-queued runs.
Enqueue run (job at v2) → Update job to v3 → Dequeue → Run executes with v2 config

latest

Queued runs automatically upgrade to the current version when dequeued. Useful when you want all runs to use the latest config regardless of when they were enqueued.
Enqueue run (job at v2) → Update job to v3 → Dequeue → Run executes with v3 config

minor

A middle ground. Queued runs upgrade only if the new version has backwards_compatible = true. This lets you control which updates are safe for in-flight runs.
Enqueue run (job at v2) → Update job to v3 (backwards_compatible=true) → Dequeue → Run upgrades to v3
Enqueue run (job at v2) → Update job to v3 (backwards_compatible=false) → Dequeue → Run stays at v2

Setting Version Policy

curl -X POST https://strait.dev/v1/jobs \
  -d '{
    "name": "my-job",
    "version_policy": "latest",
    ...
  }'
Or update an existing job:
curl -X PATCH https://strait.dev/v1/jobs/job_123 \
  -d '{"version_policy": "latest"}'
Version policy is enforced at dequeue time by the worker dispatch path. pin is still the safest default and is applied automatically when no policy is provided.

Version Snapshots

The job_versions table stores a full snapshot of the job configuration at each version:
  • endpoint_url, fallback_endpoint_url
  • max_attempts, timeout_secs
  • tags, payload_schema
  • max_concurrency, execution_window_cron
  • rate_limit_max, rate_limit_window_secs
  • retry_strategy, retry_delays_secs
  • environment_id, group_id
  • enabled
  • version_id, backwards_compatible
Snapshots are created automatically on UpdateJob. The first version (at creation time) is captured when the first update occurs.

Browsing Versions

List all versions of a job:
curl https://strait.dev/v1/jobs/job_123/versions \
  -H "Authorization: Bearer strait_..."
Response:
[
  {
    "id": "ver_abc",
    "job_id": "job_123",
    "version": 2,
    "version_id": "ver_xyz789ghi012",
    "endpoint_url": "https://old.example.com/webhook",
    "max_attempts": 3,
    "backwards_compatible": false,
    "created_at": "2025-01-15T10:30:00Z"
  },
  {
    "id": "ver_def",
    "job_id": "job_123",
    "version": 1,
    "version_id": "ver_abc123def456",
    "endpoint_url": "https://original.example.com/webhook",
    "max_attempts": 5,
    "backwards_compatible": false,
    "created_at": "2025-01-10T08:00:00Z"
  }
]

Workflow Versioning

Workflows use the same dual version model (version + version_id) and snapshot workflow definitions, including step graphs. Every time you update a workflow via PATCH /v1/workflows/{workflowID}, the version increments and the previous step graph is preserved.

Inspecting versions

  • GET /v1/workflows/{workflowID}/versions — list all version snapshots
  • GET /v1/workflows/{workflowID}/versions/{versionID} — get a specific version
  • GET /v1/workflows/{workflowID}/versions/{versionID}/steps — get the step graph for a version
  • GET /v1/workflows/{workflowID}/active-versions — list versions that have active (pending, running, or paused) runs, with status breakdowns

Breaking change detection

When you update a workflow, the response includes information about in-flight runs on the previous version:
{
  "id": "wf_abc",
  "version": 4,
  "active_runs_on_previous_version": 3,
  "previous_version_id": "ver_xyz"
}
The active_runs_on_previous_version field appears whenever there are non-terminal runs still executing on the version that was just replaced. This gives you immediate visibility into whether your update might affect running workflows. To explicitly acknowledge a breaking update, set breaking_change to true in the request body:
PATCH /v1/workflows/{workflowID}
{
  "steps": [...],
  "breaking_change": true
}
When breaking_change is true and active runs exist on the previous version, Strait records a workflow.updated_breaking audit event with the previous version ID, active run count, and new version number. This makes breaking deployments visible in your audit log.

Run Provenance

Every run records which version it was enqueued under:
{
  "id": "run_123",
  "job_id": "job_456",
  "job_version": 2,
  "job_version_id": "ver_xyz789ghi012",
  "status": "completed"
}
This gives you exact traceability: you can always determine which configuration a run executed with, even if the job has been updated many times since.