From ae10d2cc0e89aa84038b7786c75debdb3a6249ee Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Fri, 19 Jun 2026 09:30:37 +0300 Subject: [PATCH 1/3] =?UTF-8?q?chore:=20update=20repo=20references=20TeoSl?= =?UTF-8?q?ayer/pilotprotocol=20=E2=86=92=20pilot-protocol/pilotprotocol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a68d1b8..0866e41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout web4 (sibling protocol repo) uses: actions/checkout@v6 with: - repository: TeoSlayer/pilotprotocol + repository: pilot-protocol/pilotprotocol path: web4 - uses: actions/setup-go@v6 From ba6756bfddf66158b0c85d0e012b6cdd8592b91d Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Sun, 21 Jun 2026 19:39:01 +0300 Subject: [PATCH 2/3] fix: correct IsTrusted docs, clarify sig verification comment, add keygen script Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- data.go | 10 ++++++---- scripts/gen-signing-key.sh | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100755 scripts/gen-signing-key.sh diff --git a/README.md b/README.md index ef75a21..228e907 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ rt.Register(trustedagents.NewService()) | File | What it does | |---|---| -| `data.go` | Embedded JSON list. `Load`, `All`, `IsTrusted(nodeID) → (description, ok)`, `SetForTest`. | +| `data.go` | Embedded JSON list. `Load`, `All`, `IsTrusted(nodeID) → (hostname, ok)`, `SetForTest`. | | `runtime.go` | `Run(ctx)` — periodic fetcher over HTTPS to `raw.githubusercontent.com`. | | `service.go` | `*Service` — `coreapi.Service` adapter (`Name/Order/Start/Stop` + `IsTrusted`). Build tag `!no_trustedagents`. | | `service_disabled.go` | Stub `*Service` when build tag `no_trustedagents` is set. | diff --git a/data.go b/data.go index 578487b..cb852ed 100644 --- a/data.go +++ b/data.go @@ -8,8 +8,10 @@ // // The list is plain JSON in this directory, embedded at build time and // refreshed hourly from raw.githubusercontent.com by -// plugins/trustedagents.Run. Authenticity comes from HTTPS to GitHub -// plus repo write access — there is no separate signature check. +// plugins/trustedagents.Run. Authenticity is handled in two tiers: +// unsigned lists are accepted over TLS with a warning (backward-compatible); +// signed lists require embeddedPubKey to be configured and undergo full +// Ed25519 verification via VerifyAndStripSig before the payload is trusted. // // Adding an agent: edit trusted-agents.json, commit. Daemons in the // field pick it up within ~1h. Brand-new daemons get the embedded copy @@ -120,8 +122,8 @@ func All() []Agent { // skipped (backward-compatible). Set this to the real public key once the // signing infrastructure is in place. // -// TODO: inject via -ldflags at build time so the same binary can verify -// different lists (dev/staging/prod). +// To inject: go build -ldflags "-X github.com/pilot-protocol/trustedagents.embeddedPubKeyHex=<64-hex-chars>" +// Generate keypair: scripts/gen-signing-key.sh var embeddedPubKey = ed25519.PublicKey{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/scripts/gen-signing-key.sh b/scripts/gen-signing-key.sh new file mode 100755 index 0000000..7dd34ca --- /dev/null +++ b/scripts/gen-signing-key.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# gen-signing-key.sh — generate an ed25519 keypair for trusted-agents list signing. +# +# Prints: +# PRIVATE KEY (hex) — keep secret; used by the signing step in CI. +# PUBLIC KEY (hex) — embed via: +# go build -ldflags "-X github.com/pilot-protocol/trustedagents.embeddedPubKeyHex=<64-hex-chars>" +# +# Usage: bash scripts/gen-signing-key.sh +set -euo pipefail + +go run - <<'EOF' +package main + +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/hex" + "fmt" + "os" +) + +func main() { + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + fmt.Printf("PRIVATE KEY (hex, keep secret): %s\n", hex.EncodeToString(priv)) + fmt.Printf("PUBLIC KEY (hex, for embedding): %s\n", hex.EncodeToString(pub)) +} +EOF From f1e4490997983aafd6ac7bbd22d71c777c41286e Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Tue, 23 Jun 2026 16:21:21 +0300 Subject: [PATCH 3/3] harden gen-signing-key.sh: write private key to 0600 file, print only public material The script previously echoed the Ed25519 private key (hex) to stdout, exposing it to the terminal, shell history, and CI logs. Write the private key to a 0600 file instead (default ./signing-key.hex, or an operator-supplied path) and print only public material: the public key in hex (for the embeddedPubKeyHex ldflags) and in std-base64 (for the per-entry public_key pin in trusted-agents.json). Refuse to overwrite an existing key file, create the file with a tight umask before writing any secret, and gitignore signing-key.hex and *.key so the private key can never be committed. --- .gitignore | 6 +++ scripts/gen-signing-key.sh | 85 ++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 163c855..21a746e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ coverage.out *.bak *~ *.tmp + +# Ed25519 signing private keys produced by scripts/gen-signing-key.sh — +# must never be committed. +signing-key.hex +*.signing-key.hex +*.key diff --git a/scripts/gen-signing-key.sh b/scripts/gen-signing-key.sh index 7dd34ca..84c7989 100755 --- a/scripts/gen-signing-key.sh +++ b/scripts/gen-signing-key.sh @@ -1,32 +1,101 @@ #!/usr/bin/env bash # gen-signing-key.sh — generate an ed25519 keypair for trusted-agents list signing. # -# Prints: -# PRIVATE KEY (hex) — keep secret; used by the signing step in CI. -# PUBLIC KEY (hex) — embed via: -# go build -ldflags "-X github.com/pilot-protocol/trustedagents.embeddedPubKeyHex=<64-hex-chars>" +# The PRIVATE key is written to a file with 0600 permissions and is NEVER +# printed to the terminal or any log. Only PUBLIC material is printed to +# stdout: +# - PUBLIC KEY (hex) — embed via: +# go build -ldflags "-X github.com/pilot-protocol/trustedagents.embeddedPubKeyHex=<64-hex-chars>" +# - PUBLIC KEY (base64) — the value for the optional per-entry `public_key` +# pin field in trusted-agents.json (the keyring expects std-base64). # -# Usage: bash scripts/gen-signing-key.sh +# Usage: +# bash scripts/gen-signing-key.sh [output-file] +# +# output-file defaults to ./signing-key.hex. KEEP THIS FILE SECRET — it is +# the signing private key. Do NOT commit it (it is gitignored). set -euo pipefail -go run - <<'EOF' +OUT_FILE="${1:-signing-key.hex}" + +if [[ -e "$OUT_FILE" ]]; then + echo "error: refusing to overwrite existing file: $OUT_FILE" >&2 + echo " remove it or pass a different output path." >&2 + exit 1 +fi + +# Resolve the output path to an absolute one before we cd into a temp dir +# to build the helper, so a relative OUT_FILE still lands where the operator +# invoked the script. +case "$OUT_FILE" in + /*) ABS_OUT="$OUT_FILE" ;; + *) ABS_OUT="$PWD/$OUT_FILE" ;; +esac + +# Create the file with 0600 perms BEFORE writing any secret into it, so the +# private key is never world/group readable, not even for an instant. +umask 077 +: > "$ABS_OUT" +chmod 600 "$ABS_OUT" + +# `go run -` (stdin) is not portable across Go versions, so materialise the +# helper in a private temp dir and run it from there. +WORKDIR="$(mktemp -d)" +trap 'rm -rf "$WORKDIR"' EXIT + +cat > "$WORKDIR/main.go" <<'EOF' package main import ( "crypto/ed25519" "crypto/rand" + "encoding/base64" "encoding/hex" "fmt" "os" ) func main() { + privFile := os.Getenv("PRIV_FILE") + if privFile == "" { + fmt.Fprintln(os.Stderr, "error: PRIV_FILE not set") + os.Exit(1) + } + pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } - fmt.Printf("PRIVATE KEY (hex, keep secret): %s\n", hex.EncodeToString(priv)) - fmt.Printf("PUBLIC KEY (hex, for embedding): %s\n", hex.EncodeToString(pub)) + + // Write the private key (hex) to the pre-created 0600 file only. + f, err := os.OpenFile(privFile, os.O_WRONLY|os.O_TRUNC, 0o600) + if err != nil { + fmt.Fprintf(os.Stderr, "error: open private key file: %v\n", err) + os.Exit(1) + } + if _, err := fmt.Fprintln(f, hex.EncodeToString(priv)); err != nil { + _ = f.Close() + fmt.Fprintf(os.Stderr, "error: write private key: %v\n", err) + os.Exit(1) + } + if err := f.Close(); err != nil { + fmt.Fprintf(os.Stderr, "error: close private key file: %v\n", err) + os.Exit(1) + } + + // Print ONLY public material to stdout. + fmt.Printf("PRIVATE KEY written to: %s (mode 0600 — keep secret, do NOT commit)\n", privFile) + fmt.Printf("PUBLIC KEY (hex, for -ldflags embeddedPubKeyHex): %s\n", hex.EncodeToString(pub)) + fmt.Printf("PUBLIC KEY (base64, for trusted-agents.json public_key pin): %s\n", base64.StdEncoding.EncodeToString(pub)) } EOF + +( + cd "$WORKDIR" + GOWORK=off GO111MODULE=off PRIV_FILE="$ABS_OUT" go run main.go +) + +echo +echo "Done. The private key lives in: $ABS_OUT" +echo "Treat it as a secret: do not commit it, do not paste it into logs or chat."