Supply chain + cross-compile: provenance from day one
Stages 1.7 + 1.9 + the v2.0 hardening — sha256, cosign keyless, SLSA L3, eight cross-compile targets, compiler-binary reproducibility.
The structural-safety post (stages 1.4, 1.5, 2.1) covered the inside of a fastC program. This post covers the outside: where the code comes from (stage 1.7), what target it runs on (stage 1.9), and how you verify that the compiler binary you downloaded is the one we built (the v1.0.x reproducibility work, N5 in the close-out sprint).
The 2025-26 wave of supply-chain incidents against language ecosystems is the context that made this work load-bearing. Typosquats with malicious build.rs. Maintainer account takeovers. CI-action compromises. Backdoors in compression libraries that took a decade to land. Each incident worked because somewhere in the chain there was a step that ran code, or fetched without verification, or trusted a centralized choke point. fastC’s stance is to remove all three.
We will not claim this is a complete solution. It is the structural answer at the language level. It does not protect you against a maintainer who reviewed a malicious patch and merged it. It does not protect you against a dependency that is correct today and turns malicious in a future version you also approved. What it does is make every step from “you wrote fastc.toml” to “you ran the binary” auditable.
Stage 1.7 — fastc.lock with sha256, cosign keyless, SLSA L3
The package manifest is declarative TOML:
[package]
name = "my-app"
version = "0.1.0"
[deps]
fastc-http = { url = "https://github.com/Skelf-Research/fastc-http", rev = "abc123def456..." }
fastc-json = { url = "https://github.com/Skelf-Research/fastc-json", rev = "789abc012def..." }
The first time you run fastc build, the compiler resolves every dep, fetches the git object at the specified rev, computes its sha256, and writes a fastc.lock next to the manifest:
version = 1
[[deps]]
name = "fastc-http"
url = "https://github.com/Skelf-Research/fastc-http"
rev = "abc123def456789..."
sha256 = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
[[deps]]
name = "fastc-json"
url = "https://github.com/Skelf-Research/fastc-json"
rev = "789abc012def345..."
sha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"
Every subsequent build re-verifies the sha256 against the contents at the rev. Drift fails the build loudly. Vendored copies (in vendor/, checked into your repo) are verified against the lock too.
The cosign keyless signing happens on the publishing side. The fastc-core packages are built and tagged in GitHub Actions; the cosign action signs the release artifacts using the GitHub OIDC token, with no long-lived keys in the repo. The signature and the bundle are published as assets on the GitHub release. SLSA L3 provenance is attached via slsa-framework/slsa-github-generator, which produces an attestation that records the source repository, the commit, the build instructions, and the builder identity.
The verification recipe on the consumer side:
$ cosign verify-blob \
--certificate-identity-regexp '^https://github\.com/Skelf-Research/.+/\.github/workflows/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--signature fastc-http-0.1.0.tar.gz.sig \
--bundle fastc-http-0.1.0.tar.gz.bundle \
fastc-http-0.1.0.tar.gz
$ slsa-verifier verify-artifact \
--provenance-path fastc-http-0.1.0.intoto.jsonl \
--source-uri github.com/Skelf-Research/fastc-http \
--source-tag v0.1.0 \
fastc-http-0.1.0.tar.gz
fastc audit automates this. It walks fastc.lock, downloads the cosign bundle and SLSA attestation for each dep, verifies both, and fails on any mismatch.
Why “no central registry” is structural
The deeper claim is that the architecture has no choke point a compromise of which would compromise every fastC build. fastc.dev is a search frontend over GitHub — it scrapes public repos that declare themselves as fastC packages and indexes them, but it does not host artifacts. If fastc.dev were taken over tomorrow, the only thing the attacker could do is mis-rank search results. They could not replace a published artifact. They could not retroactively change what a rev + sha256 resolves to.
This is the architectural answer to the 2024 crates.io account-takeover threats, the 2017 npm event-stream takeover, the 2025 tj-actions/changed-files compromise. Each of those worked because the central service had write authority. fastC has no central service with write authority. The git URL + sha256 + cosign signature chain is verifiable end-to-end from the consumer side without trusting any intermediate.
The honest caveat is that this is a more demanding setup for package authors. A maintainer who wants to publish a fastC package writes a GitHub Action that builds, signs, and tags; the consumer points at the git URL. There is no “publish to a website with a token” path. We think this trade is worth it for the use case. We acknowledge that it raises the bar for casual contributions.
Stage 1.9 — cross-compile through zig cc
fastC emits portable C11. That sentence is the architectural claim that earns the cross-compile story. Every C cross-compiler in the world is, in principle, a fastC cross-compiler. We default to zig cc because it is the best out-of-the-box C cross-compiler we know of — it ships the sysroots, the libc options, the linker configurations, and the multi-target presets that the GCC and clang cross-toolchains require you to assemble manually.
The eight pre-wired targets:
$ fastc build --target=aarch64-linux-musl
$ fastc build --target=x86_64-linux-musl
$ fastc build --target=aarch64-linux-gnu
$ fastc build --target=x86_64-linux-gnu
$ fastc build --target=aarch64-apple-darwin
$ fastc build --target=x86_64-apple-darwin
$ fastc build --target=wasm32-wasi
$ fastc build --target=riscv64-linux-musl
One brew install zig (or the equivalent on Linux) and all eight work. The wasm32-wasi target is the one we expect to get the most use out of in the agent-runtime sandboxing case — a fastC binary running in a WASI runtime is a strong sandbox boundary on top of the structural capability story.
The escape hatch is --cc-override=<path>. Teams running proprietary toolchains (vendor-specific embedded SDKs, internal hardened compilers) point fastC at their C compiler and the rest of the pipeline still works. The output is still C11; the cross-compiler is the only thing that swaps out.
The strategic claim worth being explicit about: fastC is not betting on zig cc specifically. We are betting on portable C11 as the output. If a better cross-compiler shows up tomorrow, we adopt it. If zig cc stops being maintained, the override flag is already there for users to swap. The thing that does not move is the emit target.
Compiler-binary reproducibility (N5)
The v1.0.x sprint closed the last link in the chain: the compiler binary itself. Pre-N5, the fastc-linux-x86_64 you downloaded from a GitHub release was probably the binary we built from the tagged commit, but you had to trust the GitHub Actions runner and the build pipeline. Post-N5, you can rebuild from the same tag and produce a byte-identical binary, on your own hardware.
Two pieces made this work:
SOURCE_DATE_EPOCH from the tag’s author timestamp. The release workflow sets SOURCE_DATE_EPOCH to the Unix timestamp of the tag’s author date. Every embedded timestamp in the binary (build IDs, debug timestamps, archive member mtimes) flows from this single value. Two builds from the same tag use the same epoch and produce the same timestamps.
RUSTFLAGS=--remap-path-prefix in the release workflow. The path of the build directory is rewritten in the binary’s debug info to a stable string (/fastc-build). Without this, the binary embeds /home/runner/work/fastc/fastc/... on CI and /Users/me/projects/fastc/... on a developer rebuild, and the two diverge in their .debug_info sections.
The verification recipe:
$ git clone https://github.com/Skelf-Research/fastc
$ cd fastc && git checkout v1.0.3
$ SOURCE_DATE_EPOCH=$(git log -1 --format=%at v1.0.3) \
RUSTFLAGS="--remap-path-prefix $(pwd)=/fastc-build" \
cargo build --release
$ sha256sum target/release/fastc
$ # compare against the sha256 in the GitHub release asset list
The hashes match. We publish the expected sha256 in the release notes for every tagged version.
What this means for agent-generated code
The agent suggests use json_safe (a typosquat of json). The human reviews the PR. The PR diff shows the new entry in fastc.toml. The human sees a name they do not recognize, asks the agent to justify it, gets no justification, removes the entry. The structural piece is that the dep change had to land in a diff the human reviewed. It could not happen silently through cargo update or a transitive bump. It could not run code at fetch time. It could not exfiltrate .env files through build.rs.
The verification piece is that even if the human approved the entry, the build verifies the sha256 against the published value, verifies the cosign signature against the GitHub OIDC identity, and verifies the SLSA L3 attestation against the source repository. The “this binary came from this source” chain is end-to-end auditable.
This is the structural answer to the 2025-26 supply-chain incidents in language ecosystems. It is not a complete answer — a malicious package author who controls a real repo with valid signatures still ships valid signatures on malicious code. What the structural answer does is make every step of the chain a step a reviewer can audit, with no silent runs, no central choke points, and no trust assumptions that depend on a service we do not control.
The next post is the agent-tooling layer — fastc fix, fastc context, fastc diff, the unified diagnostic envelope, and the MCP surface that makes all of the above queryable from inside the agent loop.
Comments? Issues? Disagreements? Open an issue at github.com/Skelf-Research/fastc/issues.