Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0ccdb11
fix(download): use a read timeout instead of a 300 s total deadline
MotherSphere Aug 27, 2026
318f8b7
fix(update): never report apps as up to date when the check did not run
MotherSphere Aug 27, 2026
b1d57a4
fix(ui): make failures visible - status line everywhere, logs, and retry
MotherSphere Aug 27, 2026
62102a0
fix(security): validate remote strings before they reach URLs and paths
MotherSphere Aug 27, 2026
5cabb5c
feat(github): persist the conditional-request cache across launches
MotherSphere Aug 27, 2026
ecef473
feat(download): resume interrupted transfers
MotherSphere Aug 27, 2026
9c4ffca
fix(platform): replace running binaries safely, and stop overpromisin…
MotherSphere Aug 27, 2026
e05c224
fix(update): report every failure the user triggered
MotherSphere Aug 27, 2026
10726a5
fix(github): distinguish a broken manifest from a transient failure
MotherSphere Aug 27, 2026
cc91fd6
docs: correct every path, count and claim the code had outgrown
MotherSphere Aug 27, 2026
2a1ec0d
feat(signing): make the key rotatable and bind app assets to their re…
MotherSphere Aug 27, 2026
5ca1def
feat(store): say what is on offer before the user commits to it
MotherSphere Aug 27, 2026
f62fa1a
feat(manifest): validate colony.json, and make browsing tell the truth
MotherSphere Aug 27, 2026
0ee44b2
chore: pay down the debt the audit found, and guard it with tests
MotherSphere Aug 27, 2026
12ca4d4
ci(release): close the unsigned window, smoke-test artefacts, scope s…
MotherSphere Aug 27, 2026
c371ffd
docs: point at the one canonical release template instead of a second…
MotherSphere Aug 27, 2026
a047acd
fix(ci): repair the three checks the new jobs broke, and patch h2
MotherSphere Aug 27, 2026
a99eea0
ci: make the advisory check gate on what is actionable, not report fo…
MotherSphere Aug 27, 2026
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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ body:
attributes:
label: Colony version
description: Output of `colony --version` (or the release tag you downloaded).
placeholder: "v0.1.4"
placeholder: "0.9.2"
validations:
required: true

Expand Down
21 changes: 20 additions & 1 deletion .github/workflows/aur-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ on:
description: 'Release tag to publish to AUR (e.g. v0.7.0)'
required: true
type: string
secrets:
# Declared explicitly so the caller passes THIS secret and nothing else.
# Under `secrets: inherit` this job also received COLONY_SIGNING_KEY_PEM,
# which it has no use for - and that is the one secret whose compromise
# is unrecoverable, since every install in the field trusts the key it
# signs with.
AUR_SSH_PRIVATE_KEY:
required: true
workflow_dispatch:
inputs:
tag:
Expand Down Expand Up @@ -64,7 +72,18 @@ jobs:
mkdir -p ~/.ssh
printf '%s\n' "$SSH_KEY" > ~/.ssh/aur_bot
chmod 600 ~/.ssh/aur_bot
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null
# Pin the host key rather than trusting whatever answers today:
# a bare ssh-keyscan is trust-on-first-use on every single run.
# Fingerprint SHA256:RFzBCUItH9LZS0cKB5UE6ceAYhBD5C8GeOBip8Z11+4,
# cross-checkable against the AUR entry on the Arch wiki.
AUR_HOSTKEY='aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN'
printf '%s\n' "$AUR_HOSTKEY" > ~/.ssh/known_hosts
# A mismatch means either a legitimate rotation (update the pin) or
# something worth stopping the release for.
if ! ssh-keyscan -t ed25519 aur.archlinux.org 2>/dev/null | grep -qxF "$AUR_HOSTKEY"; then
echo "::error::aur.archlinux.org host key does not match the pinned one"
exit 1
fi

- name: Clone AUR colony-bin repo
run: |
Expand Down
58 changes: 57 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,64 @@ jobs:
- name: Format
run: cargo fmt --all -- --check

# --all-targets, or clippy never sees the 140 tests - the code most
# likely to accumulate lint debt, since nobody reads it in review.
- name: Clippy
run: cargo clippy -- -D warnings
run: cargo clippy --all-targets -- -D warnings

- name: Test
run: cargo test --verbose

# 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.
msrv:
name: MSRV
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Read the declared MSRV
id: msrv
run: |
version=$(grep '^rust-version' Cargo.toml | tr -d ' "' | cut -d= -f2)
[ -n "$version" ] || { echo "::error::could not read rust-version from Cargo.toml"; exit 1; }
echo "version=$version" >> "$GITHUB_OUTPUT"

- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable branch
with:
toolchain: ${{ steps.msrv.outputs.version }}

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
key: msrv-${{ steps.msrv.outputs.version }}

- name: Build on the declared floor
run: cargo check --locked --all-targets

# Colony downloads and executes binaries, so a known-vulnerable dependency is
# not an abstract concern. This job GATES: red here means something new and
# actionable.
#
# It gates because the alternative does not work. Left to report without
# gating, it is red on every single PR forever - and a check that is always
# red is a check nobody reads. So the advisories that genuinely cannot be
# acted on from this repository are ignored BY ID, with the reason written
# down, and everything else fails the build.
#
# RUSTSEC-2026-0194 / RUSTSEC-2026-0195 (quick-xml): held at 0.38 by
# wayland-scanner, via winit and iced 0.14 - a semver-incompatible bump we
# cannot make from here. Both advisories concern parsing untrusted XML;
# wayland-scanner is a build-time proc-macro that parses the system's own
# Wayland protocol descriptions, so neither is reachable at runtime.
# Remove these two the day iced ships a winit new enough to carry
# quick-xml >= 0.41.
audit:
name: Advisories
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
ignore: RUSTSEC-2026-0194,RUSTSEC-2026-0195
124 changes: 33 additions & 91 deletions .github/workflows/colony-rust-release.yml.template
Original file line number Diff line number Diff line change
@@ -1,94 +1,36 @@
# Colony Rust AppRelease Workflow Template
# Colony Rust programrelease workflow
#
# Copy this file to your repo as .github/workflows/release.yml
# Replace {{APP_NAME}} with your binary/repo name (lowercase).
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ THIS FILE IS NOT THE TEMPLATE. The canonical one lives in │
# │ │
# │ Project-Colony-Resources / templates/release.yml │
# │ https://github.com/Project-Colony/Project-Colony-Resources │
# │ │
# │ Copy THAT file to your repo as .github/workflows/release.yml. │
# └─────────────────────────────────────────────────────────────────────────┘
#
# Assets follow the Colony naming convention:
# {{APP_NAME}}-linux, {{APP_NAME}}-windows.exe,
# {{APP_NAME}}-macos, {{APP_NAME}}-macos-x86
# Why this is a pointer and not a copy: it used to be a copy, and the two
# drifted. The Resources template referenced a secret named
# COLONY_SIGNING_KEY while every real repo uses COLONY_SIGNING_KEY_PEM, and it
# skipped signing on Windows entirely - so a program following it shipped an
# unsigned .exe that Colony then refused to install, because the manifest said
# "signed": true. Neither mistake was visible from either side. One canonical
# file, kept next to the shared script it calls, is what stops that recurring.
#
# Colony auto-detects platforms from these asset names, so your
# colony.json only needs: { "name": "...", "category": "..." }

name: Release

on:
push:
branches: [main]

permissions:
contents: write
pull-requests: write

env:
CARGO_TERM_COLOR: always

jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
upload_url: ${{ steps.release.outputs.upload_url }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
release-type: rust

build:
name: Build ${{ matrix.asset }}
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
asset: "{{APP_NAME}}-linux"
- target: x86_64-pc-windows-msvc
os: windows-latest
asset: "{{APP_NAME}}-windows.exe"
- target: aarch64-apple-darwin
os: macos-latest
asset: "{{APP_NAME}}-macos"
- target: x86_64-apple-darwin
os: macos-latest
asset: "{{APP_NAME}}-macos-x86"

steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}

# Uncomment if your app needs GTK/X11 dependencies on Linux:
# - name: Install Linux dependencies
# if: runner.os == 'Linux'
# run: |
# sudo apt-get update
# sudo apt-get install -y libgtk-3-dev libxdo-dev libdbus-1-dev

