Files
sencho/docs/operations/verifying-images.mdx
T
Anso 5e29649f3e chore: workflow hardening (husky+commitlint, dependency-review, stale, GHCR, mesh-sidecar digest) (#863)
* chore(repo): enforce Conventional Commits via husky and commitlint

release-please parses commit subjects on main to compute the next version
and regenerate CHANGELOG.md. A non-conforming commit silently breaks both,
so the format must be enforced at commit time, not by review.

Adds husky 9 to wire a commit-msg hook, commitlint with the conventional
config, and a small rule override (loosen subject length to 120 chars,
disable subject-case so existing imperative subjects keep passing). The
prepare script runs husky on npm install so contributors do not need to
configure it manually.

* ci: add dependency-review workflow

GitHub-native action that diffs the PR's manifests (package.json,
package-lock.json) against main and fails when a new transitive dep
introduces a high or critical CVE. Existing vulnerabilities tracked in
security/vex/sencho.openvex.json and the Trivy scan in ci.yml are not
re-flagged here.

Pinned to actions/dependency-review-action v4.9.0 by commit SHA. Comment
summaries are posted to the PR only on failure to keep the conversation
clean on green PRs.

* ci: add stale workflow for issues and pull requests

Marks issues and PRs as stale after 60 days of inactivity and closes 14
days later unless re-engaged. Issues labeled pinned, security, bug, or
tracking are exempt and never auto-closed; PRs labeled pinned or security
are exempt. Runs daily at 01:30 UTC and processes up to 30 items per run
to stay within the action's rate budget.

Pinned to actions/stale v10.2.0 by commit SHA.

* chore(repo): route new issues to docs, discussions, and security policy

Disables the blank-issue option and adds three contact links the issue
chooser surfaces above the bug-report and feature-request templates:
docs.sencho.io for setup questions, GitHub Discussions for open-ended
chat, and the repository security policy for private vulnerability
reports. This keeps the bug tracker focused on actionable bug reports
and feature requests instead of support questions.

* ci(docker): publish multi-arch image to GHCR alongside Docker Hub

Mirrors every released sencho and sencho-mesh image to ghcr.io with the
same tags, signing, and supply-chain attestations as the Docker Hub copy.
Same content; pull from whichever registry your environment prefers.

- Adds packages:write to both publish jobs so the auto-provisioned
  GITHUB_TOKEN can push to ghcr.io/studio-saelix/sencho and -mesh.
- Adds a second docker/login-action step authenticating to ghcr.io. The
  Docker Hub login still resolves credentials from the production env.
- docker/metadata-action now lists both image references; one buildx push
  attaches the manifest to both registries in a single round trip.
- Cosign keyless signing already loops over $TAGS, so adding the GHCR
  reference is enough to sign both digests.
- The cosign attest step now loops over both registry paths so SBOM (CDX
  + SPDX) and OpenVEX attestations are resolvable from either registry.

Quickstart and image-verification docs note GHCR as an alternative pull
source with identical content.

* fix(mesh-sidecar): pin base image by digest

The main Sencho Dockerfile pins every base image by sha256 digest so a
republish of an upstream tag cannot silently shift content into a
release. The mesh-sidecar Dockerfile pinned node:22-alpine by tag, which
is exactly the gap that pinning closes.

Resolves node:22-alpine to its current multi-arch index digest (covers
linux/amd64 and linux/arm64) and threads it through both build stages
via a single ARG so a future digest roll only edits one line. The inline
comment documents the resolution command.

The sidecar holds an outbound websocket and has no inbound HTTP
listener, so adding a HEALTHCHECK is intentionally out of scope: a
process-liveness probe would be tautological with Docker's restart
policy. The Dockerfile now records that decision so it does not get
reopened on every audit.

* ci(docker): use repository_owner for GHCR login username

github.actor varies by event source (the maintainer who merged the
release PR on tag pushes, github-actions[bot] on bot-driven runs, the
dispatcher on workflow_dispatch). The username field is metadata only;
GITHUB_TOKEN is what authenticates against GHCR. github.repository_owner
resolves to a fixed value (studio-saelix) on every event and matches
GitHub's own example workflows for GHCR push.

* chore(repo): tighten commit subject-case rule

Disabling subject-case entirely allowed accidental ALL-CAPS or
PascalCase subjects through. Restrict to "never upper-case or
pascal-case" instead, which preserves the lowercase / kebab-case
norm of this repo and lets sentence-case subjects (used sparingly
on main) keep passing.
2026-05-02 00:49:49 -04:00

114 lines
4.0 KiB
Plaintext

---
title: Verifying Published Images
description: How to verify signatures, provenance, SBOMs, and CVE triage for Sencho Docker images.
---
Every Sencho release image is published to two registries with identical content: Docker Hub (`saelix/sencho`) and GitHub Container Registry (`ghcr.io/studio-saelix/sencho`). Every image is signed and carries verifiable supply-chain artifacts on both registries. The `cosign verify` examples below use the Docker Hub path; substitute `ghcr.io/studio-saelix/sencho:<tag>` to verify the GHCR copy.
## Prerequisites
Install the following tools once:
```bash
# cosign (Sigstore)
brew install cosign # macOS
# or: https://github.com/sigstore/cosign/releases
# syft (optional, for inspecting SBOM content)
brew install syft
# or: https://github.com/anchore/syft/releases
# Trivy (optional, for local vulnerability scan with VEX)
brew install trivy
# or: https://github.com/aquasecurity/trivy/releases
```
## Verify the image signature
Every published tag is signed with cosign keyless signing via GitHub Actions OIDC. No private key is stored anywhere; the signing identity is bound to the GitHub Actions workflow that published the image.
```bash
cosign verify saelix/sencho:<tag> \
--certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
```
A successful verification prints the verified payload and exits 0. The Rekor transparency log entry is included in the output; you can also query it directly:
```bash
cosign tree saelix/sencho:<tag>
```
## Verify SLSA provenance
BuildKit produces an SLSA v1 provenance attestation during every multi-arch build. Retrieve and verify it:
```bash
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
```
## Retrieve the CycloneDX SBOM
```bash
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
```
Alternatively, download `sbom.cdx.json` directly from the GitHub Release assets for the matching version.
## Retrieve the SPDX SBOM
```bash
cosign verify-attestation \
--type spdxjson \
--certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
```
`sbom.spdx.json` is also available as a GitHub Release asset.
## Retrieve the VEX document
The OpenVEX document explains Sencho's triage decisions for any CVEs that appear in vendored dependencies but are not reachable via Sencho's usage of those dependencies:
```bash
cosign verify-attestation \
--type openvex \
--certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
saelix/sencho:<tag> | jq -r '.payload' | base64 -d | jq
```
`sencho.openvex.json` is also available as a GitHub Release asset and at `security/vex/sencho.openvex.json` in the source repository.
## Run a local vulnerability scan with VEX applied
To reproduce the same scan that gates every release:
```bash
# Download the VEX document
curl -Lo sencho.openvex.json \
https://github.com/studio-saelix/sencho/releases/latest/download/sencho.openvex.json
# Scan with VEX applied (exit 1 if any unresolved HIGH/CRITICAL CVE)
trivy image \
--vex sencho.openvex.json \
--severity HIGH,CRITICAL \
--exit-code 1 \
saelix/sencho:<tag>
```
A clean result means no unresolved HIGH or CRITICAL CVEs. CVEs listed as `not_affected` in the VEX document are suppressed with their justification visible in the VEX file.
<Note>
Trivy 0.36.0 or later is required for `--vex` flag support.
</Note>