Versioning and the plugin ecosystem

A repository's SDK pin selects its engine. The Go SDK, TypeScript SDK and engine release together. Spark libraries use their own module versions.

Versioning per repoSection anchor link

Go pipelines pin github.com/sparkwing-dev/sparkwing in .sparkwing/go.mod. TypeScript pipelines pin @sparkwing/sdk through their manifest and native lockfile. A version range alone cannot select an exact engine. Go vX.Y.Z and TypeScript X.Y.Z identify engine vX.Y.Z, including any prerelease suffix.

For local TypeScript development, pin a version-stamped SDK directory with file:../sdk or a tarball such as file:vendor/sparkwing-sdk-0.68.0.tgz. Relative tarball paths without file: also work. Run npm install to update the native lockfile. Sparkwing reads the local package's name and version without executing it, checks that the lockfile resolves that source, and selects that exact release's engine. Local pins retain signed engine verification. Keep local sources inside the repository when they must travel with a fetched checkout. Tarball bytes, or a directory's package manifest, participate in pin identity.

A Go SDK replace directive that applies to the required version disables engine pinning for that run. Sparkwing warns and uses the installed CLI's engine to build and run the replacement. It records the replacement and installed executable's digest in the accepted identity. Resume requires that same executable. Removing the replacement restores release pinning and signed-release verification.

Pin inspection reads regular manifest and lock files without running repository code. It refuses linked or special files. Go module inspection retains its 1 MiB limit and reports oversized input before parsing it.

Sparks libraries are ordinary independent modules. A consuming pipeline pins them like other dependencies. A library upgrade does not silently change the pipeline's Sparkwing pin. See Sparks.

Go directive policySection anchor link

The repository's Go directive and CI toolchain checks own compiler requirements. A dependency's minimum Go version must be satisfiable by the pipeline's selected toolchain. go.mod retractions remain module-distribution metadata. Execution security policy comes from the verified version index.

The SDK pin selects the CLISection anchor link

Every placement acquires its sealed source, reads the pin, resolves and verifies the matching engine, then uses that engine to install/build and host the SDK. A newer ambient CLI or runner image does not substitute for an older exact pin. The CLI resolves the pin even when its installed version matches. It reuses the selected executable only after the resolver checks policy and cached bytes. A missing release fails instead of selecting another version. Retries retain the accepted portable engine identity and exact source SHA. Each machine resolves the appropriate platform asset for that same identity.

Engines come from the official GitHub Releases assets. Verification uses the shared release trust roots, signed checksums, exact asset digest, embedded version and the historical release's closed asset matrix. Removing an asset from today's publishing set cannot invalidate an older permitted release.

The machine caches verified engines under its user cache directory in sparkwing/engines. A partial or corrupt recoverable entry is a miss. SPARKWING_TOOLCHAIN=auto may fetch a missing verified release. SPARKWING_TOOLCHAIN=local requires the exact verified cached release. Current Available policy is checked before every execution, including cache hits. Pipeline binaries are compiled and cached locally rather than downloaded from a controller.

Support and security policySection anchor link

From launch freeze, the controller accepts requests emitted by every engine for twelve calendar months after its release. Older releases keep working until an explicit security revocation or minimum-version policy takes effect. Age alone never refuses execution. The selected engine supplies its matching node protocol.

The signed index at https://sparkwing.dev/versions.json records the latest release, minimum version, individual revocations and per-release support windows. Its detached signature is versions.json.sig. Clients verify the exact bytes, refuse rollback and retain the last verified copy when refresh fails. There is no fallback-age cutoff. Local-only execution reads cached policy without any network refresh. Without a verified cached policy, or for a release newer than the cached one lists, it proceeds with a warning. sparkwing update refreshes the policy into <sparkwing home>/engines, the one cache local runs, the local controller, upgrade and cache warm share. sparkwing update --dry-run and sparkwing doctor report the cached policy's status, latest release, publication time and age as version_policy, without writing anything. Cached restrictions still apply, and executable signatures and exact pins remain required. Controller runners fail closed when no verified policy is available.

Stable release publication owns both public index objects. After the GitHub release becomes public, .github/workflows/release.yaml runs bin/publish-release-index.sh with the existing update signing secret. It lists all public stable v0.x releases, adds missing records with twelve calendar months of support from each GitHub publication date, and preserves verified minimum-version restrictions, revocations and existing release history. Drafts, prereleases and tags without published releases do not enter this index. Index publication shares the release publication queue and reads the current signed policy inside that queue. Failed lookups or signature checks stop writes. The website deploy must reserve versions.json and versions.json.sig for this publisher. QA uses bin/qaindex with its separate key and origin. See Delivery for credentials, verification and the index-only recovery dispatch.

