Skip to main content

Developers Toolbox

Token Manager

Store API tokens in named profiles and print them as shell exports. Each profile can keep tokens as plaintext JSON, or encrypt them with gpg or openssl.

The PyPI name is lupaxa-token-manager. The import path is lupaxa.token_manager. The console scripts are tokenctl and token-manager.

Install

pip install lupaxa-token-manager

Requires Python 3.10 or newer. Encryption modes need the gpg or openssl binary on PATH. Plaintext profiles do not.

Quick Start

tokenctl profile init
tokenctl add --type github --name main --value 'ghp_xxx' --env-var GITHUB_TOKEN
tokenctl list
source <(tokenctl set --type github --name main --format export)

A child process cannot change the parent shell, so source the export line. Status text is coloured on a terminal: green for success, red for errors, yellow for a plaintext profile, and cyan for information. tokenctl help and tokenctl help list colour the command and option names. export, dotenv, value, JSON, and name lists stay plain so they can be sourced or parsed. Set NO_COLOR to turn colour off. CLICOLOR_FORCE turns colour on when the stream is a pipe. --profile goes before the subcommand and defaults to default:

source <(tokenctl --profile ci set --type github --name main --format export)

Names are unique within a type, so main can exist for both github and aws. Selecting by --name also needs --type. --id selects a token on its own. --env-var defaults to TYPE_TOKEN (for example GITHUB_TOKEN).

Command Reference

Command Purpose
list Print tokens as a table, JSON, or names
types Print the token types in the profile
add Store a new token
update Change type, name, value, or env var
delete Remove a token
show Print metadata, or the value if asked
set Print export, dotenv, or the raw value
profile init Create a profile and encryption mode
profile update Write config.json for a legacy profile
profile list List every profile directory
profile show Print the profile name and encryption
profile set-encryption Rewrite tokens in a new encryption mode
migrate Copy or move tokens between profiles
help Show help for every command, or one

List and Show

tokenctl list
tokenctl list --type github --format names
tokenctl list --format json
tokenctl list --format json --reveal
tokenctl list --columns name,env_var,value --max-width 100
tokenctl types
tokenctl show --type github --name main
tokenctl show --type github --name main --reveal

The default list is a grouped text table. The table and JSON both hide secret values unless you pass --reveal. show does the same.

--columns picks and orders columns. The names are name, env_var, id, updated, created, value, and type. --max-width wraps the table to that many characters.

Add, Update, and Delete

tokenctl add --type pypi --name publish
printf '%s' 'pypi-secret' | tokenctl add --type pypi --name publish --value -
tokenctl update --type github --name main --new-name prod --value 'ghp_new'
tokenctl delete --type pypi --name publish

Omit --value on add and the tool prompts without echoing. --value - reads the secret from stdin, which keeps it out of shell history and the process list. On update, omit --value to leave the secret unchanged, or pass --value - to replace it from stdin. update --env-var '' clears a stored variable name.

Set

tokenctl set --type github --name prod --format export
tokenctl set --type pypi --name publish --format dotenv
tokenctl set --type github --name prod --format value

export is the default. export and dotenv both single-quote the value. The variable name must be a shell identifier.

Profiles

tokenctl profile list
tokenctl profile init
tokenctl profile update
tokenctl --profile ci profile init --encryption openssl
tokenctl --profile ci profile show
tokenctl --profile ci profile set-encryption gpg

profile list prints every profile directory. The columns are name, whether it is initialised, and encryption. A directory with no config and no token file is listed as not initialised, with a blank encryption. Do not pass --profile to it. --format names prints one name per line. --format json prints profile, initialised, and encryption. profile init creates a profile. A profile is also available when it already has a token file and no config.json: that is a legacy profile. Commands do not create a directory for a name that has neither. A second init stops and leaves an existing profile unchanged. profile update writes config.json for a legacy profile and does not rewrite its token file. tokens.json is recorded as none, tokens.json.gpg as gpg, and tokens.json.enc as openssl. A profile that already has config.json is left as it is. If more than one token file is present and config.json is missing, the command stops rather than guessing a mode. set-encryption writes and checks the new token file before it updates config.json, then removes the previous file. Changing between gpg and openssl asks for the current passphrase and the new one. If that write fails, the existing profile is left as it is.

