CLI
The edgekms CLI is a single binary, built for Linux on x86_64 and arm64 and for macOS on Apple
silicon. You need it to log in to a tenant from a terminal (SSH, GitHub or OIDC), to manage hosted
tenants without the web console, and for the offline break-glass commands, which
decrypt and export keys from a root key with no server at all.
With mise
mise use -g github:lokkju/edgekms
edgekms --version
mise picks the release archive for your platform from GitHub. Pin a version with
github:lokkju/[email protected].
From a release
Download the archive for your platform from the
latest release, with the .sha256 file
beside it, check it, and put edgekms on your PATH:
sha256sum -c edgekms-<version>-x86_64-unknown-linux-gnu.tar.gz.sha256 # shasum -a 256 -c on macOS
tar -xzf edgekms-<version>-x86_64-unknown-linux-gnu.tar.gz
install edgekms-<version>-x86_64-unknown-linux-gnu/edgekms ~/.local/bin/
edgekms --version
The archives are named for their Rust target: x86_64-unknown-linux-gnu,
aarch64-unknown-linux-gnu and aarch64-apple-darwin.
From source
With a Rust toolchain:
cargo install --git https://github.com/lokkju/edgekms edgekms-cli
The CLI keeps nothing on disk. Every login prints its token on stdout for you to export
(VAULT_TOKEN for a tenant, EDGEKMS_CONTROL_TOKEN for the hosted control plane), and root keys
are only ever read from stdin, never from a terminal or a flag.
Reference
Generated from the definitions edgekms 0.3.0 is built from. edgekms <command> --help prints the same text.
edgekms
edgekms command line
edgekms <COMMAND>
Subcommands: login, decrypt, export, account, tenant, ops.
edgekms login
Log in to a Worker and print the token on stdout
edgekms login <COMMAND>
Subcommands: ssh, github, oidc.
edgekms login ssh
Sign a challenge with ssh-keygen -Y sign (ssh-agent, FIDO keys included)
edgekms login ssh [OPTIONS] --addr <ADDR> --role <ROLE> --key <KEY>
--addr <ADDR>(required): Worker URL; https, or http for loopback only--role <ROLE>(required): The role to log in as, from the deployment's config--key <KEY>(required): The public key file; ssh-keygen finds the private half in ssh-agent. If the agent doesn't hold it, ssh-keygen falls back to the private key file beside it (the same path without .pub) and may prompt for its passphrase--mount <MOUNT>(defaultssh): The SSH auth mount name
edgekms login github
GitHub's OAuth device flow: enter a code at github.com, from any browser
edgekms login github [OPTIONS] --addr <ADDR> --role <ROLE> --client-id <CLIENT_ID>
--addr <ADDR>(required): Worker URL; https, or http for loopback only--role <ROLE>(required): The role to log in as, from the deployment's config--client-id <CLIENT_ID>(required; envEDGEKMS_GITHUB_CLIENT_ID): The client ID of the deployment's GitHub OAuth App (device flow enabled)--mount <MOUNT>(defaultgithub-user): The GitHub user auth mount name
edgekms login oidc
OIDC in a browser: authorization code with PKCE, redirected to 127.0.0.1.
A client secret, for providers that issue one to desktop clients, is read from EDGEKMS_OIDC_CLIENT_SECRET, never from the command line.
edgekms login oidc [OPTIONS] --addr <ADDR> --role <ROLE> --issuer <ISSUER> --client-id <CLIENT_ID>
--addr <ADDR>(required): Worker URL; https, or http for loopback only--role <ROLE>(required): The role to log in as, from the deployment's config--mount <MOUNT>(defaultoidc): The JWT auth mount that trusts this issuer--issuer <ISSUER>(required; envEDGEKMS_OIDC_ISSUER): The issuer URL, as the provider's discovery document names it--client-id <CLIENT_ID>(required; envEDGEKMS_OIDC_CLIENT_ID)--scope <SCOPE>(defaultopenid email): Space-separated scopes; must include openid--port <PORT>(default8250): Loopback port for the redirect,http://127.0.0.1:<port>/oidc/callback; 0 picks a free one, for providers that accept any loopback port--no-browser: Print the URL without trying to open a browser
edgekms decrypt
Offline: decrypt a ciphertext with the root key read as base64 from stdin
edgekms decrypt --config <CONFIG> --key <KEY> <CIPHERTEXT>
--config <CONFIG>(required): The tenant or instance config (JSON) the ciphertext's key is in--key <KEY>(required): The key's name in the config<CIPHERTEXT>(required): The ciphertext,vault:v<version>:...
edgekms export
Offline: print the raw base64 key for one version, root key read as base64 from stdin
edgekms export --config <CONFIG> --key <KEY> --version <VERSION>
--config <CONFIG>(required): The tenant or instance config (JSON) the key is in--key <KEY>(required): The key's name in the config
edgekms account
The hosted edgekms service at edgekms.dev: log in to its control plane
edgekms account <COMMAND>
Subcommands: login, ssh-keys, request-invite, logout.
edgekms account login
GitHub's device flow with the hosted edgekms App, or with --ssh a key registered on your account, then print a control token on stdout for export EDGEKMS_CONTROL_TOKEN=.... The token lasts 1800 seconds (30 minutes); tenant export and account ssh-keys add need one under five minutes old. Nothing is saved to disk
edgekms account login [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only--client-id <CLIENT_ID>: Your own GitHub App's client id, for a control plane other than api.edgekms.dev. Without it the hosted App's id is used, fromEDGEKMS_HOSTED_CLIENT_IDor else from the control plane's/v1/info, and its tokens go only to https://api.edgekms.dev--ssh: Log in without GitHub, with an SSH key registered on your account (seeedgekms account ssh-keys): the control plane's challenge is signed through ssh-agent withssh-keygen -Y sign. Without --key, ssh-agent must hold exactly one Ed25519, sk-ssh-ed25519 or ECDSA P-256 key--key <KEY>: With --ssh, the public key file (ending in .pub) to sign with. ssh-keygen finds the private half in ssh-agent, or falls back to the private key file beside it (the same path without .pub) and may prompt for its passphrase
edgekms account ssh-keys
The SSH keys that can log in to your account with edgekms account login --ssh, for when GitHub is unavailable. Reads the control token from EDGEKMS_CONTROL_TOKEN
edgekms account ssh-keys [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: list, add, remove.
edgekms account ssh-keys list
Print id<TAB>fingerprint<TAB>key_type<TAB>label<TAB>added_ms<TAB>added_via<TAB>last_used_ms for each key (last_used_ms empty for a key that has never logged in), then unverified<TAB><fingerprint> for each key record that failed its integrity check and can't log in
edgekms account ssh-keys list [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms account ssh-keys add
Register a key, proving you hold it with a signature through ssh-agent (ssh-keygen -Y sign -n edgekms-register). Needs a login under five minutes old. The signed challenge names the GitHub account the key goes to, which is printed before signing; a challenge for any account but yours is refused. A registered key can log in, export root keys and add more keys, as your GitHub account can, so prefer a security key (sk-ssh-ed25519), which signs only with a touch, or an agent key added with ssh-add -c, which asks before each signature
edgekms account ssh-keys add [OPTIONS]
--key <KEY>: The public key file (ending in .pub). Without it, the one Ed25519, sk-ssh-ed25519 or ECDSA P-256 key ssh-agent holds--label <LABEL>: A label for the key, at most 64 characters. Without it, the key's comment, when that fits--expect-github-id <EXPECT_GITHUB_ID>: The GitHub id of the account the key must go to. Without it, the accountEDGEKMS_CONTROL_TOKENsays it is for--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms account ssh-keys remove
Remove a key, by the id or the SHA256: fingerprint list prints. Every control token it minted stops working
edgekms account ssh-keys remove [OPTIONS] <KEY>
<KEY>(required): The key's id or fingerprint--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms account request-invite
For a GitHub account edgekms.dev doesn't admit yet: GitHub's device flow as for login, then ask for an invite with an optional note on what you'll use edgekms for, or see how an earlier request stands. Nothing is printed on stdout or saved to disk
edgekms account request-invite [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only--client-id <CLIENT_ID>: Your own GitHub App's client id, as forlogin--note <NOTE>: What you'll use edgekms for, at most 500 characters. Without it you're asked, when stdin is a terminal
edgekms account logout
End every control token you hold, the one in EDGEKMS_CONTROL_TOKEN included, before its 1800 seconds are up
edgekms account logout [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant
The hosted edgekms service: manage your tenants. Reads the control token from EDGEKMS_CONTROL_TOKEN (see edgekms account login); a token lasts 1800 seconds
edgekms tenant [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: list, invites, accept, decline, show, create, config, member, export.
edgekms tenant list
List the tenants you are a member of, with your role in each
edgekms tenant list [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant invites
List your pending invites, with the role each offers
edgekms tenant invites [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant accept
Accept a pending invite to a tenant
edgekms tenant accept [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant decline
Decline a pending invite to a tenant
edgekms tenant decline [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant show
Print a tenant's details, config, members and pending invites as JSON
edgekms tenant show [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant create
Create a tenant, optionally from an existing root key. Besides your personal tenant, a tenant you create is named after your GitHub login: <login>-<suffix>
edgekms tenant create [OPTIONS] <NAME>
<NAME>(required)--config <CONFIG>: The tenant config, checked locally against the hosted rules before it's sent. The control plane replaces itsroot_idwith the new tenant's unless you import. Without it the control plane writes a default config--import-root-id <IMPORT_ROOT_ID>: Import an existing root key with this root id. The key is read as base64 from stdin (never a terminal), and --config is required--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant config
Read or replace a tenant's config
edgekms tenant config [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: get, set.
edgekms tenant config get
Print the config JSON on stdout and its version on stderr
edgekms tenant config get [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant config set
Replace the config with a file, checked locally first
edgekms tenant config set [OPTIONS] <NAME> <FILE>
<NAME>(required)<FILE>(required)--if-version <IF_VERSION>: The version being replaced; without it the current version is read first--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant member
Invite, change or remove a tenant member (owners only)
Also members.
edgekms tenant member [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: add, remove.
edgekms tenant member add
Invite a user by numeric GitHub id, or offer a member the owner role. Nothing changes until they run edgekms tenant accept; only lowering an owner to member applies at once
edgekms tenant member add [OPTIONS] --github-id <GITHUB_ID> <NAME>
<NAME>(required)--github-id <GITHUB_ID>(required)--owner: Make them an owner; members are read-only--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant member remove
Remove a member or withdraw a pending invite; the last owner can't be removed
edgekms tenant member remove [OPTIONS] --github-id <GITHUB_ID> <NAME>
<NAME>(required)--github-id <GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms tenant export
Print a tenant's root key as base64 on stdout (owners only, with a login under five minutes old). Pipe it straight into sops or another encrypted store
edgekms tenant export [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops
The hosted edgekms service, for its operators (OPERATOR_GITHUB_IDS) only. Reads the control token from EDGEKMS_CONTROL_TOKEN
edgekms ops [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: allowlist, tenant, invite-requests.
edgekms ops allowlist
The signup allowlist: the GitHub users who may sign up while SIGNUP_MODE is invite
edgekms ops allowlist [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: add, remove, list.
edgekms ops allowlist add
Let a GitHub user, by numeric id, sign up
edgekms ops allowlist add [OPTIONS] <GITHUB_ID>
<GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops allowlist remove
Take a GitHub user off the allowlist. Once they have signed up, they keep logging in while they belong to a tenant
edgekms ops allowlist remove [OPTIONS] <GITHUB_ID>
<GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops allowlist list
Print github_id<TAB>added_by<TAB>added_ms for each entry
edgekms ops allowlist list [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops tenant
Suspend or resume any tenant, by name
edgekms ops tenant [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: suspend, resume.
edgekms ops tenant suspend
Suspend a tenant: within about 30 seconds its host refuses every request, and its config and members can't change. Its owners can still export the root key
edgekms ops tenant suspend [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops tenant resume
Make a suspended tenant active again
edgekms ops tenant resume [OPTIONS] <NAME>
<NAME>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops invite-requests
Requests for an invite from GitHub users refused while SIGNUP_MODE is invite
edgekms ops invite-requests [OPTIONS] <COMMAND>
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
Subcommands: list, approve, decline, delete.
edgekms ops invite-requests list
Print each request as github_id login status date with its note indented below: every pending one, oldest first, then the 100 most recently decided
edgekms ops invite-requests list [OPTIONS]
--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops invite-requests approve
Put the user on the signup allowlist and mark the request approved; their next sign-in creates their personal tenant
edgekms ops invite-requests approve [OPTIONS] <GITHUB_ID>
<GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops invite-requests decline
Mark the request declined. They can't ask again until the request is deleted
edgekms ops invite-requests decline [OPTIONS] <GITHUB_ID>
<GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only
edgekms ops invite-requests delete
Delete the request, whatever its status. A declined user can then ask again; an approved one stays on the allowlist
edgekms ops invite-requests delete [OPTIONS] <GITHUB_ID>
<GITHUB_ID>(required)--api <API>(defaulthttps://api.edgekms.dev): The control plane URL; https, or http for loopback only