breadcast shipped with a full bakery.toml + CI workflows but was missing
from registry/bread-ecosystem.toml and had zero Forgejo Actions secrets
configured, so its release workflows would have failed closed (or worse,
published unsigned on an older workflow shape) the first time they ran.
Neither gap was visible until checked by hand.
doctor-channels.sh now also flags any registry product's repo missing the
BAKERY_MINISIGN_SEC_KEY_PATH secret (soft-skipped without a local Forgejo
token). onboard-product.sh handles the one genuine write step — adding a
[[products]] entry — then runs doctor-channels.sh so nothing else gets
missed silently again. Also fixes a pre-existing false positive where the
local-checkout drift scan didn't recognize worktree checkouts of
bread-ecosystem itself beyond the one literal "-fix-worktree" suffix it
special-cased.
Track switches now always take effect on `update --all` instead of
silently no-op'ing or permanently refusing on strict semver comparison.
`remove` no longer aborts cleanup on the first failed binary removal,
orphaning the systemd unit. State reads/writes are now lock-protected
and go through fsync'd atomic writes (also fixes a temp-path collision
in binary installs). The index loader falls back to a stale-but-signed
cache instead of hard-failing offline. systemd units now re-fetch on
every update instead of freezing after first install. `doctor` now
flags missing recorded binaries.
Security hardening: path-traversal guard on all index-controlled
filenames, archive extraction now rejects symlink/traversal entries
before tar touches disk, archive temp files use secure unique paths,
post_install hooks are gated behind --no-hooks/confirmation, response
buffering is capped, empty-checksum downloads get a clear error, and
both stable-track CI workflows now hard-fail on a missing signing key
(matching the existing dev/rc guard) instead of silently publishing an
index next to a stale signature. gen-index.sh now publishes the index
and its signature atomically.
Also: bakery install on an already-installed package no longer
silently reinstalls/downgrades, cmd_update exits non-zero for unknown
packages, and the unused toml dependency is removed.
Closes the packaging gap found while moving bread-ecosystem apps off
pacman onto bakery-only distribution: pacman's package() typically installs
a LICENSE file and, for GUI/onboarding apps, a .desktop entry and sometimes
a data directory (e.g. breadhelp's guide content). All three follow the
same download-verify-place pattern ConfigScaffold.example already
established:
- license_file -> ~/.local/share/licenses/<name>/LICENSE
- desktop_file -> ~/.local/share/applications/<name>.desktop
- data_archive -> a .tar.gz extracted to ~/.local/share/<name>/ (for
arbitrary data too big/structured for a single file, via `tar`)
gen-index.sh parses all three from bakery.toml, hashes the artifact, and
now excludes them from the binaries-collection loop (previously undetected
gap: they'd have been swept in as fake "binaries" with no checksum, same
class of bug the existing .toml/.service/etc exclusions guard against).
Also registers breadhelp as a bakery-channel product.
Adds a track concept to bakery (separate from the existing bakery/pacman
distribution channel): stable (unchanged tag-triggered releases), beta
(deliberate beta-v* tag promotion), and dev (published on every push to
dev). Each track gets its own signed index + artifact tree under
dl.breadway.dev so stable's paths and existing installs are untouched.
- bakery: new Track type, a global track preference in installed.json
(defaults to stable via serde, no migration needed), `bakery track
show`/`set`, a BAKERY_INDEX_BASE_URL override for testing, and a real
semver comparison in `update` (was a plain string-equality check before).
ANSI-colored/aligned CLI output (TTY + NO_COLOR aware).
- gen-index.sh: TRACK env var selects which subtree to read/write.
- CI: dev-bakery.yml/beta-bakery.yml/dev-bread-theme.yml/beta-bread-theme.yml
publish those two products on the new tracks; dev/beta skip the GitHub
Release upload step (no per-commit release spam).
- docs/release-channels.md documents the three-track policy.
Signed release binaries ship a bakery-x86_64.minisig alongside the
binary. The binaries-collection loop already filtered out .sha256/
.toml/.service/.css/.txt sidecars but not .minisig, so the signature
file itself got listed as an installable binary with no sha256 —
bakery then refused to install it (checksum mismatch) whenever it
tried to update a package with a signed binary.
The old bakery-signing-key.minisign-sec on hestia was password-encrypted
and the password was lost, so scripts/gen-index.sh never actually signed
index.json (silent no-op warning). bakery/src/manifest.rs (0.3.0+) hard-
requires that signature, so every bakery command has been failing with
'fetching index.json.minisig — the index must be signed before it can be
trusted' since the signing enforcement shipped.
Generated a new no-password minisign keypair on hestia
(~/.secrets/bakery-signing-key-2.minisign-sec), updated the hardcoded
PUBKEY in manifest.rs and get.sh to match, wired
BAKERY_MINISIGN_SEC_KEY_PATH as a Forgejo Actions secret so future CI
releases sign automatically, and manually signed+published the current
index.json on hestia so bakery works immediately.
Replaces the per-repo .forgejo/workflows/mirror.yml + MIRROR_TOKEN
pattern with Forgejo's native Push Mirror feature, provisioned
centrally instead of per-repo:
- setup-push-mirrors.sh: reads the repo list live from the Forgejo API
(GET /users/Breadway/repos — confirmed Breadway is a user account,
not an org, so the /orgs/ endpoint 404s and this falls back
correctly) instead of a hardcoded repo list, checks each repo's
existing push_mirrors for idempotency, and POSTs a new one
(sync_on_commit + 8h interval) for any repo missing one. Private
repos are skipped by default (found novacana-engine on the live
account) since mirroring one to a public GitHub repo is a
disclosure decision this script should never make silently — pass
--include-private to override per-run. --dry-run prints every
request (GH token redacted) without POSTing.
- cleanup-old-mirror-workflows.sh: deletes mirror.yml from each repo's
default branch (live commit via the contents API, not a local
change) and removes the MIRROR_TOKEN secret. Refuses to run at all
— dry-run included — without an explicit
--i-have-verified-push-mirrors-work flag, since it should only ever
run after confirming the new push mirrors are actually syncing.
Neither script has been run for real. setup-push-mirrors.sh has only
been run with --dry-run against the live Forgejo API (read-only GETs);
cleanup-old-mirror-workflows.sh has not been run at all beyond
confirming its guardrail refuses to execute.
.github/workflows/release.yml built and published the bakery binary
itself, but it lived under .github/ and targeted runs-on:
[self-hosted, hestia] — a runner label only registered against
Forgejo, never against GitHub Actions. It has therefore never run;
get.sh has been pointing at dl.breadway.dev/bakery/... this whole time
with nothing actually publishing there.
Recreated the same logic as .forgejo/workflows/release-bakery.yml,
matching the sibling release-bread-theme.yml in this repo (manual
clone instead of actions/checkout, GH_RELEASE_TOKEN instead of the
GitHub-provided GITHUB_TOKEN, same dormant-until-provisioned minisign
signing step). Removed the dead .github copy.
- Add minisign-based signing/verification for the bakery index:
scripts/gen-index.sh signs index.json (MINISIGN_SEC_KEY env var, dormant
no-op with a loud warning until a key is provisioned); bakery/src/manifest.rs
fetches index.json.minisig and verifies it with minisign-verify against a
hardcoded PUBKEY before parsing/caching, and re-verifies the cached copy
on every load (falls back to one re-fetch if the cache predates signing
or fails verification; a fresh fetch that fails verification is a hard
error).
- Close the previously-unchecksummed config-example and systemd-unit
downloads in bakery/src/install.rs (scaffold_config, install_service):
index.json now carries `sha256`/`example_sha256` for these artifacts
(computed in gen-index.sh), verified via the same download::verify_sha256
used for binaries. Downloads without a matching sha256 in the index are
refused rather than installed unverified.
- scripts/get.sh now verifies the bakery release binary itself against a
pinned minisign public key before installing it (falls back to the
existing sha256-only check with a loud warning if no .minisig is
published yet or minisign isn't installed; a present-but-invalid
signature is a hard failure).
- Add dormant "sign release binary" steps to the bakery and bread-theme
release workflows (.github/workflows/release.yml,
.forgejo/workflows/release-bread-theme.yml), gated on secrets that are
not yet configured — binaries ship unsigned exactly as before until the
owner wires up the secret.
- .gitignore: add *.minisign-sec / minisign.key so the signing key can
never be committed by accident.
- bread-theme: fix stale docs describing a "Catppuccin Mocha fallback"
(BREAD_DESIGN_SYSTEM.md, README.md, Cargo.toml/bakery.toml/registry
descriptions) — the actual implementation (palette.rs) uses a fixed BOS
dark base with only accent colors from pywal.
- bread-theme: fix the legacy css_vars() path, which had its own
hand-written @define-color block that predated the `accent` and computed
`on-*` ink colors used by the rest of the stylesheet — any caller whose
CSS referenced those names against css_vars()'s output would hit
undefined colors (the illegible-text bug). css_vars() now delegates to
the same define_colors() the full stylesheet uses, so the two can't
drift apart again.
Critical fixes:
- gen-index.sh: emit services, config, optional_system_deps from bakery.toml;
parse product list from registry TOML instead of hardcoded array; fail loudly
when bakery.toml is missing (was silently producing empty metadata in prod)
- install.rs: download service units and example configs from dl server at
install time (were never fetched); check systemctl exit codes (were swallowed);
save state before file cleanup in remove_package (was inconsistent on error)
- doctor.rs: rewrite dep detection to use `pacman -Q` as primary (no more
dependency on `which` or pkg-config name mismatches); add optional_system_deps
support returning (missing, warnings) — warnings print but never block install
- get.sh: fix GitHub fallback URL (was 404 for both latest and versioned
releases); add SHA-256 checksum verification using published .sha256 file
High priority fixes:
- bakery doctor <unknown-pkg>: exit non-zero (was silently passing)
- bakery update: add --all flag (documented in README but missing from CLI);
add doctor gate before update (was bypassing dep check)
- bread_deps: now resolved recursively with cycle detection (was ignored)
- manifest.rs: add artifact_urls() helper and optional_system_deps field
- state.rs: atomic save via tmp+rename; cmd_info shows optional_system_deps
Tests: 17 new unit tests across doctor, download, install, state modules;
scripts/test-gen-index.sh fixture test for full pipeline
- bakery: strip -x86_64 / -aarch64 / -arm64 / -armv7 suffix when placing
binary so `breadcrumbs-x86_64` installs as `breadcrumbs`
- gen-index.sh: GH_BASE was "github.com/Breadway" but repo slugs already
include the org, producing doubled paths; change to "github.com"
- Add bakery.toml describing the bakery binary as an installable product
- gen-index.sh: check DL_DIR/<pkg>/bakery.toml first (written by each
product's release workflow), fall back to sibling checkout for local dev
- gen-index.sh: include bakery itself in the products list
- release.yml: use GITHUB_WORKSPACE instead of ECOSYSTEM_DIR (the
bread-ecosystem runner IS the checkout, no separate clone needed)
- release.yml: copy bakery.toml to DL_DIR during artifact prep