Help · CLI · Credentials
CLI Credentials Guide
Where the jsm CLI stores credentials, how to silence the passphrase prompt, and how to wire up the platform-native secret store on macOS, Windows, and Linux.
CLI Credentials: Keyring, Passphrase, and Headless Setups
When you sign in with jsm login, the CLI completes Google OAuth in your
browser and then needs somewhere durable to put the resulting access and
refresh tokens. This page covers where those tokens land, what to do when
the CLI prompts you for a passphrase, and how to wire up the platform-native
secret store on each OS so you stop getting prompted at all.
Why the CLI cares about credential storage
The refresh token the CLI receives after jsm login is a bearer credential.
Anyone who reads it off your disk can use it to download premium content as
you until it expires. The CLI deliberately avoids ever writing it in
plaintext: the default path is to hand it to the operating system's secret
store (Keychain on macOS, Credential Manager on Windows, GNOME Keyring or
KWallet on Linux), and the fallback path is an AES-256-GCM-encrypted file,
credentials.enc, with a passphrase you set on first save. The file sits in
jsm's config directory: ~/Library/Application Support/jsm/ on macOS,
~/.config/jsm/ on Linux, %APPDATA%\jsm\ on Windows (jsm config show
prints it).
You can tell which path you're on by running jsm doctor. The
Credential Storage check reports one of:
- OS keyring — you're on the recommended default. No prompts.
- Encrypted file (interactive passphrase) — the keyring wasn't usable,
so the CLI fell back to the file and prompts for a passphrase on each
load. The
detailsline explains why the keyring isn't available (volatile backend, not installed, daemon not running, etc.). - Encrypted file (env passphrase) — same file fallback, but you've opted into reading the passphrase from environment variables so the CLI doesn't prompt.
- No credentials stored; jsm login will save them in the OS keyring (or
in the encrypted file) — fresh machine; run
jsm loginorjsm auth set-key. The line under it names the keyring that will be used, or says why it can't be (since jsm 0.3.29).
macOS — Keychain
Keychain works out of the box in a normal desktop terminal. The first time the
CLI writes credentials, macOS may prompt once to grant jsm access. After
that, no prompts.
An SSH session cannot display or approve that macOS authorization UI. If a
first-time jsm login --remote receives Keychain's exact "User interaction is
not allowed" status, jsm uses the AES-256-GCM encrypted credential file for
that login instead and asks you to choose a local passphrase. This fallback is
deliberately narrow: other Keychain errors still stop the login, and jsm
will not create file credentials when it cannot prove that the Keychain and
fallback file were both empty.
For a headless SSH invocation with no TTY, opt into an environment-provided passphrase before logging in:
export JSM_ALLOW_ENV_PASSPHRASE=1
export JSM_CREDENTIALS_PASSPHRASE='choose-a-long-unique-secret'
jsm login --remote
If you ever see a passphrase prompt on macOS, that means Keychain access was
unavailable for that session, or was denied or revoked. For the SSH case, the
prompt is expected and protects the encrypted fallback. For a desktop session,
open Keychain Access, search for jsm, and restore access before logging in
again.
Windows — Credential Manager
Credential Manager works out of the box. Stored credentials live under
Control Panel → User Accounts → Credential Manager → Windows Credentials
with the target name jsm. Deleting that entry and re-running jsm login
re-creates it.
Linux — GNOME Keyring (Fedora, Ubuntu, Debian, Arch)
GNOME Keyring is the most common Linux backend. It's a daemon (gnome-keyring-daemon)
that exposes the Secret Service D-Bus API the CLI talks to via the
secret-service Rust binding (pure-Rust zbus, so the static release binary
needs no libdbus). If you're in a normal desktop session, the daemon starts
automatically when you log in and the CLI uses it without you doing anything.
This needs jsm 0.3.29 or newer. Releases up to 0.3.28 were built without
the Secret Service backend, so on Linux they always used the encrypted file,
even with GNOME Keyring running and unlocked (jsm doctor blamed a "volatile
(UntilReboot) keyring backend"). Run jsm update, then jsm logout && jsm login
to move existing file credentials into the keyring.
Before it reads or writes credentials, the CLI checks that a Secret Service
is on the session bus and that its default keyring exists. An unlocked keyring
is used directly. A locked one is used only from a desktop session, where
GNOME asks you to unlock it; elsewhere (SSH, cron, the scheduled
jsm auto-update run) the CLI uses the encrypted file rather than pop up an
unlock prompt.
If jsm doctor reports the encrypted-file fallback on Linux, install
GNOME Keyring and confirm the daemon is running:
# Fedora / RHEL
sudo dnf install gnome-keyring libsecret
# Ubuntu / Debian
sudo apt install gnome-keyring libsecret-1-0
# Arch / Manjaro
sudo pacman -S gnome-keyring libsecret
In a desktop session, the daemon should already be running. To verify:
pgrep -af gnome-keyring-daemon
busctl --user list | grep -i secret
For a non-GNOME desktop or an SSH session that needs the keyring (rare but sometimes useful), start the daemon manually and export the variables it prints:
eval "$(gnome-keyring-daemon --start --components=secrets)"
export GNOME_KEYRING_CONTROL SSH_AUTH_SOCK
Once the daemon is reachable, run jsm logout && jsm login so the CLI
writes fresh credentials into the keyring instead of the file fallback.
The passphrase prompts should disappear.
Linux — KWallet (KDE)
KWallet exposes the same Secret Service API used by GNOME Keyring, so the CLI talks to it through the same Rust binding without any extra configuration. Install with:
# Fedora / KDE Plasma
sudo dnf install kwalletmanager5
# Ubuntu / Kubuntu
sudo apt install kwalletmanager
After install, the wallet daemon (kwalletd5) starts with your session.
The wallet itself is unlocked with your KDE login password.
Linux — no Secret Service (servers, SSH sessions)
When no Secret Service is reachable, the CLI uses the encrypted file and
jsm doctor says why:
Credential Storage: Encrypted file (interactive passphrase)
OS keyring not used: no Secret Service keyring (GNOME Keyring, KWallet, KeePassXC) is reachable on the D-Bus session bus...
The kernel keyutils keyring is never used for new credentials: it is
volatile (entries die at reboot). jsm 0.3.29 and newer only read it to move
credentials an older jsm left there into the encrypted file.
Headless servers — env-passphrase opt-in
On a server with no desktop session (no GNOME Keyring, no KWallet), the keyring path isn't available at all. You have two options:
Option A — Use an API key instead of OAuth
API keys are stored under the same encryption, but they're per-user identity tokens you mint from the website and revoke from the website, which is a better fit for unattended use:
# Generate a key at /settings/api-keys, then paste it at the hidden prompt
jsm auth set-key
# or pipe it in (scripts):
printf '%s\n' "$JSM_KEY" | jsm auth set-key
Both keep the key out of shell history and the process list. Releases before
these forms (v0.3.35 and older) need the key as an argument:
jsm auth set-key jsm_xxxxxxxxxxxxxxxxxxxxxxxx.
This still triggers a passphrase prompt on first save, but you only set it once on the machine.
Option B — Read the passphrase from environment variables
Add both of these to your shell rc (~/.bashrc, ~/.zshrc, systemd unit
Environment=, etc.) and the CLI will stop prompting:
export JSM_ALLOW_ENV_PASSPHRASE=1
export JSM_CREDENTIALS_PASSPHRASE='<your passphrase>'
Both variables are required. JSM_CREDENTIALS_PASSPHRASE alone is
intentionally ignored — we want the env-var path to be an explicit opt-in,
not a default that erodes the on-disk encryption guarantee silently. With
both set, the CLI prints a one-line notice to stderr on first use so you
can tell the env-var path is active:
jsm: using credential passphrase from JSM_CREDENTIALS_PASSPHRASE (JSM_ALLOW_ENV_PASSPHRASE is set).
To suppress that line in fully unattended scripts, also set JSM_QUIET=1.
If you genuinely want no keyring path at all (e.g. you're testing the
file fallback or your distro ships a broken keyring backend), set
JSM_DISABLE_KEYRING=1 so the CLI doesn't even try.
Troubleshooting
"Enter passphrase for CLI credentials:" on every command
Run jsm doctor. If Credential Storage shows "Encrypted file
(interactive passphrase)", the keyring isn't being used. The details
line tells you why — that's your starting point. Most often it's one of:
- GNOME Keyring not installed → install it (commands above).
- GNOME Keyring installed but daemon not running → check
pgrep -af gnome-keyring-daemon; if empty, you're not in a desktop session. JSM_DISABLE_KEYRING=1was set → unset it and re-run.- jsm 0.3.28 or older on Linux → those releases could not use the Secret
Service at all; run
jsm update. - "the Secret Service default keyring is locked and no desktop session is available to unlock it" → unlock the keyring (log in to your desktop) or use the env-passphrase setup below.
"Cannot prompt for passphrase in non-interactive environment"
You're running jsm over SSH or under a process that has no TTY, and the
keyring is unavailable. Set JSM_ALLOW_ENV_PASSPHRASE=1 and
JSM_CREDENTIALS_PASSPHRASE as above, or switch to API-key auth.
Wiped Keychain / Credential Manager and now things are weird
jsm logout then jsm login from a regular terminal session. The CLI
will re-acquire credentials and store them into whichever backend is
healthy.
I want to start over
jsm logout
rm -f ~/.config/jsm/credentials.enc # Linux
rm -f ~/Library/Application\ Support/jsm/credentials.enc # macOS
jsm login
jsm logout clears the keyring entries; the rm clears the encrypted
file. jsm login then starts a fresh OAuth flow.
See also
jsm doctor— top-line health check, includingCredential Storage/help/cli— CLI installation guide/settings/api-keys— generate and revoke API keys