The IPC "emit" method's no-source path took a bare event+data and sent it
straight to emit_tx tagged AdapterSource::System with zero validation of
the event name. Any same-UID process on the socket could send e.g.
{"event":"bread.power.ac.connected",...} and have it delivered to every
Lua subscriber indistinguishable from a real adapter event, since System
is the same tag the daemon uses for its own trusted, Rust-originated
sends (bread.system.startup, bread.profile.activated).
Fix, scoped to the actual threat model (same-UID Unix socket trust means
there's no way to cryptographically distinguish "the real bread-cli
binary" from any other local process, so a generic connection-identity
handshake would be theater):
- New AdapterSource::Manual tag for the no-source emit path. System is
now reserved for daemon-internal, Rust-code-originated sends only and
can never again be produced from data that arrived over the wire.
- The event name is rejected if its top-level dotted segment is one of
the reserved, adapter-owned domains (RESERVED_DOMAINS in
bread-shared/src/apps.rs) -- extended with bluetooth/workspace/window/
monitor, event families the Hyprland and Bluetooth adapters already
publish under but that were missing from that list. Freely-named
custom/test event names are untouched, so `bread emit <name>` and
bread-emit's fire-and-forget single-line-write design keep working
exactly as documented.
Bumped API_VERSION to 1.5.0 and updated Documentation.md's IPC emit
section and Namespaces reserved-domains list accordingly.
Also fixed a subscribe/emit race that surfaced while adding regression
tests for this: events.subscribe's ack is written to the client before
the server task actually registers on the broadcast channel, so a test
that emits immediately after reading the ack can race the registration.
Added a settle delay plus an explicit timeout (instead of an unbounded
read loop) so a future regression fails the test instead of hanging the
whole binary.
- modules_mgmt.rs: reject module names containing path separators, `..`,
or absolute paths before joining onto modules_dir (install_from_local,
remove_module, read_module_manifest); adds canonicalized containment
check as defense in depth. Manifest-supplied names and CLI args were
previously joined unsanitized, allowing path traversal on install/remove.
- breadd/src/lua/mod.rs: bread.exec now runs via `sh -c` instead of
`sh -lc`; no documented reason was found for login-shell semantics.
- Unify the two independently hand-written glob matchers (subscription
dispatch in breadd/src/core/subscriptions.rs vs. the CLI --filter path
in breadd/src/ipc/mod.rs) into one implementation in
bread-shared/src/glob.rs, used by both call sites.
- Remove the dead bread-sync/ tree (already excluded from the workspace
and fully unreferenced) and its stale PKGBUILD deps (libgit2, git
optdepend) and packaging docs mention.
- Correct the version-number transposition bug ("6.2.0" instead of
"0.6.2"/"0.6.6") across bread-shared, breadd, and bread-cli Cargo.toml,
and fix PKGBUILD's stale pkgver, so Cargo.toml/doctor/PKGBUILD all agree
with the latest git tag (v0.6.6).
P1-A: normalizer derives `online` from rtnetlink event kind (link.up/down,
route.default.changed, address.added/removed) so bread.network.connected
fires correctly on all systems using rtnetlink.
P1-B: stream_events consumes the subscribe ack before the event loop so the
first line is not printed as garbage.
P1-C: UPowerAdapter::probe() validates D-Bus synchronously before committing;
the sysfs fallback now actually triggers when D-Bus is unavailable.
P2-A: profile.list returns the full profile state (active + history) instead
of the always-empty profiles map.
P2-B: profile history capped at 50 entries in both StateCommand and
apply_event_to_state to prevent unbounded growth.
P2-C: RtnetlinkAdapter::new() no longer spawns an orphaned tokio task;
it validates availability by constructing and immediately dropping the
connection tuple.
P2-D: Lua-side hyprland_request_socket() logs a warn when multiple
Hyprland instances are found, matching the adapter-side behaviour.
P2-E: Malformed JSON from an IPC client returns an error response and
continues rather than closing the entire connection.
P3-A: Remove the `ends_with(".*")` prefix-match shortcut from both the
subscription table and the IPC event filter. `bread.*` now means
one segment (matching documented API semantics: `* = one segment`).
Tests updated accordingly.
P4-A: Remove unused `git2` and `glob` workspace dependencies (left over
from bread-sync extraction).
P4-B: breadd dev-dependency `tempfile` declared via workspace = true.
P4-C: Remove unreachable XDG_CONFIG_HOME branch in modules_dir(); dirs
already reads that var internally before returning None.
P4-D: Delete duplicate send_request_with_stream(); print_doctor() now
uses socket.exists() + send_request() directly.
P5-A: release.yml drops `--lib` from cargo test so integration tests run
in the release gate.
P6-A: bluetooth_spawn / bluetooth_query replace expect() on tokio runtime
construction with error logging / error propagation.
P6-B: Spin loops in lua/mod.rs add std:🧵:yield_now() after the
PAUSE hint to reduce CPU burn under sustained RwLock contention.
P6-C: All Mutex::lock().expect("... poisoned") in lua/mod.rs replaced with
unwrap_or_else(|e| e.into_inner()) for poison recovery.
P7-B: bread.system.startup event moved from main.rs into ipc::Server::serve()
so it fires after the socket is bound (smaller race window for early
subscribers).
Security:
- Remove `bread modules install github:…`. Remote fetch pulled unreviewed
third-party Lua and ran it with full bread.exec() privileges in an
unsandboxed runtime. Module install is now local-only; parse_source
rejects github:/git: with an explicit message.
bread-sync extracted from the workspace (parked for its own project):
- Removed from workspace members (now excluded); see bread-sync/EXTRACTION.md
- Removed the entire `bread sync` CLI surface and now-unused deps
(bread-sync, reqwest, tar, flate2; tempfile demoted to dev-dependency)
- Removed the sync.status IPC method from breadd plus its integration tests
- Moved the generic `expand_path` helper into bread-shared (with unit tests)
CI now actually runs and gates quality:
- Trigger on master/dev (was `main` — CI had never run, not once)
- Added `cargo fmt --check` and `clippy -D warnings`; fixed 4 clippy warnings
- Dropped the macOS matrix entry (breadd is Linux-only: udev/rtnetlink);
added the libudev-dev system dependency the Linux build needs
Hardening / honesty:
- New ipc test: daemon survives repeated reloads and the event pipeline
resumes (the prior suite only had a single happy-path reload check)
- Docs scrubbed of sync across README/Documentation/Overview/DAEMON
- "production-ready" and "compositor-agnostic" claims reworded to match
reality rather than aspiration
Note: bread-sync/src/export.rs held pre-existing local WIP authored outside
this change set and is intentionally excluded from this commit.
- Introduced a new mechanism in EventNormalizer to suppress duplicate events from child nodes of the same physical device.
- Removed the device classification logic from the normalizer and replaced it with a rule-based system using Lua scripts.
- Added support for user-defined device rules in Lua, allowing for flexible device naming based on various conditions.
- Updated the state engine to handle device rules and resolve device names before dispatching events.
- Modified the installation script to set up default configuration files for the daemon and Lua modules.
- Improved the handling of systemd user services to dynamically set the ExecStart path based on the installation directory.
- Introduced `bread-sync` module with core functionalities for syncing system state via Git.
- Implemented `MachineProfile` struct for managing machine profiles, including methods for reading and writing profiles.
- Added package management support with snapshot capabilities for `pacman`, `pip`, `npm`, and `cargo`.
- Created comprehensive tests for sync operations, package parsing, and machine profile management.
- Enhanced `udev` adapter to include vendor and product IDs for scanned devices.
- Updated state engine to handle module clearing commands.
- Introduced Lua integration for accessing machine information and file system operations.
- Improved packaging documentation for Arch Linux and systemd service setup.