Skip to content

Add plugin: Password Store (pass)#863

Draft
bojanpy wants to merge 3 commits into
noctalia-dev:mainfrom
bojanpy:passwordstore
Draft

Add plugin: Password Store (pass)#863
bojanpy wants to merge 3 commits into
noctalia-dev:mainfrom
bojanpy:passwordstore

Conversation

@bojanpy

@bojanpy bojanpy commented May 16, 2026

Copy link
Copy Markdown

Summary

Adds a launcher provider that integrates with pass (the standard Unix
password manager). Two search prefixes are supported from the
launcher:

  • pass — fuzzy search all entries, press Enter to copy the
    password to clipboard
  • po — fuzzy search all entries, press Enter to copy the
    current TOTP code to clipboard

How it works

On init the plugin runs find against $PASSWORD_STORE_DIR (falling back
to ~/.password-store) to build the entry list. Search uses FuzzySort
from qs.Commons — same engine as the app launcher — so ranking is
consistent. Activation calls pass -c or pass otp -c ,
both of which clear the clipboard after 45 seconds per pass defaults.
Wayland clipboard (wl-copy) is handled automatically by pass when
$WAYLAND_DISPLAY is set.

Dependencies

  • pass — password lookup and clipboard copy
  • pass-otp — TOTP code generation (po prefix)
  • wl-clipboard — Wayland clipboard support (recommended)

Checklist

  • manifest.json includes id, name, version, minNoctaliaVersion,
    author, license, description, tags
  • minNoctaliaVersion set to 3.6.0 (minimum version with plugin
    launcher provider API)
  • No plugin settings required — works out of the box with a standard
    pass setup
  • README.md with usage, requirements, install instructions, and
    troubleshooting
  • Tested on Hyprland / Wayland

@github-actions

This comment was marked as duplicate.

@github-actions

This comment was marked as resolved.

@github-actions

This comment was marked as resolved.

1 similar comment
@github-actions

This comment was marked as duplicate.

@github-actions

This comment was marked as duplicate.

@github-actions

This comment was marked as resolved.

@spiros132

Copy link
Copy Markdown
Collaborator

Do not add the registry file, that one gets updated automatically.

… false and is NOT needed!

(H) Line 1309: The repository field needs to point to the "https://github.com/noctalia-dev/noctalia-plugins" github repository!

Signed-off-by: Bojan Arch Jovanovic <bojan@kortechs.io>
@bojanpy

bojanpy commented May 17, 2026

Copy link
Copy Markdown
Author

Do not add the registry file, that one gets updated automatically.

Done ✔️

@spiros132 spiros132 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback about the PR! :)

Comment thread pass/Main.qml Outdated
}

Component.onCompleted: {
LauncherProviderRegistry.registerPluginProvider(pluginApi.pluginId, _providerComponent, {});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not add your launcher provider this way, create an object called LauncherProvider and provide that in the manifest itself. The plugins aren't the ones responsible with registering and unregistering their launcher providers from / to the shell.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestions @spiros132 I tested the implementation and it functions accordingly.

Comment thread pass/manifest.json Outdated
@@ -0,0 +1,20 @@
{
"id": "pass",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass might be a too small name I would say. Maybe password-store, or something similar like that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be resolved :)

Comment thread pass/README.md Outdated

## Installation

### Manual Installation

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the only subsection in the Installation section, it might confuse users. I would suggest either skip the installation section or add a small part saying that they can just download the plugin from the noctalia settings.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did as you said I said install from the plugins page, or clone from the url. I can dumb it down further if you want.

@spiros132 spiros132 marked this pull request as draft May 21, 2026 21:44
@gs-101

gs-101 commented May 30, 2026

Copy link
Copy Markdown

Can I make feature requests here? I think it would be nice if this also worked with gopass.

@bojanpy

bojanpy commented May 31, 2026

Copy link
Copy Markdown
Author

Some feedback about the PR! :)

That's a bunch for the feedback! I will incorporate the changes these days and push back 🙇

@bojanpy

bojanpy commented May 31, 2026

