Infrastructure hardening: CI enforcement, supply chain, updater, diagnostics #14
@@ -0,0 +1,103 @@
|
||||
name: Bug report
|
||||
about: Something behaves incorrectly
|
||||
title: ""
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Security vulnerabilities do **not** go here — see
|
||||
[SECURITY.md](../../SECURITY.md).
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened
|
||||
description: What you did, what you expected, and what you got instead.
|
||||
placeholder: |
|
||||
1. Opened an album from the Music library
|
||||
2. Tapped the third track
|
||||
3. Playback started from the first track instead
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: JellyTau version
|
||||
description: Settings scrolls to the bottom, or the filename you installed.
|
||||
placeholder: "0.9.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
options:
|
||||
- Linux (AppImage)
|
||||
- Linux (deb)
|
||||
- Linux (rpm)
|
||||
- Linux (Arch package)
|
||||
- Windows
|
||||
- Android
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Playback questions
|
||||
|
||||
If this involves playback, these three answers decide which of several
|
||||
very different code paths you were on. "I don't know" is a fine answer.
|
||||
|
||||
- type: dropdown
|
||||
id: source
|
||||
attributes:
|
||||
label: Was the media streaming or downloaded?
|
||||
options:
|
||||
- Streaming from the server
|
||||
- Downloaded for offline use
|
||||
- Not playback-related
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: transcode
|
||||
attributes:
|
||||
label: Was the server transcoding?
|
||||
description: Jellyfin's dashboard shows this while something is playing.
|
||||
options:
|
||||
- Direct play
|
||||
- Transcoding
|
||||
- Don't know
|
||||
- Not playback-related
|
||||
|
||||
- type: dropdown
|
||||
id: kind
|
||||
attributes:
|
||||
label: Music or video?
|
||||
options:
|
||||
- Music
|
||||
- Video (movie)
|
||||
- Video (TV episode)
|
||||
- Not playback-related
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |
|
||||
Android: `adb logcat | grep -i jellytau`.
|
||||
Linux: run from a terminal, or `RUST_LOG=debug jellytau` for more.
|
||||
In the app, `localStorage.setItem("jellytau:logLevel","debug")` in the
|
||||
webview console turns the frontend up too.
|
||||
render: shell
|
||||
|
||||
- type: textarea
|
||||
id: server
|
||||
attributes:
|
||||
label: Jellyfin server
|
||||
description: Version, and anything unusual about the library layout.
|
||||
placeholder: "10.9.11, series stored without season folders"
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Feature request
|
||||
about: Suggest something JellyTau should do
|
||||
title: ""
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: What are you trying to do?
|
||||
description: |
|
||||
The situation, not the solution. "I listen to albums in a fixed order and
|
||||
lose my place when I switch devices" tells us more than "add a sync
|
||||
button", and often has a better answer than the one you had in mind.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: What would you like it to do?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Which platforms does this matter on?
|
||||
multiple: true
|
||||
options:
|
||||
- Linux
|
||||
- Windows
|
||||
- Android
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Anything you have tried, or how other clients handle it
|
||||
@@ -0,0 +1,22 @@
|
||||
## What and why
|
||||
|
||||
<!-- What changes, and the reason. The diff shows the what; the why is what
|
||||
the commit log is for. -->
|
||||
|
||||
## How it was verified
|
||||
|
||||
<!-- What you actually ran or clicked. "Tests pass" on its own says little;
|
||||
"played a transcoded episode on Android, seeked twice, backgrounded it"
|
||||
says a lot. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `bun run check`, `bun run test`, `bun run format:check`, `bun run lint`
|
||||
- [ ] `cargo fmt`, `cargo clippy --all-targets -- -D warnings`, `cargo test`
|
||||
- [ ] `bun run check:boundary` — no Jellyfin taxonomy in the frontend
|
||||
- [ ] New requirement-implementing code carries a `TRACES:` comment, and every
|
||||
ID it names exists in `docs/requirements.md` (`bun run traces:validate`)
|
||||
- [ ] **Bug fix:** a test that reproduces it was written *first* and observed
|
||||
failing before the fix
|
||||
- [ ] Android source edits were made in `src-tauri/android/src` and synced with
|
||||
`scripts/sync-android-sources.sh` (never edit `gen/` directly)
|
||||
@@ -0,0 +1,47 @@
|
||||
# Code of Conduct
|
||||
|
||||
## The short version
|
||||
|
||||
Be decent to people. Assume the person you are talking to is acting in good
|
||||
faith and knows things you do not.
|
||||
|
||||
## What that means here
|
||||
|
||||
**Expected:**
|
||||
|
||||
- Criticise code, decisions and ideas — not the people who wrote them.
|
||||
- Accept that "no" is a complete answer. This is a small project with a
|
||||
maintainer who has finite time; a declined feature request is not a slight.
|
||||
- Give people room to be new. Everyone was once confused by Tauri's IPC.
|
||||
- Assume a bug report is someone trying to help, even when it arrives terse or
|
||||
frustrated.
|
||||
|
||||
**Not accepted:**
|
||||
|
||||
- Harassment, personal attacks, or demeaning remarks — including about someone's
|
||||
identity, background, or level of experience.
|
||||
- Sexualised language or imagery, and unwelcome attention of any kind.
|
||||
- Publishing someone's private information without their permission.
|
||||
- Persistently derailing discussions, or badgering people who have already
|
||||
answered you.
|
||||
|
||||
## Scope
|
||||
|
||||
This applies in the issue tracker, pull requests, commit messages and any other
|
||||
project space, and to anyone taking part — maintainer included.
|
||||
|
||||
## Reporting
|
||||
|
||||
Email **duncan@tourolle.paris**. Reports are read by the maintainer and handled
|
||||
privately.
|
||||
|
||||
Responses range from a quiet word through to removing comments or blocking an
|
||||
account, depending on what happened. If a report concerns the maintainer, and
|
||||
that makes reporting to them pointless, you are free to say so publicly — a
|
||||
project this size has no separate committee to appeal to, and pretending
|
||||
otherwise would be dishonest.
|
||||
|
||||
## Attribution
|
||||
|
||||
Adapted in spirit from the [Contributor Covenant](https://www.contributor-covenant.org),
|
||||
shortened to what a single-maintainer project can actually honour.
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
# Contributing to JellyTau
|
||||
|
||||
Thanks for looking. This file is the short version of how the project is built
|
||||
and what has to be true before a change lands. The long version lives in
|
||||
[CLAUDE.md](CLAUDE.md) and [docs/architecture/](docs/architecture/README.md),
|
||||
which are maintained rather than decorative — read them before a structural
|
||||
change.
|
||||
|
||||
## Getting set up
|
||||
|
||||
Package manager is **bun**. You will also need a Rust toolchain (the exact
|
||||
version is pinned in [src-tauri/rust-toolchain.toml](src-tauri/rust-toolchain.toml)
|
||||
— rustup honours it automatically) and the Tauri Linux dependencies.
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run hooks:install # do this once: it enables the pre-commit gates
|
||||
bun run tauri dev
|
||||
```
|
||||
|
||||
`hooks:install` points `core.hooksPath` at [scripts/hooks/](scripts/hooks/), so
|
||||
hook updates arrive with a `git pull` instead of needing a re-install.
|
||||
|
||||
## What has to pass
|
||||
|
||||
Everything below runs in CI, and the fast half runs in the pre-commit hook. None
|
||||
of it is advisory:
|
||||
|
||||
```bash
|
||||
bun run check # svelte-check — 0 errors
|
||||
bun run test # vitest
|
||||
bun run format:check # prettier
|
||||
bun run lint # eslint — 0 errors; the warning count is a ratchet
|
||||
bun run check:boundary # no Jellyfin taxonomy in the frontend
|
||||
bun run test:rust # cargo test
|
||||
cd src-tauri && cargo fmt --all && cargo clippy --all-targets -- -D warnings
|
||||
cd src-tauri && cargo deny check # advisories, licences, bans, sources
|
||||
```
|
||||
|
||||
`bun run test:all` runs the whole set.
|
||||
|
||||
Several of these are **ratchets** — a number that only ever moves in the
|
||||
improving direction:
|
||||
|
||||
| Ratchet | Where | Rule |
|
||||
|---|---|---|
|
||||
| eslint `--max-warnings` | [.gitea/workflows/build-and-test.yml](.gitea/workflows/build-and-test.yml) | only goes down |
|
||||
| Coverage thresholds | [vitest.config.ts](vitest.config.ts) | only go up |
|
||||
| Traceability coverage | [.gitea/workflows/traceability-check.yml](.gitea/workflows/traceability-check.yml) | only goes up |
|
||||
|
||||
Never relax one to make a build pass. Fix the thing it caught.
|
||||
|
||||
## The two rules that surprise people
|
||||
|
||||
**1. Bug fixes start with a failing test.** Write a test that reproduces the bug
|
||||
and *watch it fail* before you touch the fix. A test written against
|
||||
already-fixed code can pass for the wrong reason and guards nothing. If the logic
|
||||
is trapped in a component, extract the pure part into a plain `.ts` module and
|
||||
test that — see `episodeStrip.ts` or `TrackList.logic.ts` for the pattern.
|
||||
|
||||
**2. Domain vocabulary lives in Rust.** The frontend is presentation-only. It
|
||||
must not encode Jellyfin's *taxonomy* — for example, the set of item types that
|
||||
makes up a category like "Music". Send an opaque scope across the IPC boundary
|
||||
and let the backend expand it. `bun run check:boundary` is a tripwire, not a
|
||||
proof: it only flags item-type array literals, so a green run does not mean you
|
||||
are clear. [docs/specs/scoped-search-boundary.md](docs/specs/scoped-search-boundary.md)
|
||||
describes the leak that made this a rule.
|
||||
|
||||
## Traceability
|
||||
|
||||
Code that implements a requirement carries a `TRACES:` comment naming the
|
||||
requirement IDs, and a tool builds the matrix from those comments:
|
||||
|
||||
```rust
|
||||
/// TRACES: UR-005 | DR-001
|
||||
```
|
||||
|
||||
Every ID must exist as a row in [docs/requirements.md](docs/requirements.md) —
|
||||
`bun run traces:validate` fails on a typo or a stale rename. Internal helpers and
|
||||
requirement-less code stay untraced; do not sprinkle IDs to raise the number.
|
||||
|
||||
If you add a requirement, add its row. If you implement one, tag the code.
|
||||
|
||||
## Commits and pull requests
|
||||
|
||||
- Conventional-commit subjects: `fix(player): …`, `feat(updater): …`, `ci: …`.
|
||||
- Explain **why** in the body, not what the diff already shows. The commit log
|
||||
is the main record of why things are the way they are here, and it is used to
|
||||
draft release notes.
|
||||
- One concern per commit. A formatting sweep and a behaviour change in the same
|
||||
commit is unreviewable.
|
||||
- Rebase rather than merge-commit onto `master`.
|
||||
|
||||
## Specs
|
||||
|
||||
New features start from [docs/specs/SPEC-TEMPLATE.md](docs/specs/SPEC-TEMPLATE.md).
|
||||
Its "Layer assignment" section is the point: each piece of logic gets placed in
|
||||
Rust or the frontend *with a reason*. Review against
|
||||
[docs/specs/SPEC-REVIEW-CHECKLIST.md](docs/specs/SPEC-REVIEW-CHECKLIST.md). Do
|
||||
not frame a spec around "no Rust changes required" — correct placement is the
|
||||
goal, not minimal backend churn.
|
||||
|
||||
## CI
|
||||
|
||||
CI is **Gitea Actions** (`.gitea/workflows/`), not GitHub.
|
||||
|
||||
🔴 **CI installs no system tools.** Every build, test and packaging tool must
|
||||
already be in the Docker builder image. If a job needs a tool the image lacks,
|
||||
add it to [Dockerfile.builder](Dockerfile.builder), rebuild and push the image,
|
||||
and pin the new tag — do not `apt-get` it at job time. Details in
|
||||
[docs/build/ci-operations.md](docs/build/ci-operations.md).
|
||||
|
||||
Fetching the project's own declared dependencies (`bun install`, cargo crates,
|
||||
an advisory database) is not a toolchain install and is fine.
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Use the issue templates. For anything involving playback, include what the
|
||||
platform was, whether the media was streaming or downloaded, and whether it was
|
||||
transcoding — those three answers determine which of several code paths you were
|
||||
actually on.
|
||||
|
||||
Security issues go to [SECURITY.md](SECURITY.md), not the tracker.
|
||||
@@ -47,6 +47,29 @@ For the full set of build, test, and Android helper scripts, see
|
||||
| Traceability tooling & CI | [docs/traceability.md](docs/traceability.md), [docs/traceability-ci.md](docs/traceability-ci.md) |
|
||||
| Release checklist | [docs/release-checklist.md](docs/release-checklist.md) |
|
||||
| UX flows | [docs/ux-flows.md](docs/ux-flows.md) |
|
||||
| CI operations (builder image, secrets, runner) | [docs/build/ci-operations.md](docs/build/ci-operations.md) |
|
||||
|
||||
## Contributing
|
||||
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) covers the setup, the gates a change has to
|
||||
pass, and the two rules that catch people out (bug fixes start with a failing
|
||||
test; Jellyfin's taxonomy stays in Rust). Please also read the
|
||||
[Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
Found a security problem? Do not open an issue — see [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Verifying a download
|
||||
|
||||
Every release publishes `SHA256SUMS` covering all of its artifacts, plus an SBOM
|
||||
of what went into the build:
|
||||
|
||||
```bash
|
||||
sha256sum -c SHA256SUMS
|
||||
```
|
||||
|
||||
Desktop builds update themselves from Settings → Updates, verifying each payload
|
||||
against JellyTau's signing key before installing. Android installs are handled by
|
||||
the system installer, so the app links to the releases page instead.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Email **duncan@tourolle.paris** with `[JellyTau security]` in the subject.
|
||||
Please do **not** open a public issue for a vulnerability — JellyTau handles
|
||||
Jellyfin credentials and media, and an unfixed issue in a public tracker is an
|
||||
advisory for everyone running it.
|
||||
|
||||
Include what you have: what the problem is, how to reproduce it, the version and
|
||||
platform, and what you think an attacker could do with it. A rough report is
|
||||
worth more than a polished one that never gets sent.
|
||||
|
||||
You can expect an acknowledgement within a week. If a fix is warranted it will
|
||||
ship in the next release, and you will be credited in the release notes unless
|
||||
you would rather not be.
|
||||
|
||||
## Supported versions
|
||||
|
||||
JellyTau is a single-maintainer project without long-term support branches.
|
||||
**Only the latest release receives fixes.** Desktop builds can update themselves
|
||||
(Settings → Updates); on Android, install the latest APK from the releases page.
|
||||
|
||||
## What is in scope
|
||||
|
||||
The application and its build pipeline:
|
||||
|
||||
- The Tauri backend (`src-tauri/`) and the Svelte frontend (`src/`)
|
||||
- Credential storage — the system keyring and its encrypted-file fallback
|
||||
- The Android player service and its JNI bridge
|
||||
- The loopback media server used for downloaded playback
|
||||
- The release pipeline: artifact signing, the update manifest, the builder image
|
||||
|
||||
**Out of scope:** vulnerabilities in Jellyfin itself (report those to the
|
||||
Jellyfin project), and issues that require an already-compromised device or a
|
||||
malicious server the user deliberately configured and trusted.
|
||||
|
||||
## What the project already does
|
||||
|
||||
Not a guarantee, but so you know what has been considered:
|
||||
|
||||
- **Credentials** never go in plaintext config: the system keyring is used where
|
||||
available, with an AES-GCM encrypted file as fallback (see
|
||||
[docs/architecture/09-security.md](docs/architecture/09-security.md)).
|
||||
- **The webview runs under a restrictive CSP**, and the asset protocol is scoped
|
||||
to the thumbnail cache directory only.
|
||||
- **Path confinement** is enforced on the cache and download roots — a
|
||||
server-supplied id cannot decide where a file lands (DR-210, DR-211).
|
||||
- **Queries and URLs bind or encode their inputs** rather than interpolating
|
||||
them (DR-212).
|
||||
- **Dependencies are scanned on every build** by `cargo deny` against the RustSec
|
||||
advisory database, and licence-checked against an allow-list (DR-216).
|
||||
- **Releases carry `SHA256SUMS` and an SBOM**, so you can verify a download and
|
||||
find out what went into it.
|
||||
- **Desktop updates are signature-verified** against a key held only in CI before
|
||||
anything is installed (DR-217).
|
||||
|
||||
Windows installers are **not** Authenticode-signed — SmartScreen will warn on
|
||||
first run. That is a cost and identity problem, not an oversight; verify the
|
||||
download against `SHA256SUMS` instead.
|
||||
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
# CI operations
|
||||
|
||||
How the pipeline is kept working: the builder image, the secrets it needs, the
|
||||
gates that must stay required, and the things that only a human with access to
|
||||
the Gitea instance can do.
|
||||
|
||||
CI is **Gitea Actions** (`.gitea/workflows/`) on `gitea.tourolle.paris`, not
|
||||
GitHub.
|
||||
|
||||
## The workflows
|
||||
|
||||
| Workflow | Trigger | What it protects |
|
||||
|---|---|---|
|
||||
| [build-and-test.yml](../../.gitea/workflows/build-and-test.yml) | push/PR to `master` | Frontend + Rust gates, Android compile check, supply chain |
|
||||
| [traceability-check.yml](../../.gitea/workflows/traceability-check.yml) | push/PR | Requirement coverage ratchet, dangling IDs |
|
||||
| [build-release.yml](../../.gitea/workflows/build-release.yml) | tag `v*` | Builds, signs, publishes, and writes the update manifest |
|
||||
| [publish-docs.yml](../../.gitea/workflows/publish-docs.yml) | push to `master` | Docs site on the `gitea-pages` branch |
|
||||
| [runner-health.yml](../../.gitea/workflows/runner-health.yml) | daily 07:00 UTC | Runner disk before it fills |
|
||||
|
||||
## 🔴 CI installs no system tools
|
||||
|
||||
Every build, test and packaging **tool** lives in the Docker image the job runs
|
||||
in. Never add `apt-get`, `rustup`, `sdkmanager`, or a `curl | tar -xz` of a
|
||||
binary to a workflow step.
|
||||
|
||||
Fetching the project's *own declared dependencies* is not a toolchain install and
|
||||
is fine: `bun install`, cargo pulling crates from the lockfile, `cargo deny`
|
||||
fetching the RustSec advisory database. The distinction is tool versus data.
|
||||
|
||||
This rule has been broken twice, both times invisibly until something else
|
||||
failed. `publish-docs.yml` downloaded mdBook from GitHub releases into
|
||||
`/usr/local/bin` at job time — a hard dependency on GitHub's CDN being up
|
||||
whenever docs were published. Both mdBook and the supply-chain tools are in the
|
||||
image now.
|
||||
|
||||
## The builder image
|
||||
|
||||
`Dockerfile.builder` → `gitea.tourolle.paris/dtourolle/jellytau-builder`.
|
||||
It carries: the pinned Rust toolchain plus rustfmt/clippy and the Android,
|
||||
Windows-MSVC targets; bun and Node; the Android SDK/NDK and a local Gradle
|
||||
distribution; Linux desktop and packaging deps (WebKitGTK, libmpv, rpm, NSIS,
|
||||
cargo-xwin); and the tooling — `cargo-deny`, `cargo-cyclonedx`, `mdbook`.
|
||||
|
||||
Arch packages build in a separate `Dockerfile.arch`, because `makepkg` is
|
||||
Arch-specific.
|
||||
|
||||
### Tags are pinned, and why
|
||||
|
||||
Workflows name an **immutable dated tag** (`:2026.08`), never `:latest`. While
|
||||
every job said `:latest`, rebuilding the image silently changed what every build
|
||||
compiled against — including a rebuild of an old release tag, which is the
|
||||
opposite of reproducible.
|
||||
|
||||
`:latest` is still pushed alongside, for local `docker compose` runs and manual
|
||||
pulls.
|
||||
|
||||
Date tags rather than per-commit SHA tags on purpose: the runner shares a 74 GB
|
||||
disk with two other projects, and SHA-tagged images accumulated there until it
|
||||
filled. Keep a couple of dated tags live and prune the rest.
|
||||
|
||||
### Changing the image
|
||||
|
||||
The order matters — CI breaks if the workflow lands before the image exists.
|
||||
|
||||
```bash
|
||||
# 1. Edit Dockerfile.builder. Put new tools in the TRAILING layer: it exists so
|
||||
# a tool change is a ~2 min rebuild instead of ~15.
|
||||
# 2. Build and push, tagged with the new month:
|
||||
./scripts/build-builder-image.sh 2026.09
|
||||
# 3. Repoint every workflow at the new tag, in the same commit as whatever
|
||||
# needed the new tool:
|
||||
sed -i 's|jellytau-builder:2026.08|jellytau-builder:2026.09|g' .gitea/workflows/*.yml
|
||||
# 4. Verify the tools are actually in it:
|
||||
docker run --rm gitea.tourolle.paris/dtourolle/jellytau-builder:2026.09 \
|
||||
-c "cargo deny --version; mdbook --version"
|
||||
```
|
||||
|
||||
🔴 The Rust version is pinned in **two** places that must agree:
|
||||
`RUST_VERSION` in `Dockerfile.builder` and `channel` in
|
||||
`src-tauri/rust-toolchain.toml`. If they drift, rustup downloads the pinned
|
||||
toolchain inside the job — a toolchain install in CI. Bump both, rebuild, push,
|
||||
then merge.
|
||||
|
||||
## Secrets
|
||||
|
||||
Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI.
|
||||
|
||||
| Secret | Used by | Notes |
|
||||
|---|---|---|
|
||||
| `ANDROID_KEYSTORE_BASE64` | release | Base64 of the release keystore |
|
||||
| `ANDROID_KEYSTORE_PASSWORD` | release | |
|
||||
| `ANDROID_KEY_ALIAS` | release | |
|
||||
| `ANDROID_KEY_PASSWORD` | release | |
|
||||
| `TAURI_SIGNING_PRIVATE_KEY` | release | minisign key for the desktop updater |
|
||||
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | release | |
|
||||
| `GITEA_TOKEN` | release, docs | PAT; falls back to the auto-provided token |
|
||||
|
||||
The updater keypair's public half is committed in `src-tauri/tauri.conf.json` —
|
||||
that one is meant to be public; it is what clients verify against. The private
|
||||
half exists in the Gitea secret and in the maintainer's local `.env` (which is
|
||||
gitignored) and at `~/.tauri/jellytau.key`.
|
||||
|
||||
**Losing the private key means losing the ability to ship updates to installed
|
||||
desktop clients**, because they will only accept payloads signed by the key
|
||||
matching the public key they were built with. Recovering means generating a new
|
||||
pair, shipping a build carrying the new public key, and telling everyone on an
|
||||
older build to reinstall by hand. Back it up.
|
||||
|
||||
## Required status checks
|
||||
|
||||
Gitea → repo Settings → Branches → protect `master`, requiring:
|
||||
|
||||
- `Run Tests`
|
||||
- `Android Compile Check`
|
||||
- `Supply Chain`
|
||||
- the traceability job
|
||||
|
||||
Without branch protection, every gate in this document is advisory: a push
|
||||
straight to `master` lands whether or not CI is red. That is the state the repo
|
||||
was in for its whole history before this was set up.
|
||||
|
||||
## The runner
|
||||
|
||||
One self-hosted runner, one ~74 GB disk shared with two other projects. It fills.
|
||||
`runner-health.yml` reports usage daily and fails above 90%.
|
||||
|
||||
When it does fill:
|
||||
|
||||
```bash
|
||||
docker image prune -a
|
||||
docker volume prune -a # the -a matters: without it, NAMED volumes are kept,
|
||||
# which is exactly how this filled up unnoticed
|
||||
```
|
||||
|
||||
Never cache `src-tauri/target` — it is ~16 GB, and caching it under several keys
|
||||
is what filled the disk at ~1.15 GB/day. The workflows cache only the cargo
|
||||
registry index and `.crate` tarballs; cargo re-extracts `registry/src` for free.
|
||||
|
||||
## Release verification
|
||||
|
||||
The steps that catch a broken release before users do are in
|
||||
[release-checklist.md](../release-checklist.md) — in particular the update path:
|
||||
`latest.json` must be live on the `updater` branch, both platform entries must
|
||||
carry a non-empty signature, and the previous release should be installed and
|
||||
asked to update to the new one.
|
||||
|
||||
## Bus factor
|
||||
|
||||
The Gitea instance holds the canonical remote, the signing secrets, the container
|
||||
registry and the CI runner. **It is not backed up as part of this repository, and
|
||||
nothing in this repository can restore it.** That is the largest single risk to
|
||||
the project — larger than any gate in this document — and the backup lives
|
||||
outside it.
|
||||
Reference in New Issue
Block a user