Hamburger Cross Icon
End-of-Life Runtime Guardrails - Lunar Policy for Security And Compliance

End-of-Life Runtime Guardrails

Policy Beta Security And Compliance

Enforce that runtime versions are still maintained upstream. Two checks: not past end-of-life, and still in active support (not just security-only maintenance). Reads the `endoflife` collector's `.lang.<language>.eol` data.

Add endoflife to your lunar-config.yml:
uses: github://earthly/lunar-lib/policies/endoflife@v1.0.5

Included Guardrails

This policy includes 2 guardrails that enforce standards for your security and compliance.

Guardrail

runtime-not-eol

Fails if any detected runtime under .lang.<language>.eol is past its end-of-life date (is_eol == true). Skips cleanly when no runtime EOL data is present (e.g. component has no detectable runtime pin, or the endoflife collector did not run). All detected runtimes must be non-EOL — multi-runtime components fail if any one is EOL.

endoflife eol runtime lifecycle supply chain
View Guardrail
Guardrail

runtime-supported

Fails if any detected runtime is outside the active support window (is_supported == false). For products that distinguish active support from security-only maintenance (e.g. Node.js, Python, .NET), this catches services running on a runtime that's still patched for security but no longer receiving non-security fixes. For products without a separate active-support window (e.g. Go), this check is equivalent to runtime-not-eol. Skips cleanly when no EOL data is present.

endoflife support active support runtime lifecycle
View Guardrail

How Guardrails Fit into Lunar

Lunar guardrails define your engineering standards as code. They evaluate data collected by integrations and produce pass/fail checks with actionable feedback.

Policies support gradual enforcement—from silent scoring to blocking PRs or deployments—letting you roll out standards at your own pace without disrupting existing workflows.

Learn How Lunar Works
1
Integrations Gather Data
Collectors extract metadata from code, CI pipelines, tool outputs, and scans
2
{ } Centralized as JSON
All data merged into each component's unified metadata document
3
Guardrails Enforce Standards This Policy
Real-time feedback in PRs and AI workflows

Example Evaluated Data

This policy evaluates structured metadata from the Component JSON. Here's an example of the data it checks:

{ } component.json Example Input
{
  "lang": {
    "go": {
      "eol": {
        "source": {
          "tool": "endoflife.date",
          "integration": "api",
          "collected_at": "2026-04-27T12:00:00Z"
        },
        "product": "go",
        "cycle": "1.21",
        "detected_version": "1.21",
        "is_eol": true,
        "is_supported": false,
        "eol_date": "2024-08-13",
        "support_until": null,
        "lts": false,
        "latest_in_cycle": "1.21.13"
      }
    },
    "nodejs": {
      "eol": {
        "source": {
          "tool": "endoflife.date",
          "integration": "api",
          "collected_at": "2026-04-27T12:00:00Z"
        },
        "product": "nodejs",
        "cycle": "20",
        "detected_version": "20.11.1",
        "is_eol": false,
        "is_supported": false,
        "eol_date": "2026-04-30",
        "support_until": "2024-10-22",
        "lts": true,
        "latest_in_cycle": "20.19.0"
      }
    }
  }
}

Required Integrations

This policy evaluates data gathered by one or more of the following integration(s). Make sure to enable them in your lunar-config.yml.

Configuration

Configure this policy in your lunar-config.yml.

Inputs

Input Required Default Description

Documentation

View on GitHub

End-of-Life Runtime Guardrails

Enforce that components are pinned to runtime versions that upstream still maintains — catches the "neglected legacy service" smell of services running on EOL or out-of-support runtimes.

Overview

This policy operates on data produced by the endoflife collector, which detects pinned runtime versions across Node.js, Python, Ruby, Go, Java, .NET, and PHP, then resolves their EOL/support status against endoflife.date. Two checks: runtime-not-eol (no detected runtime is past its end-of-life date) and runtime-supported (no detected runtime is outside its active-support window). Both checks skip cleanly when no .lang.<language>.eol data is present (e.g. component has no detectable runtime pin, or the endoflife collector hasn't run).

Policies

This plugin provides the following policies (use include to select a subset):

Policy Description
runtime-not-eol Fails when any .lang.<language>.eol.is_eol is true — runtime is past eol_date, no upstream fixes (security or otherwise)
runtime-supported Fails when any .lang.<language>.eol.is_supported is false — runtime is past active support (may still receive security-only patches)

runtime-supported is strictly stricter than runtime-not-eol (an EOL runtime is also unsupported). The two coexist so you can pick the strictness for your enforcement: runtime-not-eol for the worst-case hard-block; runtime-supported to catch drift early before a runtime hits security-only maintenance. For runtimes where endoflife.date doesn't expose a separate support phase (most notably Go), is_supported is equivalent to not is_eol, so the two checks behave identically for those.

If multiple runtimes are detected on the same component (rare — e.g. a service shipping a Python sidecar plus a Java app), all must pass; failures list every offending runtime. The policy currently has no tunable inputs — pass/fail thresholds are absolute (a runtime is EOL or it isn't, in active support or not). For thresholds like "fail if EOL is within N days", file a follow-up issue.

Required Data

This policy reads from the following Component JSON paths:

Path Type Provided By
.lang.<language>.eol.is_eol boolean endoflife
.lang.<language>.eol.is_supported boolean endoflife
.lang.<language>.eol.cycle string endoflife
.lang.<language>.eol.detected_version string endoflife
.lang.<language>.eol.eol_date string | null endoflife
.lang.<language>.eol.support_until string | null endoflife
.lang.<language>.eol.product string endoflife

<language> is one of go, nodejs, python, ruby, java, dotnet, php. Both checks skip when no .lang.<language>.eol data is present for any language.

Installation

Add to your lunar-config.yml:

collectors:
  - uses: github://earthly/lunar-lib/collectors/endoflife@v1.0.0
    on: ["domain:your-domain"]

policies:
  - uses: github://earthly/lunar-lib/policies/endoflife@v1.0.0
    enforcement: report-pr
    # include: [runtime-not-eol]  # Only run the hard-EOL check

The collector and policy are paired — the policy reads .lang.<language>.eol data, which only the endoflife collector writes today. Make sure the collector is enabled wherever the policy runs.

Examples

Passing Example

{
  "lang": {
    "nodejs": {
      "version": "20.11.1",
      "eol": {
        "source": { "tool": "endoflife.date", "integration": "api" },
        "product": "nodejs",
        "cycle": "20",
        "detected_version": "20.11.1",
        "is_eol": false,
        "is_supported": true,
        "eol_date": "2026-04-30",
        "support_until": "2025-04-30",
        "lts": true,
        "latest_in_cycle": "20.19.0"
      }
    }
  }
}

Failing Example

{
  "lang": {
    "go": {
      "version": "1.21",
      "eol": {
        "source": { "tool": "endoflife.date", "integration": "api" },
        "product": "go",
        "cycle": "1.21",
        "detected_version": "1.21",
        "is_eol": true,
        "is_supported": false,
        "eol_date": "2024-08-13",
        "support_until": null,
        "lts": false,
        "latest_in_cycle": "1.21.13"
      }
    }
  }
}

Failure message (runtime-not-eol): go cycle 1.21 (detected 1.21) reached end-of-life on 2024-08-13. Move to a supported cycle (current latest: see https://endoflife.date/go).

Failure message (runtime-supported): go cycle 1.21 (detected 1.21) is out of active support and reached end-of-life on 2024-08-13 (Go has no separate active-support phase). Bump to a still-supported cycle (see https://endoflife.date/go).

Remediation

When this policy fails, you can resolve it by:

  1. runtime-not-eol: Bump the pinned runtime to a cycle whose eol_date is in the future. Update the relevant version file (.go-version, .nvmrc, .python-version, package.json engines, etc.) and rebuild. Reference the matching endoflife.date page (linked in the failure message) for the current latest cycle and any LTS recommendations.
  2. runtime-supported: Bump the pinned runtime to a cycle that's still in active support (i.e. before support_until). This is the recommended fix even when the runtime isn't yet EOL — security-only maintenance is a holding pattern, not a destination.
  3. If the bump is blocked (incompatible dependencies, planned re-platform, etc.) and you need to silence the check temporarily, configure the policy with enforcement: report-pr rather than block-pr until the upgrade lands. Don't suppress the data — leaving the EOL signal visible in the component JSON keeps the issue surfaced.

The policy does not check whether the detected version is the latest patch in its cycle (use .lang.<language>.eol.latest_in_cycle vs detected_version for a custom patch-hygiene check) and does not enforce LTS-only usage (use .lang.<language>.eol.lts for a custom LTS check). Example Component JSON is defined in lunar-policy.yml under example_component_json.

Open Source

This policy is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo