CLI tool
paolostivanin/OTPClient avatar
paolostivanin/OTPClient

OTPClient: a TOTP and HOTP client in C with a GTK4 interface

A secure, easy-to-use TOTP/HOTP client written in C with a GTK4/libadwaita interface.

562 stars51 forksCGPL-3.0

At a glance

What is it?
OTPClient manages two-factor tokens on Linux in three forms: a GTK4 and libadwaita desktop application, a command line client built for scripting, and a D-Bus daemon that plugs into GNOME Shell Activities Search and KDE Plasma KRunner. It supports TOTP and HOTP across SHA1, SHA256 and SHA512, imports and exports from several other authenticator apps, and hides codes until you ask for them.
Who is it for?
OTPClient suits a Linux user who wants their two-factor tokens in a native application rather than in a browser extension or a phone, and who wants the same token database reachable from a shell script and from the desktop search bar.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One token store, three ways to reach it

OTPClient is a highly secure GTK4 and libadwaita application for managing TOTP and HOTP two-factor authentication tokens, with CLI and desktop-search integration, and the primary language of the repository is C.

The three interfaces are worth separating, because they exist for different reasons and they share one encrypted database.

The GUI is the place where tokens are added, grouped and organised. The command line client, otpclient-cli, is for scripting and shell integration. And a separate D-Bus daemon, otpclient-search-provider, plugs into the desktop search on GNOME and KDE so that a code can be produced without opening a window.

That last one changes the shape of the daily use. The README describes the flow: type the configurable trigger keyword, default otp, followed by a query, select a result, and the daemon computes the OTP, delivers it through a system notification and copies it to the clipboard. The value never appears in the search result preview, so the preview does not expose codes.

The repository layout matches a project with several outputs. There is a CMakeLists.txt, src/ for the code, proto/ for the protocol definitions the D-Bus interface needs, man/ for the manual pages, po/ for the translations, data/ for the desktop and icon resources, tests/ for the test suite, and both .ci/ and .circleci/ directories for the continuous integration. The licence is GPL-3.0, and there is a SECURITY.md at the root. The repository topics are the 2fa, otp, totp, hotp, c, gnome, gtk4 and libadwaita ones, which is an honest summary of a native Linux application.

The audience is a Linux desktop user who wants tokens outside the browser and outside a phone, and who may want to script against them.

Standards coverage, and the limits on manual entry

The supported standards section is short and specific, and it is where you check whether your tokens will import at all.

Both TOTP and HOTP are supported. Manual entry allows digits from 4 to 10 and a period from 1 to 120 seconds, and otpauth:// URIs are handled for import, display and QR, with the same digit and period ranges. The hash algorithms are SHA1, SHA256 and SHA512, which covers the three that appear in practice, since the SHA1 case is the default in the URI scheme while the newer services use the stronger ones.

Steam guard codes are supported as well, with the details on a wiki page linked from the README. That is a separate token family with its own rules rather than a plain TOTP, and it is worth checking that page before assuming a Steam token behaves like the others.

The digit and period ranges are a limitation as much as a feature. The README lists digits 4-10 and period 1-120 for both manual entry and the URI path, which means a token with a different period, or one requiring more than ten digits, is outside what this client accepts.

The GUI can add tokens in three ways besides typing the secret: by scanning a QR code from an image file, from the webcam, or from the clipboard. That last one is the convenient path when you have already copied the provisioning URI out of a website, and it avoids retyping a long secret by hand.

Any token can also have its QR code displayed, which is how you re-pair a token onto a second device. The feature is symmetric with the import path, and the README is careful about what happens to those QR codes when the application locks: locking closes sensitive dialogs and clears their QR codes, secrets and passwords, including when an outstanding file chooser still holds the dialog open. That last clause is the kind of detail that shows the behaviour was tested in the awkward cases.

Codes stay hidden, and the countdown follows the visible code

The token list is built around a rule: OTPs are hidden by default, and you have to ask for a code to see it.

Each row has a button in the Action column which copies the TOTP code or generates the HOTP code, and briefly reveals it. The same action heads the row's right-click menu. Double-clicking a row does the same, as do Enter and Ctrl+C when the token list has focus, and Enter works from the search view as well.

Two details keep that from turning into a shortcut that burns a one-time password. Selection and keyboard navigation never consume HOTP codes, so you can move around the list with the keyboard without generating counter values on the way. And hidden codes leave the value cell empty, so an unread token shows nothing rather than showing a digit.

