feat(packages): add GNOME desktop apps for isDesktop - #1164
Conversation
Adds baobab, celluloid, cheese, eog, file-roller, gedit, gthumb, loupe, seahorse, and shotwell to the isDesktop package list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
You do not have enough credits to review this pull request. Please purchase more credits to continue. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
Mesa DescriptionTL;DRAdds 10 GNOME desktop apps and enables automatic GNOME Keyring unlock at login via TPM2 for fingerprint-compatible authentication. What changed?
Description generated by Mesa. Update settings |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
Disabled knowledge base sources:
📝 WalkthroughSummary by CodeRabbit
WalkthroughExpands Linux desktop and system package lists; enables GNOME Keyring with PAM greetd integration on the matic host; switches GPG pinentry to pinentry-gnome3 with extended cache TTLs; adds a TPM2-backed systemd user service to auto-unlock the GNOME keyring; documents setup in matic README. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant PAM as PAM / greetd
participant Systemd as systemd (user)
participant TPM as TPM2
participant Keyring as gnome-keyring
participant GPG as gpg-agent
User->>PAM: Login (greetd)
PAM->>Keyring: Request unlock (enableGnomeKeyring)
PAM->>Systemd: Start gnome-keyring-unlock service
Systemd->>TPM: Decrypt TPM2-bound credential
TPM-->>Systemd: Decrypted credential
Systemd->>Keyring: gnome-keyring-daemon --unlock (supply password)
Keyring->>GPG: gpg-agent access available (pinentry via GNOME3)
GPG-->>User: GPG agent unlocked
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request adds 10 GNOME desktop applications to the package list for desktop environments. The changes are straightforward and the new packages are correctly sorted alphabetically within the list. I have one suggestion regarding potentially redundant image viewers being installed, which could be an opportunity for improvement.
| hyprsunset | ||
| libnotify | ||
| linux-wallpaperengine | ||
| loupe |
There was a problem hiding this comment.
This change introduces loupe, and another part of the pull request adds eog (on line 137). Both are image viewers, with loupe being the modern successor to eog in GNOME. To avoid redundancy in installed applications, you might want to consider including only one of them. For a more modern setup, keeping loupe and removing eog would be a reasonable choice.
Enable gnome-keyring and hook into greetd PAM so the GPG passphrase is cached on first login and never prompted again across reboots. Switch pinentry to pinentry-gnome3 and bump cache TTL to INT_MAX. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR expands the desktop package set for Linux isDesktop hosts by adding several GNOME applications, and also tweaks the matic host’s GPG/pinentry setup and attempts to enable GNOME keyring/PAM integration.
Changes:
- Add 10 GNOME desktop applications to the
isDesktopLinux package list. - Update
maticGPG agent to usepinentry-gnome3and very large cache TTLs. - Attempt to enable GNOME keyring and greetd PAM integration on
matic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| named-hosts/matic/default.nix | Adds GNOME keyring/PAM settings and changes GPG agent pinentry + cache TTL behavior on the matic host. |
| home-manager/packages/default.nix | Adds GNOME desktop apps to the Linux isDesktop package list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| # GNOME Keyring - auto-unlocks GPG key on login via PAM | ||
| services.gnome.gnome-keyring.enable = true; | ||
| security.pam.services.greetd.enableGnomeKeyring = true; | ||
|
|
||
| # GPG agent configuration | ||
| services.gpg-agent = { | ||
| enable = true; | ||
| enableSshSupport = false; | ||
| pinentry.package = pkgs.pinentry-tty; | ||
| defaultCacheTtl = 94608000; # 3 years | ||
| maxCacheTtl = 94608000; # 3 years | ||
| pinentry.package = pkgs.pinentry-gnome3; | ||
| defaultCacheTtl = 2147483647; # max (effectively forever) | ||
| maxCacheTtl = 2147483647; # max (effectively forever) |
| defaultCacheTtl = 2147483647; # max (effectively forever) | ||
| maxCacheTtl = 2147483647; # max (effectively forever) |
| # GNOME Keyring - auto-unlocks GPG key on login via PAM | ||
| services.gnome.gnome-keyring.enable = true; | ||
| security.pam.services.greetd.enableGnomeKeyring = true; |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="named-hosts/matic/default.nix">
<violation number="1" location="named-hosts/matic/default.nix:439">
P1: Move the `greetd` PAM keyring setting out of the Home Manager user block; this is a NixOS option and will not apply from `home-manager.users`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
named-hosts/matic/default.nix (1)
445-448: Consider the security tradeoff of indefinite passphrase caching.Setting cache TTL to
2147483647(~68 years) means the GPG passphrase is effectively cached forever after the first unlock. While this achieves the desired UX of prompting only once per boot, it means:
- If the machine is compromised while running, the GPG key can be used without re-authentication
- The passphrase remains cached through sleep/hibernate cycles
This may be acceptable for a personal workstation with TPM-backed disk encryption, but consider documenting this tradeoff (e.g., in a comment or ADR) for future reference.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@named-hosts/matic/default.nix` around lines 445 - 448, The current settings defaultCacheTtl and maxCacheTtl are set to 2147483647 which effectively caches the GPG passphrase indefinitely; update the configuration by either lowering those values to a reasonable TTL (e.g., minutes/hours) or add a clear comment/ADR next to defaultCacheTtl and maxCacheTtl explaining the security tradeoffs (risk during compromise, persistence across sleep/hibernate) and why indefinite caching is acceptable for this host, and mention any mitigating controls (TPM/disk encryption) so future maintainers understand the decision.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@named-hosts/matic/default.nix`:
- Around line 445-448: The current settings defaultCacheTtl and maxCacheTtl are
set to 2147483647 which effectively caches the GPG passphrase indefinitely;
update the configuration by either lowering those values to a reasonable TTL
(e.g., minutes/hours) or add a clear comment/ADR next to defaultCacheTtl and
maxCacheTtl explaining the security tradeoffs (risk during compromise,
persistence across sleep/hibernate) and why indefinite caching is acceptable for
this host, and mention any mitigating controls (TPM/disk encryption) so future
maintainers understand the decision.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 79613b9a-37b1-4c0f-b329-f340e35224f0
📒 Files selected for processing (2)
home-manager/packages/default.nixnamed-hosts/matic/default.nix
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="named-hosts/matic/default.nix">
<violation number="1" location="named-hosts/matic/default.nix:128">
P2: `enableGnomeKeyring` here only works for password-based greetd logins; with `fprintAuth = true`, fingerprint logins will still leave the keyring locked and prompt later.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Replace empty-password approach with a systemd user service that decrypts the keyring password using a TPM2+host-bound credential at session start. Works with fingerprint login — no password prompt after reboot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also add ConditionPathExists so the unlock service skips gracefully before the credential file has been created. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
named-hosts/matic/default.nix (1)
447-465: Consider graceful handling when credential file is missing.The service will fail on every login if the TPM2 credential file hasn't been set up yet (e.g., fresh install, CI). This creates noise in
journalctl --userand may block dependent services.Consider adding a
ConditionPathExiststo skip the service gracefully when the credential isn't configured:♻️ Proposed fix
systemd.user.services.gnome-keyring-unlock = { Unit = { Description = "Unlock GNOME Keyring via TPM2 credential"; After = [ "graphical-session-pre.target" ]; PartOf = [ "graphical-session-pre.target" ]; + ConditionPathExists = "%h/.config/credstore.encrypted/gnome-keyring.cred"; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@named-hosts/matic/default.nix` around lines 447 - 465, Add a ConditionPathExists check to the gnome-keyring-unlock service so it is skipped when the TPM credential file is absent: in systemd.user.services.gnome-keyring-unlock, add Unit.ConditionPathExists = "%h/.config/credstore.encrypted/gnome-keyring.cred" (or the same path referenced by LoadCredentialEncrypted/ExecStart) so the unit is not started and will not fail/noise the journal when the credential hasn't been provisioned yet.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@named-hosts/matic/default.nix`:
- Around line 447-465: Add a ConditionPathExists check to the
gnome-keyring-unlock service so it is skipped when the TPM credential file is
absent: in systemd.user.services.gnome-keyring-unlock, add
Unit.ConditionPathExists = "%h/.config/credstore.encrypted/gnome-keyring.cred"
(or the same path referenced by LoadCredentialEncrypted/ExecStart) so the unit
is not started and will not fail/noise the journal when the credential hasn't
been provisioned yet.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 835c9b01-963b-4ca3-9d08-045261eb106c
📒 Files selected for processing (1)
named-hosts/matic/default.nix
User services cannot access TPM/host keys — only the system manager can. Move gnome-keyring-unlock from systemd.user.services to systemd.services with User=skakinoki so the system manager decrypts the credential before dropping to the user. Credential moves to /etc/credstore.encrypted/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@named-hosts/matic/default.nix`:
- Around line 472-474: The GPG agent TTLs are set to effectively forever via the
attributes defaultCacheTtl and maxCacheTtl (with pinentry.package set to
pkgs.pinentry-gnome3); change these to bounded, practical values (e.g.,
defaultCacheTtl = 600 and maxCacheTtl = 7200) to follow GnuPG recommendations
while preserving the GNOME pinentry flow; update the named attributes
defaultCacheTtl and maxCacheTtl accordingly so passphrases are not retained
indefinitely.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ffcc48f8-e7e1-4ea7-b1dd-b6cf762fd55e
📒 Files selected for processing (2)
named-hosts/matic/README.mdnamed-hosts/matic/default.nix
✅ Files skipped from review due to trivial changes (1)
- named-hosts/matic/README.md
| pinentry.package = pkgs.pinentry-gnome3; | ||
| defaultCacheTtl = 2147483647; # max (effectively forever) | ||
| maxCacheTtl = 2147483647; # max (effectively forever) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For GnuPG gpg-agent (current stable docs), what are recommended default-cache-ttlandmax-cache-ttl values for desktop usage, and what are the security implications of setting them to 2147483647?
💡 Result:
For desktop usage, the official GnuPG documentation recommends the default values for gpg-agent: default-cache-ttl of 600 seconds (10 minutes) and max-cache-ttl of 7200 seconds (2 hours). These are explicitly stated as the defaults in the gpg-agent man page and agent options documentation across multiple GnuPG versions, including the current stable series (2.5.x as of 2026). No specific "recommended" values different from defaults are given for desktop usage in official docs; the defaults are considered suitable for general interactive use, balancing convenience and security. Setting default-cache-ttl and max-cache-ttl to 2147483647 (2^31-1 seconds, approximately 68 years) effectively disables passphrase expiration. Security implications include: - Passphrases remain in memory indefinitely (until agent restart or manual eviction), increasing risk if the machine is compromised, stolen, or accessed by an unauthorized local user. - Heightens exposure to memory-dumping attacks (e.g., via tools like gpg_reaper demonstrated in a GnuPG bug report). - Conflicts with security best practices for desktop environments, where session-based caching (e.g., until logout) is preferable. - Even with long TTLs, newer GnuPG versions use timers for eviction (not just on access), but extremely long values still pose risks. To apply: Edit ~/.gnupg/gpg-agent.conf with the options (without leading --), then gpgconf --kill gpg-agent; gpgconf --launch gpg-agent.
Citations:
- 1: https://gnupg.org/documentation/manuals/gnupg-2.0/Agent-Options.html
- 2: https://manpages.ubuntu.com/manpages/xenial/man1/gpg-agent.1.html
- 3: https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html
- 4: https://www.gnupg.org/documentation/manuals/gnupg-devel/Agent-Options.html
- 5: https://manpages.ubuntu.com/manpages/jammy/man1/gpg-agent.1.html
- 6: https://www.gnupg.org/download/index.en.html
- 7: https://www.gnupg.org/(en)/documentation/manuals/gnupg24/gpg-agent.1.html
- 8: https://dev.gnupg.org/T3829
- 9: https://superuser.com/questions/624343/keep-gnupg-credentials-cached-for-entire-user-session
Avoid effectively-forever GPG cache TTLs.
Lines 473–474 set passphrase cache retention to ~68 years (2147483647 seconds). Official GnuPG documentation recommends default-cache-ttl of 600 seconds (10 minutes) and max-cache-ttl of 7200 seconds (2 hours). Extended TTLs significantly weaken workstation security if the user session is compromised, as passphrases remain in memory indefinitely and increase exposure to memory-dumping attacks. Keep the GNOME unlock flow but bound gpg-agent TTLs to practical limits.
Suggested change
services.gpg-agent = {
enable = true;
enableSshSupport = false;
pinentry.package = pkgs.pinentry-gnome3;
- defaultCacheTtl = 2147483647; # max (effectively forever)
- maxCacheTtl = 2147483647; # max (effectively forever)
+ defaultCacheTtl = 28800; # 8h
+ maxCacheTtl = 86400; # 24h cap
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pinentry.package = pkgs.pinentry-gnome3; | |
| defaultCacheTtl = 2147483647; # max (effectively forever) | |
| maxCacheTtl = 2147483647; # max (effectively forever) | |
| pinentry.package = pkgs.pinentry-gnome3; | |
| defaultCacheTtl = 28800; # 8h | |
| maxCacheTtl = 86400; # 24h cap |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@named-hosts/matic/default.nix` around lines 472 - 474, The GPG agent TTLs are
set to effectively forever via the attributes defaultCacheTtl and maxCacheTtl
(with pinentry.package set to pkgs.pinentry-gnome3); change these to bounded,
practical values (e.g., defaultCacheTtl = 600 and maxCacheTtl = 7200) to follow
GnuPG recommendations while preserving the GNOME pinentry flow; update the named
attributes defaultCacheTtl and maxCacheTtl accordingly so passphrases are not
retained indefinitely.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="named-hosts/matic/default.nix">
<violation number="1" location="named-hosts/matic/default.nix:133">
P2: Hard-coding `user@1000.service` makes the keyring unlock trigger brittle and can break when the login user’s UID is not 1000.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
… daemon Without this, gnome-keyring-daemon --unlock starts a new instance instead of connecting to the PAM-started daemon at /run/user/<uid>/keyring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gnome-keyring-daemon --unlock (v48) ignores GNOME_KEYRING_CONTROL and always starts a new instance. Replace with a Python script that speaks the control socket protocol directly: credentials byte + big-endian [oplen][op=1][pwlen][password] packet, reads [8][result] response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="named-hosts/matic/default.nix">
<violation number="1" location="named-hosts/matic/default.nix:171">
P1: Infinite loop if the daemon closes the connection before sending a full 8-byte response. `socket.recv()` returns `b""` on a closed connection, so `resp` never grows and the `while` loop spins forever. Add a check for an empty recv.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| while len(resp) < 8: | ||
| resp += s.recv(8 - len(resp)) |
There was a problem hiding this comment.
P1: Infinite loop if the daemon closes the connection before sending a full 8-byte response. socket.recv() returns b"" on a closed connection, so resp never grows and the while loop spins forever. Add a check for an empty recv.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At named-hosts/matic/default.nix, line 171:
<comment>Infinite loop if the daemon closes the connection before sending a full 8-byte response. `socket.recv()` returns `b""` on a closed connection, so `resp` never grows and the `while` loop spins forever. Add a check for an empty recv.</comment>
<file context>
@@ -137,12 +137,53 @@ inputs.nixpkgs.lib.nixosSystem {
+ s.sendall(b"\x00")
+ s.sendall(pkt)
+ resp = b""
+ while len(resp) < 8:
+ resp += s.recv(8 - len(resp))
+ _, result = struct.unpack(">II", resp)
</file context>
| while len(resp) < 8: | |
| resp += s.recv(8 - len(resp)) | |
| while len(resp) < 8: | |
| chunk = s.recv(8 - len(resp)) | |
| if not chunk: | |
| raise RuntimeError(f"connection closed after {len(resp)} bytes") | |
| resp += chunk |
The service fires when user@1000 starts, but the keyring daemon may not be fully initialized yet (PAM login still in progress). Add a retry loop (10 attempts, 3s apart) that waits for the control socket and retries DENIED results. Also bump TimeoutStartSec to 60s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="named-hosts/matic/default.nix">
<violation number="1" location="named-hosts/matic/default.nix:190">
P2: Handle `unlock(pw)` exceptions inside the retry loop; checking path existence alone does not prevent startup races from aborting the service.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| print(f"attempt {attempt+1}: waiting for control socket...", flush=True) | ||
| time.sleep(3) | ||
| continue | ||
| result = unlock(pw) |
There was a problem hiding this comment.
P2: Handle unlock(pw) exceptions inside the retry loop; checking path existence alone does not prevent startup races from aborting the service.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At named-hosts/matic/default.nix, line 190:
<comment>Handle `unlock(pw)` exceptions inside the retry loop; checking path existence alone does not prevent startup races from aborting the service.</comment>
<file context>
@@ -173,11 +174,28 @@ inputs.nixpkgs.lib.nixosSystem {
+ print(f"attempt {attempt+1}: waiting for control socket...", flush=True)
+ time.sleep(3)
+ continue
+ result = unlock(pw)
+ print(f"attempt {attempt+1}: gnome-keyring unlock: {codes.get(result, result)}", flush=True)
+ if result == 0:
</file context>
| result = unlock(pw) | |
| try: | |
| result = unlock(pw) | |
| except (OSError, RuntimeError) as exc: | |
| print(f"attempt {attempt+1}: unlock failed before daemon was ready: {exc}", flush=True) | |
| time.sleep(3) | |
| continue |
Summary
GNOME desktop apps
Adds 10 GNOME desktop apps to the
isDesktoppackage list:baobab,celluloid,cheese,eog,file-roller,gedit,gthumb,loupe,seahorse,shotwellGNOME Keyring auto-unlock via TPM2 (fingerprint-compatible)
Enables fully automatic GNOME Keyring unlock at login — including fingerprint auth — using a TPM2+host-bound
systemd-credscredential.Why not
gnome-keyring-daemon --unlock? In v48+ it ignoresGNOME_KEYRING_CONTROLand always spawns a fresh instance. Instead, a Python script speaks the control socket protocol directly ($XDG_RUNTIME_DIR/keyring/control): credentials byte + big-endian[oplen][op=1][pwlen][password], reads[8][result].Architecture:
systemd.services) withUser=skakinoki— system manager handles TPM decryption, then drops to userLoadCredentialEncryptedfrom/etc/credstore.encrypted/gnome-keyring.credpam_gnome_keyringstill handles password logins via PAM; this service covers fingerprint loginsOne-time setup (see
named-hosts/matic/README.md):Test plan
make build && make switchsucceedssudo systemctl restart gnome-keyring-unlock.servicelogsgnome-keyring unlock: OK🤖 Generated with Claude Code