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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ jobs:
- name: Test
run: cargo test --verbose

# SignPath signs only an .exe whose ProductName is the project name and
# whose ProductVersion is set. build.rs embeds both; this proves it on
# every PR, and the release workflow checks the shipped file again.
# `-cne`, not `-ne`: PowerShell compares strings case-insensitively, and
# winresource's default ProductName is the lowercase package name.
- name: Check the Windows version resource
if: runner.os == 'Windows'
shell: pwsh
run: |
cargo build
if ($LASTEXITCODE) { exit $LASTEXITCODE }
$expected = (cargo metadata --no-deps --format-version 1 | ConvertFrom-Json).packages[0].version
$info = (Get-Item target\debug\colony.exe).VersionInfo
"ProductName='$($info.ProductName)' ProductVersion='$($info.ProductVersion)' FileVersion='$($info.FileVersion)'"
if ($info.ProductName -cne 'Colony' -or $info.ProductVersion -cne $expected) {
"::error::colony.exe has ProductName '$($info.ProductName)' and ProductVersion '$($info.ProductVersion)', expected 'Colony' and '$expected'"
exit 1
}

# The declared MSRV was 1.80 for years and could never have built: 70
# transitive dependencies require more. Nothing checked it, because CI only
# ever ran on stable. This leg is what stops it drifting again.
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/colony-rust-release.yml.template
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
# key rotation, and how to adopt signatures in a program that already ships -
# is in Project-Colony-Resources / design/releases.md.
#
# Colony's own release pipeline (.github/workflows/release-please.yml) is the
# reference implementation and stays separate: it self-updates, so its
# signature verification is mandatory rather than opt-in.
# Colony's own release pipeline (.github/workflows/release-please.yml) signs and
# publishes through the shared workflow in Project-Colony-Resources,
# .github/workflows/sign-and-publish.yml (caller example:
# templates/sign-and-publish-caller.yml). Colony self-updates, so for it the
# signatures are mandatory rather than opt-in.
234 changes: 93 additions & 141 deletions .github/workflows/release-please.yml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ strip = false

[dev-dependencies]
tempfile = "3"

# Embeds the Windows version resource (build.rs). Without default features it
# pulls in nothing but version_check, and it does nothing on other targets.
[build-dependencies]
winresource = { version = "0.1.31", default-features = false }
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,29 @@ how these move up.
| [Colony spec](docs/colony-spec.md) | Full `colony.json` manifest reference |
| [Contributing](CONTRIBUTING.md) | How to add your app + how to contribute to Colony itself |

## Code signing policy

Free code signing provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org).

Windows builds are Authenticode-signed once the SignPath Foundation has accepted the project; until then they ship without Authenticode. Every release asset, on every platform, is always signed with the organisation's ed25519 key, and Colony verifies that signature before it applies a self-update (see [Release signing](docs/release-signing.md)).

Team roles and members:

- Committers and reviewers: [MotherSphere](https://github.com/MotherSphere)
- Approvers: [MotherSphere](https://github.com/MotherSphere)

### Privacy policy

Colony talks to GitHub and to no other server. It has no telemetry, no analytics and no crash reporting.

- **At every start, automatically**, and again when you press Refresh: it reads the Project-Colony catalogue from the GitHub API (`api.github.com`): the organisation's repository list and, for each repository, its `colony.json`, README, licence, changelog and icon, plus its latest release when the manifest does not list its files.
- **Update checks, automatically unless turned off** (Settings, "Check automatically", on by default) or when you press "Check for updates": it asks the GitHub API for the release of every installed app and the latest release of Colony itself. These requests name the repositories of the apps you have installed, so GitHub can see which Colony apps you use.
- **When you press "What's new in {version}"** on an app's page: it asks the GitHub API for that release, to show its notes.
- **When you install or update an app, or update Colony**: it looks the release up through the GitHub API, then downloads the release file and its `.sig`, `.meta` and `.meta.sig` from `github.com`, which serves them from GitHub's own download hosts.
- **When you sign in with GitHub** (optional): it uses GitHub's device flow (`github.com/login/device/code` and `github.com/login/oauth/access_token`, scope `read:user`), opens the verification page in your browser, and reads your user name from `api.github.com/user`. While you are signed in, your GitHub token is sent with the requests above, to GitHub only, to raise the API rate limit.

Requests carry a `Colony-Launcher` user agent. Beyond that, they tell GitHub only which repositories and files are asked for (a download's file name shows your platform) and, while you are signed in, which account is asking; nothing from your settings or your files is sent. GitHub also sees what any web request shows it, such as your IP address, under the [GitHub General Privacy Statement](https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement). Links you click open in your web browser. The apps you install through Colony are separate programs with their own behaviour.

## License

[GPL-3.0-or-later](LICENSE) © 2026 Project Colony contributors
Expand Down
17 changes: 17 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Windows version resource: SignPath signs only an .exe whose ProductName is
// the project name and whose ProductVersion is set, and the release workflow
// checks both on the built file. Decided on the TARGET, not with cfg!(windows):
// build scripts run on the host.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
let version = env!("CARGO_PKG_VERSION");
winresource::WindowsResource::new()
.set("ProductName", "Colony")
.set("FileDescription", "Colony")
.set("ProductVersion", version)
.set("FileVersion", version)
.compile()
.expect("could not embed the Windows version resource");
}
}
41 changes: 30 additions & 11 deletions docs/release-signing.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ download time, so the staging file cannot be swapped in between.

Because verification is fail-closed, a release published without the
`colony-<platform>.sig`, `.meta` and `.meta.sig` assets will make self-update
fail for users on that channel. The CI `sign` job checks all three exist for all
four platforms and fails the release otherwise.
fail for users on that channel. The release workflow's `sign-and-publish` job
checks all three exist and verify for all four platforms and fails the release
otherwise.

## Signing (CI or local)

Expand Down Expand Up @@ -90,7 +91,9 @@ tells you which state you are in.

**Still a draft, incomplete.** Nobody is affected: `/releases/latest` still
points at the previous version and no client has been offered anything. Fix the
cause and use **"Re-run failed jobs"**.
cause and use **"Re-run failed jobs"**. The build artifacts are kept for one
day; after that, dispatch the workflow again from the tag itself:
`gh workflow run release-please.yml --ref <tag> -f tag=<tag>`.

**Never "Re-run all jobs".** release-please re-runs against a `main` whose
release already exists, emits an empty `release_created`, and every downstream
Expand All @@ -113,14 +116,30 @@ then follow the manual signing procedure above and re-publish with

Since the v0.7.0 incident (a release shipped unsigned because signing was a
manual step, bricking self-update for every existing install), signing is a
mandatory job in the release workflow: `.github/workflows/release-please.yml`
(`sign` job) downloads the four built binaries, signs them with the
`COLONY_SIGNING_KEY_PEM` secret (the PEM contents), verifies that every asset
came out with a `.sig`, a `.meta` and a `.meta.sig`, and uploads them. The job
**fails the release** if the secret is missing or if any of those files is
missing or empty, so a release the launcher cannot verify can no longer ship
silently. The manual procedure above remains for re-signing an old release by
hand.
mandatory part of the release workflow, `.github/workflows/release-please.yml`.
Its build legs only build, smoke-test and upload each binary as a workflow
artifact. The `sign-and-publish` job then calls the organisation's shared
workflow, `.github/workflows/sign-and-publish.yml` in
[Project-Colony-Resources](https://github.com/Project-Colony/Project-Colony-Resources),
pinned by commit, which in that same run:

1. checks the release is still a draft;
2. sends `colony-windows.exe` to SignPath for Authenticode, once SignPath is
turned on for this repository (`signpath-project-slug`), and waits for a
person to approve the request;
3. writes `.sig`, `.meta` and `.meta.sig` over the final bytes with the
`COLONY_SIGNING_KEY_PEM` secret (the PEM contents), in the same format as
`scripts/sign-release.sh`;
4. uploads everything to the draft, downloads it again and verifies every
signature and digest against what users will download;
5. publishes the release.

The order is the point: Authenticode rewrites the `.exe`, so a `.sig` or
`.meta` computed before it would describe bytes that no longer exist, and the
launcher would refuse the update. The job **fails the release** if the secret is
missing or any file is missing, empty or does not verify, so a release the
launcher cannot verify can no longer ship silently. The manual procedure above
remains for re-signing an old release by hand.

## Key custody

Expand Down
Loading