Sync config from arch

- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
This commit is contained in:
asepharyana
2026-09-23 15:19:12 +07:00
commit 1cdb82a76f
300 changed files with 78143 additions and 0 deletions
@@ -0,0 +1,521 @@
# Implementation Plan: Opt-in Bitwarden SSH Agent
## Overview
Build the SSH-agent design in `docs/ideas/ssh-agent.md` as an optional,
disabled-by-default feature. The existing Quickshell panel remains the only
component that invokes `bw` and owns `BW_SESSION`; a supervised Rust companion
implements the local SSH-agent protocol, holds private SSH keys only while the
vault is unlocked, and makes signing contingent on a live approval or bounded
process grant. Before any agent work, the ordinary vault read is sanitized
outside QML so unsupported cipher types and `sshKey.privateKey` cannot enter
the long-lived panel process.
Detailed tasks and checkpoints are tracked in `tasks/todo.md`. No feature code
should be written until this plan has human approval.
## Development Worktree and Live Checkpoints
- Develop on branch `feature/ssh-agent` in `.worktrees/ssh-agent`, based on the
latest fetched `origin/master`. Keep the primary `master` checkout free of
feature changes.
- The enabled Omarchy plugin path
`~/.config/omarchy/plugins/io.github.elevate08.qs-bitwarden-cli` must resolve
to this worktree before any live test.
- At the end of every task slice, run its focused tests plus the applicable
regression/QML/lint/manifest gates, verify the plugin symlink target, run
`omarchy restart shell`, confirm `omarchy-shell shell ping`, summon the
Bitwarden panel, and call its non-secret `status` IPC method.
- Pause after that live reload so the user can exercise the plugin manually.
Do not start the next task until the user confirms the checkpoint or reports
issues to fix.
## Scope
The first release includes:
- a public-only, read-only SSH-key item experience in the panel;
- Ed25519 and RSA SHA-2 authentication/signing through a stable Unix socket;
- explicit per-signature approval and short per-process grants;
- opt-in unlock-on-demand, safe UWSM client routing, and advisory diagnostics;
- public-key file projection for normal Git SSH-signing configuration;
- an x86_64 GNU helper bundled with the plugin and verified by reproducible CI.
It does not include key creation/import, SSH item editing/cloning, master-
password re-prompt keys, forwarding, persistent grants, GPG-agent support,
item types 6–8, aarch64, or a Bitwarden SDK/direct service integration.
## Architecture Decisions
- Sanitize `bw list items` with static `jq` programs before QML parses it. QML
receives intact types 1–4 and a public-only type-5 projection; all other
types fail closed out of the display model.
- Keep one panel-owned `bw list items` read per unlock/sync. When the agent is
enabled, a bounded `tee` branch sends only eligible type-5 private material
to a nonce-framed FIFO. Failure of that optional branch must never prevent
the panel list from loading.
- Use one Rust companion and one stable socket under `$XDG_RUNTIME_DIR`. The
companion spawns no child processes, receives neither `BW_SESSION` nor
unrelated vault data, and exits when its control channel closes.
- Make approval, epoch checks, request limits, and final allow/deny decisions
authoritative in the companion. Process information is prompt context, not
an authentication boundary.
- Treat public-key file export as v1 scope because normal Git SSH signing needs
file paths. The planning assumption is that the companion owns this
projection from its validated public keystore; Task 4 records or corrects
that ownership before implementation proceeds.
- Ship only `x86_64-unknown-linux-gnu` initially. Commit the source, lockfile,
pinned toolchain, release bytes, and checksum; compare clean CI output byte
for byte before anything reaches `master` or a release.
## Dependency Graph
```text
Task 1 sanitized read contract
└── Task 2 panel SSH-key slice
└── Task 3 prerequisites and diagnostics
Task 4 Rust dependency/security decision
└── Task 5 protocol and signing
└── Task 6 epoch keystore
├── Task 7 nonce FIFO loading
└── Task 8 approvals and grants (also depends on Task 5)
└── Task 9 companion lifecycle (also depends on Task 7)
Tasks 3 + 9
└── Task 10 QML supervision
├── Task 11 opt-in setup
└── Task 12 shared vault fan-out (also depends on Tasks 1 + 7)
└── Task 13 vault lifecycle (also depends on Tasks 8 + 9)
├── Task 14 approval UI
└── Task 15 public-key projection
Task 9
└── Task 16 reproducible build
└── Task 17 pull-request gates
Tasks 10 + 16
└── Task 18 bundled-helper validation
└── Task 19 protected release (also depends on Task 17)
Tasks 3 + 11 + 14 + 15 + 19
└── Task 20 documentation and release validation
```
## Task List
### Phase 1: Remove the Existing Exposure
- [x] Task 1: Introduce the sanitized vault-read contract
- [x] Task 2: Deliver the public SSH-key panel slice
- [x] Task 3: Enforce SSH support prerequisites
### Checkpoint: Safe Panel Baseline
- [ ] QML-facing fixture output contains no SSH private-key marker or unknown
cipher-type marker.
- [ ] SSH keys are public-only and cannot reach generic detail/edit paths.
- [ ] All current JavaScript and QML tests pass; the plugin validates and lints
at its existing baseline.
- [ ] Human review approves the security boundary before Rust work continues.
### Phase 2: Prove the Headless Agent Core
- [x] Task 4: Pin the Rust security foundation
- [x] Task 5: Implement bounded SSH protocol signing
- [x] Task 6: Implement the vault-epoch keystore
### Checkpoint: Rust Primitives
- [x] Ed25519 and both RSA SHA-2 modes pass protocol-vector tests.
- [x] Lock, malformed input, mismatch, and limit paths fail closed.
- [x] The dependency/zeroization decision is recorded and reviewed.
- [x] Task 7: Implement nonce-framed FIFO loading
- [x] Task 8: Authorize signatures with bounded grants
- [x] Task 9: Complete the supervised companion lifecycle
### Checkpoint: Headless Companion
- [ ] Disposable-key tests cover identities, signing, multiple clients,
approval, grants, lock races, and FIFO rejection.
- [ ] The helper serves only its private stable socket, has no vault credential,
spawns no process, and exits on control EOF.
- [ ] Manual headless authentication and Git SSH-signing smoke tests pass.
- [ ] Human review approves proceeding to panel integration.
### Phase 3: Integrate the Optional Feature
- [x] Task 10: Establish companion supervision
- [x] Task 11: Deliver opt-in session setup
- [x] Task 12: Feed the companion from the shared vault read
### Checkpoint: Optional Data Plane
- [x] Disabled mode starts no helper and creates no socket, FIFO, or private-key
branch.
- [x] Enabled mode loads keys from the same vault read without delaying or
breaking the ordinary list on helper failure.
- [x] QML remains responsive during blocked SSH clients and helper events.
- [x] Task 13: Enforce vault lifecycle transitions
- [x] Task 14: Deliver signing authorization UX
- [x] Task 15: Project validated public-key files
### Checkpoint: End-to-End Feature
- [x] Lock, logout, account change, suspend, screen lock, disable, crash, and
Quickshell reload all satisfy the state machine.
- [x] Authentication, commit signing, and multi-commit signing work with no
private key on disk.
- [x] Public projections survive lock but clear on logout/account change/disable.
- [ ] Human security and usability review approves packaging.
### Phase 4: Establish the Artifact Trust Path
- [x] Task 16: Make the release build reproducible
- [x] Task 17: Add read-only pull-request gates
### Checkpoint: Candidate Artifact
- [x] Two clean pinned builds produce identical stripped bytes.
- [x] Existing, Rust, audit, license, and end-to-end gates pass with no PR
secrets or write token.
- [x] Task 18: Validate the bundled helper at launch
- [~] Task 19: Protect release provenance -- workflow, environment and tests
are in place; the first protected tag run is still outstanding
### Checkpoint: Shippable Artifact
- [ ] The tracked helper is executable, native-tested, checksum-consistent,
protocol-compatible, and byte-identical to the protected build.
- [ ] A release produces provenance attestation, SBOM, and dependency/license
report with narrowly scoped permissions.
### Phase 5: Document and Release
- [~] Task 20: Complete release documentation -- README, CHANGELOG, manifest
and the design draft are done; the manual release matrix is not
### Checkpoint: Complete
- [ ] Every task's acceptance criteria and the project Definition of Done pass.
- [ ] Setup, conflict, logout/login, upgrade, verification, and uninstall paths
are documented and manually exercised.
- [ ] No private key, session token, signed payload, or signature appears in
QML state, argv, logs, files, CI artifacts, or test diagnostics.
- [ ] Human review approves merge and release.
## Verification Strategy
Use focused tests after each task and these full gates at checkpoints:
```bash
for test_file in tests/*.test.js; do node "$test_file" || exit 1; done
QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml
mkdir -p /tmp/qs-imports
ln -sfn /usr/share/omarchy/shell /tmp/qs-imports/qs
/usr/lib/qt6/bin/qmllint -I /tmp/qs-imports Panel.qml FormPickerRow.qml
omarchy plugin validate .
cargo fmt --manifest-path agent/Cargo.toml --check
cargo clippy --manifest-path agent/Cargo.toml --locked --all-targets -- -D warnings
cargo test --manifest-path agent/Cargo.toml --locked --all-targets
cargo deny --manifest-path agent/Cargo.toml check
```
The final runtime matrix must include a disposable fixture vault, a fake panel
controller, real OpenSSH clients, Git authentication, Git SSH signing, lock
during every sensitive transition, helper crash/reload, and both unlock-on-
demand modes. CI must never use a real vault or credential.
## Parallelization Opportunities
- After Task 3, the panel baseline can remain stable while Tasks 4–9 build the
headless Rust core; do not parallelize work that changes the sanitized read
contract until Task 1 is merged.
- After Task 9, Task 16's reproducible-build work can run alongside Tasks
10–11 because it consumes the frozen helper interface rather than QML state.
- After Task 13, Tasks 14 and 15 can proceed independently if Task 4 has fixed
the public-export owner and the control protocol is frozen.
- Tasks 17 and 18 may proceed in parallel after Task 16, but Task 19 waits for
both so release policy matches launch-time validation.
## Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| QML receives private SSH material before sanitization | High | Make Tasks 1–3 a standalone prerequisite gate with marker-based real-pipeline tests. |
| Selected Rust key types retain secret clones after lock | High | Fail the Task 4/6 spike if parsed private components cannot be bounded and best-effort-zeroized. |
| A lock races a load, approval, grant, or signature | High | Use vault epochs, an atomic deny linearization point, candidate keystores, bounded acknowledgments, and kill-on-timeout. |
| Same-UID FIFO injection replaces the key set | High | Require a fresh 128-bit load nonce delivered only over the private control pipe and reject duplicate/stale payloads. |
| A slow or broken agent branch stalls the core vault list | High | Bound input/output/time, drain eagerly, supervise one process group, and retry the list once without the optional branch. |
| Process attribution creates false security confidence | Medium | Enforce peer UID; present PID/path/start-time as context only; scope grants narrowly and revalidate at sign time. |
| UWSM setup overwrites another agent configuration | High | Use one plugin-owned atomic file, refuse symlinks/unexpected contents, show conflicts, and require confirmation plus re-login. |
| Bundled binary drifts from source | High | Pin every build input and compare clean release bytes byte-for-byte before merge/release. |
| Fork PR artifact policy becomes impossible to satisfy | Medium | Upload read-only candidates for source-only fork PRs; require the matching bytes before merge to `master`, not inside the untrusted fork job. |
| Older/self-hosted servers expose partial SSH support | Medium | Enforce the CLI floor, report capability as unconfirmed when appropriate, and document the 2026.8.0 malformed-item fix. |
| Feature complexity degrades the ordinary vault | High | Keep disabled mode inert, isolate helper failure, checkpoint every 2–3 tasks, and run the full existing regression suite throughout. |
## Open Questions for Human Review
- **Public export scope:** Revision 2 says both that public-key export is
required in v1 and that it remains later work. This plan follows the stronger
end-to-end requirement and includes it in v1. Confirm that choice.
- **Public export owner:** This plan recommends the companion write the
projection from its validated public keystore, using an absolute data path
supplied at launch. Task 4 must record the final owner and its path/cleanup
contract before implementation.
- **Dependency outcome:** The Rust crate set, Bitwarden v2 agent status, RFC
status, zeroization behavior, and license/audit surface must be re-verified
from current primary sources during Task 4; the design draft's review date is
not sufficient evidence.
- **Release governance:** *Resolved during Task 19.* The protected environment
is `release`; its required reviewer is `@Elevate08`, with self-review
permitted because this repository has one maintainer -- the gate exists so
that write, OIDC and attestation credentials never come into existence
without a deliberate human click, not to simulate a second pair of eyes that
does not exist. Deployments are restricted to `v*` tags. CODEOWNERS names
`@Elevate08` throughout, and calls out `/.github/workflows/release.yml`
separately as the only workflow that can write, mint a token, or sign.
## Follow-up Plan: Centered SSH Approval Popup
The approved follow-up specification is
`docs/ideas/ssh-approval-popup.md`. Add a disabled-by-default presentation
choice without changing the companion protocol or authorization rules.
### Dependency order
```text
setting contract
-> centered approval surface
-> locked-vault unlock surface
-> documentation and full verification
```
### Architecture decisions
- Keep request and credential state in `Panel.qml`; popup components are views
that call the same approve, deny, PIN, fingerprint, and password functions.
- Use a full-output transparent `PanelWindow`, `ExclusionMode.Ignore`, the
Wayland overlay layer, and a brief Exclusive-to-OnDemand focus prime,
matching Omarchy's centered transient surfaces.
- Reuse `SshApprovalScreen.qml` between both presentation modes so the key,
process, grant, loading, and deadline semantics cannot drift.
- Keep account login in the full panel. The centered prompt handles only the
signed-in locked/unlocked states involved in SSH requests.
### Risks and mitigations
| Risk | Mitigation |
|---|---|
| Popup accidentally becomes a second authorization authority | It only calls existing `Panel.qml` request functions; helper checks remain unchanged. |
| PIN/fingerprint results are discarded because the anchored panel is closed | Define one transient-auth-surface predicate used by all unlock acceptance checks. |
| The invisible experience leaves a password or pending process behind | Dismissal denies the request and runs popup-specific credential/prewarm cleanup. |
| A request label injects markup | Every request-derived `Text` remains `Text.PlainText`. |
| Overlay traps input after the request ends | Visibility and Wayland keyboard focus bind directly to the live pending request. |
---
# Implementation Plan: Colorized Menu-Bar Icon
## Overview
Add an opt-in `colorizeIcon` boolean setting to the Bitwarden panel. When
enabled, the primary menu-bar shield follows Omarchy's live `Color.accent`
theme color; when disabled, it keeps the current bar foreground color. The
locked padlock, missing-dependency badge, and urgent/error indicators remain
unchanged so color personalization does not erase status meaning.
The existing settings renderer already turns boolean schema entries into
keyboard-operable toggle rows. No custom color picker, palette model, new
dependency, or arbitrary color persistence is required.
## Architecture Decisions
- Store only `colorizeIcon: boolean`; derive the actual color from
`Color.accent` at render time so theme changes are inherited automatically.
- Default the setting to `false` so existing installations retain their current
appearance.
- Apply the setting only to the primary shield glyph in `shieldIconComp`.
Leave the padlock and setup/error badges on their existing bindings.
- Put the row in the General settings group, using the existing schema-driven
toggle and `omarchy bar set` persistence path.
- Treat malformed external values as `false`, using the existing strict
boolean-setting behavior.
## Dependency Graph
```text
manifest default/schema + model schema
│
├── settings persistence/value lookup tests
│
└── Panel color binding + schema-driven General toggle
│
└── QML/static checks + runtime/theme verification
```
## Task List
### Phase 1: Settings Contract
- [x] Task 1: Add the colorization setting contract
### Checkpoint: Settings Contract
- [x] `colorizeIcon` exists in both `manifest.json` and `BitwardenModel.js`
with boolean type and a `false` default.
- [x] Valid booleans round-trip through the existing writer, while malformed
values resolve to `false`.
- [x] Focused settings/model tests pass before UI wiring begins.
### Phase 2: Vertical UI Slice
- [x] Task 2: Wire the theme-accent shield and General toggle
### Checkpoint: Colorized Icon Behavior
- [x] With the setting off, the primary shield still uses the bar foreground.
- [x] With the setting on, the primary shield uses `Color.accent`.
- [x] Lock/setup/error badges retain their existing foreground or urgent colors.
- [x] The settings row is keyboard-operable and persists through the existing
shell reload path.
### Phase 3: Verification and Documentation
- [~] Task 3: Add regression coverage and document the setting
### Checkpoint: Complete
- [x] Focused and full JavaScript tests pass.
- [x] QML tests/lint and plugin validation pass where the Omarchy environment is
available.
- [ ] Manual verification covers both toggle states, a theme accent change,
locked state, setup-required state, and an error/urgent state.
- [x] README or user-facing feature documentation explains the toggle and its
theme-derived behavior.
- [ ] Human review approves the implementation before merge; no commit or push
is performed automatically.
## Task Details
### Task 1: Add the colorization setting contract
**Description:** Register `colorizeIcon` as a General boolean setting in the
manifest and model schema, expose its default through the widget defaults, and
ensure the existing settings value/read/write paths recognize it without
special-case persistence code.
**Acceptance criteria:**
- [x] The manifest and model declare the same `colorizeIcon` key, boolean type,
label, description, and `false` default.
- [x] `boolSetting("colorizeIcon", true)` returns true; malformed values such
as strings and numbers fall back to false.
- [x] The setting is grouped under General and included in visible settings
without changing existing group ordering or setting semantics.
**Verification:**
- [x] Tests pass: `node tests/setup-settings.test.js`
- [x] Tests pass: `node tests/lock-state.test.js`
- [x] Static check confirms manifest/model schema parity.
**Dependencies:** None
**Files likely touched:**
- `manifest.json`
- `BitwardenModel.js`
- `tests/setup-settings.test.js`
- `tests/lock-state.test.js`
**Estimated scope:** Medium: 3–4 files
### Task 2: Wire the theme-accent shield and General toggle
**Description:** Add the root property that reads `colorizeIcon`, use it only
for the primary shield glyph in the menu-bar icon component, and rely on the
existing schema-driven settings delegate to render and persist the toggle.
Add focused static assertions for the binding and for the unchanged status
badge color paths.
**Acceptance criteria:**
- [x] `colorizeIcon` is read from the live setting with a false-safe default.
- [x] The primary shield resolves to `Color.accent` when enabled and the
existing bar foreground when disabled.
- [x] The padlock, missing-tool badge, and urgent/error color bindings are not
redirected through the new preference.
**Verification:**
- [x] Tests pass: `node tests/settings-screen.test.js`
- [x] Focused source assertions pass for shield color precedence and badge
independence.
- [x] QML lint passes for `Panel.qml` with the repository's Omarchy import path.
**Dependencies:** Task 1
**Files likely touched:**
- `Panel.qml`
- `tests/settings-screen.test.js`
**Estimated scope:** Small: 1–2 files
### Task 3: Add regression coverage and document the setting
**Description:** Complete the feature's regression matrix and user-facing
documentation. Verify that persistence survives the shell reload path and that
the selected accent is inherited from the active theme while status indicators
remain recognizable.
**Acceptance criteria:**
- [x] Tests cover default-off behavior, enabling/disabling through the settings
path, malformed persisted values, and preservation of badge colors.
- [x] User-facing documentation says the toggle follows the active Omarchy
theme accent and does not offer arbitrary color selection.
- [x] No unrelated panel colors or status semantics change.
**Verification:**
- [x] Full JavaScript suite passes:
`for test_file in tests/*.test.js; do node "$test_file" || exit 1; done`
- [x] QML suite passes:
`env -u DISPLAY -u WAYLAND_DISPLAY -u QT_QPA_PLATFORMTHEME QML_XHR_ALLOW_FILE_READ=1 QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml`
- [x] Plugin validation passes: `omarchy plugin validate .`
- [ ] Manual runtime check confirms both toggle states and theme-derived color
behavior when the desktop environment is available.
**Dependencies:** Task 2
**Files likely touched:**
- `README.md` or the relevant feature documentation
- `tests/setup-settings.test.js`
- `tests/settings-screen.test.js`
- `tests/lock-state.test.js`
**Estimated scope:** Medium: 3–4 files
## Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Accent color has poor contrast on a supported theme | Medium | Check light/dark themes manually; retain the existing bar foreground as the safe default and do not alter urgent badges. |
| The setting is added to one schema but not the other | Medium | Keep manifest/model parity assertions in the focused settings tests. |
| The setting accidentally recolors status badges | High | Limit the binding change to the primary shield `Text` and assert badge bindings remain independent. |
| Shell reload does not refresh the bar icon immediately | Low | Verify the existing `omarchy bar set` hot-reload behavior; do not add a second persistence mechanism. |
## Open Questions
- Confirm final user-facing label: **Colorize menu-bar icon** versus **Use
theme accent for icon**. The plan assumes the former.
- Confirm which user-facing documentation file should receive the short setting
note; `README.md` is the default unless project conventions prefer
`docs/features.md`.