> For the complete documentation index, see [llms.txt](https://whitepaper.litho.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whitepaper.litho.ai/docs/governance/supply-chain.md).

# Supply Chain Hardening

Every container image Lithosphere publishes to GHCR carries three verifiable artifacts produced by `.github/workflows/publish-images.yaml`:

1. **Cosign signature** — keyless, identity-bound to the GitHub Actions workflow that built the image. Proves the image was produced by *this* repository, not a typo-squatter.
2. **SLSA build-provenance attestation** — in-toto SLSA Provenance v1.0 statement linking the image digest to the specific workflow run, source commit, and build invocation. Proves *how* the image was produced.
3. **SBOM (SPDX)** — Software Bill of Materials enumerating every package in the image. Required for CVE response and license-policy audits.

Together they satisfy SLSA Build Level 2 (signed provenance from a hosted build platform). Level 3 (non-falsifiable provenance) would require a hardened isolation layer beyond GitHub-hosted runners — out of scope for testnet posture.

## Verifying a published image

Pick any tag on any image at `ghcr.io/kajlabs/lithosphere-*`. Three checks, in order of increasing strength:

### 1. Cosign signature

```bash
cosign verify ghcr.io/kajlabs/lithosphere-api:sha-<short> \
  --certificate-identity-regexp 'https://github.com/KaJLabs/Lithosphere/.+' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

Success output includes the Fulcio cert subject (workflow ref + actor + SHA) and the Rekor transparency log entry. A mismatch means either the image wasn't built by this repo, or the workflow was tampered with.

### 2. SLSA build provenance

```bash
gh attestation verify oci://ghcr.io/kajlabs/lithosphere-api:sha-<short> \
  --owner KaJLabs
```

This validates the in-toto attestation pushed to the registry alongside the image. The output includes the build parameters, source repo + commit, and runner platform. Stricter than the Cosign check because the attestation includes a structured description of how the build happened, not just "this org signed it."

### 3. SBOM

```bash
gh run download <run-id> -n sbom-api -D ./sbom
cat sbom/sbom-api.spdx.json | jq '.packages | length'
```

SBOMs are uploaded as workflow artifacts (90-day retention). For CVE response: `jq '.packages[] | select(.name == "<dep>")'` finds whether a known-vulnerable package is in your image.

## What each verification rules out

| Threat                                                           | Cosign sig | SLSA provenance | SBOM |
| ---------------------------------------------------------------- | :--------: | :-------------: | :--: |
| Typo-squatted image (`lithosphere-apl` instead of `-api`)        |      ✅     |        ✅        |   —  |
| Image rebuilt offline + force-pushed to GHCR with a stolen token |      ✅     |        ✅        |   —  |
| Workflow modified to inject malicious code into the build        |      —     |        ✅        |   —  |
| Same Dockerfile, different source commit                         |      —     |        ✅        |   —  |
| Same source commit, but pulled-in dep CVEd post-build            |      —     |        —        |   ✅  |

The three are complementary; a serious compliance review checks all three.

## How the gates layer

`publish-images.yaml` pipeline order:

```
checkout
  → buildx + metadata
    → build & push image
      → Trivy scan (HIGH/CRITICAL to SARIF, CRITICAL gate)
        → Cosign keyless sign
          → SLSA build-provenance attestation (push-to-registry)
            → SBOM (SPDX, uploaded as artifact)
```

Trivy runs *before* signing so a CVE-laden image never gets an attestation in the first place. The CRITICAL gate is hard-fail; HIGH findings upload to the GitHub Security tab for triage (see [license policy](/docs/governance/license-policy.md) for the parallel dependency-side gate).

## Deployment-side verification

The `Verify GHCR Image Signatures` step in `deploy-simple.yaml` runs Cosign + `gh attestation verify` against each published image for the deploy's commit SHA. Today the bastion still builds from source, so the check is **advisory** (`continue-on-error: true`) — a `failed` or `skipped` row doesn't block the deploy. Results land in the deployment summary's `## Supply Chain Verification` table next to the `## Build SHA Verification` table.

Outcomes the step reports:

| Status                | Meaning                                                                                                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅ verified            | Cosign keyless signature AND SLSA Build L2 attestation both match `https://github.com/KaJLabs/Lithosphere/.+` identity.                                                   |
| ⚠️ partial (sig-only) | Image is signed but lacks (or has an invalid) SLSA attestation. Treat as suspicious.                                                                                      |
| ⚠️ partial (att-only) | Attestation present but the Cosign signature didn't verify. Usually means a stale registry mirror or a key-rotation in progress.                                          |
| ❌ failed              | Neither artifact verified. Investigate before promoting.                                                                                                                  |
| — (skipped)           | No image for this SHA. Normal when the commit only touched workflows / docs / chain — `publish-images.yaml` runs only on `Makalu/{api,indexer,explorer}/**` path changes. |

### Promotion path to a blocking gate

The day deployments switch from build-from-source to pull-published-image:

1. Remove `continue-on-error: true` from `Verify GHCR Image Signatures`.
2. Change the bastion deploy script to `docker compose pull` then `docker compose up -d --no-build`.
3. The verification step now blocks any deploy whose images don't carry matching Cosign + SLSA artifacts. Tampered, typo-squatted, or offline-rebuilt images can't reach production.

Until then, the advisory mode is the right posture: it surfaces the signal in every deploy summary without inheriting publish-image's schedule constraints.

## Rotation & key management

Cosign keyless avoids holding a long-lived signing key — every signature is bound to a short-lived Fulcio certificate issued during the workflow run. The trust anchor is the GitHub Actions OIDC issuer + the `KaJLabs/Lithosphere` repo identity. No key to rotate, no key to leak.

For npm package publishes (SDK), the equivalent identity binding is npm provenance attestations — wired in `release.yaml` via `--provenance`. See [release-process.md](/docs/governance/release-process.md).

## Code-level static analysis

Image-layer attestations and dependency-license enforcement don't catch bugs in the source we own — SQL injection, SSRF, path traversal, weak crypto, prototype pollution. That's what [`codeql.yaml`](https://github.com/KaJLabs/lithosphere/blob/main/.github/workflows/codeql.yaml) covers: GitHub's CodeQL runs on every push to `main`, every PR, and weekly via cron. The JS/TS extractor (build-mode `none`, query suite `security-and-quality`) indexes `Makalu/{api,indexer,explorer,packages,templates,contracts/scripts,tooling}` plus repo-level `scripts/`. Findings post to the Security tab under the `codeql-javascript-typescript` category, alongside Trivy's `trivy-{api,indexer,explorer}` entries.

The three layers compose:

| Layer          | Catches                      | Workflow                                     |
| -------------- | ---------------------------- | -------------------------------------------- |
| Source SAST    | bugs in code we wrote        | `codeql.yaml`                                |
| Container scan | OS/library CVEs              | `publish-images.yaml` (Trivy)                |
| Supply chain   | image tampering / typo-squat | `publish-images.yaml` (Cosign + SLSA + SBOM) |

### Triage workflow for CodeQL findings

CodeQL's first-scan baseline often contains a long tail of style-level notes plus a handful of legitimate-but-false-positive flow alerts (e.g. `router.push(\`/blocks/${userInput}\`)`flagged as DOM-XSS because the extractor can't prove the destination route doesn't`innerHTML\` the segment). The expectation is **not** "zero open alerts" — it's "every open alert has been triaged":

1. **Fix at source** when the alert points at a genuine issue. Recent examples (commit landing this section): `js/log-injection` from `console.warn` with raw user input → `sanitizeForLog()` helper that strips ASCII control chars; `js/file-system-race` from an `existsSync`→`appendFileSync` pair → drop the pre-check (the append creates on demand); `js/polynomial-redos` on `/=+$/` → manual trailing-char strip with no regex.
2. **Dismiss with a comment** when the alert is a false positive. Use the GitHub Security UI ("Dismiss alert" → "False positive" / "Used in tests" / "Won't fix") and include the reason. Don't leave open alerts indefinitely without dismissal — they create noise that masks real findings.
3. **Track as work** when the alert is real but the fix needs design (e.g. SSRF in a controlled-base proxy endpoint — needs an explicit URL allow-list). Create an issue, link the alert, leave the alert open until the issue closes.

Cadence: triage every Monday alongside the weekly CodeQL cron run.

## Related

* [License Policy](/docs/governance/license-policy.md) — dependency-side supply chain
* [Key Rotation Runbook](/docs/governance/key-rotation-runbook.md) — for the cases where rotation IS required (RPC keys, deployer private keys, NPM\_TOKEN)
* [Deployment Approvals](/docs/governance/deployment-approvals.md) — the human gate layered on top of the technical gates
* [Phase 10 work in the project memory tracker](/docs/phases.md) — broader security posture


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://whitepaper.litho.ai/docs/governance/supply-chain.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
