Stella Product Security Metadata Contract v1

Contract: stella.ops/productSecurityMetadata@v1 Status: Active Sprint: SPRINT_20260430_001 Owner: Stella Ops manufacturer / Router Gateway owner Schema-pinning policy: Pinned per Stella minor release; /.well-known/stella-product-security.json MUST NOT auto-track upstream metadata templates. Adding new top-level fields requires a v2 bump. Adding new currentMinorTrains[] entries is a runtime publication action and not a schema change. Deterministic fixture location: Router integration coverage lives under src/Router/__Tests/StellaOps.Gateway.WebService.Tests/Integration/ProductMetadataEndpointsTests.cs. The endpoint serving this document MUST fail closed if publication is disabled, sealed config is absent, operational preflight is incomplete, the release manifest signature is not verified, or any required manufacturer metadata is missing or still holds a documented TBD-* placeholder. Replay/offline expectations: The served document and any backing schema files MUST be present in offline-kit installations; consumers (CSAF feed indexer, advisory ingest, doc lint) MUST validate without external network access.


Purpose

Defines the JSON shape served by Router at /.well-known/stella-product-security.json before static console fallback or the generic /.well-known Authority proxy. This document is the machine-readable companion to the Stella CVD policy and to the public support/EOL policy.

Audience: Router/Gateway implementers, release engineers who author the signed update manifest, and downstream consumers of Stella’s product-security metadata.

It is consumed by:

Endpoint family and field sources (implementation)

This document is produced by ProductMetadataDocumentProvider (in src/Router/StellaOps.Gateway.WebService/ProductMetadata/) and surfaced by ProductMetadataMiddleware, which intercepts three sibling paths before the routing pipeline:

PathMethodBody
/.well-known/stella-product-security.jsonGET/HEADthis contract (stella.ops/productSecurityMetadata@v1)
/.well-known/security.txtGET/HEADRFC 9116 plaintext (Contact/Encryption/Preferred-Languages/Canonical/Policy/Expires; Expires = GeneratedAtUtc + 1 year)
/api/v1/product/support-lifecycleGET/HEADstella.ops/productLifecycle@v1 lifecycle document

The endpoint is unauthenticated — there is no StellaOps scope gate in ProductMetadataMiddleware; access control is “is the deployment configured to publish” (the fail-closed gate), not an OAuth scope. (No entry in StellaOpsScopes applies to this surface.)

Provenance of the served fields:


Schema version metadata

FieldValue
schemaVersionstella.ops/productSecurityMetadata@v1
schemaPinningPolicyper-stella-minor-release
breakingChangesRequirenew vN+1 document plus migration notes
nonBreakingExtensionsadditive optional fields permitted within v1 only when documented in this contract’s revision log

Any document carrying schemaVersion = stella.ops/productSecurityMetadata@v1 MUST conform exactly to the field set below.


JSON shape and committed planning example

The example below records the approved CRA-Q1 through CRA-Q3 planning defaults for the 2026.9 release train. It is not a live publication artifact. The pgpKey.fingerprint value is the website-derived release-signing lineage fingerprint, not evidence that the expired release@stella-ops.org key is a live security-intake key. The served endpoint must fail closed until a non-expired encryption-capable security-intake key or subkey is published and the configured live fingerprint is verified.

{
  "schemaVersion": "stella.ops/productSecurityMetadata@v1",
  "productName": "Stella Ops",
  "productSecurityContact": {
    "intakeMailbox": "security@stella-ops.org",
    "policyUrl": "https://stella-ops.org/security/coordinated-vulnerability-disclosure",
    "preferredLanguages": ["en"]
  },
  "pgpKey": {
    "url": "https://stella-ops.org/.well-known/stella-product-security-pgp.asc",
    "fingerprint": "1A045258307194881527D2FE095C5809C63DBA65"
  },
  "cveFeedUrl": "https://stella-ops.org/security/advisories/index.json",
  "csafFeedUrl": "https://stella-ops.org/security/csaf/feed.json",
  "supportPolicy": {
    "defaultWindowYears": 5,
    "eolDeprecationNoticeMonths": 12,
    "currentMinorTrains": [
      {
        "version": "2026.9",
        "releasedAt": "2026-09-01",
        "securitySupportUntil": "2031-09-01",
        "eolAt": "2031-09-01",
        "lts": false
      }
    ]
  },
  "placedOnMarketAt": null,
  "madeAvailableInEu": "2026-09-01",
  "craApplicability": "pre-cra",
  "lastModified": "2026-04-30T00:00:00Z"
}

Field definitions

Top-level

FieldTypeRequiredDescription
schemaVersionstringyesAlways stella.ops/productSecurityMetadata@v1.
productNamestringyesAlways Stella Ops for documents served from a canonical Stella endpoint.
productSecurityContactobjectyesPublic security contact block. See sub-fields.
pgpKeyobjectyesPGP key reference for encrypted intake. See sub-fields.
cveFeedUrlstring (URL)yesURL of the CVE-style index for Stella product advisories.
csafFeedUrlstring (URL)yesURL of the CSAF 2.0 advisory feed for Stella product advisories. CSAF version is governed by decisions-log.md §CRA-Q4 and the feed payload contract in stella-product-csaf-advisory-v1.md.
supportPolicyobjectyesSupport and end-of-life policy block. See sub-fields.
placedOnMarketAtstring (ISO-8601 date) or nullyesDate Stella as a product was first placed on the market in the EU under the full-CRA placement anchor. null for pre-full-CRA releases. See ../legal/cra-product-placement-anchor.md.
madeAvailableInEustring (ISO-8601 date)yesDate the current advertised release was made available to EU users. May be earlier than placedOnMarketAt for pre-full-CRA releases. Read verbatim from the release manifest (required, non-empty). A TBD- value in the manifest fails the endpoint closed (whole-manifest scan), so the served value is always a real date.
craApplicabilitystring enumyesExpected to be one of: pre-cra, cra-reporting-only (post-2026-09-11, pre-2027-12-11), cra-full (post-2027-12-11 placement). As implemented, the served value is copied verbatim from the release manifest’s craApplicability string (ProductMetadataDocumentProvider.LoadReleaseManifestGetRequiredString(root, "craApplicability")); the endpoint does NOT validate it against this enum nor compute it from the placement-anchor ADR. The manifest author is responsible for setting the correct value.
lastModifiedstring (ISO-8601 timestamp, UTC)yesEmitted as the sealed Platform:Product:GeneratedAtUtc configuration value (ProductMetadataDocumentProvider.GetProductSecurityJsonlastModified = readiness.Options.GeneratedAtUtc). It is the configured generation timestamp, NOT the wall-clock time the document was actually served. The endpoint fails closed if this string is not parseable as a date (ProductMetadataOptions.GeneratedAtUtc; default 2026-04-30T00:00:00Z).

productSecurityContact

FieldTypeRequiredDescription
intakeMailboxstring (RFC 5322 mailbox)yesCanonical security intake. Default: security@stella-ops.org.
policyUrlstring (URL)yesURL of the published CVD policy (../security/coordinated-vulnerability-disclosure.md).
preferredLanguagesstring arrayyesRFC 5646 language tags accepted at intake. Default: ["en"].

pgpKey

FieldTypeRequiredDescription
urlstring (URL)yesCanonical URL of the intake PGP public key (ASCII-armored). MUST match the Encryption: line in security.txt.
fingerprintstringyesOpenPGP key fingerprint (40-character uppercase hex without spaces). As implemented, this is the compile-time constant ProductMetadataDocumentProvider.PgpFingerprint = "1A045258307194881527D2FE095C5809C63DBA65"; it is emitted verbatim on every published response and is NOT read from the release manifest, configuration, or a live key. This planning value comes from the website-published Stella release-signing key lineage; it is not proof of a live intake key. The KeyFreshnessVerified and KeyFingerprintVerified operational-preflight booleans (ProductMetadataPreflightOptions) gate publication, but the served fingerprint string itself is the constant regardless of which key was verified. Draft/roadmap: the placeholder convention TBD-confirm-after-key-creation and the requirement to emit a verified live fingerprint that may differ from the lineage value are forward-looking; they are not yet enforced or wired (the TBD- placeholder scan applies only to the release manifest — see Validation rules — and the manifest does not carry a PGP fingerprint).

supportPolicy

FieldTypeRequiredDescription
defaultWindowYearsintegeryesPublic security-support window from the GA date of each minor train. Default: 5 (per decisions-log.md §CRA-Q2).
eolDeprecationNoticeMonthsintegeryesMinimum months of public notice before a release reaches end-of-life. Default: 12.
currentMinorTrainsarray of objectsyesCatalogue of currently advertised minor trains. See sub-fields. As implemented, the served array always has exactly one element, populated from the release manifest’s supportPolicy.releaseTrain object (ProductMetadataDocumentProvider.GetProductSecurityJson). The endpoint cannot emit multiple trains in v1. The committed planning record is 2026.9 (releasedAt = 2026-09-01, securitySupportUntil/eolAt = 2031-09-01, lts = false). Any TBD- token anywhere in the manifest causes a hard 503 (whole-manifest scan; see Validation rules), so the published array never contains a TBD-release-roadmap placeholder.

supportPolicy.currentMinorTrains[]

Each element is copied field-for-field from the release manifest’s required supportPolicy.releaseTrain object (GetRequiredString/GetBoolean); all four string fields are required and non-empty, and lts is a required boolean. The endpoint does NOT compute securitySupportUntil/eolAt from releasedAt and does NOT enforce eolAt >= securitySupportUntil — those are conventions the manifest author must honour. A TBD- substring in any of these (via the manifest scan) fails closed, so served values are always concrete.

