From d418b8001a509fd5a5b638527cee8a3a5973345a Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Wed, 14 Oct 2026 12:33:03 +0200 Subject: [PATCH] docs: add a security policy and bring the design pages in line with the programs that use the kit - SECURITY.md: private reporting, scope (the shared signing workflow, the release templates, colony_ui::paths, the colony.json schema) and the supported versions (latest colony-ui, main and the organisation pin). - README Status: Colony and Spotter use the 0.1.6 preferences kit. - filesystem.md: Digger (repository D1Gg2r) and Spotter conform; the non-conformance table is empty. - navigation.md, settings-page.md: Digger's files are under src/ui/ in D1Gg2r; add Spotter to the comparison tables; Colony no longer has a sidebar slide, and its tutorial text changed. - repository-layout.md: Colony's file sizes, then and now. - releases.md: a 0.x library sets bump-minor-pre-major: true. --- README.md | 16 ++++----- SECURITY.md | 62 ++++++++++++++++++++++++++++++++++ design/filesystem.md | 22 +++++++------ design/navigation.md | 66 ++++++++++++++++++++----------------- design/releases.md | 15 +++++---- design/repository-layout.md | 23 +++++++------ design/settings-page.md | 38 +++++++++++---------- 7 files changed, 160 insertions(+), 82 deletions(-) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index 32901c8..fd08034 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..1d32491 --- /dev/null +++ b/SECURITY.md @@ -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//` 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. diff --git a/design/filesystem.md b/design/filesystem.md index dd881f9..4eb400d 100644 --- a/design/filesystem.md +++ b/design/filesystem.md @@ -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 `/digger/`, lowercase, no `Colony/` level | `/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 `/Colony/Digger/` and its +preferences in `/Colony/Digger/`; `History::migrate_legacy_db` copies +the database, with its WAL sidecars, from the lowercase `/digger/` it used +before, and leaves the original where it was. ### Migrating a program diff --git a/design/navigation.md b/design/navigation.md index 95cf953..2f8c6ce 100644 --- a/design/navigation.md +++ b/design/navigation.md @@ -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 @@ -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. @@ -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, @@ -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 @@ -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. diff --git a/design/releases.md b/design/releases.md index 2d66192..09e67c2 100644 --- a/design/releases.md +++ b/design/releases.md @@ -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 diff --git a/design/repository-layout.md b/design/repository-layout.md index a1f4d46..dbe2053 100644 --- a/design/repository-layout.md +++ b/design/repository-layout.md @@ -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. diff --git a/design/settings-page.md b/design/settings-page.md index 0f442f1..26a11a2 100644 --- a/design/settings-page.md +++ b/design/settings-page.md @@ -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 @@ -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 @@ -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.