The Validity countdown follows the code rather than the selection: every TOTP that is showing one has a countdown, whether or not its row is selected. That is the behaviour you want, since the countdown is about the code's remaining life and not about what the keyboard is pointing at. Display behaviour is configurable in Settings -> Display.

The rest of the row furniture is ordinary: drag-and-drop reordering of the token list, and the token list itself with groups.

The safety behaviour around the clipboard is more involved than the display rules. There is a configurable clipboard wipe, and it is also applied on lock and exit, as long as OTPClient still owns the clipboard. Copying something elsewhere cancels the wipe, and cancels automatic next-code copying as well. That last rule is a good one: an application that wipes your clipboard later would destroy something you copied in the meantime, so it only wipes what it still owns.

Auto-lock on idle and on screensaver with a configurable timeout, optional minimise to tray (built in but off until enabled), optional start minimised and optional start at login round out the desktop behaviour. Start at login is a desktop file in ~/.config/autostart/com.github.paolostivanin.OTPClient.desktop for native builds, and it can also be triggered from the command line:

bash
otpclient --start-minimized

The preference is inherited by the autostart entry.

Databases, groups and how tokens are grouped across apps

Multiple encrypted databases are listed in the sidebar, and the rules around them are specific enough to be worth reading twice.

The first database you create becomes the default. It loads automatically on startup and is marked with a star. Clicking another row switches the currently open database, which is shown in bold, without changing the default, and the right-click menu offers Set as Primary to change which one loads on next startup. So the database in use and the database at startup are separate decisions, which is what you want when you keep work accounts and personal accounts apart.

A HOTP result from another database opens that database first, and you unlock it and generate the code there explicitly. The library does not reach across database boundaries to generate a code for you.

Tokens can be assigned to groups such as Work or Personal, through the right-click menu, the edit dialog, or at the point of adding the token, and the groups survive Aegis, AuthenticatorPro and 2FAS import and export. Search spans databases and understands a group filter with the group:<name> or #<name> syntax.

HOTP counters are saved before a code is shown or copied, and the GUI and the CLI share the same counter convention. The consequence is that a counter is never advanced by a display that then failed to deliver the code, which is the failure that locks somebody out of an account.

The migration story is broader than the grouping. Import and export are supported for Aegis in encrypted and plain form, AuthenticatorPro in encrypted and plain form, 2FAS in encrypted and plain form, FreeOTPPlus in plain key URI format, and Google migration QR codes for import only.

The rules around errors are the part to look at before you delete anything. Encrypted exports require a nonempty password, although existing encrypted exports with empty passwords can still be imported. Partial imports report the number and reasons for skipped entries, and the README asks you to review those warnings before deleting the source. The CLI keeps a successful exit status when valid entries were imported and prints the warnings to standard error. And entirely invalid input fails without changing any token.

The last point is the one that matters most in practice: a failed import is a no-op rather than a partial write.

The CLI, and the desktop search daemon behind it

The command line client is a first-class interface rather than a thin wrapper, and its options are worth reading as a specification of what can be scripted.

--show and --list are the basics, and --list-databases prints the configured databases by name. --account and --issuer select the token for --show, with --match-exact for exact matching and --show-next for the next TOTP. --database picks a database by path or by name from --list-databases. Import and export work against the same database the GUI uses, with --list-types printing the accepted --type identifiers, --file and --output-dir setting the paths, and --output-dir being ignored in the Flatpak build.

Machine-readable output is available through --output={table,json,csv}, and the README restricts that to --show, --list and --list-databases. The restriction is sensible, since a listing is what you want to pipe while a mutation is not.

Secrets are handled with --password-file, which reads the master password from a file rather than prompting, and preferences are backed up with --export-settings and restored with --import-settings, in the GSettings format the desktop uses.

bash
otpclient --start-minimized

is the command line form of the start minimised preference, which the README lists alongside the Settings -> Integration option. Bash, zsh and fish completions are installed by default, which is a small detail that tells you the CLI is expected to be used interactively as well as from scripts.

The desktop search provider is the other half. It requires Secret Service access and a saved database password, and disabling the provider, clearing its keyword or disabling Secret Service immediately invalidates its caches and activation IDs. Keyword changes take effect without restarting, and the README is explicit that the keyword filters queries and is not a password or an authentication mechanism.

On clipboard handling outside KDE, the provider uses Klipper's D-Bus interface on KDE Plasma, wl-copy on Wayland, and xclip or xsel on X11, and those tools must be installed for the clipboard step to work. The README also documents a Plasma-side latency: activating a result from Kickoff with Enter can take about a second before the code is copied, while clicking the result or using KRunner is instant, and the daemon itself computes and copies the OTP in well under 100 ms. That is a specific, useful diagnosis of a delay that would otherwise look like the application being slow.

The HOTP counter convention, and what an upgrade can break

There is one migration hazard in this application, and it is documented in enough detail to be worth understanding before you upgrade.

The stored counter means the next unused code in both interfaces. But older GUI versions stored the last generated counter, while the CLI stored the next one. Existing values are preserved after an upgrade, because the last writer cannot be inferred, and newly imported counters are interpreted as the next unused value.

So the failure mode is specific. If an existing HOTP account rejects its first code after upgrading, the counter is off by one in the direction the old GUI used, and the README says to generate the next code or resynchronize its counter with the provider. Since the counter is saved before a code is shown, generating the next code advances it and the following attempt succeeds.

That is a genuine inconvenience, and the reason it is spelled out is that the symptom appears on the service side, where a rejected code can look like an account problem rather than a client problem.

Backup and restore is the other operation to know about. Settings -> Backup has four buttons covering both your application preferences, saved as JSON, and your tokens. The README section on it is cut off in the visible text, so the exact split between the four buttons is not spelled out here, but the shape is clear: preferences and token data are backed up separately, which is the right separation since one is a file you might want to sync between machines and the other is the encrypted database.

The release history shows active maintenance. v5.2.0 on 2026-09-16, v5.2.1 on 2026-09-23 and v5.2.2 on 2026-09-24, with the last push to the repository on 2026-09-24.

Editorial conclusion

OTPClient suits a Linux user who wants their two-factor tokens in a native application rather than in a browser extension or a phone, and who wants the same token database reachable from a shell script and from the desktop search bar. It does not suit a macOS or Windows user, since the interface is GTK4 and libadwaita, and it does not suit a workflow that expects the desktop search to be an authentication surface, because the README states plainly that the trigger keyword filters queries and is not a password or an authentication mechanism. Check three things before you rely on it: that your tokens are in a supported format, since Aegis, AuthenticatorPro, 2FAS and FreeOTPPlus are covered but Google migration QR codes can only be imported, whether an existing HOTP account still accepts its next code after an upgrade, because older GUI versions and the CLI stored different counter conventions, and that the clipboard wiping tools your session needs are installed, since outside KDE Plasma the provider relies on wl-copy, xclip or xsel. The last push to the repository was on 2026-09-24 and the current release is v5.2.2.

Frequently asked questions

What is OTPClient and which interfaces does it provide?

It is a GTK4 and libadwaita application in C for managing TOTP and HOTP two-factor tokens, with two companion interfaces: the otpclient-cli command line client for scripting and shell integration, and the otpclient-search-provider D-Bus daemon for GNOME Shell Activities Search and KDE Plasma KRunner.

Which token formats can OTPClient import and export?

It supports Aegis, AuthenticatorPro and 2FAS in encrypted and plain form, FreeOTPPlus in plain key URI format, and Google migration QR codes for import only. Encrypted exports require a nonempty password, partial imports report the number and reasons for skipped entries, and entirely invalid input fails without changing tokens.

How does OTPClient handle HOTP counters after an upgrade?

The stored counter means the next unused code in both interfaces, but older GUI versions stored the last generated counter while the CLI stored the next one, and existing values are preserved because the last writer cannot be inferred. If an account rejects its first code after upgrading, the README says to generate the next code or resynchronize the counter with the provider.

Is the OTPClient desktop search keyword a security feature?

No. The README states that the keyword filters queries and is not a password or an authentication mechanism, and the provider requires Secret Service access and a saved database password. The OTP value never appears in the search result preview, and disabling the provider or Secret Service invalidates its caches immediately.

What output formats does the OTPClient CLI support?

The --output option takes table, json or csv, and it applies to --show, --list and --list-databases only. The CLI also offers --account and --issuer to select a token, --match-exact, --show-next, --database, import and export with --file and --output-dir, --password-file, and GSettings backup and restore with --export-settings and --import-settings.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. paolostivanin/OTPClient on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/paolostivanin-otpclient.svg)](https://hysenlabs.com/projects/paolostivanin-otpclient)

Was this useful?