Copy link
Copy Markdown
Author

Can I make feature requests here? I think it would be nice if this also worked with gopass.

@gs-101 For sure, I kinda had this in my mind to be a 0 conf plugin, but if you think we can accomodate both easy then I say go for it!

@gs-101

gs-101 commented May 31, 2026

Copy link
Copy Markdown

Can I make feature requests here? I think it would be nice if this also worked with gopass.

@gs-101 For sure, I kinda had this in my mind to be a 0 conf plugin, but if you think we can accomodate both easy then I say go for it!

Not sure about building the entry list, but I think you can just do it with "(go)pass ls". As for implementing this, only a drop-down menu for "gopass" or "pass" would be necessary, as gopass is compatible with pass commands.

Not gonna lie, I think that even having a "pass" binary on path which is a symlink to "gopass" would be enough. I'll try that.

@gs-101

gs-101 commented May 31, 2026

Copy link
Copy Markdown

Not gonna lie, I think that even having a "pass" binary on path which is a symlink to "gopass" would be enough. I'll try that.

Yep, that was it. And also setting the directory to "/home/YOUR_USER_HERE/.local/share/gopass/stores/root".

The issue is that the plugin doesn't seem to be able to detect age-encrypted secrets. I entered one of my folders and found nothing.

…r and provided that obj in the manifest itself.

Also changed to a more meaningful id, not just pass.

Also let claude fix the readme :)
@bojanpy

bojanpy commented Jun 2, 2026

Copy link
Copy Markdown
Author

Not gonna lie, I think that even having a "pass" binary on path which is a symlink to "gopass" would be enough. I'll try that.

Yep, that was it. And also setting the directory to "/home/YOUR_USER_HERE/.local/share/gopass/stores/root".

The issue is that the plugin doesn't seem to be able to detect age-encrypted secrets. I entered one of my folders and found nothing.

@gs-101 There is a line that says:

    command: [
        "sh", "-c",
        "store=\"${PASSWORD_STORE_DIR:-$HOME/.password-store}\"; " +
        "find \"$store\" -name '*.gpg' | sed \"s|$store/||;s|\\.gpg$||\" | sort"
    ]

find *.gpg, the age secrets I think are a different extension, I don't use that encryption but if you want feel free to make a change test if it works on your box, and ill merge it in.

@bojanpy

bojanpy commented Jun 2, 2026

Copy link
Copy Markdown
Author

@gs-101

Try something like this:

    command: [
        "sh", "-c",
        "store=\"${PASSWORD_STORE_DIR:-$HOME/.password-store}\"; " +
        "find \"$store\" \\( -name '*.gpg' -o -name '*.age' \\) | sed \"s|$store/||;s|\\.gpg$||;s|\\.age$||\" | sort"
    ]

@gs-101

gs-101 commented Jun 3, 2026

Copy link
Copy Markdown

Replacing all the code related to gpg with age did show the entries:

LauncherProvider.qml
170:        "-mindepth", "1", "-type", "f", "-name", "*.age", "-printf", "%P\n"])
174:        "-type", "f", "-name", "*.age", "-printf", "%f\n",
192:      if (!isDir && name.endsWith('.age')) {

But pressing Enter on an entry didn't prompt for a password (to unlock the store). Is that intended (sorry if it is, I haven't looked that much into this)?

@bojanpy

bojanpy commented Jun 3, 2026

Copy link
Copy Markdown
Author

Replacing all the code related to gpg with age did show the entries:

LauncherProvider.qml
170:        "-mindepth", "1", "-type", "f", "-name", "*.age", "-printf", "%P\n"])
174:        "-type", "f", "-name", "*.age", "-printf", "%f\n",
192:      if (!isDir && name.endsWith('.age')) {

But pressing Enter on an entry didn't prompt for a password (to unlock the store). Is that intended (sorry if it is, I haven't looked that much into this)?

Gotcha! Ok that will be an issue with gpg too in case pinentry need to be opened to provide a password.

Ive got them unprotected all locally so I never tested. Ill sort that out and you can test again :)

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants