Zum Inhalt springen

Code Signing

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

This guide covers how to sign your Wails applications for macOS, Windows, and Linux. Wails v3 provides built-in CLI tools for code signing, notarization, and PGP key management.

  • macOS - Sign and notarize your macOS applications
  • Windows - Sign your Windows executables and packages
  • Linux - Sign DEB and RPM packages with PGP keys

This matrix shows what you can sign from each source platform:

Target FormatFrom WindowsFrom macOSFrom Linux
Windows EXE/MSI
macOS .app bundle
macOS notarization
Linux DEB
Linux RPM

Wails automatically selects the best available signing backend:

PlatformNative BackendCross-Platform Backend
Windowssigntool.exe (Windows SDK)Built-in
macOScodesign (Xcode)Not available
LinuxN/ABuilt-in

When running on the native platform, Wails uses the native tools for maximum compatibility. When cross-compiling, it uses the built-in signing support.

The easiest way to configure signing is using the interactive setup wizard:

Terminal window
wails3 setup signing

This command:

  • Walks you through configuring signing credentials for each platform
  • On macOS, lists available Developer ID certificates from your keychain
  • For Linux, can generate a new PGP key if you don’t have one
  • Stores passwords securely in your system keychain (not in Taskfiles)
  • Updates the vars section in each platform’s Taskfile with non-sensitive config

To configure only specific platforms:

Terminal window
wails3 setup signing --platform darwin
wails3 setup signing --platform windows --platform linux

Alternatively, you can manually edit the platform-specific Taskfiles. Edit the vars section at the top of each file:

Edit build/darwin/Taskfile.yml:

vars:
SIGN_IDENTITY: "Developer ID Application: Your Company (TEAMID)"
KEYCHAIN_PROFILE: "my-notarize-profile"
# ENTITLEMENTS: "build/darwin/entitlements.plist"

Then run:

Terminal window
wails3 task darwin:sign # Sign only
wails3 task darwin:sign:notarize # Sign and notarize

You can also inspect signing state from the system directly:

Terminal window
# List available macOS code-signing identities
security find-identity -v -p codesigning
# List PGP keys (Linux package signing)
gpg --list-keys

For interactive setup of every platform’s signing configuration, use the wizard:

Terminal window
wails3 setup signing
  • Apple Developer Account ($99/year)
  • Developer ID Application certificate
  • Xcode Command Line Tools installed

Check available signing identities:

Terminal window
security find-identity -v -p codesigning

Output:

Found 2 signing identities:
Developer ID Application: Your Company (ABCD1234) [valid]
Hash: ABC123DEF456...
Apple Development: [email protected] (XYZ789) [valid]
Hash: DEF789ABC123...

Edit build/darwin/Taskfile.yml and set the signing variables:

vars:
SIGN_IDENTITY: "Developer ID Application: Your Company (TEAMID)"
KEYCHAIN_PROFILE: "my-notarize-profile"
ENTITLEMENTS: "build/darwin/entitlements.plist"
VariableRequiredDescription
SIGN_IDENTITYYesYour Developer ID (e.g., “Developer ID Application: Your Company (TEAMID)“)
KEYCHAIN_PROFILEFor notarizationKeychain profile name with stored credentials
ENTITLEMENTSNoPath to entitlements file

Then run:

Terminal window
wails3 task darwin:sign # Build, package, and sign
wails3 task darwin:sign:notarize # Build, package, sign, and notarize

Entitlements control what capabilities your app has access to. Wails apps typically need different entitlements for development vs production:

  • Development: Requires JIT, unsigned memory, and debugging entitlements
  • Production: Minimal entitlements (just network access)

Use the interactive setup wizard to generate both files:

Terminal window
wails3 setup entitlements

This creates:

  • build/darwin/entitlements.dev.plist - For development builds
  • build/darwin/entitlements.plist - For production/signed builds

Presets available:

PresetDescription
DevelopmentJIT, unsigned memory, debugging, network
ProductionNetwork only (minimal, most secure)
BothCreates both dev and production files (recommended)
App StoreSandbox enabled with network and file access
CustomChoose individual entitlements

Then set ENTITLEMENTS in your Taskfile vars to point to the appropriate file.

Apple requires all distributed apps to be notarized.

  1. Store your credentials in the keychain (one-time setup). Either run wails3 setup signing (it prompts for these values and calls notarytool under the hood) or call notarytool directly:

    Terminal window
    xcrun notarytool store-credentials "my-notarize-profile" \
    --apple-id "[email protected]" \
    --team-id "ABCD1234" \
    --password "app-specific-password"
  2. Set KEYCHAIN_PROFILE in your Taskfile to match the profile name above.

  3. Sign and notarize your app:

    Terminal window
    wails3 task darwin:sign:notarize
  4. Verify notarization:

    Terminal window
    spctl --assess --verbose=2 bin/MyApp.app
  • Code signing certificate (from DigiCert, Sectigo, etc.)
  • For native signing: Windows SDK installed (for signtool.exe)
  • For cross-platform: Just the certificate file

Edit build/windows/Taskfile.yml and set the signing variables:

vars:
SIGN_CERTIFICATE: "path/to/certificate.pfx"
# Or use thumbprint instead:
# SIGN_THUMBPRINT: "certificate-thumbprint"
# TIMESTAMP_SERVER: "http://timestamp.digicert.com"
VariableRequiredDescription
SIGN_CERTIFICATEOne of thesePath to .pfx/.p12 certificate file
SIGN_THUMBPRINTOne of theseCertificate thumbprint in Windows cert store
TIMESTAMP_SERVERNoTimestamp server URL (default: http://timestamp.digicert.com)

Then run:

Terminal window
wails3 task windows:sign # Build and sign executable
wails3 task windows:sign:installer # Build and sign NSIS installer

Windows executables can be signed from any platform. The same Taskfile configuration and commands work on macOS and Linux.

FormatExtensionNotes
Executables.exeStandard PE signing
Installers.msiWindows Installer packages
App Packages.msix, .appxModern Windows apps

Linux packages (DEB and RPM) are signed using PGP/GPG keys. Unlike Windows and macOS code signing, Linux package signing proves the package came from a trusted source rather than that the code is trusted by the OS.

  • PGP key pair (can be generated with Wails)

If you don’t have a PGP key, generate one with gpg (the wails3 setup signing wizard offers to do this for you and asks the same questions):

Terminal window
# Interactive — the wizard will prompt for name, email, key size and expiry.
gpg --full-generate-key
# Export the key pair to ASCII-armoured files for the Taskfile to consume.
gpg --armor --export-secret-keys "[email protected]" > signing-key.asc
gpg --armor --export "[email protected]" > signing-key.pub.asc

Recommended settings: RSA 4096-bit, 1-year expiry, protected with a strong password.

Edit build/linux/Taskfile.yml and set the signing variables:

vars:
PGP_KEY: "path/to/signing-key.asc"
# SIGN_ROLE: "builder" # Options: origin, maint, archive, builder
VariableRequiredDescription
PGP_KEYYesPath to PGP private key file
SIGN_ROLENoDEB signing role (default: builder)

Then run:

Terminal window
wails3 task linux:sign:deb # Build and sign DEB package
wails3 task linux:sign:rpm # Build and sign RPM package
wails3 task linux:sign:packages # Build and sign all packages

For DEB packages, you can specify the signing role via SIGN_ROLE:

  • origin: Signature from the package origin
  • maint: Signature from the package maintainer
  • archive: Signature from the archive maintainer
  • builder: Signature from the package builder (default)

Linux packages can be signed from any platform. The same Taskfile configuration and commands work on Windows and macOS.

Terminal window
gpg --show-keys signing-key.asc

Output:

pub rsa4096 2024-01-15 [SC] [expires: 2025-01-15]
1234 5678 90AB CDEF 1234 5678 90AB CDEF 1234 5678
uid Your Name <[email protected]>
Terminal window
# Verify DEB signature
dpkg-sig --verify myapp_1.0.0_amd64.deb
# Verify RPM signature
rpm --checksig myapp-1.0.0.x86_64.rpm

Users need your public key to verify packages:

Terminal window
# Export public key for distribution
gpg --armor --export "[email protected]" > myapp-signing.pub.asc
# Users can import it:
# For DEB (apt):
sudo apt-key add myapp-signing.pub.asc
# Or for modern apt:
sudo cp myapp-signing.pub.asc /etc/apt/trusted.gpg.d/
# For RPM:
sudo rpm --import myapp-signing.pub.asc

In CI environments, passwords are provided via environment variables instead of the system keychain:

Environment VariableDescription
WAILS_WINDOWS_CERT_PASSWORDWindows certificate password
WAILS_PGP_PASSWORDPGP key password for Linux packages

You can also pass Taskfile variables directly:

Terminal window
wails3 task darwin:sign SIGN_IDENTITY="$SIGN_IDENTITY" KEYCHAIN_PROFILE="$KEYCHAIN_PROFILE"
name: Build and Sign macOS
on:
push:
tags: ['v*']
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Install Wails
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
- name: Import Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }}
CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security create-keychain -p "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain
- name: Store Notarization Credentials
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
xcrun notarytool store-credentials "notarize-profile" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD"
- name: Build, Sign, and Notarize
env:
SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
run: |
wails3 task darwin:sign:notarize \
SIGN_IDENTITY="$SIGN_IDENTITY" \
KEYCHAIN_PROFILE="notarize-profile"
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: MyApp-macOS
path: bin/*.app
name: Build and Sign Windows
on:
push:
tags: ['v*']
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Install Wails
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
- name: Import Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE }}
run: |
$certBytes = [Convert]::FromBase64String($env:CERTIFICATE_BASE64)
[IO.File]::WriteAllBytes("certificate.pfx", $certBytes)
- name: Build and Sign
env:
WAILS_WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
wails3 task windows:sign SIGN_CERTIFICATE=certificate.pfx
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: MyApp-Windows
path: bin/*.exe

Sign Windows and Linux packages from a single Linux runner:

name: Build and Sign (Cross-Platform)
on:
push:
tags: ['v*']
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Install Wails
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
- name: Install Build Dependencies
run: |
sudo apt-get update
sudo apt-get install -y nsis rpm
# Import certificates
- name: Import Certificates
env:
WINDOWS_CERT_BASE64: ${{ secrets.WINDOWS_CERTIFICATE }}
PGP_KEY_BASE64: ${{ secrets.PGP_PRIVATE_KEY }}
run: |
echo "$WINDOWS_CERT_BASE64" | base64 -d > certificate.pfx
echo "$PGP_KEY_BASE64" | base64 -d > signing-key.asc
# Build and sign Windows
- name: Build and Sign Windows
env:
WAILS_WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
wails3 task windows:sign SIGN_CERTIFICATE=certificate.pfx
# Build and sign Linux packages
- name: Build and Sign Linux Packages
env:
WAILS_PGP_PASSWORD: ${{ secrets.PGP_PASSWORD }}
run: |
wails3 task linux:sign:packages PGP_KEY=signing-key.asc
# Cleanup secrets
- name: Cleanup
if: always()
run: rm -f certificate.pfx signing-key.asc
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: signed-binaries
path: |
bin/*.exe
bin/*.deb
bin/*.rpm

Interactive wizard to configure signing for your project.

Terminal window
wails3 setup signing [flags]
Flags:
--platform Platform to configure (darwin, windows, linux). Repeatable.
If omitted, auto-detects which platforms to configure from the build directory.

The wizard guides you through:

  • macOS: Selecting a Developer ID certificate, configuring notarization credentials (calls xcrun notarytool store-credentials).
  • Windows: Choosing between certificate file or thumbprint, setting password and timestamp server.
  • Linux: Using an existing PGP key or generating a new one (calls gpg), configuring signing role.

Interactive wizard to configure macOS entitlements.

Terminal window
wails3 setup entitlements [flags]
Flags:
--output Output path for entitlements.plist (default: build/darwin/entitlements.plist)

Presets:

  • Development: Creates entitlements.dev.plist with JIT, debugging, and network
  • Production: Creates entitlements.plist with minimal entitlements
  • Both: Creates both files (recommended)
  • App Store: Creates sandboxed entitlements for Mac App Store
  • Custom: Choose individual entitlements and target file

Sign binaries and packages for the current or specified platform. This is a wrapper that calls the appropriate platform-specific signing task.

Terminal window
wails3 sign
wails3 sign GOOS=darwin
wails3 sign GOOS=windows
wails3 sign GOOS=linux

This runs the corresponding <platform>:sign task which uses the signing configuration from your Taskfile.

Low-level command to sign a specific file directly. Used internally by the Taskfiles.

Terminal window
wails3 tool sign [flags]

Common Flags:

FlagDescription
--inputPath to the file to sign
--outputOutput path (optional, defaults to in-place)
--verboseEnable verbose output

Windows/macOS Flags:

FlagDescription
--certificatePath to PKCS#12 (.pfx/.p12) certificate
--passwordCertificate password
--timestampTimestamp server URL

macOS-Specific Flags:

FlagDescription
--identitySigning identity (use ’-’ for ad-hoc)
--entitlementsPath to entitlements plist
--hardened-runtimeEnable hardened runtime (default: true)
--notarizeSubmit for notarization
--keychain-profileKeychain profile for notarization

Windows-Specific Flags:

FlagDescription
--thumbprintCertificate thumbprint in Windows store

Linux-Specific Flags:

FlagDescription
--pgp-keyPath to PGP private key
--pgp-passwordPGP key password
--roleDEB signing role (origin/maint/archive/builder)

There is no wails3 signing command in v3. To inspect signing state, use the native tooling directly:

TaskCommand
List macOS code-signing identitiessecurity find-identity -v -p codesigning
Store notarization credentialsxcrun notarytool store-credentials "<profile>" --apple-id … --team-id … --password …
Inspect a PGP key filegpg --show-keys <key.asc>
Generate a PGP key pairgpg --full-generate-key
Export a public keygpg --armor --export <email>

“No Developer ID certificate found”

  • Ensure your certificate is installed in the Keychain
  • Check it hasn’t expired with security find-identity -v -p codesigning
  • Make sure you have a “Developer ID Application” certificate (not just “Apple Development”)

“Notarization failed”

  • Check the notarization log: xcrun notarytool log <submission-id> --keychain-profile <profile>
  • Ensure hardened runtime is enabled
  • Verify your app doesn’t include unsigned binaries

“Codesign failed”

  • Make sure the keychain is unlocked: security unlock-keychain
  • Check file permissions on the app bundle

“Certificate not found”

  • Verify the certificate path is correct
  • Check the certificate password
  • Ensure the certificate is valid (not expired or revoked)

“Timestamp server error”

  • Try a different timestamp server:
    • http://timestamp.digicert.com
    • http://timestamp.sectigo.com
    • http://timestamp.comodoca.com

“Invalid PGP key”

  • Ensure the key file is in ASCII-armored format
  • Check the key hasn’t expired with gpg --show-keys <key.asc>
  • Verify the password is correct

“Signature verification failed”

  • Ensure the public key is properly imported
  • Check that the package wasn’t modified after signing