feat(build): native add-on per-arch artifact signer (agent-release ed25519) (#3425) #3474

Merged
mfreeman451 merged 1 commit from feat/native-addon-artifact-signer into staging 2026-05-31 19:55:51 +00:00
Owner

What

The per-arch artifact signer — the agent-facing core of build-signing §2.2, and the one piece native add-ons need that the WASM pipeline has no analogue for.

The agent verifies a pushed-artifact with verifyAddonArtifactSignatureed25519.Verify(releasePublicKey, rawArtifactBytes, signature), reusing the existing agent release trust root (SERVICERADAR_AGENT_RELEASE_PUBLIC_KEY, per the 3425 "share the existing signing key" decision). This adds the producer: a stdlib-only tool that signs each per-arch tarball (or bare binary) with the agent release key and emits exactly the raw ed25519 signature the agent accepts — the value the control plane records as the AddonAssignment's artifact_signature.

(Distinct from the two WASM-shared signatures: cosign over the OCI bundle, and the PLUGIN_UPLOAD_SIGNING ed25519 upload-signature over the bundle. This is the third, per-arch, agent-verified one.)

build/native_addons/addon_artifact_signature_tool.go

  • sign --artifact <f> [--out]hex(ed25519.Sign(<release-priv>, bytes))
  • verify --artifact <f> --signature <hex|base64|@file>ed25519.Verify with the release public key, decoding hex + the 4 base64 variants exactly like the agent's decodeReleaseSignature
  • public-key → hex public key (derives from the private key when the public env is unset)
  • private key accepts a 64-byte ed25519 key or a 32-byte seed; keys come only from env (SERVICERADAR_AGENT_RELEASE_PRIVATE_KEY / _PUBLIC_KEY) — never committed
  • BUILD.bazel: go_binary + go_test (no external deps)

Verification

  • Local, against a real assembler-produced netprobe tarball: hex + base64 verify pass; a tampered artifact and a wrong key are both rejected; @file signature input works.
  • Unit tests assert the round trip matches the agent's exact ed25519.Verify-over-raw-bytes contract, seed/full-key acceptance, public-key derivation, key-unset failure, and every accepted encoding. go test ./build/native_addons/ passes; gofmt + buildifier clean; bazel query //build/native_addons:all loads with the new targets.

Next (build-signing remainder)

The orchestration on top: sign/index/verify wrapper scripts, the cosign OCI bundle signing + the PLUGIN_UPLOAD_SIGNING upload-signature (reuse scripts/cosign_common.sh + upload_signature_tool + the WASM rails), serviceradar-native-addon-index.json, and the .forgejo/workflows/native-addons.yml lane. Production signing runs in CI where the release/upload/cosign keys live.

🤖 Generated with Claude Code

## What The **per-arch artifact signer** — the agent-facing core of build-signing §2.2, and the one piece native add-ons need that the WASM pipeline has no analogue for. The agent verifies a pushed-artifact with `verifyAddonArtifactSignature` → `ed25519.Verify(releasePublicKey, rawArtifactBytes, signature)`, reusing the existing **agent release** trust root (`SERVICERADAR_AGENT_RELEASE_PUBLIC_KEY`, per the 3425 "share the existing signing key" decision). This adds the **producer**: a stdlib-only tool that signs each per-arch tarball (or bare binary) with the agent release key and emits exactly the raw ed25519 signature the agent accepts — the value the control plane records as the `AddonAssignment`'s `artifact_signature`. (Distinct from the two WASM-shared signatures: cosign over the OCI bundle, and the `PLUGIN_UPLOAD_SIGNING` ed25519 upload-signature over the bundle. This is the third, per-arch, agent-verified one.) ## `build/native_addons/addon_artifact_signature_tool.go` - `sign --artifact <f> [--out]` → `hex(ed25519.Sign(<release-priv>, bytes))` - `verify --artifact <f> --signature <hex|base64|@file>` → `ed25519.Verify` with the release public key, decoding hex + the 4 base64 variants **exactly like the agent's `decodeReleaseSignature`** - `public-key` → hex public key (derives from the private key when the public env is unset) - private key accepts a 64-byte ed25519 key **or** a 32-byte seed; keys come only from env (`SERVICERADAR_AGENT_RELEASE_PRIVATE_KEY` / `_PUBLIC_KEY`) — never committed - `BUILD.bazel`: `go_binary` + `go_test` (no external deps) ## Verification - **Local, against a real assembler-produced netprobe tarball:** hex + base64 verify pass; a tampered artifact and a wrong key are both rejected; `@file` signature input works. - **Unit tests** assert the round trip matches the agent's exact `ed25519.Verify`-over-raw-bytes contract, seed/full-key acceptance, public-key derivation, key-unset failure, and every accepted encoding. `go test ./build/native_addons/` passes; `gofmt` + `buildifier` clean; `bazel query //build/native_addons:all` loads with the new targets. ## Next (build-signing remainder) The orchestration on top: `sign`/`index`/`verify` wrapper scripts, the cosign OCI bundle signing + the `PLUGIN_UPLOAD_SIGNING` upload-signature (reuse `scripts/cosign_common.sh` + `upload_signature_tool` + the WASM rails), `serviceradar-native-addon-index.json`, and the `.forgejo/workflows/native-addons.yml` lane. Production signing runs in CI where the release/upload/cosign keys live. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(build): native add-on per-arch artifact signer (agent-release ed25519) (#3425)
Some checks failed
Secret Scan / gitleaks (pull_request) Successful in 45s
lint / lint (push) Successful in 1m44s
lint / lint (pull_request) Successful in 1m45s
Golang Tests / test-go (push) Successful in 2m7s
CI / build (pull_request) Failing after 3m56s
40a08af9c8
build-signing §2.2 core. The agent verifies a pushed-artifact with
verifyAddonArtifactSignature -> ed25519.Verify(releasePublicKey, rawArtifactBytes,
signature), reusing the existing agent release trust root. This adds the producer
side: a stdlib-only tool that signs each per-arch tarball (or bare binary) with the
agent release key, emitting exactly the raw ed25519 signature (hex, which the
agent's decodeReleaseSignature accepts) that the control plane records as the
AddonAssignment's artifact_signature.

build/native_addons/addon_artifact_signature_tool.go:
- `sign --artifact <f> [--out]` -> hex(ed25519.Sign(SERVICERADAR_AGENT_RELEASE_PRIVATE_KEY, bytes))
- `verify --artifact <f> --signature <hex|base64|@file>` -> ed25519.Verify with
  SERVICERADAR_AGENT_RELEASE_PUBLIC_KEY, decoding hex + the 4 base64 variants
  exactly like the agent.
- `public-key` -> hex public key (derives from the private key when unset).
- private key accepts a 64-byte ed25519 key or a 32-byte seed; keys come only from
  env (never committed).
- BUILD.bazel: go_binary + go_test (stdlib-only).

Verified locally: signed a real assembler-produced netprobe tarball; hex + base64
verify pass; tampered artifact and wrong key are rejected; `@file` signature works.
Unit tests assert the round trip matches the agent's exact ed25519.Verify-over-raw-
bytes contract, seed/full-key acceptance, public-key derivation, and all accepted
encodings. `go test` + gofmt + buildifier clean; bazel package loads.

Next (build-signing remainder): the sign/index/verify wrapper scripts + the cosign
OCI bundle signing + the ed25519 upload-signature + the .forgejo native-addons lane
(reuse the WASM rails; production signing runs in CI where the release/upload/cosign
keys live).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mfreeman451 left a comment

lgtm

lgtm
mfreeman451 deleted branch feat/native-addon-artifact-signer 2026-05-31 19:55:56 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!3474
No description provided.