diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..c5b34b3
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,243 @@
+# Turns a pushed version tag into a finished release, built on GitHub and
+# nowhere else: tests, builds NVENCForgeGUI.exe with "wails build" and
+# publishes it as the release asset. The asset name stays NVENCForgeGUI.exe,
+# because the window's own update button looks for exactly that file.
+#
+# Two ways in:
+# - push a tag vX.Y.Z (equal to guiVersion in main.go), or
+# - Actions > Release > Run workflow with an existing tag.
+# The release text is .github/release-notes/vX.Y.Z.md if that file exists,
+# otherwise the message of the annotated tag (git tag -a).
+#
+# Dry run: tick "dry run" when starting it by hand. The exe is built and
+# checked and stays on the run page as a workflow artifact; nothing is
+# signed or published. Pull requests that change this workflow, wails.json,
+# build/windows/ or .signpath/ get a dry run by themselves.
+#
+# Code signing (SignPath Foundation) is prepared but off until the
+# repository has the variable SIGNPATH_ORGANIZATION_ID and the secret
+# SIGNPATH_API_TOKEN; see .signpath/README.md. Without them the exe is
+# released unsigned.
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: 'Existing tag to release (for example v2.2.0)'
+ required: true
+ dry_run:
+ description: 'Dry run: build and check only, sign and publish nothing'
+ type: boolean
+ default: false
+ pull_request:
+ paths:
+ - '.github/workflows/release.yml'
+ - '.signpath/**'
+ - 'wails.json'
+ - 'build/windows/**'
+
+# Publishing needs write access to the repository contents; actions: read
+# lets SignPath fetch the unsigned exe from this run.
+permissions:
+ contents: write
+ actions: read
+
+env:
+ DRY_RUN: ${{ github.event_name == 'pull_request' || inputs.dry_run == true }}
+
+jobs:
+ release:
+ runs-on: windows-latest
+ # Includes up to one hour of waiting for the signing approval.
+ timeout-minutes: 120
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.tag || github.ref }}
+ # The annotated tag message can be the release text, and it only
+ # exists in a clone that carries the tag objects.
+ fetch-depth: 0
+
+ - uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+ cache: true
+
+ # Node only runs the window checks; the interface itself has no build step.
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ # A tag that disagrees with guiVersion would publish an exe that keeps
+ # offering itself as an update. A pull request has no tag: it builds
+ # the version in main.go.
+ - name: Tag matches guiVersion
+ run: |
+ $match = Select-String -Path main.go -Pattern '^const guiVersion = "(.+)"' | Select-Object -First 1
+ if (-not $match) {
+ Write-Host "::error::Could not find guiVersion in main.go"
+ exit 1
+ }
+ $inCode = $match.Matches[0].Groups[1].Value
+ $tag = "${{ inputs.tag || github.ref_name }}"
+ if ($env:GITHUB_EVENT_NAME -eq 'pull_request') { $tag = "v$inCode" }
+ if ("v$inCode" -ne $tag) {
+ Write-Host "::error::Tag $tag does not match guiVersion $inCode in main.go"
+ exit 1
+ }
+ Write-Host "Tag $tag matches guiVersion $inCode."
+ "TAG=$tag" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
+ "VERSION=$inCode" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
+
+ - name: go test
+ run: go test ./...
+
+ - name: window checks
+ run: |
+ $checks = Get-ChildItem -Path frontend\*_check.js | Sort-Object Name
+ if (-not $checks) {
+ Write-Host "::error::No frontend\*_check.js found - nothing was checked."
+ exit 1
+ }
+ $failed = @()
+ foreach ($check in $checks) {
+ node $check.FullName
+ if ($LASTEXITCODE -ne 0) { $failed += $check.Name }
+ }
+ if ($failed) {
+ Write-Host "::error::These window checks failed: $($failed -join ', ')"
+ exit 1
+ }
+
+ # wails.json feeds the file properties (right-click > Properties >
+ # Details); SignPath only signs an exe whose properties name
+ # NVENCForgeGUI and the version of the release.
+ - name: Version into wails.json
+ run: |
+ $text = [IO.File]::ReadAllText("$PWD\wails.json")
+ $text = [regex]::Replace($text, '"productVersion": "[^"]*"', "`"productVersion`": `"$env:VERSION`"")
+ [IO.File]::WriteAllText("$PWD\wails.json", $text, [Text.UTF8Encoding]::new($false))
+
+ # The Wails CLI of the same version as in go.mod; "go run" needs no
+ # global install.
+ - name: Build
+ run: |
+ go run github.com/wailsapp/wails/v2/cmd/wails@v2.14.0 build -trimpath
+ if ($LASTEXITCODE -ne 0) { exit 1 }
+ Copy-Item build/bin/NVENCForgeGUI.exe NVENCForgeGUI.exe
+
+ - name: Check the file properties
+ run: ./.signpath/check-files.ps1 -ProductName NVENCForgeGUI -ProductVersion $env:VERSION -Path NVENCForgeGUI.exe
+
+ # --- code signing ------------------------------------------------------
+ - name: 'code signing: on or off?'
+ id: signing
+ env:
+ ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
+ HAS_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN != '' }}
+ run: |
+ $hasOrganization = -not [string]::IsNullOrWhiteSpace($env:ORGANIZATION_ID)
+ $hasToken = $env:HAS_TOKEN -eq 'true'
+ if ($env:DRY_RUN -eq 'true') {
+ $on = $false
+ Write-Host 'Dry run: nothing is signed.'
+ } elseif ($hasOrganization -ne $hasToken) {
+ Write-Host '::error::Code signing is half set up: SIGNPATH_ORGANIZATION_ID and SIGNPATH_API_TOKEN must both be set, or neither (see .signpath/README.md).'
+ exit 1
+ } else {
+ $on = $hasOrganization -and $hasToken
+ Write-Host ($(if ($on) { 'Code signing is on.' } else { 'Code signing is off: this release is unsigned.' }))
+ }
+ "enabled=$($on.ToString().ToLower())" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
+
+ - name: 'code signing: collect the exe'
+ if: steps.signing.outputs.enabled == 'true'
+ run: |
+ New-Item -ItemType Directory sign/unsigned -Force | Out-Null
+ Copy-Item NVENCForgeGUI.exe sign/unsigned/
+
+ - name: 'code signing: upload the exe'
+ id: unsigned
+ if: steps.signing.outputs.enabled == 'true'
+ uses: actions/upload-artifact@v7
+ with:
+ name: unsigned
+ path: sign/unsigned
+ if-no-files-found: error
+
+ - name: 'code signing: sign (approve the request in SignPath)'
+ if: steps.signing.outputs.enabled == 'true'
+ uses: signpath/github-action-submit-signing-request@v3
+ with:
+ api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
+ organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
+ project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG || 'NVENCForgeGUI' }}
+ signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG || 'release-signing' }}
+ artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
+ github-artifact-id: ${{ steps.unsigned.outputs.artifact-id }}
+ wait-for-completion: true
+ wait-for-completion-timeout-in-seconds: 3600
+ output-artifact-directory: sign/signed
+ parameters: |
+ version: "${{ env.VERSION }}"
+
+ - name: 'code signing: take over the signed exe'
+ if: steps.signing.outputs.enabled == 'true'
+ run: |
+ Copy-Item sign/signed/NVENCForgeGUI.exe NVENCForgeGUI.exe -Force
+ ./.signpath/check-files.ps1 -ProductName NVENCForgeGUI -ProductVersion $env:VERSION -Path NVENCForgeGUI.exe -Signed
+
+ - name: 'dry run: keep the exe on the run page'
+ if: env.DRY_RUN == 'true'
+ uses: actions/upload-artifact@v7
+ with:
+ name: release-dry-run
+ path: NVENCForgeGUI.exe
+ retention-days: 7
+
+ # gh is preinstalled on GitHub runners and signs in with the token this
+ # workflow already holds, so no personal access token is involved.
+ - name: Publish release
+ if: env.DRY_RUN != 'true'
+ env:
+ GH_TOKEN: ${{ github.token }}
+ SIGNED: ${{ steps.signing.outputs.enabled }}
+ run: |
+ $tag = $env:TAG
+ $title = "NVENCForgeGUI $tag"
+ $file = ".github/release-notes/$tag.md"
+ if (Test-Path $file) {
+ $notes = [IO.File]::ReadAllText("$PWD/$file")
+ } else {
+ $notes = (git tag -l --format='%(contents)' $tag) -join "`n"
+ }
+ if ([string]::IsNullOrWhiteSpace($notes)) {
+ Write-Host "::error::No release text: add $file or create the tag with 'git tag -a'."
+ exit 1
+ }
+ # The release page is the download page, so it links the code
+ # signing policy and says whether this exe is signed.
+ $policy = "https://github.com/$env:GITHUB_REPOSITORY/blob/main/CODE_SIGNING.md"
+ if ($env:SIGNED -eq 'true') {
+ $footer = "NVENCForgeGUI.exe is signed. Free code signing provided by [SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org). [Code signing policy]($policy)"
+ } else {
+ $footer = "NVENCForgeGUI.exe is not code signed, so Windows SmartScreen may warn. [Code signing policy]($policy)"
+ }
+ [IO.File]::WriteAllText("$PWD/release-notes.md", $notes.TrimEnd() + "`n`n---`n`n$footer`n", [Text.UTF8Encoding]::new($false))
+
+ Write-Host "Publishing $tag with NVENCForgeGUI.exe ($((Get-Item NVENCForgeGUI.exe).Length) bytes)."
+ # A rerun (a corrected workflow, a retried build) refreshes the
+ # release that is there instead of failing on the second attempt.
+ gh release view $tag *> $null
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Release $tag already exists - refreshing its notes and asset."
+ gh release edit $tag --title $title --notes-file release-notes.md --draft=false
+ gh release upload $tag NVENCForgeGUI.exe --clobber
+ } else {
+ gh release create $tag NVENCForgeGUI.exe --title $title --notes-file release-notes.md
+ }
diff --git a/.signpath/README.md b/.signpath/README.md
new file mode 100644
index 0000000..bfe450e
--- /dev/null
+++ b/.signpath/README.md
@@ -0,0 +1,65 @@
+# Code signing with SignPath: prepared, off until switched on
+
+NVENCForgeGUI has applied to the [SignPath Foundation](https://signpath.org) for free
+code signing of its Windows files. Everything for it is prepared here and in
+[`release.yml`](../.github/workflows/release.yml), but **it stays off** until it is
+switched on below. Until then every release is built and published exactly as before,
+unsigned, and its release text says so.
+
+| File | What it is |
+| ---- | ---------- |
+| [`../CODE_SIGNING.md`](../CODE_SIGNING.md) | The public code signing policy (required by SignPath Foundation): roles, what is signed, privacy |
+| [`artifact-configuration.xml`](artifact-configuration.xml) | The SignPath artifact configuration (the project's default): signs NVENCForgeGUI.exe only |
+| [`check-files.ps1`](check-files.ps1) | Run by every release build: checks product name and version of every file to be signed (SignPath enforces both), and with `-Signed` that the signatures are valid |
+
+## How the release workflow decides
+
+| Repository settings | What a release does |
+| ------------------- | ------------------- |
+| Neither `SIGNPATH_ORGANIZATION_ID` nor `SIGNPATH_API_TOKEN` set | Builds and publishes unsigned, as before |
+| Both set | Uploads the files, sends a signing request to SignPath, **waits up to one hour for the approval**, downloads the signed files, checks their signatures and publishes them |
+| Only one of them set | Stops with an error, so a half finished setup never ships unsigned files by surprise |
+
+**Dry run:** *Actions > Release > Run workflow* with "dry run" ticked builds and checks
+everything, keeps the files on the run page as a workflow artifact, never signs and
+publishes nothing. It also runs by itself for every pull request that changes the
+release workflow or this folder.
+
+## Switching it on (after SignPath Foundation said yes)
+
+1. **Two-factor authentication** on GitHub and on SignPath for everybody in the team
+ (a condition of SignPath Foundation).
+2. **In SignPath** (the Foundation sets up most of it with you):
+ - the project, slug `NVENCForgeGUI` (another slug: set the variable `SIGNPATH_PROJECT_SLUG`),
+ - the trusted build system *GitHub.com* linked to the project, and the SignPath
+ GitHub App installed on this repository (SignPath shows the link),
+ - the artifact configuration from [`artifact-configuration.xml`](artifact-configuration.xml), set as the project's default,
+ - the signing policy `release-signing` with manual approval by the approver
+ (another slug: set `SIGNPATH_SIGNING_POLICY_SLUG`),
+ - a CI user with the *Submitter* role on that policy, and an API token for it.
+3. **On GitHub:** *Settings > Secrets and variables > Actions*
+ - secret `SIGNPATH_API_TOKEN`: the API token of the CI user,
+ - variable `SIGNPATH_ORGANIZATION_ID`: the organization ID from SignPath.
+4. **Recommended:** a ruleset that protects `main` and the `v*` tags from force pushes
+ and deletion (*Settings > Rules > Rulesets*). SignPath can require this in its
+ policies.
+5. **Update the texts:** the status line at the top of
+ [`CODE_SIGNING.md`](../CODE_SIGNING.md) and the SmartScreen note in the README.
+6. **Release as usual.** Approve the signing request in SignPath within one hour (the
+ link is in the run log and in the e-mail from SignPath), then the release is published
+ with signed files.
+
+| Variable / secret | Required | Default |
+| ----------------- | -------- | ------- |
+| `SIGNPATH_API_TOKEN` (secret) | yes | |
+| `SIGNPATH_ORGANIZATION_ID` | yes | |
+| `SIGNPATH_PROJECT_SLUG` | no | `NVENCForgeGUI` |
+| `SIGNPATH_SIGNING_POLICY_SLUG` | no | `release-signing` |
+| `SIGNPATH_ARTIFACT_CONFIGURATION_SLUG` | no | the project's default artifact configuration |
+
+## Taking it out again
+
+Never setting the secret and the variable is enough: nothing is signed then. To remove
+it completely: delete this folder and `CODE_SIGNING.md`, remove the steps marked
+`code signing` in `release.yml` (the file check step uses `check-files.ps1` from this
+folder) and the links to `CODE_SIGNING.md` in the README.
diff --git a/.signpath/artifact-configuration.xml b/.signpath/artifact-configuration.xml
new file mode 100644
index 0000000..04de42d
--- /dev/null
+++ b/.signpath/artifact-configuration.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.signpath/check-files.ps1 b/.signpath/check-files.ps1
new file mode 100644
index 0000000..d448886
--- /dev/null
+++ b/.signpath/check-files.ps1
@@ -0,0 +1,61 @@
+# NVENCForgeGUI (https://github.com/burnersen/NVENCForgeGUI)
+# Copyright (C) 2026 burnersen
+# SPDX-License-Identifier: GPL-3.0-only
+#
+# Checks the Windows files a release is about to sign or publish:
+#
+# - every file exists,
+# - its file properties carry exactly the product name and product version
+# that the SignPath artifact configuration enforces (product-name,
+# product-version). SignPath refuses a signing request whose files do not
+# match, so this check runs on every release build, signed or not: a
+# mismatch shows up long before the first signed release.
+# - with -Signed: every file carries a valid Authenticode signature.
+#
+# Usage (from the release workflow):
+# ./.signpath/check-files.ps1 -ProductName X -ProductVersion 1.2.3 -Path a.exe, b.exe [-Signed]
+
+param(
+ [Parameter(Mandatory)] [string[]] $Path,
+ [Parameter(Mandatory)] [string] $ProductName,
+ [Parameter(Mandatory)] [string] $ProductVersion,
+ [switch] $Signed
+)
+
+$ErrorActionPreference = 'Stop'
+$failed = 0
+
+foreach ($file in $Path) {
+ if (-not (Test-Path -LiteralPath $file -PathType Leaf)) {
+ Write-Host "::error::$file is missing"
+ $failed++
+ continue
+ }
+ $info = (Get-Item -LiteralPath $file).VersionInfo
+ $name = "$($info.ProductName)".Trim()
+ $version = "$($info.ProductVersion)".Trim()
+ if ($name -ne $ProductName) {
+ Write-Host "::error::$file has product name '$name', expected '$ProductName'"
+ $failed++
+ }
+ if ($version -ne $ProductVersion) {
+ Write-Host "::error::$file has product version '$version', expected '$ProductVersion'"
+ $failed++
+ }
+ $line = "{0}: {1} {2}" -f $file, $name, $version
+ if ($Signed) {
+ $signature = Get-AuthenticodeSignature -LiteralPath $file
+ if ($signature.Status -ne 'Valid') {
+ Write-Host "::error::$file is not validly signed ($($signature.Status): $($signature.StatusMessage))"
+ $failed++
+ } else {
+ $line += ", signed by $($signature.SignerCertificate.Subject)"
+ }
+ }
+ Write-Host $line
+}
+
+if ($failed -gt 0) {
+ Write-Host "::error::$failed problem(s) with the release files"
+ exit 1
+}
diff --git a/CODE_SIGNING.md b/CODE_SIGNING.md
new file mode 100644
index 0000000..c4c52cf
--- /dev/null
+++ b/CODE_SIGNING.md
@@ -0,0 +1,77 @@
+# Code signing policy
+
+Free code signing provided by [SignPath.io](https://about.signpath.io),
+certificate by [SignPath Foundation](https://signpath.org).
+
+> **Status:** NVENCForgeGUI has applied to the SignPath Foundation. Until
+> the signing is active, `NVENCForgeGUI.exe` is not signed. Every release
+> says in its text whether its exe is signed.
+
+## What is signed
+
+Only `NVENCForgeGUI.exe`, built from the source code in this repository by
+the [release workflow](.github/workflows/release.yml) on GitHub-hosted
+runners. Nothing that was built elsewhere is signed with this certificate.
+Every signing request is approved by hand, release by release.
+
+Not part of this project's releases: the converter `NVENCForge.exe`, which
+the window fetches from the releases of
+[NVENCForge](https://github.com/burnersen/NVENCForge) (a project of its own
+with its own [code signing policy](https://github.com/burnersen/NVENCForge/blob/main/CODE_SIGNING.md)),
+and FFmpeg, which the converter downloads on its first run.
+
+The file properties of the signed exe name the product `NVENCForgeGUI` and
+the version of the release; SignPath refuses to sign a file that does not
+match.
+
+## Team roles
+
+| Role | Who |
+| ---- | --- |
+| Committers and reviewers | [burnersen](https://github.com/burnersen) |
+| Approvers | [burnersen](https://github.com/burnersen) |
+
+Changes from anybody else (pull requests) are reviewed by a committer before
+they are merged. All team members use multi-factor authentication for GitHub
+and for SignPath.
+
+## Privacy policy
+
+This program will not transfer any information to other networked systems
+unless specifically requested by the user or the person installing or
+operating it.
+
+What NVENCForgeGUI does on the network, and only when you click for it:
+
+- **Fetching the converter:** if `NVENCForge.exe` is missing, or when you
+ update it, the window asks the GitHub API for the newest NVENCForge
+ release and downloads `NVENCForge.exe` from GitHub. The converter then
+ fetches FFmpeg itself (see its
+ [privacy policy](https://github.com/burnersen/NVENCForge/blob/main/CODE_SIGNING.md#privacy-policy)).
+- **Updating the window:** on the About page, and only when you press the
+ button, it asks the GitHub API for the newest NVENCForgeGUI release and,
+ on a second click, downloads it from GitHub.
+
+Like any network request, these reveal your IP address to GitHub. Your
+videos are converted on your own PC and never leave it. There is no
+telemetry, no automatic update check, no account and no advertising.
+
+## Installing and uninstalling
+
+There is no installer. NVENCForgeGUI keeps everything (the converter,
+FFmpeg, its settings, profiles and window position) in the `tools` folder
+next to the exe and writes nothing to the registry. To uninstall, delete the
+folder.
+
+## Verifying a download
+
+- **Signature:** right-click `NVENCForgeGUI.exe` > *Properties* > *Digital
+ Signatures*. A signed release shows *SignPath Foundation*.
+- **Build it yourself:** see [Building it yourself](README.md#building-it-yourself).
+
+## Reporting a problem
+
+If you think a signed file of this project does something it should not,
+please open an [issue](https://github.com/burnersen/NVENCForgeGUI/issues).
+Signed files that break these rules can also be reported to the
+[SignPath Foundation](https://signpath.org).
diff --git a/README.md b/README.md
index e20da09..1d0d244 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Same converter, same results, same settings file — just visible.
[](#-license)
[](https://ko-fi.com/burnersen)
-**[⬇️ Download the latest release](https://github.com/burnersen/NVENCForgeGUI/releases/latest)** · **[⌨️ Prefer the command line?](https://github.com/burnersen/NVENCForge)** · **[🌐 Website](https://tech.burnersen.de/en/tools/nvencforge-en/)** · **[☕ Buy me a coffee](https://ko-fi.com/burnersen)**
+**[⬇️ Download the latest release](https://github.com/burnersen/NVENCForgeGUI/releases/latest)** · **[⌨️ Prefer the command line?](https://github.com/burnersen/NVENCForge)** · **[🌐 Website](https://tech.burnersen.de/en/tools/nvencforge-en/)** · **[🔏 Code signing policy](CODE_SIGNING.md)** · **[☕ Buy me a coffee](https://ko-fi.com/burnersen)**
🇩🇪 Auf Deutsch: [tech.burnersen.de/tools/nvencforge](https://tech.burnersen.de/tools/nvencforge/)
@@ -141,7 +141,9 @@ The tool modes (Split, Join, DaVinci) always run one at a time. They copy instea
## 🛡️ Windows SmartScreen / antivirus warnings
-Windows or your antivirus may warn you the first time you run `NVENCForgeGUI.exe`. The honest reason: **the EXE is not code-signed.** Signing certificates cost several hundred euros *per year*, and this is a free hobby project with zero income. Unsigned Go binaries are frequent false-positive targets; there is nothing I can do about it except be transparent.
+Windows or your antivirus may warn you the first time you run `NVENCForgeGUI.exe`. The honest reason: **the EXE is not code-signed yet.** Signing certificates cost several hundred euros *per year*, and this is a free hobby project with zero income. Free code signing through the [SignPath Foundation](https://signpath.org) is applied for and already prepared; see the [code signing policy](CODE_SIGNING.md). Until then, unsigned Go binaries are frequent false-positive targets, and all I can do is be transparent.
+
+Every release is built by GitHub Actions from this repository ([release workflow](.github/workflows/release.yml)), never on a private PC.
You don't have to trust me blindly: **scan it** on [VirusTotal](https://www.virustotal.com), **read it** — the complete source is in this repository — or **[build it yourself](#building-it-yourself)**.
@@ -162,6 +164,8 @@ wails build
**Without `-clean`** — that would delete `build\bin\tools\` and with it the converter sitting next to the window.
+Releases are built by the [release workflow](.github/workflows/release.yml) on GitHub: push a tag `vX.Y.Z` equal to `guiVersion` in `main.go`. It runs the tests and the window checks, writes the version into the file properties and, once code signing is switched on, has the exe signed ([`.signpath/README.md`](.signpath/README.md)). "Run workflow" with "dry run" ticked builds and checks everything without publishing.
+
The interface is a single HTML file with no build step: no npm, no bundler, no node_modules.
@@ -191,7 +195,10 @@ NVENCForgeGUI is free software under the [GNU General Public License, version 3]
- **Use it:** Anyone may use NVENCForgeGUI free of charge, at home or at work, and read, change and share its source code.
- **The one condition:** Whoever passes on NVENCForgeGUI or parts of it — unchanged, changed or built into a program of their own — must include the complete source code and pass everything on under the same license. A closed, paid product with NVENCForgeGUI inside is therefore not possible.
- **Free of charge:** NVENCForgeGUI is and stays free. The official source is [github.com/burnersen/NVENCForgeGUI](https://github.com/burnersen/NVENCForgeGUI) — anyone asking money for NVENCForgeGUI is selling you what you can get here for free.
-- **A separate license for companies:** Want to build NVENCForgeGUI into a product that is not meant to be under the GPLv3? You can ask the author for a separate license — just [open an issue](https://github.com/burnersen/NVENCForgeGUI/issues).
+
+### Code signing and privacy
+
+What is signed, who approves it and what the window fetches from GitHub (only when you click for it): [code signing policy](CODE_SIGNING.md).