Wire breadlock to the bread event bus (app id lock)
Emit bread.lock.locked when ext-session-lock-v1 is accepted and bread.lock.unlocked after PAM success. Fail-silent if breadd is down. No command verbs (no pin/blur stubs). breadgreet is not on the bus. Note that Forgejo Arch registry packages are unsigned (SigLevel=Never on BOS) without changing the package.yml registry.
This commit is contained in:
parent
0937c3b05c
commit
2a541ca5e9
9 changed files with 105 additions and 0 deletions
|
|
@ -7,6 +7,10 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
package:
|
package:
|
||||||
runs-on: [self-hosted, hestia]
|
runs-on: [self-hosted, hestia]
|
||||||
|
# Forgejo's Arch package registry does not GPG-sign packages for pacman.
|
||||||
|
# BOS therefore uses SigLevel=Never on [Breadway.os.git.breadway.dev]
|
||||||
|
# until a signed repo exists. Keep publishing here — do not flip this
|
||||||
|
# job to a different registry just to get signatures.
|
||||||
container:
|
container:
|
||||||
image: archlinux:latest
|
image: archlinux:latest
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
24
Cargo.lock
generated
24
Cargo.lock
generated
|
|
@ -64,6 +64,17 @@ dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bread-shared"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "git+https://git.breadway.dev/Breadway/bread?tag=v0.7.0#22e34e2cf2202305d7960759dfccb54dc79f948b"
|
||||||
|
dependencies = [
|
||||||
|
"dirs",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"toml 0.8.23",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bread-theme"
|
name = "bread-theme"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
@ -75,6 +86,17 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bread-utils"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886"
|
||||||
|
dependencies = [
|
||||||
|
"bread-shared",
|
||||||
|
"dirs",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "breadgreet"
|
name = "breadgreet"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -97,10 +119,12 @@ dependencies = [
|
||||||
name = "breadlock"
|
name = "breadlock"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bread-utils",
|
||||||
"breadlock-ui",
|
"breadlock-ui",
|
||||||
"chrono",
|
"chrono",
|
||||||
"pam-client2",
|
"pam-client2",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" }
|
bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" }
|
||||||
|
bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
|
|
||||||
44
EVENTS.md
Normal file
44
EVENTS.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# breadlock — bread event integration
|
||||||
|
|
||||||
|
breadlock is a standalone session locker: it works exactly the same with
|
||||||
|
or without `breadd` running. When breadd *is* present, `breadlock`
|
||||||
|
publishes events into the shared bread automation fabric. See the parent
|
||||||
|
`bread` repo's `Documentation.md` — specifically its "Namespaces" and
|
||||||
|
"Integrating a bread\* app" sections — for the general convention this
|
||||||
|
follows.
|
||||||
|
|
||||||
|
App id: **`lock`**. Transport: `bread-utils`'s `bread_client` module
|
||||||
|
(feature `bread-client`) — `breadlock` links it directly. Each `emit` is
|
||||||
|
its own short-lived connection (`BreadClient::emit` is fire-and-forget);
|
||||||
|
there is no long-running subscription half because breadlock has no
|
||||||
|
command verbs (see below).
|
||||||
|
|
||||||
|
`breadgreet` is not wired to the bus. It runs under greetd (typically as
|
||||||
|
the dedicated greeter user, before a user session exists), so breadd is
|
||||||
|
usually not there to receive anything, and login is a different lifecycle
|
||||||
|
from session lock/unlock.
|
||||||
|
|
||||||
|
## Events published (`bread.lock.*`)
|
||||||
|
|
||||||
|
| Event | Data | When |
|
||||||
|
|-------|------|------|
|
||||||
|
| `bread.lock.locked` | `{}` | The compositor accepted the `ext-session-lock-v1` request (`SessionLockHandler::locked`). Not emitted merely because breadlock started or asked to lock. |
|
||||||
|
| `bread.lock.unlocked` | `{}` | PAM authenticated successfully and breadlock sent `unlock` to the compositor. Not emitted on a compositor-ended lock (`finished`), a dispatch-error exit (fail-secure: the session stays locked), or a failed/typo password. |
|
||||||
|
|
||||||
|
## Commands honored (`bread.command.lock.*`)
|
||||||
|
|
||||||
|
None. breadlock is started by hypridle / `loginctl lock-session` (or
|
||||||
|
directly) and unlocks only via PAM on this process. There is no
|
||||||
|
`lock`/`unlock`/`pin`/`blur` verb, and none is stubbed as a no-op.
|
||||||
|
|
||||||
|
`background.blur` in `breadlock.toml` remains a documented locker no-op
|
||||||
|
(accepted, warned, surface drawn unblurred). That is appearance config,
|
||||||
|
not a bus command — do not invent `bread.command.lock.blur` for it.
|
||||||
|
|
||||||
|
## Fail-safe behavior
|
||||||
|
|
||||||
|
- If breadd isn't installed or isn't running, `emit` is a silent no-op
|
||||||
|
(`BreadClient::emit` never blocks or errors the caller) — breadlock's
|
||||||
|
actual lock/unlock path is entirely unaffected either way.
|
||||||
|
- There is no command subscription, so a breadd restart while the lock
|
||||||
|
screen is up changes nothing on this side.
|
||||||
|
|
@ -9,6 +9,13 @@ Two binaries, one workspace:
|
||||||
|
|
||||||
Both use [`bread-theme`](https://git.breadway.dev/Breadway/bread-ecosystem) for palette loading, matching the rest of the bread* ecosystem (breadbar, breadbox, bos-settings).
|
Both use [`bread-theme`](https://git.breadway.dev/Breadway/bread-ecosystem) for palette loading, matching the rest of the bread* ecosystem (breadbar, breadbox, bos-settings).
|
||||||
|
|
||||||
|
## bread event integration
|
||||||
|
|
||||||
|
`breadlock` works the same with or without `breadd`. When `breadd` is
|
||||||
|
running, it publishes `bread.lock.locked` / `bread.lock.unlocked`. See
|
||||||
|
[EVENTS.md](EVENTS.md) for the bus contract. `breadgreet` is not on the
|
||||||
|
bus. There is no `bakery.toml` (PAM / pacman exception).
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ path = "src/bin/breadlock-auth-check.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
breadlock-ui = { path = "../breadlock-ui", features = ["paint"] }
|
breadlock-ui = { path = "../breadlock-ui", features = ["paint"] }
|
||||||
|
bread-utils = { workspace = true, features = ["bread-client"] }
|
||||||
smithay-client-toolkit = "0.20"
|
smithay-client-toolkit = "0.20"
|
||||||
wayland-client = "0.31"
|
wayland-client = "0.31"
|
||||||
tiny-skia = "0.12"
|
tiny-skia = "0.12"
|
||||||
|
|
@ -26,6 +27,7 @@ chrono = "0.4"
|
||||||
pam-client2 = { version = "0.5", default-features = false }
|
pam-client2 = { version = "0.5", default-features = false }
|
||||||
zeroize = { version = "1", features = ["std"] }
|
zeroize = { version = "1", features = ["std"] }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
|
|
|
||||||
20
breadlock/src/bread_events.rs
Normal file
20
breadlock/src/bread_events.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
//! `bread.lock.*` event integration — optional, non-blocking. See
|
||||||
|
//! `EVENTS.md` at the repo root for the full contract. breadlock works
|
||||||
|
//! identically with or without breadd running; every call here is
|
||||||
|
//! fire-and-forget (`BreadClient::emit` never blocks or errors this
|
||||||
|
//! process) so a missing or restarting breadd never affects locking
|
||||||
|
//! itself.
|
||||||
|
|
||||||
|
use bread_utils::bread_client::BreadClient;
|
||||||
|
|
||||||
|
/// This app's id in bread's sibling-app namespace registry
|
||||||
|
/// (`bread_shared::apps::KNOWN_APPS`) — events publish as `bread.lock.*`.
|
||||||
|
pub const APP_ID: &str = "lock";
|
||||||
|
|
||||||
|
pub fn emit_locked() {
|
||||||
|
BreadClient::connect(APP_ID).emit("bread.lock.locked", serde_json::json!({}));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_unlocked() {
|
||||||
|
BreadClient::connect(APP_ID).emit("bread.lock.unlocked", serde_json::json!({}));
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ impl SessionLockHandler for AppState {
|
||||||
fn locked(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, session_lock: SessionLock) {
|
fn locked(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, session_lock: SessionLock) {
|
||||||
tracing::info!("session locked");
|
tracing::info!("session locked");
|
||||||
self.session_lock = Some(session_lock);
|
self.session_lock = Some(session_lock);
|
||||||
|
crate::bread_events::emit_locked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The compositor denied the lock request, or ended an active lock out
|
/// The compositor denied the lock request, or ended an active lock out
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod auth;
|
mod auth;
|
||||||
mod background;
|
mod background;
|
||||||
|
mod bread_events;
|
||||||
mod config;
|
mod config;
|
||||||
mod input;
|
mod input;
|
||||||
mod lock;
|
mod lock;
|
||||||
|
|
@ -53,6 +54,7 @@ fn main() {
|
||||||
tracing::info!("authenticated, unlocking");
|
tracing::info!("authenticated, unlocking");
|
||||||
if let Some(lock) = state.session_lock.take() {
|
if let Some(lock) = state.session_lock.take() {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
bread_events::emit_unlocked();
|
||||||
}
|
}
|
||||||
state.exit = true;
|
state.exit = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue