Skip to content

Switchyard v0.9.0 — v1 release candidate

Latest

Choose a tag to compare

@jenksed jenksed released this 24 Jul 16:40

Latest release CI Elixir 1.19 Phoenix 1.8 PostgreSQL 17 License Apache 2.0

Switchyard v0.9 — The v1 release candidate

Released 2026-07-24. Application, relay artifact, and unified CLI version
0.9.0; relay protocol remains 1; endpoint configuration document
remains version 2.

v0.9 hardens Switchyard for self-hosted operation: optional multi-node
clustering, forward-only rolling upgrades, three documented deployment
profiles sharing one immutable non-root image, measured performance
boundaries, and a release-candidate freeze that becomes the v1 stability
contract. No user-facing feature was added; every change is operational
hardening behind the same stable API v1, unified CLI, and relay protocol.

v0.9 is the v1 release candidate (PROJECT_PLAN Stop 9). It proves that the
product runs correctly on more than one node, upgrades without down
migrations, deploys through documented profiles, and survives a two-hour
soak while preserving the v0.8 surface exactly. The work is intentionally
invisible to everyday users: endpoints, events, deliveries, routing,
notifications, evidence, and templates behave as they did in v0.8.

User-visible capabilities

There are no new user-visible features in v0.9. The release keeps every
v0.8 capability intact and adds operator-facing hardening:

  • Optional multi-node operation (V09-B, SPEC-044, ADR 051..053).
    Clustering is opt-in and fail-closed. Two or more nodes gossip on a
    private network with libcluster using an encrypted gossip secret and a
    separate Erlang distribution cookie; invalid configuration fails startup
    rather than falling back to a single-node mode. Relay destination
    ownership uses :global.register_name/3 with a 5-second fail-closed
    registration deadline and :global.notify_all_name/3 for graceful
    takeover. A node that dies with an in-flight delivery leaves a fenced
    claim that the next owner rejects and retries, leaving exactly one
    terminal succeeded row.

  • Forward-only rolling upgrades (V09-B, ADR 054). Migrations are
    additive and never reversed. The N/N+1 read window means old application
    code continues to read the new schema until all nodes are upgraded, then
    the new code path becomes active. bin/verify-rolling-upgrade proves the
    prior (v0.8.0) → forward (v0.9.0) → back (v0.8.0) cycle with
    /health and /health/ready green on every boot and no down migration.

  • Three deployment profiles (V09-D, docs/v0.9/PROFILES.md). The same
    non-root, read-only, digest-addressed application image serves
    single-node development (compose.yaml), single-node team
    (compose.team.yaml with TLS, SMTP, backups, restricted egress), and
    multi-node team (compose.cluster.yaml) deployments. Secrets are
    mounted as 0600 files; the image is reused, not rebuilt, across
    profiles.

  • Measured performance boundaries (V09-C, SPEC-045,
    docs/v0.9/TESTED_LIMITS.md). A deterministic harness runs six
    scenarios (sustained ingest, remote delivery, relay, concurrent LiveView,
    retention under load, JSON/form/binary growth) and a default 7,200-second
    soak. The full soak on an Apple M1 Pro/16 GiB RAM/PostgreSQL 17 accepted
    142,120 events at 19.74 events/s sustained with zero hard invariant
    violations and zero duplicates. These numbers are human-accepted under
    SPEC-045 and are cited only with their full hardware, software,
    topology, and duration qualifiers — they are not universal capacity
    claims.

  • Operational runbooks (V09-D). Nine runbooks complete the Stop 9
    list: application installation, configuration, upgrade/rollback,
    delivery troubleshooting, relay troubleshooting, database maintenance,
    retention sizing, security configuration, and known limitations.
    bin/verify-runbooks checks every referenced file and every safe
    non-destructive command.

