This guide covers code signing, installer configuration, and distribution of the Quip Node Manager Tauri app on Windows.
Windows code signing requires a certificate from a trusted Certificate
Authority (CA). Since 2023 every CA must keep the private key in hardware or
in a cloud HSM, so no certificate type ships as a .pfx file.
| Type | Validates | SmartScreen | Cost per year |
|---|---|---|---|
| IV (Individual) | A person's identity | Reputation builds over time | $100--200 |
| OV (Organization) | The legal entity | Reputation builds over time | $200--400 |
| EV (Extended) | The legal entity, stricter | Trusted from the first download | $300--600 |
The project uses an SSL.com eSigner certificate. eSigner is SSL.com's cloud HSM. The key never leaves SSL.com, and signing is an authenticated API call. A document signing certificate cannot sign code, because Windows rejects its key usage.
Recommendation: use an EV certificate. The immediate SmartScreen trust is worth the higher cost for user-facing software.
The sign-windows-x86_64 job in .gitlab-ci.yml signs
dist/quip-node-manager-windows-x86_64.exe after build-windows-x86_64
produces it. The job runs on a Linux runner. eSigner's CodeSignTool hashes
the file locally and sends only the hash to SSL.com. The returned Authenticode
signature is then written into the file. No Windows API is involved.
Two scripts do the work:
| Script | Purpose |
|---|---|
scripts/sign-windows.sh |
Downloads a sha256-pinned CodeSignTool and signs the file in place |
scripts/verify-windows-signing.sh |
Reads the signed file with osslsigncode and fails the job unless the digest, timestamp, and signer match |
The job runs on every tag. On branches it is a manual job, but the protected variables make it fail there. Test a change to the scripts on a release candidate tag instead.
All five variables are set, hold the production values, and are protected.
GitLab withholds a protected variable from an unprotected branch, so the
manual branch job now fails with ESIGNER_ENV is not set. Signing runs on a
v* tag, which is protected.
Four of the five are masked. GitLab refuses to mask ESIGNER_PASSWORD,
because masking accepts only values of at least 8 characters, on one line,
from the base64 alphabet plus @:.~+=/-. Nothing in the pipeline prints the
password, and sign-windows.sh never enables set -x. A password that meets
those rules can be masked after a rotation.
| Variable | Description |
|---|---|
ESIGNER_ENV |
TEST for the SSL.com sandbox, PROD for the real service |
ESIGNER_USERNAME |
SSL.com account username |
ESIGNER_PASSWORD |
SSL.com account password |
ESIGNER_CREDENTIAL_ID |
eSigner credential ID of the code signing certificate |
ESIGNER_TOTP_SECRET |
TOTP secret shown once during eSigner enrollment |
GitLab refuses to mask a value that contains characters outside its masking
alphabet, such as #. A password with such characters must be stored
unmasked, or changed.
ESIGNER_ENV=TEST points CodeSignTool at SSL.com's sandbox. SSL.com
publishes a demo account for it, so the pipeline can run before the real
certificate exists:
| Variable | Sandbox value |
|---|---|
ESIGNER_USERNAME |
esigner_demo |
ESIGNER_PASSWORD |
esignerDemo#1 |
ESIGNER_CREDENTIAL_ID |
Run CodeSignTool get_credential_ids with the two values above |
ESIGNER_TOTP_SECRET |
RDXYgV9qju+6/7GnMf1vCbKexXVJmUVr+86Wq/8aIGg= |
A sandbox signature chains to "SSL.com EV Root Certification Authority RSA R2
- Development". No Windows machine trusts it, so a sandbox-signed binary
shows the same SmartScreen dialog as an unsigned one. The sign job refuses to
run with
TESTon a release tag. Tags that contain-rcare allowed, so a release candidate can exercise the path.
The sandbox API endpoint chains to a TLS root that the Java runtime does not
carry. sign-windows.sh adds that root to a private trust store for the
sandbox only. Production endpoints need nothing.
- Enroll the issued certificate in eSigner (next section).
- Set
ESIGNER_USERNAME,ESIGNER_PASSWORD,ESIGNER_CREDENTIAL_ID, andESIGNER_TOTP_SECRETto the real values. - Set
EXPECTED_CN_PRODinscripts/verify-windows-signing.shto the common name on the certificate. Windows shows this string as the publisher. The verify step fails while it is empty, so a production run cannot pass by accident. - Set
ESIGNER_ENVtoPROD.
Enrollment is possible only after SSL.com validates the order and issues the certificate. In the SSL.com portal:
- Open Orders, find the code signing order, and click details.
- Scroll to eSigner Cloud Signing Enrollment.
- Under Second factor authentication, choose OTP APP.
- Enter a 4 digit PIN and store it.
- Click create OTP and issue certificate.
- A QR code appears with its secret as text. Copy the text into
ESIGNER_TOTP_SECRETnow. A page reload hides it, and recovering it means a reset in the portal.
The credential ID is on the same order page, or from:
CodeSignTool get_credential_ids -username=<user> -password=<password>An empty list means the certificate is not yet enrolled, whatever the portal shows.
Get-AuthenticodeSignature .\quip-node-manager-windows-x86_64.exe | Format-List
signtool verify /pa /v .\quip-node-manager-windows-x86_64.exeStatus must read Valid. SignerCertificate.Subject names
HADAMARD GATE INCORPORATED, the organization on the issued certificate.
CI checks that same name through verify-windows-signing.sh. A sandbox-signed file reports
UnknownError or NotTrusted. That is expected.
scripts/install.ps1 makes this check on every install and refuses a file
whose status is not Valid or whose signer is not that name. A sandbox-signed
release candidate does not install through the script for that reason.
Windows SmartScreen protects users from unknown software:
| Certificate Type | SmartScreen Behavior |
|---|---|
| EV | Immediate trust. No warnings from the first download. |
| OV / IV | Warnings shown until enough users download and run the software. Reputation builds over weeks to months. |
| None | "Windows protected your PC" blocking dialog. Most users will not proceed. |
CI ships the bare executable, built with tauri build --no-bundle. Tauri can
also produce an NSIS installer, a single
.exe setup file with an uninstaller, Start menu shortcuts, and Add/Remove
Programs registration. A build with bundling writes it to:
src-tauri/target/release/bundle/nsis/Quip Node Manager Setup.exe
To ship the installer, add it to the sign job. CodeSignTool batch_sign
signs a directory of files with one OTP.
For distribution through the Microsoft Store:
-
Register as a Microsoft developer ($19 one-time for individuals, $99 for organizations) at developer.microsoft.com.
-
Tauri supports MSIX packaging -- see the Tauri MSIX documentation.
-
MSIX packages use a separate signing flow managed by the Microsoft Store submission process. No external code signing certificate is required for Store-distributed builds.
The Microsoft Store provides automatic updates, sandboxing, and visibility to Windows users who prefer installing from the Store.