{"record":{"id":"7f775caab1e16109","repo":"netbirdio/netbird","slug":"sign-artifact-w","errorCode":null,"errorMessage":"sign artifact: %w","messagePattern":"sign artifact: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/artifactsign.go","lineNumber":146,"sourceCode":"\t\t\treturn fmt.Errorf(\"read private key file: %w\", err)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"artifact private key must be provided via %s environment variable or --artifact-key-file flag\", envArtifactPrivateKey)\n\t}\n\n\tprivateKey, err := reposign.ParseArtifactKey(privKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse artifact private key: %w\", err)\n\t}\n\n\tartifactData, err := os.ReadFile(artifactFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read artifact file: %w\", err)\n\t}\n\n\tsignature, err := reposign.SignData(privateKey, artifactData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sign artifact: %w\", err)\n\t}\n\n\tsigFile := artifactFile + \".sig\"\n\tif err := os.WriteFile(artifactFile+\".sig\", signature, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"write signature file (%s): %w\", sigFile, err)\n\t}\n\n\tcmd.Printf(\"✅ Artifact signed successfully.\\n\")\n\tcmd.Printf(\"Signature file: %s\\n\", sigFile)\n\treturn nil\n}\n\nfunc handleVerifyArtifact(cmd *cobra.Command, pubKeyFile, artifactFile, signatureFile string) error {\n\tcmd.Println(\"🔍 Verifying artifact...\")\n\n\t// Read artifact public key\n\tpubKeyPEM, err := os.ReadFile(pubKeyFile)\n\tif err != nil {","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/artifactsign.go#L128-L164","documentation":"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.","triggerScenarios":"--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.","commonSituations":"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.","solutions":["Check the artifact is non-empty: stat -c %s <file>; rebuild if it is 0 bytes","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","Verify system time with timedatectl or ntp status before signing","If the key was regenerated, make sure downstream verifiers fetch the new root-signed artifact-key-pub.pem"],"exampleFix":"// before\nsignature, err := reposign.SignData(privateKey, artifactData)\nif err != nil {\n    return fmt.Errorf(\"sign artifact: %w\", err)\n}\n\n// after\nif len(artifactData) == 0 {\n    return fmt.Errorf(\"artifact %s is empty\", artifactFile)\n}\nif !privateKey.Metadata.ExpiresAt.IsZero() && time.Now().After(privateKey.Metadata.ExpiresAt) {\n    return fmt.Errorf(\"artifact key expired at %v\", privateKey.Metadata.ExpiresAt)\n}\nsignature, err := reposign.SignData(privateKey, artifactData)\nif err != nil {\n    return fmt.Errorf(\"sign artifact: %w\", err)\n}","handlingStrategy":"validation","validationCode":"info, err := os.Stat(artifactFile)\nif err != nil || info.Size() == 0 {\n    log.Fatalf(\"artifact %s must exist and be non-empty\", artifactFile)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Record each artifact key's ExpiresAt in the release runbook and rotate before it lapses","Add a pre-sign check that the file size is positive","Keep NTP enabled on signing hosts so expiry checks are not skewed"],"tags":["go","crypto","ed25519","release-signing","validation"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}