Migrate

tokenctl migrate --from-profile default --to-profile ci --dry-run
tokenctl migrate --from-profile default --to-profile ci --type github --name prod --move --overwrite

--dry-run prints the change and writes nothing. It still reads both profiles, so an encrypted profile asks for its passphrase. The destination must already exist. Each migrated token is stored with the destination profile's encryption. A plaintext destination stores plaintext. A gpg or openssl destination stores that mode. Without --move, the source profile is left as it is. --move changes the source only after the destination reads back every migrated token. --overwrite replaces a destination token that already uses the same type and name, or the same id with different contents. A conflict without --overwrite is still an error during a dry run. Copying the same token again is allowed when the destination copy is unchanged. A migrate asks once when one profile is encrypted, and twice when both are. TOKENCTL_PASSPHRASE is used only when one passphrase is required.

Storage and Encryption

Tokens live under $XDG_CONFIG_HOME/tokenctl (or ~/.config/tokenctl). Each profile is profiles/<profile>/ with a config.json encryption mode of none, gpg, or openssl.

Mode File Tool
none tokens.json Plaintext JSON, mode 0600
gpg tokens.json.gpg gpg --symmetric AES256
openssl tokens.json.enc openssl enc -aes-256-cbc -pbkdf2

Profile names use letters, digits, ., _, and -. Profile directories are mode 0700. Set TOKENCTL_PASSPHRASE for a non-interactive shell when the command needs one passphrase. A migrate between two encrypted profiles ignores it and asks for each passphrase on a terminal. That passphrase is passed to gpg or openssl on its own pipe. It is not written into the environment. Each write uses a private temporary file, is read back, and then replaces the live file. A profile lock is held for the whole command. A token file that is missing, unreadable, or not a list of tokens stops the command and is left in place. config.json must name none, gpg, or openssl; any other value stops the command.

Each token stores id, type, name, value, env_var, created_at, and updated_at. Timestamps are UTC in YYYY-MM-DDTHH:MM:SSZ form.

Exit 0 is success. Exit 1 is a runtime failure (missing token, conflict, or crypto error). Exit 2 is a usage error, such as --name without --type.

Library

from lupaxa.token_manager import Store

store = Store(profile="default")
for token in store.load():
    print(token.name, token.type)

Store(profile, config_dir=...) overrides the config root. Hold store.exclusive() across a load, change, and save so another process cannot write the profile in between.

Documentation

Site pages live in mkdocs/ and publish to https://token-manager.thelupaxaproject.org/.

make init
make python-install-dev
make mkdocs-serve
The Lupaxa Project Footer

Metadata

Release files for lupaxa-token-manager 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lupaxa-token-manager 0.2.0
File Size Uploaded
lupaxa_token_manager-0.2.0.tar.gz 23.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lupaxa-token-manager 0.2.0
File Interpreter ABI Platform
lupaxa_token_manager-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 51.0 kB

Release files / lupaxa_token_manager-0.2.0.tar.gz

Download URL lupaxa_token_manager-0.2.0.tar.gz
Size 23.0 kB
Tags Source
SHA-256 checksum
How to use checksums
4be8b42ec058b6a344728a3a77a71e59b6509d7f71636695c8029d4a216c3123
BLAKE2b-256 checksum
How to use checksums
c1ae71b7b15ac4a5891ba63a018fa2d21e4d997c002a6703f5ac3dfd2d3d3afa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / lupaxa_token_manager-0.2.0-py3-none-any.whl

Download URL lupaxa_token_manager-0.2.0-py3-none-any.whl
Size 28.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a43e8f549e7d65e55b6e309e79d8135b2d0c4f5695fad10bdf4dcb28d1d76a99
BLAKE2b-256 checksum
How to use checksums
80fd8b6eb1002b8d58013d073fdc48f7568572b3667bfcd07e22afd430407064
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page