- name: Build
run: cargo build --release --target ${{ matrix.target }}

- name: Rename binary (Unix)
if: runner.os != 'Windows'
run: cp target/${{ matrix.target }}/release/{{APP_NAME}} ${{ matrix.asset }}

- name: Rename binary (Windows)
if: runner.os == 'Windows'
run: copy target\${{ matrix.target }}\release\{{APP_NAME}}.exe ${{ matrix.asset }}

- name: Upload binary to GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.release-please.outputs.tag_name }}
files: ${{ matrix.asset }}
# What you need, in short:
#
# 1. colony.json at your repo root (name + category is usually the whole
# file). Validate it with: colony validate-manifest colony.json
# 2. templates/release.yml -> .github/workflows/release.yml
# 3. templates/sign-release.sh -> scripts/sign-release.sh (only if you set
# "signed": true; the workflow calls it from there)
# 4. The COLONY_SIGNING_KEY_PEM organisation secret, which is restricted to
# Project-Colony repositories.
#
# The full contract - asset naming, the manifest, signing, the .meta sidecar,
# 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.
55 changes: 54 additions & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ jobs:
with:
release-type: rust

# release-please's own step PUBLISHES the release - and moves
# /releases/latest - before a single binary exists. For the whole
# build+sign window every running Colony therefore showed an update badge
# whose every click failed: a missing asset first, then a fail-closed
# signature refusal once `build` had uploaded but `sign` had not. And if
# any build leg failed, that state was permanent until someone noticed.
#
# Held as a draft here rather than via a release-please config file: the
# config-file route means switching the action to manifest mode, which
# changes the outputs contract that the build, sign and aur jobs all
# depend on and that nine releases have proven. This is one gh call, and
# it narrows the window from minutes to the second between the two steps.
- name: Hold the release as a draft until it is signed
if: ${{ steps.release.outputs.release_created }}
env:
GH_TOKEN: ${{ github.token }}
run: gh release edit "${{ steps.release.outputs.tag_name }}" --draft=true

build:
name: Build ${{ matrix.asset }}
needs: release-please
Expand Down Expand Up @@ -74,6 +92,31 @@ jobs:
if: runner.os == 'Windows'
run: copy target\${{ matrix.target }}\release\colony.exe ${{ matrix.asset }}

# The pipeline's signature checks prove provenance and say nothing about
# whether the binary RUNS. An asset built for the wrong architecture, or
# one that aborts under the release profile's LTO, would be signed,
# verified, published and hashed into the AUR PKGBUILD with every check
# green. `--version` exits before opening a window, so this is a genuine
# end-to-end smoke test of the shipped artefact.
- name: Smoke-test the artefact
if: matrix.target != 'x86_64-apple-darwin'
shell: bash
run: |
chmod +x "${{ matrix.asset }}" || true
"./${{ matrix.asset }}" --version

# The Intel macOS asset is cross-compiled on an arm64 runner and cannot
# be executed there, so assert the architecture instead - which is the
# regression that would otherwise survive longest, on the leg with the
# fewest users.
- name: Check the cross-compiled artefact's architecture
if: matrix.target == 'x86_64-apple-darwin'
shell: bash
run: |
file "${{ matrix.asset }}"
file "${{ matrix.asset }}" | grep -q 'x86_64' \
|| { echo "::error::${{ matrix.asset }} is not an x86_64 binary"; exit 1; }

- name: Upload binary to GitHub Release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
Expand Down Expand Up @@ -159,6 +202,13 @@ jobs:
done
echo "published release verified"

# Only now does the release become visible. Until this step, /releases/
# latest still points at the previous version, so no client is offered an
# update it cannot apply, and the README's download link never points at
# an empty release.
- name: Publish the release
run: gh release edit "$TAG" --draft=false

