Product Lifecycle Contract v1

Contract: stella.ops/productLifecycle@v1 Status: Active schema; 2026.9 lifecycle values approved for planning and live publication subject to endpoint/key/mailbox preflight in archived sprint SPRINT_20260430_006_DOCS_support_eol_policy.md. Sprint: SPRINT_20260430_006 Owner: Stella Ops manufacturer of record + Router Gateway owner Schema-pinning policy: Pinned per Stella minor release. Consumers MUST require the exact schemaVersion value they support and MUST NOT auto-follow a newer lifecycle schema. Deterministic fixture location: Router endpoint coverage lives under src/Router/__Tests/StellaOps.Gateway.WebService.Tests/Integration/ProductMetadataEndpointsTests.cs. The serving code is src/Router/StellaOps.Gateway.WebService/Middleware/ProductMetadataMiddleware.cs and src/Router/StellaOps.Gateway.WebService/ProductMetadata/ProductMetadataDocumentProvider.cs. Replay/offline expectations: Lifecycle documents, release-manifest lifecycle blocks, and offline-kit metadata MUST validate without external network access.


Purpose

Defines the machine-readable lifecycle shape for Stella Ops support and end-of-life (EOL) metadata. It is the contract for anyone publishing, consuming, or offline-validating Stella release-train support windows — Router gateway operators, offline-kit tooling, and downstream consumers of the public lifecycle endpoint.

The same release-train fields are consumed by:

Implementation note: the live lifecycle endpoint does not generate or compute lifecycle values. ProductMetadataDocumentProvider.GetSupportLifecycleJson() reads the single supportPolicy.releaseTrain record from the sealed, signature-verified release manifest and emits it verbatim. The static policy block below is hard-coded in the provider, and generatedAt is taken from the Platform:Product:GeneratedAtUtc config value (not from manifest content).

The human-readable policy is ../legal/product-support-policy.md. The EOL transition runbook is ../legal/eol-policy.md.


Schema Version Metadata

FieldValue
schemaVersionstella.ops/productLifecycle@v1
schemaPinningPolicyper-stella-minor-release
breakingChangesRequireNew vN+1 contract document plus migration notes.
nonBreakingExtensionsAdditive optional fields only when documented in this contract’s revision log.

Any document carrying schemaVersion = stella.ops/productLifecycle@v1 MUST conform to the field definitions and validation rules below.


JSON Shape

{
  "schemaVersion": "stella.ops/productLifecycle@v1",
  "generatedAt": "2026-04-30T00:00:00Z",
  "policy": {
    "defaultWindowYears": 5,
    "supportAnchor": "ga-minor-release-train",
    "patchReleaseRule": "inherit-parent-minor-train-unless-explicitly-extended",
    "ltsSelectionRule": "last-stable-minor-of-calendar-year-unless-overridden",
    "eolDeprecationNoticeMonths": 12,
    "extendedSupportTier": "commercial-option-outside-baseline-cra"
  },
  "releaseTrains": [
    {
      "version": "2026.9",
      "releasedAt": "2026-09-01",
      "securitySupportUntil": "2031-09-01",
      "eolAt": "2031-09-01",
      "lts": false
    }
  ]
}

Field Definitions

Top-level fields

FieldTypeRequiredDescription
schemaVersionstringyesAlways stella.ops/productLifecycle@v1 for this contract.
generatedAtstring (ISO-8601 timestamp, UTC)yesTimestamp when the lifecycle document was generated. In the live Router endpoint this is the Platform:Product:GeneratedAtUtc config value, normalised to UTC, not a value derived from manifest content.
policyobjectyesStatic policy values approved by CRA-Q2. Hard-coded in ProductMetadataDocumentProvider.GetSupportLifecycleJson().
releaseTrainsarray of objectsyesStella minor train lifecycle records. The schema permits multiple records (current plus historical), but the live Router endpoint currently emits exactly one element — the single supportPolicy.releaseTrain copied from the running build’s signed release manifest.

policy

FieldTypeRequiredDescription
defaultWindowYearsintegeryesBaseline security-support window from each GA minor train. Default and minimum: 5.
supportAnchorstring enumyesAlways ga-minor-release-train for v1.
patchReleaseRulestring enumyesAlways inherit-parent-minor-train-unless-explicitly-extended for v1.
ltsSelectionRulestring enumyesAlways last-stable-minor-of-calendar-year-unless-overridden for v1.
eolDeprecationNoticeMonthsintegeryesMinimum public notice before EOL. Default and minimum: 12.
extendedSupportTierstring enumyesAlways commercial-option-outside-baseline-cra for v1.

releaseTrains[]

FieldTypeRequiredDescription
versionstringyesStella minor train identifier. Patch releases inherit the parent train and SHOULD NOT appear as separate v1 records unless an explicit extension changes train-level lifecycle data.
releasedAtstring (ISO-8601 date, UTC)yesGA date for the minor train. The placeholder TBD-release-roadmap is allowed only before live publication.
securitySupportUntilstring (ISO-8601 date, UTC)yesLast date for baseline security support. Computed as releasedAt + 5 years unless Stella publishes a later date. The placeholder TBD-release-roadmap is allowed only before live publication.
eolAtstring (ISO-8601 date, UTC)yesEnd-of-life date for the train. MUST be greater than or equal to securitySupportUntil. The placeholder TBD-release-roadmap is allowed only before live publication.
ltsbooleanyestrue when the train is the named LTS release for its calendar year.

Validation Rules


Release Manifest Integration

The signed product update manifest (stella.ops/productUpdateManifest@v1) MUST reference this contract when it carries support lifecycle data, and is the authoritative source the live lifecycle endpoint reads from. See product-update-manifest-v1.mdfor the full manifest contract. The manifest lifecycle block uses the following shape (field names verified against ProductMetadataDocumentProvider.LoadReleaseManifest):

{
  "supportPolicy": {
    "lifecycleSchema": "stella.ops/productLifecycle@v1",
    "releaseTrain": {
      "version": "2026.9",
      "releasedAt": "2026-09-01",
      "securitySupportUntil": "2031-09-01",
      "eolAt": "2031-09-01",
      "lts": false
    }
  }
}

The release manifest MUST copy lifecycle values from the same pinned source used to generate the public lifecycle endpoint. It MUST NOT hand-edit support dates independently of this contract. The Router gateway enforces manifest integrity at serve time: the manifest’s top-level version MUST match the running product version (Platform:Product:RunningProductVersion), and when Platform:Product:RunningImageDigest is set the manifest images[] array MUST contain that digest — otherwise the endpoint fails closed with HTTP 503.


Offline Determinism

Lifecycle JSON must be generated from release metadata committed or packaged with the release. Offline validators must be able to:

Router Runtime Binding

ProductMetadataMiddleware (registered in Program.cs via app.UseMiddleware<ProductMetadataMiddleware>()) serves GET /api/v1/product/support-lifecycle from sealed, signature-verified release-manifest metadata, ahead of generic /api/* microservice dispatch. The endpoint is unauthenticated metadata served by middleware before routing/auth — no StellaOpsScopes scope gates it. The same middleware also serves /.well-known/security.txt and /.well-known/stella-product-security.json.

HTTP behaviour

Fail-closed conditions (HTTP 503)

The endpoint fails closed when TryBuildDocuments() cannot produce a verified document, including when:

Configuration keys (Platform:Product section)

Bound by ProductMetadataOptions.Bind; each also accepts a flattened dotted key (e.g. platform.product.publishMetadata).

KeyTypeDefaultPurpose
PublishMetadataboolfalseMaster enable for product metadata publication.
SealedConfigboolfalseAsserts the metadata config has been sealed.
CanonicalBaseUrlstringhttps://stella-ops.orgHTTPS base URL used in security.txt / product-security URLs.
GeneratedAtUtcstring2026-04-30T00:00:00ZSource of the lifecycle generatedAt field; must be parseable.
CacheMaxAgeSecondsint300Drives Cache-Control: public, max-age=.
RunningProductVersionstringassembly informational versionMust equal the manifest top-level version.
RunningImageDigeststring?unsetWhen set, must be present in manifest images[].digest.
ReleaseManifestPathstring?unsetFilesystem path to the signed product update manifest JSON.
ReleaseManifestSignatureVerifiedboolfalseAsserts the release manifest signature was verified out-of-band.
Preflight:*bool (×8)falseOperational preflight gates; all must be true (see above).

Revision Log

Date (UTC)ChangeNotes
2026-05-30Doc/code reconciliation against Router gateway implementation.Documented HEAD/405/304 behaviour, ETag format (quoted lowercase hex SHA-256), full fail-closed condition list, and the complete Platform:Product config-key set; clarified that the live endpoint copies a single supportPolicy.releaseTrain from the signed manifest verbatim rather than generating values. Grounded in ProductMetadataMiddleware, ProductMetadataDocumentProvider, and ProductMetadataOptions.
2026-04-30Filled 2026.9 lifecycle planning values.2026.9 releases on 2026-09-01; baseline support and EOL both run through 2031-09-01.
2026-04-30v1 introduced.Captures CRA-Q2 lifecycle fields: version, releasedAt, securitySupportUntil, eolAt, and lts.