Background CLI utility that copies OTPs from Gmail to clipboard on hotkey trigger
Project description
OTPilot
Press a hotkey. Your OTP is already copied.
pip install otpilot
✈ OTPilot v2.3
Background utility that fetches OTPs from Gmail and copies them to your clipboard with a single hotkey press.
GitHub • Install • Quickstart • Contributing
How It Works
- OTPilot sits in your system tray with no background polling.
- Press your hotkey or run
otpilot fetchto scan recent Gmail messages. - OTP is copied to clipboard and optionally auto-pasted.
Installation
pip install otpilot
Requirements
- Python 3.10+
- A Gmail account
Linux users must install clipboard support:
sudo apt install xclip # Debian/Ubuntu
Quickstart
1. Run Setup
otpilot setup
The wizard will:
- Let you choose one of 3 authentication modes (Firebase, credentials.json, or IMAP App Password)
- Let you configure hotkey, notifications, and scan preferences
- Save everything locally
2. Start OTPilot
otpilot start
OTPilot runs in the background with a system tray icon when supported.
3. Daily Use
- Receive an OTP email
- Press your hotkey (default:
Ctrl+Shift+O) - Paste
Authentication Modes
OTPilot supports 3 authentication modes. Choose one during otpilot setup.
Setup Responsibilities (You vs End Users)
Use this quick split to know what you (OTPilot deployer/maintainer) must set up once, versus what each end user must do locally.
If you provide Firebase hosted auth
You (maintainer) set up once:
- Build/deploy the web auth page (for example
https://jenil-otpilot.vercel.app/auth) - Configure Firebase project + Google sign-in
- Add authorized domains in Firebase Auth (
jenil-otpilot.vercel.app,localhost) - Set web env vars (
NEXT_PUBLIC_FIREBASE_API_KEY,NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,NEXT_PUBLIC_FIREBASE_PROJECT_ID) - Ensure linked Google Cloud project has Gmail API enabled and consent screen configured for
gmail.readonly
Each user sets up locally:
- Install OTPilot (
pip install otpilot) - Run
otpilot setup - Choose [1] Firebase Auth
- Paste your hosted auth page URL when prompted
- Complete Google sign-in in browser
If users bring their own OAuth client (credentials.json)
You (maintainer) set up:
- Nothing required for hosted auth
Each user sets up locally:
- Create their own Google Cloud project + OAuth client
- Enable Gmail API on that project
- Download
credentials.jsonand place at~/.otpilot/credentials.json - Run
otpilot setupand choose [2] My own credentials.json
If users use Gmail App Password (IMAP)
You (maintainer) set up:
- Nothing required for hosted auth
Each user sets up locally:
- Enable 2-Step Verification on Gmail account
- Generate Gmail App Password at
https://myaccount.google.com/apppasswords - Run
otpilot setupand choose [3] Gmail App Password - Enter Gmail address + app password
Mode 1: Firebase Auth (Recommended)
Use this when you have a hosted Firebase web auth page that performs Google sign-in and redirects back to OTPilot.
You need:
- A Firebase web page URL that:
- Requests Gmail readonly scope (
https://www.googleapis.com/auth/gmail.readonly) - Retrieves
accessTokenandrefreshToken - Redirects to the provided local
redirect_uriwith query params:access_tokenrefresh_token(if available)expires_at(unix timestamp, if available)
- Requests Gmail readonly scope (
Setup flow:
- Run
otpilot setup - Choose [1] Firebase Auth
- Enter your Firebase auth page URL when prompted
- Complete browser sign-in
Mode 2: My own credentials.json
Use this when you want your own Google Cloud OAuth client.
You need:
- A Google Cloud project with Gmail API enabled
- OAuth client credentials downloaded as
credentials.json - File placed at:
~/.otpilot/credentials.json
Setup flow:
- Run
otpilot setup - Choose [2] My own credentials.json
- Confirm once
~/.otpilot/credentials.jsonexists - Complete browser sign-in
Mode 3: Gmail App Password (IMAP)
Use this when you prefer no OAuth flow inside OTPilot.
You need:
- A Gmail account with 2-Step Verification enabled
- A Gmail App Password from:
Setup flow:
- Run
otpilot setup - Choose [3] Gmail App Password
- Enter your Gmail address
- Enter your App Password
CLI Commands
| Command | Description |
|---|---|
otpilot setup |
Run or re-run the interactive setup wizard |
otpilot start |
Start the background service |
otpilot stop |
Stop the background service |
otpilot fetch |
Trigger a one-time OTP fetch from the CLI |
otpilot history |
Show recent OTP history |
otpilot history --clear |
Clear OTP history |
otpilot logs |
Tail the OTPilot log file |
otpilot status |
Show auth state, hotkey, and config |
otpilot hotkey |
View or reconfigure the global hotkey |
otpilot logout |
Clear stored auth token |
otpilot update |
Check PyPI for updates and upgrade |
otpilot version |
Print the installed version |
Configuration
OTPilot stores its configuration at ~/.otpilot/config.json:
{
"auth_mode": "firebase",
"hotkey": "ctrl+shift+o",
"notify_on_copy": true,
"otp_max_age_minutes": 10,
"email_fetch_count": 10,
"otp_history_count": 10,
"auto_paste": false,
"auto_start_on_boot": false,
"notification_sound": false,
"mask_otp_in_notification": true,
"check_updates_on_start": true,
"setup_complete": true,
"firebase_web_url": "",
"imap_user": "",
"imap_host": "imap.gmail.com",
"imap_port": 993
}
| Field | Type | Default | Description |
|---|---|---|---|
auth_mode |
string | firebase |
Auth backend: firebase, credentials, or imap |
hotkey |
string | ctrl+shift+o |
Global hotkey combination |
notify_on_copy |
bool | true |
Show desktop notification when OTP is copied |
otp_max_age_minutes |
int | 10 |
Ignore emails older than this (minutes) |
email_fetch_count |
int | 10 |
Number of recent emails to scan (max 50) |
otp_history_count |
int | 10 |
Number of OTP history entries to keep (max 50) |
auto_paste |
bool | false |
Auto-paste OTP after copying |
auto_start_on_boot |
bool | false |
Launch OTPilot on login |
notification_sound |
bool | false |
Play a sound with notifications |
mask_otp_in_notification |
bool | true |
Mask middle digits in notification (e.g. 84••93) |
check_updates_on_start |
bool | true |
Check PyPI for a newer version on startup |
setup_complete |
bool | false |
Indicates setup has been completed |
firebase_web_url |
string | "" |
URL of your hosted Firebase auth page |
imap_user |
string | "" |
Gmail address used for IMAP mode |
imap_host |
string | imap.gmail.com |
IMAP host for app-password mode |
imap_port |
int | 993 |
IMAP SSL port |
Files Stored Locally
| Path | Purpose |
|---|---|
~/.otpilot/config.json |
Hotkey and runtime settings |
~/.otpilot/history.json |
OTP history entries |
~/.otpilot/otpilot.log |
Background service log |
~/.otpilot/otpilot.pid |
PID of the running background process |
System keyring (otpilot) |
Preferred OAuth token storage |
~/.otpilot/token.json |
Fallback token storage |
~/.otpilot/app_password.txt |
Fallback IMAP app-password storage |
OTP History
Show recent entries:
otpilot history
Show only 3 entries:
otpilot history --count 3
Clear history:
otpilot history --clear
Platform Support
| Platform | Status | Notes |
|---|---|---|
| macOS | ✅ | Full support |
| Windows | ✅ | Full support |
| Linux | ✅ | Requires xclip or xsel |
How OTP Extraction Works
OTPilot scans the subject line and body of your recent emails for:
- 4–8 digit standalone numbers near context words like OTP, code, verify, one-time, passcode, authentication, 2FA
- Only emails within
otp_max_age_minutesare considered - Subject line matches are prioritized over body matches
Security & Privacy
- Read-only Gmail access
- On-demand only, no background polling
- Local storage for tokens and configuration
- No telemetry or analytics
Troubleshooting
| Issue | Solution |
|---|---|
| "Not authenticated" error | Run otpilot setup to re-authenticate |
| "Access token expired..." | Use Firebase/credentials mode with refresh token, or switch to IMAP App Password in setup |
| No OTP found | Check otp_max_age_minutes — the email might be too old |
| Clipboard not working (Linux) | Install xclip: sudo apt install xclip |
| Hotkey not working | Run otpilot hotkey to reconfigure |
| Tray icon not visible | Check your system tray / menu bar settings |
| Stop doesn’t work | Run otpilot stop again or delete ~/.otpilot/otpilot.pid |
| Log file empty | Start OTPilot and run otpilot logs after a fetch |
Contributing
Contributions are welcome. See docs/CONTRIBUTING.md for dev setup and code standards.
License
MIT License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file otpilot-2.3.2.tar.gz.
File metadata
- Download URL: otpilot-2.3.2.tar.gz
- Upload date:
- Size: 48.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e824de7650e96337bbd7851c98f0acb831be4adacd960183628d1fcc6504fe84
|
|
| MD5 |
8f95877e6799ec82faf74b888d5cd4c0
|
|
| BLAKE2b-256 |
017c1b03e1fd5c86bb00b9c7cd4cb82ca7ee2bf148302080ebff05ab8884836e
|
File details
Details for the file otpilot-2.3.2-py3-none-any.whl.
File metadata
- Download URL: otpilot-2.3.2-py3-none-any.whl
- Upload date:
- Size: 40.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29fba152971df9cdcfc8f3ce6f9bbfcc99c578226c8e7aa417b1e271728a9dfc
|
|
| MD5 |
2c6ba4fe269d49cf8849b8bfe416e601
|
|
| BLAKE2b-256 |
b42d17de4fb4fcd2c34e74714b199e491c346cf66da1bb7c1367d86823dade57
|