A development build skips signed version-policy checks and logs once per resolver that the checks are off. This applies to run admission, run pages, compatibility queries, engine selection and runner updates. A development build needs an explicit --version when upgrading a repository because it does not fetch the latest-version index. Released controller runners retain signed policy checks, regardless of the version reported by a runner or selected by a pipeline.

A service build deployed from a main commit is stamped vX.Y.Z-N-gSHA, the output of git describe --tags --long, and enforces signed policy like a release. Its own version never enters the index: the signed index governs the engine each run selects, which every job checks before it executes pipeline code, and revoked or below-minimum engines still refuse. The deployment's Cloud launcher, which holds an operator-issued service token, claims work without its own version being looked up, because it is the signed service image the deploy workflow built. Self-hosted runners still claim only as a listed release that is neither revoked nor below the minimum. Any other unreleased stamp, such as commit-<sha> or -dev, is a development build.

sparkwing run build-images stamps images with that form by default. It fetches tags from origin (unshallowing a shallow clone), runs git describe --tags --long --abbrev=12 against the newest stable vX.Y.Z tag, and fails the build when the result is not vX.Y.Z-N-gSHA. --tag still overrides the stamp.

Cloud components refuse to run customer work from a development build. A controller with --deployment=cloud refuses to start unless it is given --allow-development-build. A launcher refuses to start unless SPARKWING_DEVELOPMENT_BUILD=1 is set. A Cloud job refuses to run, with no override, because admission keeps the launcher's environment out of job pods. Each refusal names the build's version and how to fix it. Local and team controllers, self-hosted runners and the local CLI keep the development-build behavior above.

Warnings appear in run output and the dashboard before enforcement, with signed reason and fix guidance. A revoked or unsupported release fails loudly and names sparkwing upgrade. The CLI renders the same notice data as run status. Before launch, a hard cut changes code, wire, data, tests and docs together.

Upgrading a repositorySection anchor link

sparkwing upgrade --dry-run
sparkwing upgrade
sparkwing upgrade --version v1.2.3

upgrade stages concrete SDK pin and lockfile edits in a private checkout. The selected verified engine installs the staged SDK through InstallSDK. cache warm uses the same selected-engine installer. The hidden SDK-install command delegates to the engine package, which owns Go builds and npm ci. Each installation makes one helper attempt with a ten-minute deadline. Failure stops the install, reports the cause, and kills and reaps its helper process tree. Verification then compiles Go packages or type-checks TypeScript and runs list, describe and plan through that engine, including hidden pipelines. It never runs job bodies. Install and check environments pass through the shared environment builder, and registered proxy credentials are masked in failures.

Pin and lockfile edits apply once the new SDK resolves against the verified engine. When go mod tidy fails on a package the new release removed, the Go pin moves with go get instead. Each original digest is checked again so concurrent user edits are not overwritten. When the pipeline then fails to compile, describe or plan against the new pin, the pin stays applied and the command reports the failure, names the migration guides between the two versions and exits non-zero. Its report names the manifest and lockfile it changed (for Go, .sparkwing/go.mod and .sparkwing/go.sum); reverting those files, and only those, returns to the old pin without touching other uncommitted pipeline edits. A failure to verify for any other reason, such as a failed SDK download, a missing tool or a malformed answer from the engine, leaves the original pins unchanged. A dry run reports proposed edits without installing or applying them.

sparkwing update owns installed machine software and refreshes managed services and timers. Runners check their own signed minimum/revocation policy and prepare a verified replacement. A blocked old runner takes no new work while existing claims retain renewal and drain paths.

The daemon's wire surfaceSection anchor link

The local controller owns one store, admission, queue and dashboard. Its HTTP API socket uses the same messages and route policy as a remote controller. The admission socket remains a separate protocol. Unknown additive API fields and future canonical error codes remain readable. Required types still validate.

Store incompatibility is an actionable refusal. It never silently opens another store. v0.68.0 starts a new store and refuses one from v0.67 or earlier. Before 1.0, schema changes may require moving the local store aside or recreating a server database. The binary refuses a mismatched store with a move-aside remedy for an older local store. v0.68.3 ships baseline 28 as a store reset. Schema 29 is the first forward migration: a v0.68.3 baseline store upgrades in place on its first open, and an older binary then refuses it. See Schema compatibility. Explicit standalone CI mode uses the same controller code in process. See Local execution and Controller HTTP API.

What this means for plugin authorsSection anchor link

Pin exact SDK versions and test supported versions of the library. Describe the supported SDK range in the library's README. For local Go development, a replace directive can point at editable source. Published pins must remain reproducible. A release's SDK declaration contract and its matching engine travel together.