This guide covers code signing for both Windows and macOS.
Cloud-based signing via SSL.com eSigner + Jsign. No USB token or YubiKey needed.
On SSL.com, buy a Code Signing Certificate.
During checkout, you MUST enable eSigner cloud signing.
Look for this checkbox and make sure it is checked:
Enable certificate issuance for remote signing, team sharing,
and presign malware scanning
If this box is NOT checked:
- eSigner credentials will NOT be created
- you will NOT get a Credential ID
- the certificate cannot be fixed after issuance
After purchase, SSL.com will perform standard validation (identity, email, phone – depends on certificate type).
Once validation is complete and the certificate is issued, you can continue.
After issuance:
- Go to SSL.com Dashboard
- Open Orders
- Find your Code Signing order
- Click Download
- Scroll to SIGNING CREDENTIALS
You should see:
- Credential ID (UUID)
- signing credential status = enabled
Example:
SSL_COM_CREDENTIAL_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
In the same Order page, find the eSigner PIN / QR code section.
Steps:
- Set a 4-digit PIN
- Choose OTP App
- Generate QR code
- Save BOTH:
- the QR code
- the Base32 secret shown as text
The Base32 secret looks like:
UFZ3SGLG1KVDIDE3KWJEKVAGG24S5PWDQMQTPBAAJDSC566KKFGB
This value is your TOTP secret.
Generate a code and compare it against Google Authenticator at the same moment.
On macOS:
brew install oath-toolkit
oathtool --totp -b <BASE32_SECRET>
On Windows (with uv):
uv run --with pyotp python -c "import pyotp; print(pyotp.TOTP('<BASE32_SECRET>').now())"If the 6-digit code matches Google Authenticator, your TOTP setup is correct.
Tauri calls a custom sign command for every binary it wants to sign (main exe, sidecars, NSIS plugins, installer, etc.).
We use scripts/sign_windows.py as that command. It whitelists
only the files worth signing and skips everything else, keeping us
under the eSigner monthly signing limit.
What gets signed:
vibe.exe(main app)vibe-*setup*.exe(NSIS installer)
What gets skipped:
- Sidecars (ffmpeg, sona, sona-diarize)
- NSIS plugins and resource DLLs
By default the script runs in dry run mode. Set SIGN_ENABLED=true
to actually sign.
In desktop/src-tauri/tauri.windows.conf.json:
"signCommand": {
"cmd": "python",
"args": ["scripts/sign_windows.py", "%1"]
}Tauri passes %1 as the file path. The script checks the filename
against the whitelist and either signs or skips.
choco install jsign
choco install temurin
SIGN_ENABLED=true
SSL_COM_CREDENTIAL_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SSL_COM_USERNAME=your@email.com
SSL_COM_PASSWORD=your_ssl_com_password
SSL_COM_TOTP_SECRET=BASE32_SECRET
uv run scripts/sign_windows.py path\to\file.exe
signtool verify /pa /v path\to\file.exeLocal signing via codesign + notarization via notarytool.
No per-sign limits. Tauri handles everything automatically.
A $99/year Apple Developer Program membership at https://developer.apple.com/programs/
- Open Xcode → Settings → Accounts → Manage Certificates
- Create a Developer ID Application certificate
- Export it as a
.p12file with a password
CI needs the .p12 as a base64 string:
uv run python -c "import base64, pathlib; print(base64.b64encode(pathlib.Path('cert.p12').read_bytes()).decode())"Copy the output into your APPLE_CERTIFICATE secret.
security find-identity -v -p codesigningLook for Developer ID Application: Your Name (TEAMID).
Go to https://appleid.apple.com/account/manage → Sign-In and Security → App-Specific Passwords → Generate.
Set these in CI:
APPLE_CERTIFICATE=<base64 from step 3>
APPLE_CERTIFICATE_PASSWORD=<.p12 password>
APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)"
APPLE_ID=<your Apple ID email>
APPLE_PASSWORD=<app-specific password from step 5>
APPLE_TEAM_ID=<your 10-char team ID>
Find your team ID:
uv run python -c "import subprocess, re; o=subprocess.check_output(['security','find-identity','-v','-p','codesigning']).decode(); print(set(re.findall(r'\(([A-Z0-9]{10})\)', o)))"Tauri automatically:
- Signs all binaries with
codesignusing your Developer ID cert - Notarizes the
.dmgwithnotarytool - Staples the notarization ticket
No custom scripts or whitelists needed. All binaries must be signed for notarization to pass.
codesign --verify --deep --strict /path/to/Vibe.app
spctl --assess --type exec /path/to/Vibe.appIn .github/workflows/release.yml:
- Windows: check
sign-windowsinput to enable signing. The workflow setsSIGN_ENABLED=trueand passesSSL_COM_*secrets. - macOS: set
APPLE_*secrets in repo settings. Tauri signs automatically.
Add all secrets under repo Settings → Secrets and variables → Actions.
| Windows | macOS | |
|---|---|---|
| Provider | SSL.com eSigner | Apple Developer |
| Cost | ~$60-90/yr + eSigner tier | $99/yr |
| Signing limit | Yes (depends on tier) | No |
| Signing tool | Jsign (via sign_windows.py) |
codesign (built-in) |
| Notarization | N/A | notarytool (automatic) |
| Sign everything? | No, whitelist only | Yes, required |
| Custom script | scripts/sign_windows.py |
None needed |