@nottana/cli
v0.8.4
Published
Nottana command line interface — sign in and use your zero-knowledge vault from the terminal.
Maintainers
Readme
@nottana/cli
Sign in to your Nottana vault from the terminal. Pull secrets straight into your scripts, your CI and your local environment, without pasting them anywhere.
npm install -g @nottana/cli
nottana loginmacOS, Linux and Windows. Requires Node.js 18.17 or newer.
Read the code. This package ships unminified — about 28,000 lines with the comments intact, including the ones explaining why each decision was made. You do not have to take the claims below on trust:
npm pack @nottana/cli
tar -xzf nottana-cli-*.tgz && $EDITOR package/dist/cli.jsThe only host it talks to is https://api.nottana.com. There are no telemetry
calls and no third-party endpoints.
Nottana is zero-knowledge: your master key is derived and used on your machine only. The server stores encrypted blobs it cannot read, and that does not change when you use the CLI.
Why this exists
Today secrets end up in plaintext on developer machines — a .env file, a
config pasted into chat, an API key in shell history. That is normal, and it is
the genuinely risky part of the status quo: a plaintext file cannot be
un-shared once it leaks.
The CLI exists so none of that has to happen. You pull what you need from your vault at the moment you need it, instead of leaving copies lying around. It is not a new door into your account — it is another device you approve, exactly like adding a phone, with the same access your account already has and the ability to disconnect it whenever you want.
Install
npm install -g @nottana/cliRequires Node 18.17 or newer.
Updating
npm update -g @nottana/cliYour session survives an update — the token lives in your OS keystore, not in the package.
Uninstalling
Sign out first. Removing the package deletes the command, not the session: the refresh token stays in your keystore and the machine stays connected to your account until it is revoked.
nottana logout && npm uninstall -g @nottana/cliIf you removed the package before signing out, disconnect that machine from Settings → Security → Connected devices instead.
Sign in
nottana loginThe CLI prints a short code and a URL:
Open: https://app.nottana.com/activate
Code: R96Q-WP5T
Check that this code matches the one shown in the browser
before approving. If you did not start this, close the page.Open that page in a browser where you are already signed in, type the code, and confirm. You will be asked for your password or passkey — approving a device is what lets it read your vault, so it needs more than an open session.
The code is never a link you can click. That is deliberate: the single most common attack on this kind of flow is a stranger sending you a pre-filled approval URL. If a code arrives from anywhere other than your own terminal, it is not yours to approve.
Commands
| Command | What it does |
| ------------------------------------ | --------------------------------------------------------------------------------------------- |
| nottana login [--label <name>] | Sign in by approving in your browser. --label names this machine in your device list. |
| nottana login --store-key | Sign in and keep the vault key, so you can read items. Opt-in — see below. |
| nottana unlock | Get a vault key for the session you already have — master password, no browser. |
| nottana unlock --replace | Same, but start a fresh window when a key is already stored. |
| nottana lock | Remove that stored key, without signing out. forget-key is the older name for it. |
| nottana logout | Revoke this session, and destroy any stored key. |
| nottana status | Which account this terminal is on, where the token lives, and whether it holds a vault key. |
| nottana whoami | Just the account, one line. Exits 1 when nobody is signed in, so it works as a guard. |
| nottana list | List every item you can read. |
| nottana list --vault "<name>" | List one vault. Filters the output; does not restrict what the key can open. |
| nottana get <vault>/<item>/<field> | Print one field to stdout, bare, so $(…) is exact. |
| nottana get --id <id> <field> | Pick one item when two share a title in one vault. |
| nottana get <item> | List the fields that item has, including your own custom fields. |
| nottana run -- <command> | Run something with nottana:// references in its environment resolved. |
| nottana inject --in <template> | Fill a template's references in. --out <path> writes it 0600. |
| nottana --version | Which build is installed. |
| nottana --help | The above, in your terminal. |
Reading your vault
list and get need the vault key on this machine, and by default it is not
there. A plain nottana login stores only a session token, so a stolen laptop
gets API access to data it cannot decrypt.
nottana login # once per machine — approves in your browser
nottana unlock # then this, whenever you need to read
nottana unlock --30 # or a 30-minute window
nottana get "Dev/Stripe/password"
nottana lock # done reading — locks without signing out--store-key or unlock?
Both end with a vault key on this machine. They differ in what they cost you, and neither is simply better:
| | login --store-key | unlock |
| ------------------------------------- | ------------------- | ------------ |
| Browser round trip | yes | no |
| Master password typed into a terminal | no | yes |
| Needs an existing session | no — it creates one | yes |
| Cost when the 12h key expires | full re-approval | one command |
If a key is already stored, unlock tells you when it expires and does
nothing — it will not ask for your password when there is nothing to do, and it
will not silently swap a 12-hour key for a 30-minute one. unlock --replace
starts a fresh window.
The rule: login --store-key keeps your master password out of the
terminal, at the cost of a browser approval. unlock does the reverse. Pick
whichever cost you would rather pay.
Two cases decide it for you:
- You sign in with a passkey and do not have your master password to hand.
Use
--store-key. Your browser can seal the key because your passkey already unlocked it;unlockhas nothing to work with. - A shared, borrowed, or screen-shared machine. Use
--store-key. The password never enters that terminal at all, and the browser approval can happen on your phone.
Whichever you use, your master password never leaves your machine. unlock
asks Nottana for the same encrypted blob it already stores — which it cannot
read — and unwraps it locally. That is what a browser login does, in a
terminal. There is no way to do it any other way: Nottana has never held your
master key, so no server can hand one out.
A stored key opens your whole vault — there is no per-vault restriction yet,
and --vault filters what is printed rather than what the key can open. It
lives in the same OS keystore as the session, in a separate entry, which is why
forget-key can remove it without signing you out.
Secret references
nottana://<vault>/<item>/<field> names where a value lives, so the file
you commit holds the location and not the secret:
# .env.template — safe in git
STRIPE_KEY=nottana://Dev/stripe/api_key
nottana run --env-file .env.template -- npm start
nottana inject --in .env.template --out .env # writes 0600Use {{ nottana://… }} to embed a reference inside a longer string, such as a
connection URL; the bare form is read only when it is the whole value or runs to
the end of its line.
A reference that cannot be resolved stops the run — nothing is spawned and nothing is written. Passing the literal through would send a string that looks like a secret to a real API, and you would meet the failure later as somebody else's 401.
Secrets reach the child process through its environment, never its
arguments: a reference written into the command's own arguments is refused,
because argv is readable by every user on the machine via ps and your shell
history keeps a copy.
Where the session is stored
nottana login stores a refresh token — never your password — in the most
protected place available. nottana login --store-key adds a second, separate
entry holding an expiring vault key; without that flag no key material is
written to this machine at all.
| Platform | Storage |
| -------- | ---------------------------------------------------------------------- |
| macOS | Keychain |
| Linux | Secret Service (GNOME Keyring / KWallet) |
| Windows | A DPAPI-encrypted file, readable only by your Windows user (see below) |
| Fallback | A file with 0600 permissions, announced on stderr when used |
On Windows the token is encrypted with DPAPI. It is protected with
ProtectedData under the CurrentUser scope and written to a file in
%APPDATA%\nottana, so it is unreadable by other Windows accounts and is not
inherited by the processes you launch. nottana logout removes it.
Before 0.4.4 this was a plaintext user environment variable, readable by any program running as you. If you are upgrading, your session carries over and the old plaintext copy is deleted the next time the token is written.
It is a DPAPI-encrypted file rather than Windows Credential Manager, so it does not appear in the Credential Manager UI. Credential Manager protects its own blobs with DPAPI too, so this is comparable protection with less manageability — moving to it is tracked separately.
The token is never passed as a command-line argument, because arguments are
visible to any other process via ps.
Versioning
The CLI is on npm and everything documented here works. It is pre-1.0, so
commands, flags and output can change between minor versions, and a change that
invalidates a saved session gets a minor bump rather than a major one until
1.0. Pin the version if a script of yours depends on it, and run
nottana --version to see which one you have.
What's new
npm has no release-notes field of its own — the package page shows this README
and a list of version numbers, nothing more — so the notes live here. Run
nottana --version to see what you are on, and npm update -g @nottana/cli to
move.
0.8.4
Messages about your session now say WHICH server they are about, which stops a loop that could not be escaped by reading either message.
Sessions are stored per API origin, so a login against one server and a logout against another are separate things. With
NOTTANA_API_URLpointed at a local server,nottana loginwould find that session and say "Already connected — runnottana logoutfirst", while a barenottana logoutlooked at the default origin, found nothing, and said "Not signed in." Each command told you to run the other.The hint now carries the origin, so it can be pasted and work:
Run \NOTTANA_API_URL=http://localhost:3001 nottana logout` first. And a logout that finds no session names the origin it checked, on stderr — stdout is unchanged, sonottana logout || true` and anything parsing that line still behave exactly as before.
0.8.3
Nothing changed in the CLI itself, and there is nothing to do after updating.
The web app's hybrid encryption envelope is now authenticated (AES-GCM rather than AES-CBC with no MAC). The CLI opens sealed key material by delegating to that same code rather than reimplementing it, so it follows the change automatically — this release only updates the test that pinned the old envelope shape. Key material the CLI receives is a 32-byte key, which has always taken the direct RSA-OAEP path, so no sealed blob changes format.
0.8.2
A refused
unlockduration now tells you the one that works. Nothing else changed, and there is nothing to do after updating.nottana unlock --1440explained that 12 hours is the maximum and then left you to work out that it means--720. It now ends withFor the longest window allowed: nottana unlock --720.The ceiling itself is unchanged and is deliberate: a stored key opens your whole vault, so it cannot outlive a working day. Over-long requests are still refused rather than quietly shortened, because someone who typed
--1440believes they have it.
0.8.1
Documentation only — no behaviour changed, and nothing to do after updating.
The comment describing how the Windows session token is stored had stopped matching the code. It now says what that branch actually does: DPAPI (
ProtectedData::Protect, CurrentUser scope) writing to a file under%APPDATA%\nottana. A security note that describes an older implementation is worse than none, because it is what someone reads instead of the code.
0.8.0
nottana whoami— the account this terminal is on, one line, nothing else.statusanswers eight questions at once, which is right when you are reading it and wrong when a script is. This prints the account and exits 1 when there is nobody to name, so it is usable as a guard:nottana whoami >/dev/null || { echo "not signed in"; exit 1; }It is deliberately not an alias for
status: an alias would print the same paragraph and be worth nothing to the scripts that are the reason it exists. If the server does not report an account it says so and still exits 1, rather than printing a blank line a script could read as an identity.
0.7.0
nottana runnow says something when it resolves nothing.It reads no file on its own — it resolves references already present in the environment. So the natural mistake is a
.envfull ofnottana://references and no--env-file: nothing is filled in, nothing is said, and your command receives the literalnottana://…string as a value. The failure then turns up somewhere else entirely — an SDK rejecting an API key, a captcha that never loads — with nothing pointing back at how it was started.No references resolved. .env contains 3 nottana:// references, but nottana run does not read a file on its own. Did you mean: nottana run --env-file .env -- <command>It only appears when there is something concrete to point at: a readable env file in the working directory that genuinely contains references. If there is no file, no references, or you passed
--env-fileyourself, it stays quiet.Only the COUNT and the filename are printed, never a value — a reference URI names a vault and an item even though the secret itself is not in the file.
0.6.2
A signed-out terminal is no longer sent round in a circle.
run,get,list,injectandvaultsused to answer a missing vault key with "Runnottana unlock" whether or not you were signed in — andunlockreplies "You are not signed in. Runnottana login."unlockcannot work without a session, so that was two commands and no way out.Being signed out now says so, and points at the one command that fixes both at once:
error: not signed in on this machine, so there is no vault key. Sign in and store a key in one step: nottana login --store-keyunlockis still offered first when you ARE signed in, since that is the cheap fix for the 12-hour key expiring, and it is still the per-API message that wins whenNOTTANA_API_URLis in play — being pointed at the wrong API makes both "sign in" and "unlock" a way to make things worse.The "no OS keychain available" warning no longer tells Windows and macOS users to install a Linux package.
It ended with "install libsecret (secret-tool) for keychain storage" on every platform, so a Windows box with no
powershellonPATH— a stripped image such as Nano Server, or some CI containers — fell back to file storage and was handed advice that cannot work there. The remedy now names the command the CLI actually looked for:powershellon Windows,securityon macOS,secret-toolon Linux. A platform we do not probe for gets no remedy line at all, rather than a guess.Cosmetic in the sense that nothing behaves differently — but this is the one message whose entire job is to be trusted about where your session token went, which is why it is worth a release.
0.6.1
nottana injectnow handles a quoted reference:MONGO_URI="nottana://Local Dev 2026/api/MONGO_URI".It read to the raw end of the line, so the closing quote became part of the field name (
MONGO_URI") and the reference resolved to nothing — whilenottana runhandled the same file perfectly, because it strips quotes before parsing. Two commands disagreeing about one documented syntax. The quotes are now left in place around the substituted value, so a JSON or YAML template stays valid.A reference buried mid-string is still refused, unchanged: nothing can tell where it ends, and
{{ nottana://… }}is the form for that.
0.6.0
nottana statusandnottana loginnow print the account the terminal is signed in as, above the session id.Approval happens in your browser, so a terminal joins whichever account was signed in there. That is how the device flow is meant to work, and it means a terminal can end up on an account you did not intend — previously with nothing on screen to reveal it. The symptom was
nottana unlockrefusing a master password that was correct: the session belonged to another account, so the key it fetched was that account's and yours could not open it.nottana unlocknow names the account when it refuses a password, and points atnottana logoutif it is the wrong one.
0.5.1
- npm page only: clearer README, keywords, and the removal of a repository link that went nowhere. No change to the CLI itself.
0.5.0
Windows: the session token is now encrypted with DPAPI, in a file only your Windows user can decrypt.
Until this version it was stored as a plaintext per-user environment variable in
HKCU\Environment, readable by anything running as you and visible inGet-ChildItem Env:. Found by running the keystore verifier on real Windows hardware. macOS (Keychain) and Linux (Secret Service) were never affected.If you used 0.4.x on Windows, run
nottana logoutand sign in again — that clears the old environment variable. The CLI also migrates and deletes it automatically the next time it writes, but signing out is the certain path.
0.4.3
- Corrected a false claim in the docs: the storage table said Windows used Credential Manager. It did not. The storage itself was fixed in 0.5.0.
0.4.2
First public release. login, logout, status, unlock, lock, vaults,
list, get, run and inject, with the vault key opt-in and time-bound.
Disconnecting a machine
Sign a device out from Settings → Security → Connected devices in the web
app. That list holds every session, not only terminals, and can be filtered to
Command line or searched by the session id nottana status prints. Disconnecting takes effect immediately, and that machine has to be
approved again before it can read anything.
Environment
| Variable | Purpose |
| ----------------- | ------------------------------------------------------------- |
| NOTTANA_API_URL | Override the API base. Defaults to https://api.nottana.com. Must be https, unless the host is localhost or 127.0.0.1 — anything else is refused before the command runs. When it is set, the CLI says so on stderr. |
Common questions
When do I use unlock and when do I use login --store-key?
Both leave a vault key on this machine. login --store-key never puts your
master password in a terminal, but costs a browser approval every time the key
expires. unlock skips the browser but asks for the password. If you sign in
with a passkey and do not know your master password, --store-key is your only
option. See the table under Reading your vault.
Does unlock send my master password anywhere?
No. Nottana returns the encrypted blob it already stores and cannot read, and
your password is used locally to unwrap it. The prompt does not echo, and the
password is never accepted as a flag — --password is refused, because
anything in the arguments is visible to every other user on the machine via
ps and is kept in shell history.
Is this risky to hand to a junior engineer?
That is the realistic risk, more than any exotic attack — someone pipes a
secret into a file and forgets it. nottana run exists for exactly that: it
passes the secret straight into the process that needs it, so it never lands in
a file or in shell history. The vault key is also opt-in and expires, so the
default install holds nothing that can decrypt anything.
Scoping is still not shipped: today a connected device has the same access your account does, so treat approving one as you would handing over your signed-in laptop. Per-vault access needs per-vault keys, because item-to-vault membership exists only inside the ciphertext — the server cannot withhold items by vault even if we asked it to.
So it can read all my passwords? Yes — the same way your browser can when you are signed in. It is your vault, so anything you can see, it can see. That is why approving a device asks for your password or passkey, and why the screen spells out what you are granting before you say yes.
What if someone steals my laptop? Then they have a signed-in laptop, the same as if they took your phone. You can disconnect that machine instantly from any other device, and your vault still locks — it is not sitting there decrypted waiting for them.
Is this really safer than a .env file?
A .env file is plaintext forever. It gets copied into backups, pasted into
chat, and committed to git by accident — and once it has leaked you cannot
un-leak it. A CLI session is encrypted, tied to one machine, and revocable.
That last part is the real difference.
Why is there no link to click to approve? Deliberate — see above. A one-click approval link is what makes this kind of sign-in phishable, so we never send one.
Do I need to be a developer to use Nottana? No. If you never open a terminal, ignore this entirely; nothing about your account changes.
Can Nottana read my vault when I use the CLI? No, and that does not change between the web app, the phone app and the terminal. Even when you approve a new machine, your browser seals the key to that machine specifically — we relay a blob we cannot open, and delete it the moment it is collected.
Reporting a security issue
Email [email protected]. Please do not open a public issue.
Licence
MIT — see LICENSE.
