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
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ the class of breakage this repo exists to end.
> **Status:** `colony-ui` is published on crates.io, and Colony uses its
> palettes and its `paths` module: Colony's hand-maintained palette constants
> are gone. The preferences kit new in 0.1.6 (`StandardPreferences`, the fonts,
> the navigation and Preferences page widgets) is adopted by no program yet;
> Colony still draws its own page, so the kit is proven by unit tests and a
> compiled example, not by a program in daily use. CI runs the `paths` tests on
> Linux, Windows and macOS. `colony-ui` releases now go through release-please
> and crates.io Trusted Publishing; that path has not published yet, because
> 0.1.7 waits on the Trusted Publisher registration on crates.io. The SignPath
> path of the shared signing workflow is wired but unused: the organisation
> secret and variable it needs do not exist yet.
> the navigation and Preferences page widgets) is in daily use: Colony and
> Spotter build their Preferences page, and the identity button that opens it,
> from the kit on `colony-ui` 0.1.6. CI runs the `paths` tests on Linux,
> Windows and macOS. `colony-ui` releases now go through release-please and
> crates.io Trusted Publishing; that path has not published yet, because 0.1.7
> waits on the Trusted Publisher registration on crates.io. The SignPath path
> of the shared signing workflow is wired but unused: the organisation secret
> and variable it needs do not exist yet.

## How it works

Expand Down
62 changes: 62 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Security Policy

Every Project Colony program releases through this repository's signing
workflow, copies its release templates and builds on `colony-ui`, so a flaw
here reaches every program at once.

## Reporting a vulnerability