FieldTypeRequiredDescription
versionstringyesStella minor train identifier (e.g. 2026.9). Served verbatim from manifest releaseTrain.version.
releasedAtstring (ISO-8601 date)yesGA date for the minor train. Served verbatim from manifest releaseTrain.releasedAt.
securitySupportUntilstring (ISO-8601 date)yesEnd-of-security-support date. Convention (not enforced by the endpoint): releasedAt + defaultWindowYears unless explicitly extended. Served verbatim from the manifest.
eolAtstring (ISO-8601 date)yesEnd-of-life date (no further patch or commercial support). Convention (not enforced by the endpoint): >= securitySupportUntil. Served verbatim from the manifest.
ltsbooleanyestrue if this minor train is named LTS for its calendar year. Served verbatim from manifest releaseTrain.lts. The annual LTS rule lives in decisions-log.md §CRA-Q2.

craApplicability derivation

The intended legal semantics of the value (set by whoever authors the signed release manifest) are:

Implementation note (was INCORRECT): the well-known endpoint does not itself compute this value or cross-check it against madeAvailableInEu/placedOnMarketAt. ProductMetadataDocumentProvider.LoadReleaseManifest reads the craApplicability string straight out of the signed product-update-manifest.json (stella.ops/productUpdateManifest@v1) and serves it verbatim, with no enum validation. The manifest is the source-of-truth and is signature-verified (ReleaseManifestSignatureVerified must be true), so “MUST NOT be hand-edited at deploy time” is satisfied by signing the manifest, not by gateway-side computation. The substantialModificationOf field is present in the manifest schema (and in the test fixture) but is not consumed by the gateway provider.


Validation rules

The authoritative fail-closed logic is ProductMetadataDocumentProvider.TryBuildDocuments + LoadReleaseManifest; the HTTP behaviour is ProductMetadataMiddleware.InvokeAsync. The endpoint serves 200 OK only when every check below passes; otherwise it returns 503 Service Unavailable with an empty body and Cache-Control: no-store.


Operational preflight

No TBD-* placeholder remains in the committed 2026.9 planning example. The well-known endpoint still MUST fail closed when deployment preflight cannot verify the inputs below. As implemented, preflight is expressed as eight booleans on ProductMetadataPreflightOptions (config prefix Platform:Product:Preflight:*), all of which must be true (AllPassed):

These inputs are tracked operationally (verified externally and asserted to the gateway via the boolean flags — the gateway does not itself probe the mailbox or load/inspect the PGP key); they are not open product decisions.

Operational key note: the sibling website repo contains public release-signing key material for Stella Ops Release Signing <release@stella-ops.org> with fingerprint 1A045258307194881527D2FE095C5809C63DBA65, created 2025-07-29 and expired 2025-07-30. The fingerprint is accepted as the website-derived planning/key-lineage value, but live publication requires a new non-expired security-intake key or subkey certified by that lineage during the Stella key ceremony.

Operational retention/access note: the CVD mailbox default is 7-year retention for vulnerability reports and triage records, access limited to the three dedicated product-security roles, and quarterly access review. This does not add a metadata field in v1; it is documented in the CVD policy contract and enforced by operational preflight/audit.


Revision log

Date (UTC)ChangeNotes
2026-05-30Doc↔code reconciliation against ProductMetadataDocumentProvider/ProductMetadataOptions/ProductMetadataMiddleware.Corrected: lastModified = configured GeneratedAtUtc (not actual regeneration time); pgpKey.fingerprint is a hardcoded constant (not manifest/live-key derived, and the TBD- placeholder convention is roadmap-only); craApplicability and currentMinorTrains are read verbatim from the signed release manifest (not computed/enum-validated); the TBD- scan is whole-manifest, not field-scoped. Removed the unimplemented “24h freshness” and “feed reachability cross-check” rules (marked NOT IMPLEMENTED). Added the full fail-closed condition set (HTTPS base URL, generatedAt parse, eight preflight booleans, manifest schema/version/digest matching, JSON validity) and documented the sibling security.txt + support-lifecycle endpoints and unauthenticated access.
2026-04-30Recorded CVD mailbox retention/access defaults.7-year report retention, three-role access, quarterly access review; no metadata field added.
2026-04-30Filled 2026.9 lifecycle and website-derived planning fingerprint.2026.9 releases on 2026-09-01 and remains pre-cra; live key freshness is still a deployment preflight requirement.
2026-04-30Added operational key lineage note.Website release-signing key found but expired; endpoint remains fail-closed until a valid security-intake key is published.
2026-04-30v1 introduced.Initial pin; consumes decisions-log.md §CRA-Q1, §CRA-Q2, §CRA-Q3, §CRA-Q4 outputs.