Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 6 additions & 3 deletions .github/workflows/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,11 @@ jobs:
run: |
echo "Building for ${GOOS}/${GOARCH}..."
if [ "${GOOS}" = "windows" ]; then export EXT=".exe"; else export EXT=""; fi
go build -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o dist/${GOOS}-${GOARCH}/sc${EXT} ./cmd/sc
tar -czf .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}.tar.gz -C dist/${GOOS}-${GOARCH} sc${EXT}
go build -trimpath -buildvcs=false -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o dist/${GOOS}-${GOARCH}/sc${EXT} ./cmd/sc
# Deterministic tarball: fixed entry order, zeroed mtime/owner, a
# forced mode (independent of the builder umask) and gzip -9 -n
# (no embedded timestamp) so a rebuild reproduces it bit-for-bit.
tar --sort=name --mtime='@0' --owner=0 --group=0 --numeric-owner --mode='u=rwx,go=rx' -cf - -C dist/${GOOS}-${GOARCH} sc${EXT} | gzip -9 -n > .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}.tar.gz
cp .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}.tar.gz .sc/stacks/dist/bundle/sc-${GOOS}-${GOARCH}-v${VERSION}.tar.gz
# Phase 2: per-tarball SHA-256, CycloneDX SBOM, cosign keyless signature
# (self-contained bundle), SLSA provenance. Soft-fail per step — the tarball
Expand Down Expand Up @@ -314,7 +317,7 @@ jobs:
VERSION: ${{ needs.prepare.outputs.version }}
CGO_ENABLED: "0"
run: |
go build -a -installsuffix cgo -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o ${{ matrix.output }} ./cmd/${{ matrix.cmd }}
go build -trimpath -buildvcs=false -a -installsuffix cgo -ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=${VERSION}" -o ${{ matrix.output }} ./cmd/${{ matrix.cmd }}
- name: upload ${{ matrix.target }} binary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down
19 changes: 18 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ linters:
- unused
- staticcheck
- gci
- goheader

linters-settings:
goheader:
# Enforces the per-file SPDX + copyright header on every source file
# (OpenSSF Gold: license_per_file + copyright_per_file). Year-less by design
# so it is set-once and never needs an annual bump. Generated mocks are
# excluded below (issues.exclude-rules) — they carry the generator header.
template: |-
SPDX-License-Identifier: MIT
Copyright (c) Simple Container
gci:
sections:
- standard # Standard section: captures all standard packages.
Expand All @@ -31,4 +40,12 @@ linters-settings:
revive:
rules:
- name: var-naming # usually this is not critical
disabled: true
disabled: true

issues:
exclude-rules:
# Auto-generated mocks carry the mockery "Code generated" header and are
# regenerated by `go generate`; they get no SPDX/copyright header.
- path: mocks/
linters:
- goheader
3 changes: 3 additions & 0 deletions cmd/cloud-helpers/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package main

import (
Expand Down
3 changes: 3 additions & 0 deletions cmd/generate-embeddings/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package main

import (
Expand Down
9 changes: 7 additions & 2 deletions cmd/github-actions/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package main

import (
Expand Down Expand Up @@ -339,7 +342,8 @@ func runGitCommandWithAuth(ctx context.Context, log logger.Logger, dir string, a

// Set up environment with authentication for submodule URLs
// This converts https://github.com/ URLs to use the token
env := append(os.Environ(),
env := append(
os.Environ(),
"GIT_TERMINAL_PROMPT=0",
"GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no",
"GIT_ASKPASS=echo",
Expand Down Expand Up @@ -412,7 +416,8 @@ func runGitCommand(ctx context.Context, log logger.Logger, dir string, args []st

cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = dir
cmd.Env = append(os.Environ(),
cmd.Env = append(
os.Environ(),
"GIT_TERMINAL_PROMPT=0",
"GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no",
"GIT_ASKPASS=echo", // Prevent any password prompts
Expand Down
3 changes: 3 additions & 0 deletions cmd/sc/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package main

import (
Expand Down
3 changes: 3 additions & 0 deletions cmd/schema-gen/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package main

import (
Expand Down
77 changes: 77 additions & 0 deletions docs/REPRODUCIBLE-BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Reproducible builds

The release binaries (`sc`, `cloud-helpers`, `github-actions`) and the
published `sc-<os>-<arch>.tar.gz` archives are built deterministically:
rebuilding the **same source at the same version** on a clean machine
yields a **bit-for-bit identical** binary and archive. This lets anyone
independently rebuild a release and confirm the published artifact (and
its `cosign` / SLSA digest) was built from the published source.

## What makes the build reproducible

| Input | How it is pinned | Where |
|---|---|---|
| **Go toolchain** | exact patch version pinned by the `go` directive; `GOTOOLCHAIN` resolves it deterministically | [`go.mod`](../go.mod) |
| **Filesystem paths** | `-trimpath` strips the build machine's `$GOPATH`/module paths from the binary | release jobs in [`.github/workflows/push.yaml`](../.github/workflows/push.yaml) (`build sc`, `build <target>`) and the [`welder.yaml`](../welder.yaml) `build` / `build-cloud-helpers` / `build-github-actions` tasks |
| **VCS stamping** | `-buildvcs=false` — Go would otherwise embed `vcs.revision`/`vcs.time`/`vcs.modified` into the binary (which `-trimpath` does **not** strip), so a rebuild from a source tarball without `.git`, or from a tree with any local modification, would diverge | every release build (same `go build` invocations as above) |
| **C toolchain variance** | `CGO_ENABLED=0` — pure-Go static build, no host libc/linker dependency | every release build: `push.yaml` `build sc` + `build <target>`; `welder.yaml` `build`, `build-cloud-helpers`, `build-github-actions` |
| **Embedded version string** | `-ldflags "-s -w -X .../internal/build.Version=$VERSION"` — symbol table stripped, version supplied explicitly (not derived from the wall-clock) | `push.yaml` + `welder.yaml` `default.build.args.ld-flags` |
| **Archive metadata** | `tar --sort=name --mtime='@0' --owner=0 --group=0 --numeric-owner --mode='u=rwx,go=rx' \| gzip -9 -n` — fixed entry order, zeroed timestamps/ownership, a forced `0755` mode (independent of the builder's umask), no gzip timestamp | `push.yaml` `build sc`, `welder.yaml` `build` |
| **AI-assistant embeddings** | committed to the repo (`pkg/assistant/embeddings/vectors/`) and consumed via `go:embed`; the build never calls an external LLM | repo tree |
| **Dependencies** | module graph pinned by `go.sum`; Docker base images + GitHub Actions are SHA-pinned | `go.sum`, Dockerfiles, `.github/` |

The only intentional build input is `VERSION` — the **bare** CalVer
value (e.g. `2026.6.20`, no `v` prefix) that the release embeds into
`internal/build.Version`. Two builds of the same commit with the same
`VERSION` are identical; the version is passed in explicitly rather than
read from the clock or the build host, so it introduces no nondeterminism.

## Verifying reproducibility

Use the **bare** CalVer the release embedded (the `v` prefix only ever
appears in the `-v<version>` copy of the tarball filename, never in
`build.Version`). Build twice from a clean checkout and compare:

```sh
git checkout <released-tag>
VERSION=2026.6.20 welder run build -a os=linux -a arch=amd64 # bare version
sha256sum .sc/stacks/dist/bundle/sc-linux-amd64.tar.gz

git clean -fdx
VERSION=2026.6.20 welder run build -a os=linux -a arch=amd64
sha256sum .sc/stacks/dist/bundle/sc-linux-amd64.tar.gz # identical digest
```

Or just the binary, directly with the Go toolchain (matches the flags the
release uses):

```sh
CGO_ENABLED=0 go build -trimpath -buildvcs=false \
-ldflags "-s -w -X=github.com/simple-container-com/api/internal/build.Version=2026.6.20" \
-o sc ./cmd/sc
sha256sum sc
```

`-buildvcs=false` is required: without it `go build` either embeds the
local VCS state (diverging across checkouts) or, when run from a source
tarball with no `.git`, fails with `error obtaining VCS status`.

To confirm a **published** release matches the source, rebuild the tag as
above and compare the rebuilt `sc-<os>-<arch>.tar.gz` digest (or the inner
binary's digest) against the release's `cosign verify-blob --bundle` /
SLSA provenance subject digest — see [`SECURITY.md`](SECURITY.md) →
"Verifying tarballs".

## Known boundaries

- The **Docker images** wrap the reproducible binaries; image-layer
reproducibility additionally depends on the SHA-pinned base images and
the deterministic `welder docker build` context.
- Reproducibility is asserted for the release toolchain (Linux/macOS,
amd64/arm64); each cross-build target uses the same flags per
`GOOS`/`GOARCH`.
- The deterministic archive step relies on GNU `tar` options
(`--sort`, `--mtime`, `--owner`/`--group`, `--mode`). Release archives
are produced on the Linux CI runners (GNU tar); to reproduce the
archive byte-for-byte on macOS use GNU tar (`gtar`) rather than the
bundled BSD `tar`. The binary itself reproduces with either.
3 changes: 3 additions & 0 deletions docs/embedfs.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package docs

import "embed"
Expand Down
3 changes: 3 additions & 0 deletions internal/build/build_info.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package build

var Version string = "latest"
3 changes: 3 additions & 0 deletions pkg/api/alerts.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

type (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/api_compose_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/api_polish_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/client.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/client_more_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/cloud_helpers.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import "github.com/pkg/errors"
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/config.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/config_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/copy.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/copy_more_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/copy_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/dns.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import sdk "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/ignore.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package git

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/opts.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package git

type Option func(r *repo) error
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/path_util/path_util.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package path_util

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/path_util/path_util_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package path_util

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/repo.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package git

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/repo_lifecycle_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package git

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/git/repo_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package git

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/logger/color/color.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package color

import "github.com/fatih/color"
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/logger/color/color_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package color

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/logger/logger.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package logger

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/logger/logger_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package logger

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/mapping.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/mapping_read_more_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/models.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) Simple Container

package api

import (
Expand Down
Loading
Loading