Please do not report a vulnerability through a public issue, pull request or
discussion. Report it privately instead: open this repository's **Security**
tab and click **Report a vulnerability**, or go straight to
[the form](https://github.com/Project-Colony/Project-Colony-Resources/security/advisories/new).
Only the maintainers can see the report. Please include:

- the affected file, crate version or commit,
- what an attacker can do, and the steps to reproduce it,
- a proof of concept, if you have one.

We will acknowledge the report, keep you informed while we work on it, and
credit you in the advisory once a fix is released, unless you prefer to stay
anonymous.

## Scope

Reports of particular interest:

- **The shared signing workflow**,
[`.github/workflows/sign-and-publish.yml`](.github/workflows/sign-and-publish.yml).
It runs inside each program's release, holds the organisation's ed25519
release key for one job that builds nothing, and signs every asset and its
`.meta` before the release is published. Anything that exposes the key, gets
a file signed that the workflow did not verify, publishes a release whose
signatures do not verify, or skips Authenticode signing through SignPath
while it is switched on is critical.
- **The release templates** in [`templates/`](templates/), above all
`sign-and-publish-caller.yml`. Every program copies them, so a weakness in
them (an injectable input, a credential left in a checkout, a step that runs
a binary it has not verified) is copied too.
- **`colony_ui::paths`**, which joins a program name into directories that
programs then write to and delete. A name that gets out of its
`Colony/<Program>/` directory, or onto the shared `Colony/apps/` install
root, is a vulnerability.
- **The `colony.json` schema**,
[`generated/colony.schema.json`](generated/colony.schema.json). Manifests
come from third-party repositories and are untrusted input; a manifest the
schema accepts but no program should trust belongs here. A flaw in how the
launcher handles a manifest belongs on
[Colony](https://github.com/Project-Colony/Colony).

Out of scope: vulnerabilities in a program built on these resources (report
them on that program's repository), and in the third-party actions and crates
used here (report them upstream, and tell us if we should move off a version).

## Supported versions

- **`colony-ui`**: the latest version on crates.io. Older versions are not
patched; update to the latest.
- **The signing workflow**: `main`, and the commit every program pins (the
organisation pin in
[`templates/sign-and-publish-caller.yml`](templates/sign-and-publish-caller.yml)).
A fix lands on `main`, then the organisation pin moves to it for every
program at once.
22 changes: 12 additions & 10 deletions design/filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,18 @@ Three reasons this matters more than it looks:

## Where the ecosystem does not conform

Current state, so a migration is a decision rather than a discovery:

| Program | What | Should be | Checked |
|---|---|---|---|
| **Digger** | `history.db` in `<data>/digger/`, lowercase, no `Colony/` level | `<data>/Colony/Digger/` | 2026-08-24 |

Colony, Grape and Eidos conform as of 2026-10-12. Each resolves its roots
through `colony_ui::paths` and carries its older layout over at startup by the
rules below: Colony in `persistence::migrate_legacy_paths`, Grape in
`config::migrate`, Eidos in the `eidos-paths` crate.
Current state, so a migration is a decision rather than a discovery: no
program checked is off the layout today.

Colony, Grape and Eidos conform as of 2026-10-12, Digger (repository `D1Gg2r`)
and Spotter as of 2026-10-14. Each resolves its roots through
`colony_ui::paths` and carries its older layout over at startup by the rules
below: Colony in `persistence::migrate_legacy_paths`, Grape in
`config::migrate`, Eidos in the `eidos-paths` crate, Spotter in
`paths::migrate`. Digger keeps `history.db` in `<data>/Colony/Digger/` and its
preferences in `<config>/Colony/Digger/`; `History::migrate_legacy_db` copies
the database, with its WAL sidecars, from the lowercase `<data>/digger/` it used
before, and leaves the original where it was.

### Migrating a program

Expand Down
66 changes: 35 additions & 31 deletions design/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

How a Colony program is laid out and how the user reaches its settings.

Read from the three programs that implement it today: Colony's
`src/ui/sidebar.rs`, Digger's `src/ui.rs`, Grape's `src/ui/app/view.rs`. They
agree on the rule and differ on the chrome, so this page separates the two. The
parts every program shares are in `colony_ui::widgets`; a Rust/iced program
builds its chrome from them rather than copying Colony's.
Read from the four programs that implement it today: Colony's
`src/ui/sidebar.rs` and Spotter's `src/views/sidebar.rs`, both built from the
kit, Digger's `src/ui/mod.rs` (repository `D1Gg2r`) and Grape's
`src/ui/app/view.rs`. They agree on the rule and differ on the chrome, so this
page separates the two. The parts every program shares are in
`colony_ui::widgets`; a Rust/iced program builds its chrome from them rather
than copying Colony's.

## The rule: the program's identity is the way in

Expand All @@ -16,7 +18,7 @@ sections, not a menu bar.

That corner is the largest, most stable target in the window, and it is already
where a user looks to answer "what am I in?". Making it also answer "how do I
configure this?" costs no screen space and no extra chrome. All three programs
configure this?" costs no screen space and no extra chrome. All four programs
do it, and a new Colony program that puts settings somewhere else is the one
behaving oddly.

Expand All @@ -35,8 +37,8 @@ Pass it the same message as the Preferences page's Close button.

## Two shapes, both correct

**Direct**: the click goes straight to the settings page. Colony and Digger.
Right when settings is the only thing behind the name. This is what
**Direct**: the click goes straight to the settings page. Colony, Spotter and
Digger. Right when settings is the only thing behind the name. This is what
`identity_button` draws.

**Menu**: the click opens a small menu, and settings is one entry in it. Grape,
Expand All @@ -46,15 +48,15 @@ has several top-level destinations that are not tabs.
Do not add a menu with a single entry in it. If settings is the only thing
behind the name, go direct.

## What the three actually do
## What each program actually does

| | Colony | Digger | Grape |
|---|---|---|---|
| Chrome | vertical sidebar | horizontal tab bar | top bar |
| The button | `Colony` at `sz(30)` bold + gear glyph `\u{f013}` at `sz(14)`, one button | `{ICON} Digger`, `button::text`, size 15, accent-coloured | logo image 28×28 + `Grape` at `size(20)` semibold |
| Opens | the settings page | the settings page | a menu: Library / Playlist / Queue / Preferences / filters |
| Open indicator | gear goes `text_dimmer` → `text_primary`; background `bg_selected` | label gains a trailing close glyph | the menu is visible |
| Padding / radius | `[4, 8]`, radius 8 | `[2, 4]` | `[XS, MD]` |
| | Colony | Spotter | Digger | Grape |
|---|---|---|---|---|
| Chrome | vertical sidebar | vertical sidebar | horizontal tab bar | top bar |
| The button | `identity_button`: `Colony` at `sz(30)` bold + gear glyph `\u{f013}` at `sz(14)` | `identity_button`: `Spotter` at `sz(24)` bold + the same gear | `{ICON} Digger`, `button::text`, `sz(15)`, accent-coloured | logo image 28×28 + `Grape` at `size(20)` semibold |
| Opens | the settings page | the settings page | the settings page | a menu: Library / Playlist / Queue / Preferences / filters |
| Open indicator | gear goes `text_dimmer` → `text_primary`; background `bg_selected` | as Colony | label gains a trailing close glyph | the menu is visible |
| Padding / radius | `[4, 8]`, radius 8 | `[4, 8]`, radius 8 | `[4, 8]` | `[XS, MD]` |

The indicator mechanism is deliberately not standardised: a sidebar can afford a
background change, a text-styled button in a tab bar cannot. Pick whatever reads
Expand Down Expand Up @@ -105,26 +107,28 @@ selection makes a list feel broken.

## Animation

Colony's sidebar slide is 200 ms (`App::SIDEBAR_ANIM_MS`). Motion is a user
preference (Preferences → Accessibility → Motion), and a program that animates
must honour it. Gate every animation on `colony_ui::effects_enabled()`: it is
false when the user turned Appearance → Effects off or asked for reduced motion.
Colony fades its notifications in and out and eases its progress bar. Motion is
a user preference (Preferences → Accessibility → Motion), and a program that
animates must honour it. Gate every animation on `colony_ui::effects_enabled()`:
it is false when the user turned Appearance → Effects off or asked for reduced
motion.

## The word the user sees is "Preferences"

Colony's `settings_title` is `Preferences` in English (the canonical string)
and `Préférences` in the French locale. Grape agrees; its menu entry is
Preferences. That is settled: **the user-facing word is Preferences**, not
Settings. `colony_ui::i18n` ships the page's strings, `settings_title` among
them, so a program using the kit cannot get this wrong.
`settings_title` is `Preferences` in English (the canonical string) and
`Préférences` in the French locale; Colony and Spotter take it from the kit.
Grape agrees; its menu entry is Preferences. That is settled: **the user-facing
word is Preferences**, not Settings. `colony_ui::i18n` ships the page's strings,
`settings_title` among them, so a program using the kit cannot get this wrong.

Only the *code* is inconsistent. Colony calls the message `ToggleSettings` and
prefixes every key `settings_*`; Grape calls it `OpenPreferences`. That costs
nothing to a user and is not worth a rename on its own, but a new program should
name its internals after what the screen says (`TogglePreferences`), and its own
keys `preferences_*`. The shared keys keep the `settings_*` prefix the theme and
accent labels already use; see [i18n.md](i18n.md).
prefixes its own keys `settings_*`; Grape calls it `OpenPreferences`. That
costs nothing to a user and is not worth a rename on its own, but a new program
should name its internals after what the screen says (`TogglePreferences`), and
its own keys `preferences_*`, as Spotter does (`PreferencesMessage::Toggle`,
`preferences_cat_library`). The shared keys keep the `settings_*` prefix the
theme and accent labels already use; see [i18n.md](i18n.md).

Colony's own tutorial states the rule better than this page can:

> The gear icon next to the title opens the preferences.
> The Colony name at the top of the sidebar opens Preferences.
15 changes: 9 additions & 6 deletions design/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,16 @@ Do not set `separate-pull-requests` to `false` for a single-package repository.
### Libraries published to crates.io

A library, such as `colony-ui` in this repository, goes through the same
release-please chain, with two differences. It is a release-please component
release-please chain, with three differences. It is a release-please component
in a workspace (`include-component-in-tag`, tags like `colony-ui-v0.1.7`, the
`cargo-workspace` plugin so the root `Cargo.lock` moves in the release PR),
and its tag publishes to crates.io instead of building assets. Publishing uses
crates.io Trusted Publishing: the publish job exchanges its GitHub OIDC
identity for a token that lives only for that job, and the crate's trusted
publisher names the repository, `release.yml` and the `crates-io`
`cargo-workspace` plugin so the root `Cargo.lock` moves in the release PR).
While it is 0.x it sets `bump-minor-pre-major: true`, where the template says
`false`: a breaking change then bumps the minor version (0.1.x to 0.2.0), which
is what cargo treats as incompatible for a 0.x crate, instead of jumping to
1.0.0. And its tag publishes to crates.io instead of building assets.
Publishing uses crates.io Trusted Publishing: the publish job exchanges its
GitHub OIDC identity for a token that lives only for that job, and the crate's
trusted publisher names the repository, `release.yml` and the `crates-io`
environment. No registry token is stored as a secret, and no signing key is
involved: the ed25519 signatures of section 5 are for what the launcher
installs, and crates.io checksums what cargo downloads. See
Expand Down
23 changes: 13 additions & 10 deletions design/repository-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,23 @@ doc comment says "no behaviour lives here", which is what keeps it honest.
## File size

There is no hard limit, but a file you navigate by scrolling is a module asking
to be split. For calibration, the largest files in Colony today:
to be split. For calibration, the three largest files in Colony when this page
was written (2026-08), and what became of them by 2026-10-14:

| | |
|---|---|
| `src/ui/theme.rs` | 100 KB |
| `src/ui/settings.rs` | 64 KB |
| `src/download.rs` | 52 KB |
| | Then | Now |
|---|---|---|
| `src/ui/theme.rs` | 100 KB | 4 KB |
| `src/ui/settings.rs` | 64 KB | 18.6 KB |
| `src/download.rs` | 52 KB | split into `src/download/`, six files |

`theme.rs` is the cautionary one, and the reason this repository exists: 100 KB
of hand-maintained colour constants that another repository ended up downloading
and regex-parsing because there was no better way to reach them. It is now
generated from `tokens/`. `settings.rs` at 64 KB is the next candidate: its
sections are independent and would read better as `ui/settings/{general,
appearance,accessibility}.rs`.
and regex-parsing because there was no better way to reach them. The palettes
are now generated from `tokens/` and ship in `colony-ui`; what is left is a
re-export and Colony's own button styling. `settings.rs` shrank because the
page frame, Appearance and Accessibility now come from the `colony-ui`
preferences kit, leaving Colony's own sections and categories. The largest file
in Colony today is `src/persistence.rs`, at 62 KB.

Data does not belong in source files. A table that a human maintains and a
machine reads is a data file plus a generator, not a `const` array.
Expand Down
38 changes: 21 additions & 17 deletions design/settings-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ Every Colony program's settings page has the same skeleton. A user who has
configured one should not have to relearn anything to configure the next.

Reference implementation: the preferences kit in `colony-ui`
(`colony_ui::widgets`, `colony_ui::preferences`), ported from Colony's
`src/ui/settings.rs` and cross-checked against Digger (`src/ui.rs`) and Grape
(`src/ui/app/preferences/`), which is where the per-program variations below
come from. `crates/colony-ui/examples/preferences.rs` builds a complete page
with it. How the user *gets* here is in [navigation.md](navigation.md).
(`colony_ui::widgets`, `colony_ui::preferences`), ported from Colony's page and
cross-checked against Digger (repository `D1Gg2r`, `src/ui/settings/`) and
Grape (`src/ui/app/preferences/`), which is where the per-program variations
below come from. Colony (`src/ui/settings.rs`) and Spotter
(`src/views/preferences.rs`) now build their pages from it, and
`crates/colony-ui/examples/preferences.rs` builds a complete page with it. How
the user *gets* here is in [navigation.md](navigation.md).

**A Rust/iced program builds this page from the kit rather than copying it.**
Every measurement on this page is what the kit draws, so a program that calls it
Expand Down Expand Up @@ -86,10 +88,11 @@ on Ayu Dark, where the selected category disappeared. `contrast_on(accent)`
reaches at least 4.0:1, not 4.5:1, on every theme and accent; the worst case is
Night Owl light at 4.21:1 (see [navigation.md](navigation.md#selection-state)).

The list is 180 wide at a text scale of 1.0 and grows with the scale. Colony's
fixed 160 holds "Accessibility" at the default size only: at large text, or in
OpenDyslexic, it no longer fits on one line. A test measures every standard
category name, in both locales and both fonts, at all twelve size combinations.
The list is 180 wide at a text scale of 1.0 and grows with the scale. The fixed
160 Colony used before the kit held "Accessibility" at the default size only:
at large text, or in OpenDyslexic, it no longer fit on one line. A test
measures every standard category name, in both locales and both fonts, at all
twelve size combinations.

## The categories

Expand All @@ -105,17 +108,18 @@ category name, in both locales and both fonts, at all twelve size combinations.
`widgets::ABOUT_CATEGORY` the key of About.

After those come the program's own, and **About last** where it exists. That
tail is genuinely per-program. What the three do today:
tail is genuinely per-program. What each program does today:

| | Colony | Digger | Grape |
|---|---|---|---|
| 1-3 | General, Appearance, Accessibility | General, Appearance, Accessibility | General, Appearance, Accessibility |
| then | Storage, Shortcuts | Language | Audio |
| last | About | About | none |
| | Colony | Spotter | Digger | Grape |
|---|---|---|---|---|
| 1-3 | General, Appearance, Accessibility | General, Appearance, Accessibility | General, Appearance, Accessibility | General, Appearance, Accessibility |
| then | Storage, Shortcuts | Library, Imports | Language | Audio |
| last | About | none | About | none |

A program adds a category when it has a domain to configure that does not fit
the first three: Grape has audio devices and an equalizer, Digger gives language
its own tab rather than burying it in General as Colony does. It does not rename
the first three: Grape has audio devices and an equalizer, Spotter its game
library and what follows a library import, and Digger gives language its own tab
rather than putting it in General as Colony and Spotter do. It does not rename
one of the first three into something else. Omitting is fine; repurposing is
not.

Expand Down
Loading