netbirdio/netbird · error

sign artifact: %w

Error message

sign artifact: %w

What it means

reposign.SignData refused to sign. Two concrete refusals exist in reposign/artifact.go:267-281: an empty artifact ("artifact length must be positive, got 0") and an expired artifact key ("artifact key expired at ...", checked against the ExpiresAt embedded in the key). A BLAKE2s hash write error is theoretically possible but practically never occurs.

Source

Thrown at client/cmd/signer/artifactsign.go:146

			return fmt.Errorf("read private key file: %w", err)
		}
	} else {
		return fmt.Errorf("artifact private key must be provided via %s environment variable or --artifact-key-file flag", envArtifactPrivateKey)
	}

	privateKey, err := reposign.ParseArtifactKey(privKeyPEM)
	if err != nil {
		return fmt.Errorf("failed to parse artifact private key: %w", err)
	}

	artifactData, err := os.ReadFile(artifactFile)
	if err != nil {
		return fmt.Errorf("read artifact file: %w", err)
	}

	signature, err := reposign.SignData(privateKey, artifactData)
	if err != nil {
		return fmt.Errorf("sign artifact: %w", err)
	}

	sigFile := artifactFile + ".sig"
	if err := os.WriteFile(artifactFile+".sig", signature, 0o600); err != nil {
		return fmt.Errorf("write signature file (%s): %w", sigFile, err)
	}

	cmd.Printf("✅ Artifact signed successfully.\n")
	cmd.Printf("Signature file: %s\n", sigFile)
	return nil
}

func handleVerifyArtifact(cmd *cobra.Command, pubKeyFile, artifactFile, signatureFile string) error {
	cmd.Println("🔍 Verifying artifact...")

	// Read artifact public key
	pubKeyPEM, err := os.ReadFile(pubKeyFile)
	if err != nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Check the artifact is non-empty: stat -c %s <file>; rebuild if it is 0 bytes
  2. Inspect the key's ExpiresAt (printed by generate-artifact-key / visible in the PEM JSON); if expired, generate a fresh artifact key and publish its root-signed public bundle
  3. Verify system time with timedatectl or ntp status before signing
  4. If the key was regenerated, make sure downstream verifiers fetch the new root-signed artifact-key-pub.pem

Example fix

// before
signature, err := reposign.SignData(privateKey, artifactData)
if err != nil {
    return fmt.Errorf("sign artifact: %w", err)
}

// after
if len(artifactData) == 0 {
    return fmt.Errorf("artifact %s is empty", artifactFile)
}
if !privateKey.Metadata.ExpiresAt.IsZero() && time.Now().After(privateKey.Metadata.ExpiresAt) {
    return fmt.Errorf("artifact key expired at %v", privateKey.Metadata.ExpiresAt)
}
signature, err := reposign.SignData(privateKey, artifactData)
if err != nil {
    return fmt.Errorf("sign artifact: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(artifactFile)
if err != nil || info.Size() == 0 {
    log.Fatalf("artifact %s must exist and be non-empty", artifactFile)
}

Prevention

When it happens

Trigger: --artifact-file points at a 0-byte file (touched placeholder or failed build); the artifact key's embedded ExpiresAt is in the past; significant clock skew on the signing host.

Common situations: Pipeline signs a placeholder file created by touch; reusing an artifact key past the expiration window chosen at generate-artifact-key time; host clock drift.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/7f775caab1e16109. Report an issue: GitHub.