Operational changes

  • bin/build-app-image builds the single immutable non-root application
    image and writes a manifest at tmp/v0.9/application-image-manifest.json.
    The image runs as an unprivileged user with a read-only root filesystem
    and loads runtime secrets from mounted files.

  • compose.cluster.yaml and .env.cluster.example define the multi-node
    team profile; compose.team.yaml and .env.team.example define the
    single-node team profile. The development profile remains
    compose.yaml.

  • bin/verify-v0.9-b-cluster proves the two-node image cluster: relay
    ownership handoff on simultaneous join, graceful replacement, abrupt
    node loss, partition/heal convergence, and in-flight delivery recovery.

  • bin/rehearse-upgrade-rollback runs the prior → forward → back cycle
    against a disposable database, confirming /health and /health/ready
    are 200 on every boot.

  • bin/verify-v0.9-c chains V09-B, builds the released relay escript,
    runs the six short harness scenarios and a ten-second soak in a
    disposable database, and validates the receipts.

  • bin/verify-v0.9-d chains V09-C, bin/verify-runbooks,
    bin/verify-backup-restore, and bin/rehearse-upgrade-rollback.

  • bin/verify-v0.9 is the operator exit gate: it chains V09-D, runs the
    live failure walkthrough, and verifies offline artifact signatures.

  • bin/sign-artifacts and bin/verify-signatures migrated to Cosign
    v3.1.2 Sigstore bundles (*.sigstore.json), fully offline, with bundle
    names that encode the artifact path to prevent basename collisions.

  • bin/verify-signing-flow proves the signing contract with disposable
    keys: sign, bundle naming, proxy-blocked offline verify, tampered
    artifact / missing bundle / corrupted bundle / wrong-key rejection, and
    missing/unsupported cosign errors.

  • bin/verify-v0.9-failure-walkthrough provisions a disposable
    deployment, drives the documented CLI-only operator path for an induced
    HTTP-500 fixture, asserts timing (112 s of the 180 s bound),
    sanitization, and hard=0, and cleans up.

Compatibility

docs/v0.9/compatibility.json records the frozen contracts. API v1,
relay protocol 1, configuration document 2, receipt format 1, and
CLI exit codes 0/2/3/4/5/6/7/8/78 are unchanged. The public surface did
not expand; every v0.8 client, token, script, and relay escript continues
to work against v0.9.0 without modification.

The frozen contracts are listed in docs/v0.9/FREEZE.md and become the
seed of the v1 stability contract. After v0.9.0, only critical
corrections (security vulnerabilities, violated invariants, or
release-blocking defects) may change them, and each requires an
acceptance row, a regression test, and a new ADR entry.

Verification

POSTGRES_PORT=55432 bin/verify-v0.9 is the durable release gate. Exit 0
means the v0.9 release candidate is met. The gate chains:

  1. The complete v0.8 release gate (bin/verify-v0.8).
  2. V09-B multi-node correctness (bin/verify-v0.9-b), including the
    two-node image cluster and the rolling-upgrade/rollback matrix.
  3. V09-C tested limits (bin/verify-v0.9-c), including the harness
    contract, the released relay escript, the six short scenarios, and the
    soak fencing.
  4. V09-D deployment profiles and operational runbooks
    (bin/verify-v0.9-d), including runbook reference checks,
    backup/restore, and the upgrade/rollback rehearsal.
  5. V09-E operator exit gate: offline signature verification and the live
    failure walkthrough.

The final review also ran 452 focused LiveView/controller tests after the
V09-E-01 accessibility remediation and produced four review reports under
docs/v0.9/reviews/.

Known limits

  • Delivery remains at-least-once; SSE delivery is also at-least-once
    (clients dedupe by event UUID).
  • Multi-node clustering is opt-in, requires a private network, and is not
    cross-region active/active.
  • Configuration apply remains personal-principal only; machine
    principals receive a documented 403.
  • The tested-limit numbers apply only to the named hardware, software,
    topology, and duration in docs/v0.9/TESTED_LIMITS.md.
  • Only macOS arm64 artifacts are verified in this release; Linux
    arm64/x86_64, Windows x86_64, and macOS x86_64 have documented local
    procedures and honest Not run status.

Sign-off

The V09-E human checkpoint accepted the full risk list
(docs/v0.9/RISKS.md), signed the release-candidate freeze
(docs/v0.9/FREEZE.md), and signed the Stop 9 audit
(docs/v0.9/STOP9_AUDIT.md) on 2026-07-23. The operator exit gate
(bin/verify-v0.9) passed, including the live failure walkthrough
(112 s of the 180 s bound). Annotated tag v0.9.0 points to the verified
release commit.

The next stop is v1.0: the stability contract. v1.0 adds no new features
and hardens the v0.9 freeze into the long-term compatibility promise.

Artifact signatures

Release artifacts are signed with Cosign v3.1.2 keyless/Sigstore bundles.

Verify offline after downloading an artifact and its .sigstore.json bundle:

cosign verify-blob \
  --key config/signing/keys/switchyard-release.pub \
  --bundle artifacts/v0.9/signatures/<artifact>.sigstore.json \
  --use-signing-config=false \
  --insecure-ignore-tlog \
  <path-to-downloaded-artifact>

The repository also provides bin/verify-signatures, which checks every
bundle in artifacts/v0.9/signatures/ against the committed public key
without network access.