Ubuntu Touch native password manager program for Linux pass
  • Rust 64%
  • QML 34.9%
  • JavaScript 0.7%
  • Shell 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Austin Schaefer 68e08e984d
All checks were successful
CI / ci (push) Successful in 4m53s
CI / audit (push) Successful in 14s
CI / qml (push) Successful in 44s
Merge pull request 'Testing tiers in CLAUDE.md' (#25) from feat/testing-tiers into main
Reviewed-on: #25
Reviewed-by: Austin Schaefer <austin.schaefer@mailo.eu>
2026-10-17 09:38:28 +00:00
.cargo Adopt the ~/dev Rust conventions (fmt, clippy, cargo-make, CI) 2026-09-30 12:55:13 +02:00
.forgejo docs: add testing tiers, PR template gets the tier line 2026-10-16 22:33:00 +02:00
.githooks Adopt the ~/dev Rust conventions (fmt, clippy, cargo-make, CI) 2026-09-30 12:55:13 +02:00
assets Add public-domain key-and-folder app icon 2026-09-30 11:19:06 +02:00
docs Release date 0.1.0 is the 14th; the store tagline as submitted 2026-10-14 00:00:53 +02:00
passcore Maintainer email: austin.schaefer@mailo.eu (#10) 2026-10-13 22:29:05 +02:00
po Version check: read manifest.json's version, not its exact spacing 2026-10-13 23:31:05 +02:00
qml About in the ☰ menu; an X closes dialogs that only show something 2026-10-13 23:28:42 +02:00
src Warn when a build has no commit; test the Sync dialog's X 2026-10-13 23:36:31 +02:00
tests/qml Warn when a build has no commit; test the Sync dialog's X 2026-10-13 23:36:31 +02:00
.gitignore Clearer names and comments in sync conflict resolution 2026-10-01 10:16:02 +02:00
Cargo.lock Clean up after a killed app, walk with walkdir, app ID touchpass.aps 2026-10-01 11:48:21 +02:00
Cargo.toml Maintainer email: austin.schaefer@mailo.eu (#10) 2026-10-13 22:29:05 +02:00
CHANGELOG.md Release date 0.1.0 is the 14th; the store tagline as submitted 2026-10-14 00:00:53 +02:00
CLAUDE.md docs: address review on testing tiers 2026-10-16 22:41:22 +02:00
clickable.yaml Scaffold TouchPass: pass client for Ubuntu Touch 2026-09-30 11:13:56 +02:00
clippy.toml Clearer names and comments in sync conflict resolution 2026-10-01 10:16:02 +02:00
LICENSE Scaffold TouchPass: pass client for Ubuntu Touch 2026-09-30 11:13:56 +02:00
Makefile.toml About dialog: version, commit, author, license, source 2026-10-13 23:22:15 +02:00
manifest.json Maintainer email: austin.schaefer@mailo.eu (#10) 2026-10-13 22:29:05 +02:00
README.md About in the ☰ menu; an X closes dialogs that only show something 2026-10-13 23:28:42 +02:00
touchpass.apparmor qml: automatic sync with retries, status line and settings page 2026-09-30 16:15:27 +02:00
touchpass.desktop Scaffold TouchPass: pass client for Ubuntu Touch 2026-09-30 11:13:56 +02:00

TouchPass

A native Ubuntu Touch (Lomiri) client for the pass password store, in the spirit of Android's Password Store app. Rust + QML via qmetaobject-rs, built with clickable.

What works so far

  • Fully confined (standard AppArmor policy, passes click-review), so it qualifies for the OpenStore without manual review.
  • First-run wizard: generate a key (passphrase optional) or import an exported one through the system file picker (ContentHub), then create a new store. Or clone one: the wizard asks for the key it is encrypted to, sets up a clone without .gpg-id (e.g. an empty repository) in place, and can start over (only if the remote has everything) after cloning the wrong one.
  • Browses the store folder by folder; search finds entries in the current folder and below.
  • Tapping an entry decrypts it in-process. A protected key's passphrase is asked once and remembered in memory until Lock or the app exits.
  • Shows the password (hidden until toggled) and the remaining lines (user, URL, notes).
  • Copy password puts it on the system clipboard with Lomiri's native Clipboard API, so any app can paste it. It is cleared as soon as you come back to TouchPass from pasting it, or after 45 s if you stay. Lomiri suspends TouchPass while you are in another app, so until you come back the password stays pasteable.
  • Follows the system light/dark theme, live.
  • About (☰ menu on the entry list): version and the commit it was built from, for bug reports, plus author, license and a link to the source.
  • Add and edit entries, with a password generator (length, character classes, your own symbol set, look-alike characters left out). Username and notes lines keep their layout, so entries edited on the phone still read the same in pass and browserpass.
  • Rename and delete entries. Change the name in the editor to rename (moved like pass mv, so an edit made on another device follows it); delete from the entry, after a confirmation. Both are refused if a sync changed the entry since you opened it.
  • Git sync with the remote the store was cloned from: on open, after every change, and retried with backoff (and as soon as the phone is back online) until it goes through. Edits of the same entry on two devices resolve to the newer one; the other stays in git history.

Layout

Path What
passcore/ Pure-Rust library: store, entry format, password generator, Keyring (rPGP: key generation, import, encryption, decryption, recipient checks), git clone and sync (fetch/merge/push) with the re-encryption guard. No Qt, testable on any Linux box.
src/bridge.rs, src/sync_service.rs The PassStore and SyncService QObjects exposed to QML. OpenPGP and network work runs on a worker thread and reports back via signals.
qml/ One file per page, dialog or non-visual helper (SyncScheduler.qml decides when to sync); Main.qml only starts the app and holds the app-wide objects.
tests/qml/ QML unit tests (Qt Quick Test) for SyncScheduler and EntryEditor, against fake SyncService/PassStore objects.
touchpass.apparmor Confined: connectivity, content_exchange and networking policy groups.

Design notes

  • App-owned data. A confined app may only write under its own directories, so everything lives in ~/.local/share/touchpass.aps/: store/ (a normal pass store) and keys/ (one armored secret key per file, as exported by gpg, mode 0600). ~/.password-store and ~/.gnupg are not visible to the app; pass in the Terminal keeps using those.
  • OpenPGP in-process with rPGP. Confined apps cannot exec /usr/bin/gpg or reach gpg-agent. The tests check interoperability both ways against real GnuPG: entries written by pass (compressed or not) decrypt here, entries written here decrypt in gpg, and keys exported by gpg --export-secret-keys or --export-secret-subkeys (GnuPG "stub" primary) import fine. Encryption uses SEIPDv1, which all GnuPG versions read.
  • Passphrase cache. Without gpg-agent, the app keeps the last working passphrase in memory only. Nothing is written to disk.
  • Sync. Every change is committed at once, so unpushed commits are the queue of pending changes; nothing else is persisted. Conflicts resolve to the newer change (per entry, by the time of the last commit that really changed it; merges and re-encryptions don't count), deletes included, with a tie between an edit and a delete keeping the edit; an entry renamed differently on two devices keeps both names, and one added on both keeps the local one as <name>-conflict. Saving an edit is refused if a sync changed the entry since it was opened. After a merge, entries whose recipients no longer match their .gpg-id are re-encrypted (the local ref refs/touchpass/checked tracks how far that was verified). Syncs only run while the app is open: Lomiri suspends apps in the background.
  • Framework ubuntu-touch-24.04-1.x. Check that the phone supports it with ls /usr/share/click/frameworks/ on the device.

Build & run

Same conventions as the other Rust projects in ~/dev: rustfmt defaults, clippy lints in [workspace.lints] with thresholds in clippy.toml, tasks in Makefile.toml (cargo-make), and the same checks in Forgejo CI (.forgejo/workflows/ci.yml).

cargo make install-hooks     # once per clone: format on every commit
cargo make                   # = cargo make ci: fmt check, clippy -D warnings,
                             #   tests + coverage, release check, cargo audit
cargo make qml-test          # QML tests in clickable's amd64 image (Docker)
cargo make click             # build the arm64 .click (clickable; runs click-review)
cargo make click-install     # ... and install + launch it on the phone over adb
clickable logs --arch arm64  # follow the app's output on the phone

The host checks need Qt 5 dev files (qmake) and gettext for the app crate; passcore's tests use gpg and git, if installed, as reference implementations. Accepted cargo audit advisories are listed, with reasons, in .cargo/audit.toml.

The QML tests need Lomiri, which most desktops don't package, so they run in clickable's image (tests/qml/run.sh under Xvfb; CI's qml job does the same). Not clickable test: it reports success even when tests fail. Pass a test to run just that one, e.g. cargo make qml-test SyncScheduler::test_syncs_on_start.

Roadmap

  • Folder navigation, with search scoped to the current folder.
  • Clone an existing store from Git (git2, SSH deploy key) — the way to bring an existing store in.
  • Git sync (pull/push), automatic with retries.
  • Wizard: handle a store that exists but has no matching key yet (import it, or start over).
  • Cache unlocked keys instead of the passphrase, to skip the S2K on every open.
  • Add / edit / generate entries (encrypt to the recipients in .gpg-id).
  • Delete / rename entries.
  • Import other people's public keys (shared stores).
  • OTP (otpauth:// lines) support.

Credits

The app icon (assets/logo.svg) is public domain. Its key is "Simple key" by rygle from OpenClipart (public domain); the folder and background were drawn for this project.

License

Copyright (C) 2026 Austin Schaefer

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.