HakobuHakobu

Windows Signing & Metadata

Add PE metadata and Authenticode signatures to Windows executables.

Overview

Hakobu can inject version metadata into Windows PE executables and sign them with Authenticode. Signed executables display your publisher name in Windows SmartScreen and UAC dialogs instead of "Unknown Publisher."

PE Metadata Injection

Windows executables have a VERSIONINFO resource that stores product name, version, company, description, and icon. Hakobu injects these using resedit, a pure-JS PE resource editor that works cross-platform -- you can set Windows metadata from macOS or Linux.

Configure via package.json

package.json
{
  "hakobu": {
    "metadata": {
      "productName": "My Application",
      "fileDescription": "My App does amazing things",
      "companyName": "ACME Corporation",
      "legalCopyright": "Copyright 2026 ACME Corporation",
      "fileVersion": "1.2.3",
      "icon": "assets/app.ico"
    }
  }
}

Configure via CLI flags

hakobu . --target node24-win-x64 --output dist/my-app.exe \
  --product-name "My Application" \
  --file-description "My App does amazing things" \
  --company-name "ACME Corporation" \
  --file-version 1.2.3 \
  --icon assets/app.ico

CLI flags override package.json configuration.

Supported Fields

FieldCLI Flagpackage.json KeyPE Resource
Product name--product-namemetadata.productNameProductName
File description--file-descriptionmetadata.fileDescriptionFileDescription
Company name--company-namemetadata.companyNameCompanyName
Copyright(config only)metadata.legalCopyrightLegalCopyright
File version--file-versionmetadata.fileVersionFileVersion
Product version--product-versionmetadata.productVersionProductVersion
Icon--iconmetadata.iconApplication icon

Version Format

Versions are dot-separated with up to 4 parts: MAJOR.MINOR.PATCH.BUILD. Missing parts default to 0:

1.2.3     → 1.2.3.0
1.0       → 1.0.0.0
2.1.0.42  → 2.1.0.42

Icon Requirements

The icon must be a Windows .ico file. PNG, JPEG, and SVG are not supported directly. Multi-resolution .ico files are recommended (16x16, 32x32, 48x48, 256x256).

# Convert with ImageMagick
magick convert icon.png -resize 256x256 icon.ico

PE metadata is Windows-only. macOS uses .app bundles with Info.plist, and Linux ELF executables have no standard metadata format. These flags are ignored for non-Windows targets.

Authenticode Signing

Signing Tiers

TierWhat HappensWhen to Use
Unsigned (default)No signing -- SmartScreen may warn "Unknown Publisher"Local development, internal use
Self-signedSigned with a self-issued certificate -- SmartScreen still warnsTesting the signing pipeline
Authenticode (OV/EV)Signed with a trusted CA certificatePublic distribution

OV certificates build SmartScreen reputation over time. EV certificates are trusted immediately with no reputation period.

Quick Start

Obtain a code signing certificate

You need an Authenticode certificate from a trusted CA in PKCS#12 format (.pfx or .p12):

TypeSmartScreen BehaviorCost
OV (Organization Validation)Builds reputation over time~$200--400/year
EV (Extended Validation)Immediate trust~$400--700/year

For open source projects, SignPath Foundation offers free EV signing for qualifying projects.

Export your certificate as PFX

If you need to export from an existing certificate:

PowerShell
$cert = Get-ChildItem Cert:\CurrentUser\My\THUMBPRINT
$password = ConvertTo-SecureString -String "export-password" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath cert.pfx -Password $password

Or from a CA-issued .crt + .key:

openssl pkcs12 -export -out cert.pfx \
  -inkey private.key -in certificate.crt -certfile ca-chain.crt

Install a signing tool

On Windows (GitHub Actions windows-latest): signtool.exe is pre-installed via the Windows SDK.

On macOS or Linux (cross-signing):

# macOS
brew install osslsigncode

# Ubuntu/Debian
sudo apt install osslsigncode

Both tools produce identical Authenticode signatures.

Sign the executable

Pass your certificate via the --win-cert flag or environment variable:

hakobu ./my-app --target node24-win-x64 --output ./dist/app.exe \
  --win-cert ./certs/my-cert.pfx --win-cert-password "secret"

Or use environment variables:

export HAKOBU_WIN_CERT="./certs/my-cert.pfx"
export HAKOBU_WIN_CERT_PASSWORD="secret"
hakobu ./my-app --target node24-win-x64 --output ./dist/app.exe

Pipeline Order

Hakobu handles the ordering of metadata injection and signing automatically:

package → inject PE metadata → sign (Authenticode)

Metadata must be injected before signing because the signature covers the entire file including resources. You can combine both in a single command:

hakobu . --target node24-win-x64 \
  --product-name "My App" --file-version 1.0.0 \
  --win-cert cert.pfx --win-cert-password "secret"

Environment Variables

VariableRequiredDescription
HAKOBU_WIN_CERTYes (for signing)Path to .pfx/.p12 certificate file
HAKOBU_WIN_CERT_PASSWORDIf cert is password-protectedCertificate password
HAKOBU_WIN_TIMESTAMP_URLNoTimestamp server (default: http://timestamp.digicert.com)

CLI Flags

FlagDescription
--win-cert <path>Path to .pfx/.p12 certificate (overrides HAKOBU_WIN_CERT)
--win-cert-password <pw>Certificate password (overrides HAKOBU_WIN_CERT_PASSWORD)

Behavior When Credentials Are Absent

ScenarioBehavior
No HAKOBU_WIN_CERT and no --win-certUnsigned executable (default)
Certificate set but signing tool missingError with instructions to install signtool or osslsigncode
Certificate set but wrong passwordError from the signing tool with diagnostic message
Non-Windows target with --win-certFlag is ignored (Authenticode is Windows-only)

CI/CD Setup

Store your certificate as a base64-encoded GitHub secret:

base64 -i cert.pfx | tr -d '\n' > cert-base64.txt
# Copy contents to the WIN_CERT_BASE64 repository secret

Signing on Windows runners

.github/workflows/build.yml
- name: Decode signing certificate
  if: runner.os == 'Windows'
  run: |
    echo "${{ secrets.WIN_CERT_BASE64 }}" > cert-base64.txt
    certutil -decode cert-base64.txt cert.pfx
    del cert-base64.txt
  shell: cmd

- name: Package and sign
  if: runner.os == 'Windows'
  env:
    HAKOBU_WIN_CERT: cert.pfx
    HAKOBU_WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
  run: npx hakobu ./my-app --target node24-win-x64 --output ./dist/app.exe

- name: Clean up certificate
  if: always() && runner.os == 'Windows'
  run: del cert.pfx
  shell: cmd

Cross-signing from macOS or Linux

.github/workflows/build.yml
- name: Install osslsigncode
  run: |
    if [[ "$RUNNER_OS" == "macOS" ]]; then
      brew install osslsigncode
    else
      sudo apt-get install -y osslsigncode
    fi

- name: Decode certificate
  run: echo "${{ secrets.WIN_CERT_BASE64 }}" | base64 -d > cert.pfx

- name: Package and sign
  env:
    HAKOBU_WIN_CERT: cert.pfx
    HAKOBU_WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
  run: npx hakobu ./my-app --target node24-win-x64 --output ./dist/app.exe

- name: Clean up
  if: always()
  run: rm -f cert.pfx

Verifying a Signed Executable

PowerShell
Get-AuthenticodeSignature .\app.exe

# Or with signtool
signtool verify /pa /v .\app.exe

On macOS or Linux with osslsigncode:

osslsigncode verify -in app.exe

Troubleshooting

"SignTool Error: No certificates were found"

The .pfx file doesn't contain a valid code signing certificate, or the password is wrong. Re-export and verify:

signtool sign /f cert.pfx /p "password" /fd SHA256 test.exe

SmartScreen still shows "Unknown Publisher"

OV certificates need to build reputation over time. Sign consistently and the warning will disappear. EV certificates are trusted immediately. Ensure timestamps are included -- unsigned timestamps cause warnings after certificate expiry.

Metadata not showing in file properties

Right-click the .exe, then select Properties and open the Details tab. Ensure the target is win (metadata is Windows-only) and that fields are spelled correctly in the configuration.

On this page