Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions .github/workflows/rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,45 @@ jobs:
cargo test -p linkunbound-core --quiet || exit 1
done

# The release refuses notices behind the lockfiles; this says so the morning after, not on the tag.
# The release refuses notices behind the lockfiles; this says so on the pull request that moves them, not on the tag.
notices:
name: The notices name what ships
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- id: look
env:
BASE: ${{ github.event.pull_request.base.sha }}
run: |
if [ -z "$BASE" ]; then
echo "touched=true" >> "$GITHUB_OUTPUT"
exit 0
fi
watched='^(scripts/third-party\.mjs|THIRD-PARTY-BUNDLED\.md|Cargo\.(lock|toml)|.*/Cargo\.toml|app/package(-lock)?\.json|\.github/workflows/rules\.yml)$'
from=$(git merge-base "$BASE" HEAD)
if git diff --name-only --diff-filter=d "$from" HEAD | grep -qE "$watched"; then
echo "touched=true" >> "$GITHUB_OUTPUT"
else
echo "touched=false" >> "$GITHUB_OUTPUT"
fi
- if: steps.look.outputs.touched == 'true'
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
package-manager-cache: false
- uses: ./.github/actions/toolchain
- name: Regenerated, they match the copy on main
- if: steps.look.outputs.touched == 'true'
uses: ./.github/actions/toolchain
- name: Regenerated, they match the copy in the tree
if: steps.look.outputs.touched == 'true'
run: |
(cd app && npm ci)
npm run notices
if ! git diff --exit-code THIRD-PARTY-BUNDLED.md; then
echo "::error::THIRD-PARTY-BUNDLED.md is behind the lockfiles; run npm run notices and land it before the next tag"
echo "::error::THIRD-PARTY-BUNDLED.md is behind the lockfiles; run npm run notices and commit it"
exit 1
fi
36 changes: 34 additions & 2 deletions COMMERCIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ licence: it does not. Internal use is not distribution.
- Ship LinkUnbound, or code derived from it, inside a **product you distribute
to others**, without licensing that product under the GPL.
- Redistribute it under **your own brand** without the GPL's source disclosure
and licence-notice requirements.
requirements.
- Bundle it with hardware or preinstall it on machines you sell.
- Satisfy a policy or contract that **forbids copyleft** dependencies in what
you ship.
Expand All @@ -48,17 +48,49 @@ fine, carry on".
license your own product under the GPL.
- Written permission you can hand to your legal or procurement team.

It covers LinkUnbound's own code and nothing else. The components it is built
on keep their own licences, and the notices those licences ask for still go
with every copy: see [Third-party components](#third-party-components).

Terms, scope, and price are agreed per case rather than published, because a
single-seat integration and an OEM redistribution are not the same deal.
Contact <github@apirest.cl> with what you want to do and the scale of it.

## Third-party components

LinkUnbound is built on work it does not own, and no licence from this project
can relicense it. Everything that ships inside the binaries is listed, with its
version and licence, in [THIRD-PARTY-BUNDLED.md](THIRD-PARTY-BUNDLED.md). Most
of it is MIT, Apache-2.0, BSD or ISC: free to redistribute inside a closed
product, as long as its notices travel with it. Two need a closer look.

**Slint** draws the picker. SixtyFPS GmbH offers it under the GPL-3.0, under
the Slint Royalty-free Desktop, Mobile, and Web Applications License 2.0, or
under a paid commercial licence. A product that takes LinkUnbound under a
commercial licence uses Slint under the Royalty-free licence: no charge, on
condition that the product shows Slint's `AboutSlint` widget in its About
screen, or the
[#MadeWithSlint badge](https://github.com/slint-ui/slint/tree/master/logo) on
the public page its binaries are downloaded from. That licence does not cover
embedded systems, nor a product that exposes Slint's API to its own users;
those need Slint's commercial licence, bought from SixtyFPS, not from this
project.

**MPL-2.0** covers one crate in the binaries, `option-ext`, which Tauri
reaches through `dirs`. The MPL is copyleft per file: the source of that crate,
and of any change made to it, stays available under the MPL, and the rest of
the product is untouched. LinkUnbound ships it unmodified.

## Store distribution

The GPL's section 10 forbids imposing further restrictions on recipients,
which conflicts with the terms of some application stores — Apple's App Store
being the well-known case. That conflict binds **licensees**, not the
copyright holder: a third party cannot publish LinkUnbound there, and the
project itself can, under separate terms it grants to itself.
project itself can, under separate terms it grants to itself. It holds that
right over its own code only. For Slint the project is a licensee like anyone
else, so a build for such a store has to take Slint under its Royalty-free
licence instead of the GPL, with the attribution that licence asks for.

In practice today: macOS builds are distributed as a signed and notarised DMG
under the GPL, and the Microsoft Store build is unaffected, since Microsoft's
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,16 @@ I built LinkUnbound because I was tired of my OS not letting me choose which bro

---

## Acknowledgements

The picker is drawn with [Slint](https://slint.dev), and the settings window runs on [Tauri](https://tauri.app).

<a href="https://slint.dev">
<img src="https://raw.githubusercontent.com/slint-ui/slint/master/logo/MadeWithSlint-logo-whitebg.png" alt="Made with Slint" height="60"/>
</a>

---

## Project health

<p>
Expand Down
Loading
Loading