# Chained here (not on `release: published`) because release-please creates
# the release with the default GITHUB_TOKEN, whose events do not trigger
# other workflows - the standalone trigger never fired once in nine
Expand All @@ -171,4 +221,7 @@ jobs:
uses: ./.github/workflows/aur-publish.yml
with:
tag: ${{ needs.release-please.outputs.tag_name }}
secrets: inherit
# NOT `inherit`: the AUR job needs one secret, and inheriting handed it
# the release signing key as well.
secrets:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Minimal form:
}
```

Valid categories: `Development`, `Graphics`, `Network`, `Office`, `Multimedia`, `System`, `Utilities`, `Games`, `Other`.
Valid categories: `Development`, `Graphics`, `Network`, `Office`, `Multimedia`, `System`, `Utilities`, `Security`, `Games`, `Other`.

**App icon (recommended):** put a square PNG at `assets/icons/icon.png` and declare it in the manifest — `"icon": "assets/icons/icon.png"`. Colony shows it in the grid; without it your app gets a generated category tile. The same folder is the standard home for your `icon.ico` (Windows) and `icon.icns` (macOS). See the [App icon](docs/colony-spec.md#app-icon) section of the spec.

Expand All @@ -46,7 +46,7 @@ You can upload any subset — Colony only advertises the platforms whose asset i

### Step 3 — Wire up a release workflow (Rust apps)

A ready-to-use GitHub Actions template is in this repo at [`.github/workflows/colony-rust-release.yml.template`](.github/workflows/colony-rust-release.yml.template). Copy it to your own repo as `.github/workflows/release.yml` and replace `{{APP_NAME}}` with your binary/repo name (lowercase).
The ready-to-use GitHub Actions template lives in Project-Colony-Resources at [`templates/release.yml`](https://github.com/Project-Colony/Project-Colony-Resources/blob/main/templates/release.yml), next to the shared signing script it calls. Copy it to your own repo as `.github/workflows/release.yml` and replace `{{APP_NAME}}` with your binary/repo name (lowercase).

It uses [`release-please`](https://github.com/googleapis/release-please) so every merged PR tagged with a conventional-commit prefix (`feat:`, `fix:`, etc.) opens a release PR; merging that PR tags the version, builds the matrix of 4 platforms, and uploads the assets under the convention above. Zero manual release work afterwards.

Expand All @@ -73,7 +73,7 @@ Your app card should appear in the category you declared. If not:

- Make sure the release is **published** (not a draft).
- Make sure asset names match the convention (lowercase, no version in the name, correct extension).
- Look in `~/.cache/colony/repos_cache.json` if your repo is there but without platforms, the `colony.json` or the asset names are wrong. See the [spec](docs/colony-spec.md) for the precise validation rules.
- Look in `~/.config/Colony/Colony/cache/repos_cache.json` - if your repo is there but without platforms, the `colony.json` or the asset names are wrong. See the [spec](docs/colony-spec.md) for the precise validation rules.
- Verify you aren't hitting the GitHub rate limit: Settings → GitHub → Connect.

---
Expand All @@ -96,7 +96,7 @@ cargo build --release # optimized build
**Linux runtime dependencies** (for building and running):

```
libgtk-3-dev libxdo-dev libdbus-1-dev libasound2-dev libglib2.0-dev pkg-config
libgtk-3-dev libxdo-dev libdbus-1-dev pkg-config
```

### Code organization
Expand All @@ -113,7 +113,7 @@ See [docs/architecture.md](docs/architecture.md) for the full layout. Short vers
- `src/scan.rs` — system app detection (Linux `.desktop`, Windows Start Menu, macOS `.app`).
- `src/sections.rs` — categories + filter logic.
- `src/ui/` — widgets and panels (sidebar, app grid, detail view, settings).
- `src/ui/theme.rs` — all 24 theme families + 50+ palettes.
- `src/ui/theme.rs` — all 25 theme families + 57 palettes.

### Style

Expand Down Expand Up @@ -141,7 +141,7 @@ See [docs/architecture.md](docs/architecture.md) for the full layout. Short vers

### Reporting bugs / feature requests

Use the templates in `.github/ISSUE_TEMPLATE/` when filing. Include Colony version (`colony --version`), OS, and relevant log excerpts from `~/.cache/colony/`.
Use the templates in `.github/ISSUE_TEMPLATE/` when filing. Include Colony version (`colony --version`), OS, and relevant log excerpts from `~/.cache/colony/colony.log`.

---

Expand Down
Loading
Loading