Secrets
Secrets keeps your API keys, MCP keys and other secret keys in a vault on your Mac. Agents use a key by its name and never get its value back from Next Term. A key is used only after you approve, on the Mac, with Touch ID or your password. It is practical protection, and this page says plainly what it protects and what it doesn’t.
What Secrets does
Section titled “What Secrets does”- Keys are sealed on disk. Each key’s value is encrypted, and the key that opens the values is guarded by your Mac’s Secure Enclave, so opening them takes Touch ID or your password. A Mac without a Secure Enclave keeps that key in the login keychain instead, and Settings calls it less protected. The names and what each key is for are not encrypted, so lists and masking work before you unlock.
- Agents use names, not values. Over MCP an agent can list your keys by name, ask you to type one into Next Term, hand over a key it already holds, ask you to import keys from a file or a password manager, or run a command with keys in that command’s environment once you approve. No tool returns a key’s value, and there is no tool that gets one.
- MCP servers get their keys when they start. A server’s config starts it through
nxtrm secret run, so the key leaves the config file. Import From › MCP Configs… does this for you, one ticked row at a time, with Undo. - Keys come in from where they are. Import From › A File… copies keys from
.envfiles, JSON, TOML, YAML and CSV files and password managers’ exports, and 1Password… and Bitwarden… take them from your own 1Password and Bitwarden command-line tools, run in a tab you can see. You tick each key first, and the file stays as it was. - A key remembers where it came from. An optional link to the provider’s page where you made it, so Rotate… opens that page and the paste field together, keeps the old value masked for 30 days, and can remind you when a key gets old.
- Stored keys are masked as
•••, best effort, in what agents read through Next Term and in what Next Term saves (what is masked). - You choose how much a key shows. A key set to Never show this key is never shown or copied again, and a key set to Ask for Touch ID every time it’s used, as new keys are, is never covered by a lasting approval.
The Secrets menu
Section titled “The Secrets menu”The Secrets menu sits between Agents and Git:
| Item | What it does |
|---|---|
| Show Secrets | Opens the Secrets window: the same list as Settings › Secrets, in a window of its own. |
| Add Key… | Opens Add…’s sheet (Add a key). |
| Import From | A File… and MCP Configs…, then 1Password…, Bitwarden… and Bitwarden Secrets Manager…, as the list’s Import From button has them (Bring keys in). |
| Keys Due to Rotate | Each key past its “Remind me to rotate” age, up to ten, then Show All 14 in Secrets. Choosing a key opens Rotate… for it. With none due it shows No Keys Due. A badge counts the keys due. |
| Show Key Request | Brings the request on screen forward, without taking the keyboard: the panel for a use, the window an agent’s request_secret opens, or the Secrets window with an agent’s import. You answer it there; the menu never does. A badge counts the requests waiting. |
| Pause Key Requests for 1 Hour… | Asks first, then declines every waiting request, refuses new ones for an hour, and ends every approval you gave. While the pause runs, the item reads Resume Key Requests, which resumes at once, and the line under the list says “Key requests resumed.” |
| Revoke All Approvals… | On while an approval is in force. Asks first, saying how many it ends, such as “This ends 3 approvals you gave. The next use of each key asks again.” |
Add Key…, the imports, Pause Key Requests for 1 Hour… and Revoke All Approvals… act through the list: Settings › Secrets when that tab is in front, else the Secrets window, brought forward. Add Key… and the imports open their sheets there. Pause Key Requests for 1 Hour… and Revoke All Approvals… confirm in a sheet on it, where Return is Cancel. When a sheet is open on the list already, nothing more opens, and the line under the list says “Finish or cancel what’s open first.” The one exception: when the only sheet open is an agent’s import and it isn’t copying keys in, Pause Key Requests for 1 Hour… confirms over that sheet, and the pause ends that request; Cancel leaves the sheet as it was. While the vault can’t be opened on this Mac, or was changed outside Next Term, Add Key… and the imports are off, and Keys Due to Rotate shows “Secrets Can’t Change Keys Now”.
No item has a default shortcut. Settings › Keyboard Shortcuts lists them under Secrets, so you can give any of them one; a shortcut you gave Window › Secrets in an earlier version stays on Show Secrets. No item shows or copies a value, and the menu’s titles, tooltips and badges hold key names and counts only. Reveal, Copy, Never Show This Key… and the commands for one key stay in the list, next to the key they act on.
Add a key
Section titled “Add a key”Open the Secrets window (Secrets › Show Secrets) or Settings › Secrets: the two show the same list. Click Add…, or choose Secrets › Add Key…, and fill in:
- Name: capital letters, digits and
_, starting with a letter, at most 64 characters, such asGITHUB_TOKEN. Letters are made capitals and spaces and hyphens become_as you type, and the line under the field shows the name it will be saved as. - What it’s for: one line, such as “GitHub MCP server”. Agents see it when they list your keys, and like the name it is stored unencrypted, so write what helps an agent pick the right key, and nothing private.
- Where it came from: the page where you made the key, optional. Next Term suggests one as you type (Where a key came from).
- Remind me to rotate: Never, or 30, 90, 180 or 365 days (Reminders).
- Ask for Touch ID every time it’s used: ticked unless you changed the default (what it does).
- Value: the key itself, on one line, up to 16 KB. Spaces at either end are taken off. Keys with line breaks, such as PEM files, can’t be stored yet.
- Never show this key: unticked unless you changed the default (Never show a key).
The reminder and the two boxes start as Settings › Secrets sets them for new keys (Defaults for new keys).
Save asks for Touch ID or your password, in a sheet that reads: “Next Term” is trying to save GITHUB_TOKEN. If you pasted the value, Next Term clears the clipboard once it is saved, unless you copied something else since. A key shorter than 12 characters gets a warning, because Next Term can’t mask it by its value (why).
Every change asks for Touch ID or your password once: adding, editing, replacing, rotating, setting a key never to show, deleting, revealing and copying a key, an import and its Undo. There is no separate PIN; your Mac password is the fallback macOS offers.
The list shows, for each key:
- its name and what it’s for. Text an agent wrote for what a key is for is marked “(an agent wrote this)” until you edit it;
- Page: the host of the page it came from, such as
platform.openai.com; - a lock for a key that is never shown, with the tooltip “Never shown”;
- Rotated: when its value was last set, “Today”, “12 days ago”, or the date past a year; a key that never got a new value reads “Added 12 days ago”. Once its reminder has passed, Due comes first;
- Last used: when and by whom, such as 5 min ago by Claude Code in “api”, or Never used.
The buttons under it are Add…, Edit…, Replace…, Rotate… and Delete…, then Reveal, Copy, Never Show This Key… and Import From. Edit… changes a key’s name, what it’s for, where it came from, its reminder and whether it asks every time; Replace… gives it a new value. Double-click a row to edit it, or its Page cell to open the page. Right-click a row for its commands: Open platform.openai.com (named by the key’s own page, when it has one), Edit…, Replace…, Rotate…, Reveal, Copy, Never Show This Key… and Delete…. Secrets holds up to 500 keys.
Never show a key
Section titled “Never show a key”A key set to Never show this key is never shown or copied again, even to you. A command you allow still gets it. The box is in Add…, in Replace…, in the window an agent’s request_secret opens, and on each row of an import. Under it: “Next Term never shows or copies this key again. Commands you allow still get it. Keep your own copy if you’ll need it on another Mac. This can’t be undone.”
- In the list, the key has a lock, whose tooltip says “Never shown”, and when you select it the line under the list reads “Never shown: Next Term never shows or copies this key. Replace it to change it.” Reveal, Copy and Never Show This Key… are off for it. The panel that approves a use marks it “Never shown” too.
- It can’t be undone. Replace… gives the key a new value and keeps it never shown: the box stays ticked, dimmed, with the tooltip “This key is never shown, and stays so.” So does an agent’s
request_secretorstore_secretthat replaces it. Edit… still changes its name, what it’s for, its page, its reminder and whether it asks every time, and Delete… removes it. - For a key you have already, select it and click Never Show This Key…, or choose it in the key’s right-click menu. It asks first, “Never show OPENAI_API_KEY again?”: “You can’t show or copy it after this. Commands you allow still get it. To change it, replace it with a new value. This can’t be undone.” Never Show then asks for Touch ID or your password.
- The choice is sealed with the value, so editing the vault’s file can’t clear it.
- Undo leaves it. An Undo of an MCP-config move never writes a never-shown key back into a file: the entry that held it stays as it is, and the line under the list says so.
Keep your own copy of such a key if you will need it again: keys don’t move to a new Mac (On a new Mac).
Ask for Touch ID every time it’s used
Section titled “Ask for Touch ID every time it’s used”Each key has the box Ask for Touch ID every time it’s used, in Add… and Edit…. It is ticked unless you untick it, or change the default for new keys.
- While it is ticked, a request that uses the key offers only Allow once in the approval panel, with a line such as “GITHUB_TOKEN asks for Touch ID every time it’s used, so only Allow once is offered.” No approval for 15 minutes or until the Mac locks covers it, so each use asks again. A request that names several keys offers only Allow once when any of them is set so.
- Untick it in the key’s Edit… sheet, and Save, to be offered the lasting approvals for it again. Under the box: “When it’s off, you can allow a command to use it for 15 minutes, or until the Mac locks.”
- Ticking it ends, at once, every lasting approval in force that holds the key.
- A key saved before the box existed reads as ticked, the safer choice. Untick it key by key where you want lasting approvals.
Defaults for new keys
Section titled “Defaults for new keys”Under the key buttons, Settings › Secrets and the Secrets window show the New keys group, with two boxes. Changing them asks for no Touch ID, and a change in one window shows in the other.
- Never show their values, off at first, ticks Never show this key in Add…, in the window an agent’s
request_secretopens, and on each row of an import, from a file or a password manager or from your MCP configs. You can still untick it there, and commands you allow still get the keys. A key an agent hands over withstore_secretis saved as one you can show. - Ask for Touch ID every time they’re used, on at first, ticks the box in Add…, and every other new key is saved so: one you type for an agent’s
request_secret, one an agent hands over withstore_secret, and each key an import brings in. Untick it for one key in its Edit… sheet.
Reminders, under it, has the third default, New keys: Remind me to rotate (Reminders).
Reveal and copy
Section titled “Reveal and copy”Reveal shows a key in a small popover after Touch ID. It stays open until you close it, lock the Mac or hide Next Term, or close or minimize its window, and it is kept out of screen sharing and screenshots.
Copy puts it on the clipboard after Touch ID, with the line “Copied. Next Term clears the clipboard in 60 seconds.” Next Term marks it as concealed and transient, so clipboard managers that follow those marks skip it, and asks macOS to keep it off your other devices. After 60 seconds Next Term clears it, unless you copied something else since.
Neither is offered for a key never shown.
Where a key came from
Section titled “Where a key came from”Each key can keep a link to the page where you made it, which is where you revoke it too. Set it under Where it came from in Add… and Edit…, and in the window an agent’s request_secret opens, where only you fill it in. Its placeholder reads “https://… the page where you made this key”.
- Suggested on your Mac. As you paste a value or type a name, a line under the field suggests a page, such as “Suggested: OpenAI · API Keys”, and Use takes it. The suggestion comes from the value’s first characters, then the key’s name, matched against a table of 34 provider pages built into Next Term, from OpenAI, Anthropic and GitHub to AWS, Stripe, Pinecone and LangSmith. Only the page is kept, never a character of the value. A start that several providers’ keys share, such as a bare
sk-, suggests nothing by itself; the name still may. - Opened by you. The list shows the page’s host under Page. Open platform.openai.com in the key’s right-click menu, or a double-click on its Page cell, opens it in your browser, and the item names the host before anything opens. If Secrets’ file changed since Next Term last read it, opening a page asks for Touch ID or your password first.
- Checked before it opens. A link is
https, orhttpto this Mac for a service you run yourself, with no user name or password, no host label in another script (xn--), at most 2,048 characters, and no token, key or password after its?or#. A link that holds one of your stored keys is refused. It is checked as you type it, when the list is read, and again just before it opens; a stored link that fails reads “This link can’t be opened.”, with the reason as its tooltip. - Only you set it. No MCP tool takes a link or returns one.
list_secretsgives agents only the provider’s name, asservice, such as"OpenAI", never the link, since a full address can carry your organization’s or project’s id. - Next Term never fetches it. No page is loaded, previewed or checked over the network. Next Term doesn’t check that a link is the right page, or that a key works: it suggests a page from the key’s value or name, and you decide.
An import fills the link from the source when it has one: a web address in the comment line just above a .env key, a Bitwarden or 1Password item’s website, or a url, website or login_uri column in a CSV. Otherwise it suggests one, as above.
Rotate a key
Section titled “Rotate a key”Rotate… puts a key’s rotation in one sheet. It is a button under the list and an item in the key’s right-click menu, a due key in Secrets › Keys Due to Rotate opens it, and so does a click on a reminder. Its three steps are all shown:
- Open platform.openai.com opens the key’s page in your browser: “Make a new key there, then copy it.” For a key whose page is GitHub’s fine-grained tokens page, it opens GitHub’s new-token form instead: “GitHub’s form opens filled in: the key’s name, today’s date and a 90-day expiry.” A key with no page shows Where it came from here, with its suggestion, and the page is saved with the new value.
- Paste the new key. A tip sits beside the field for a provider whose rotation has a twist: for AWS, “Create a second access key, then deactivate the old one.”; for Azure, “Regenerate the key you are not using.”; for Stripe, “Choose when the old key expires.” The key you have now is refused as you paste it, “That’s the key you have now.”, found by its masking fingerprints, since the sheet never holds the old value. A key that looks like another provider’s than its page’s gets a warning, such as “This key looks like one from OpenRouter, but its page is OpenAI’s.”, and you can still save it. Remind me to rotate is here too. Save asks for Touch ID or your password once.
- “Now delete or deactivate the old key on that page.”, with Open Page Again, Remind Me Tomorrow and Done. Remind Me Tomorrow posts one notification the next day to delete the old key; Done closes the sheet.
Once it is saved:
- The old value stays masked for 30 days: “Next Term keeps masking the old key for 30 days, while it may still work.”
- Approvals that held the key end, as after Replace…. “MCP servers that started before now keep the old key until they restart: /mcp in Claude Code, or restart the agent.”
- Rotated reads “Today”, and the key’s reminder counts from today.
Next Term makes no network call to rotate a key. You make the new key on the provider’s page, and deleting or deactivating the old one there is up to you.
Reminders
Section titled “Reminders”Each key has Remind me to rotate: Never, 30, 90, 180 or 365 days, set in Add…, Edit… and Rotate…. It counts from when the key’s value was last set, by Add…, Replace…, Rotate… or an import; a rename or a new “What it’s for” doesn’t move it. A key saved before Next Term recorded that date counts from when it was added, so its reminder can come early, never late.
- Due. Once a key is past its age, Due leads its Rotated cell, with a tooltip such as “94 days ago. Due: you asked to be reminded to rotate it after 90 days.”, and the key is listed in Secrets › Keys Due to Rotate.
- A notification, “Time to rotate a key”: “OPENAI_API_KEY was last rotated 92 days ago”, or “… was added 92 days ago and hasn’t been rotated since”. Clicking it opens Rotate… for that key in the Secrets window. Next Term checks once the vault has loaded at launch, then once a day, and sends at most one notification about a key in 7 days. More than three keys due at once share one notification. A notification holds the key’s name and age, never a value or a link, and a reminder never asks for Touch ID.
- Settings › Secrets › Reminders has Remind me in Notification Center, on at first, which sends those notifications; with it off, a due key’s row still shows Due. Beside it, New keys: Remind me to rotate, Never at first, sets the age a new key starts with, from Add…, an agent’s
request_secretorstore_secret, or an import. - Remind Me Tomorrow in Rotate… sends its one notification the next day even with Remind me in Notification Center off, since you asked for it then.
Approving a use
Section titled “Approving a use”When an agent or an MCP server asks to use a key, Next Term shows its own panel first, unless an approval you gave earlier covers that exact request. The panel never takes the keyboard and has no Return button, so typing meant for a terminal never answers it. It shows above your other windows on the Space you are on, without bringing Next Term forward, and when Next Term is in the background its Dock icon bounces.
Its title says who asks, for what and with which keys, such as “Claude Code asks to run npm with OPENAI_API_KEY” or “Codex asks to start npx with GITHUB_TOKEN”. Below it:
- The keys: each name, what it’s for, “Never shown” for a key never shown, and when it was last used.
- The command, in full. Line breaks, tabs and invisible characters are written out as escapes (
\n,\u{202E}), never left out, so what you read is what runs. A command fromrun_with_secretssays “Runs as a shell command”. - The folder it runs in, and
NAME → ENVNAMEwhere a key is set as another variable. - Warnings, each with a warning sign and words, before you allow:
- a variable that can change what the command runs, such as “NODE_OPTIONS is set: it can change what this command runs.” (also
BASH_ENV,PYTHONSTARTUP,PYTHONPATH,LD_PRELOAD,DYLD_*and others); - a proxy or certificate setting: “HTTPS_PROXY is set. The key may pass through that proxy.”;
- a
.envin the folder that points somewhere: “.env in this folder sets OPENAI_BASE_URL. The key may go to that address.”; - a package with no pinned version, for
npx -y,bunx,uvx,pnpm dlx,yarn dlxandpipx run, since code you haven’t seen would get the key; - a command that starts a shell or an agent, since anything it runs gets the key.
- a variable that can change what the command runs, such as “NODE_OPTIONS is set: it can change what this command runs.” (also
- The agent says: its reason, quoted and marked as its own words, not Next Term’s.
- Asked by: the agent, “going by its program’s name” (Next Term never claims to have checked an agent’s name), its tab and project, or “A program outside Next Term’s tabs”. A request from
nxtrm secret runadds “Asked through Next Term’s own nxtrm (checked)”. Details shows the process, its program’s path and every argument.
Then choose how long. While a key the request names is set to Ask for Touch ID every time it’s used, as new keys are, only Allow once is offered, with a line that says so, such as “GITHUB_TOKEN asks for Touch ID every time it’s used, so only Allow once is offered.” Otherwise:
- Allow once, selected at first.
- Allow for 15 minutes: only this agent, these keys, this exact command and folder. Anything different asks again, and so does a change to the folder’s
.envor to a warned variable. - Allow this command until the Mac locks, offered for
nxtrm secret runin an MCP server’s config or a script: for that exact command, started by that agent in that folder. It ends when you lock the Mac, it goes to sleep, you switch to another user, or Next Term quits.
Allow then asks for Touch ID or your password, in a sheet that names the keys, the program and the folder, such as “Next Term” is trying to use OPENAI_API_KEY for npm in api. Cancel it and nothing is used.
The other buttons:
- Decline: that same request is declined at once, with no panel, for the next 10 minutes. A different request from the same agent still asks.
- Decline and Stop Asking: that agent’s key requests are declined until Next Term quits.
- Pause Key Requests for 1 Hour…: after a confirm, declines this and every waiting request, refuses new ones for an hour and ends every approval you gave. MCP servers that need keys won’t start meanwhile. Settings › Secrets shows “Pausing key requests until 5:05 PM.” with Resume Asking. Secrets › Pause Key Requests for 1 Hour… pauses from the menu bar, with no request on screen, and while the pause runs the same item reads Resume Key Requests.
One panel asks at a time, and up to five requests wait behind it. After 10 declined or unanswered requests from one agent in a session, that agent is refused, and after 30 across all agents, every request is, until you resume them in Settings › Secrets or restart Next Term.
Take back an approval
Section titled “Take back an approval”Settings › Secrets and the Secrets window list the approvals in effect under Allowed now: who, which keys, the command, and the time left, such as “12 min” or “until the Mac locks”. Revoke ends one, Revoke All ends them all. Secrets › Revoke All Approvals… ends them all from the menu bar, after a confirm that says how many it ends, where Return is Cancel.
An approval also ends when its 15 minutes are up, when you lock the Mac, it goes to sleep or you switch to another user, when Next Term quits, when one of its keys is replaced, rotated, renamed or deleted, when you tick Ask for Touch ID every time it’s used for one of its keys, on Pause Key Requests for 1 Hour…, and on Start Over…. An approval an agent got through run_with_secrets also ends when another agent, or a remote connection, types into that agent’s tab through Next Term. Locking the Mac ends all of them.
Revoking stops new starts. A server already running keeps the key until it quits: restart it in your agent (in Claude Code, /mcp), or restart the agent, to stop it using the key.
Agents and Secrets
Section titled “Agents and Secrets”Agents look here first. When a program or an MCP server needs an API key, a token or a password, Next Term’s own instructions tell every agent to call list_secrets before reading your shell profile or a .env file, or asking you. The agent then uses the key by name. A key you don’t have yet is asked for in Next Term with request_secret, never in the chat. Next Term’s built-in skill next-term-secrets (get_skill) gives agents the recipes for an MCP server’s config.
Five MCP tools let an agent use your keys by name. None returns a key’s value.
| Tool | What it does | Changes anything? |
|---|---|---|
list_secrets | Your keys by name: what each is for (for) and who wrote that (purpose_by), whether you or an agent added it (added_by), whether it is set to never show (write_only), when it was last used, the provider whose page it came from (service, never the link), the day its value was last set (rotated) and whether its reminder has passed (rotate_due). vault is ok, none (nothing stored yet), cant_open or tampered, and protected_by says what guards the vault. | No |
request_secret | Asks you to add a key, by name, with the agent’s reason. | Only if you save it |
store_secret | Saves a key the agent already holds, such as one it just created. | Only if you allow it |
run_with_secrets | Runs a command in a Next Term tab with keys in that command’s environment only. | Only if you allow it |
import_secrets | Opens the import on your Mac for a file of keys or a password manager, with the agent’s reason. It gets back the names imported and counts. | Only if you import |
list_secrets answers like this, with no values:
{"vault": "ok", "protected_by": "secure-enclave", "secrets": [{"name": "GITHUB_TOKEN", "for": "GitHub MCP server", "purpose_by": "user", "added_by": "user", "write_only": false, "last_used": "2026-10-09T14:02:11Z", "service": "GitHub", "rotated": "2026-08-03", "rotate_due": false}], "note": "Values are never returned. Use run_with_secrets, or nxtrm secret run in an MCP server's config."}request_secret, such as request_secret("STRIPE_KEY", "to run the billing tests") from Codex, opens a window on your Mac titled “Codex asks you to add STRIPE_KEY”, with the agent’s reason as its own words. The name and what it’s for are filled in from the request, and you can change both. It doesn’t take the keyboard until you click it, and has no Return button. You paste the key, click Save and pass Touch ID, and the agent gets only {"status": "saved", "name": "STRIPE_KEY", "replaced": false}.
store_secret, such as store_secret("DEPLOY_TOKEN", <value>, "made for the deploy script"), shows a panel that says the key is already in the agent’s conversation, that Next Term keeps it out of what agents read from then on, and that you may want to rotate it. When it would replace a key you added, the title says so, “Codex asks to replace DEPLOY_TOKEN, a key you added”, and a warning names the day you added it. Save and Touch ID store it.
run_with_secrets, such as keys ["OPENAI_API_KEY"], npm test and the project’s folder, opens a new tab in that folder without taking the keyboard, or uses the tab given as tab_id when its shell is at a prompt (the panel then warns about that tab’s variables). Next Term types one line there, nxtrm secret run --ticket …, and the panel shows. Once you allow it, npm test runs under /bin/sh -c with the key in its environment, and the agent gets started with the tab’s id and reads the screen with read_tab, stored keys masked. The tab’s shell never gets the key, only the command does, and the shell’s history holds only the ticket line. Give a key as NAME=ENVNAME to set it as another variable. A command that holds a stored key’s value is refused before any tab opens.
import_secrets, such as path .env.local with the reason “to move the API keys into Secrets”, takes a file’s path (absolute, from ~, or relative to the agent’s tab’s folder) or a source, 1password, bitwarden or bws, never both. It opens the import on your Mac: for a file, the preview of its keys; for a source, the sheet that types that password manager’s line into a new tab (From 1Password or Bitwarden). A banner names who asks, from where and why, such as “Claude Code in rag-api asks to import keys from ~/Code/rag-api/.env.local: to move the API keys into Secrets”. The Secrets window comes forward without taking the keyboard or making Next Term the active app (with another app in front, Next Term’s Dock icon bounces), and the sheet waits for your click. Its action, Import… or Open in a New Tab, has no Return default, so a Return meant for a terminal never presses it; ⎋ still cancels. You tick the keys and approve with Touch ID, or cancel. A key you have already is replaced only if you tick its row, which never starts ticked. The agent gets back names and counts:
{"status": "imported", "imported": ["OPENAI_API_KEY", "PINECONE_API_KEY"], "replaced": [], "found": 3, "not_imported": 1, "format": "env"}It never gets a value or any part of one, a name you didn’t import, or where in the file a key was, and its reason goes into the log, never into a key’s “What it’s for”. It may name a .env file or an export inside or outside the open projects, which read_file refuses, since nothing of the file goes back to the agent. A path inside Secrets’ own folder, ~/Library/Keychains, ~/.ssh or ~/.gnupg is refused with the same note as a missing file, “not found or protected”, so the answer never says whether such a file exists. A file over 5 MB (50 MB for a 1Password .1pux export) is refused as too large. An agent’s MCP config that Import From › MCP Configs… moves is refused, with a note that you start that import yourself.
The four tools that ask answer within about 50 seconds with one of these:
| Status | When |
|---|---|
started | run_with_secrets started the command, with tab_id, and approved: once, for 15 minutes or by an earlier approval. |
saved | The key is saved, with name and replaced. |
imported | import_secrets: you copied keys in. imported lists the names stored and replaced the ones among them you had already; found and not_imported count the keys found, website passwords not listed included, and those left out, and format says how the file was read, such as env, json or bitwarden. |
nothing_found | import_secrets: the file holds no key. |
unreadable | import_secrets: Next Term couldn’t read it, with a reason such as “an encrypted Bitwarden export” or “too many keys to list”, and nothing of the file. |
declined | You declined, chose Decline and Stop Asking, closed the panel or cancelled an import; or that request is paused, that agent stopped, or key requests paused for an hour. The note says which, such as “paused because you declined this at 4:02 PM”. |
pending | No answer yet: call again with the request_id, which works while an update waits for busy tabs too. A request_id only ever reports a status, never a value. |
busy | Five requests are waiting already, or, for import_secrets, Secrets shows another sheet on the Mac, such as Edit… or another import. |
limited | The agent asked too often this session. You can resume it in Settings › Secrets. |
unknown | run_with_secrets named a key that isn’t stored. request_secret asks you to add it. |
value_in_command | The command holds a stored key’s value. Use its name. |
cant_open | The vault can’t be opened on this Mac, or was changed outside Next Term (Where your keys are). |
refused | Bad arguments, a tab that can’t be used, a tab whose shell didn’t start nxtrm within 30 seconds, or a path Next Term won’t import from. |
The instructions Next Term gives every agent say that no tool returns a key’s value.
Keys for MCP servers
Section titled “Keys for MCP servers”An MCP server that needs a key can get it from Secrets when it starts. Its config runs the server through nxtrm secret run:
nxtrm secret run NAME[=ENVNAME]... -- command [arguments...]nxtrm asks Next Term for the keys, Next Term shows its panel, and after you allow it, nxtrm sets the keys in its environment and becomes the server. Every other stored key’s name is taken out of that environment. It writes nothing to standard output, which is the server’s channel to the agent, and only one line to standard error when it fails. nxtrm secret help prints the usage.
Use the path of the nxtrm link Next Term made, such as /opt/homebrew/bin/nxtrm or ~/.local/bin/nxtrm written out in full (Installing it), since agents and desktop apps start servers with a short PATH. The import writes it for you.
Claude Code, in ~/.claude.json or a project’s .mcp.json:
{ "mcpServers": { "github": { "command": "/opt/homebrew/bin/nxtrm", "args": ["secret", "run", "GITHUB_TOKEN=GITHUB_PERSONAL_ACCESS_TOKEN", "--", "npx", "-y", "@modelcontextprotocol/server-github"] } }}Codex, in ~/.codex/config.toml:
[mcp_servers.github]command = "/opt/homebrew/bin/nxtrm"args = ["secret", "run", "GITHUB_TOKEN=GITHUB_PERSONAL_ACCESS_TOKEN", "--", "npx", "-y", "@modelcontextprotocol/server-github"]startup_timeout_sec = 30OpenCode, in ~/.config/opencode/opencode.json, where the wrapper goes first in command:
{ "mcp": { "github": { "type": "local", "command": ["/opt/homebrew/bin/nxtrm", "secret", "run", "GITHUB_TOKEN=GITHUB_PERSONAL_ACCESS_TOKEN", "--", "npx", "-y", "@modelcontextprotocol/server-github"] } }}GITHUB_TOKEN=GITHUB_PERSONAL_ACCESS_TOKEN gives your stored GITHUB_TOKEN to the server as GITHUB_PERSONAL_ACCESS_TOKEN, the variable it reads. Pin the package’s version (@modelcontextprotocol/server-github@<version>) and the panel stops warning that code you haven’t seen gets the key.
- Codex’s
startup_timeout_sec. Codex gives a server 10 seconds to start by default, less than the 25 secondsnxtrmwaits for you. Set it to 30, as above, so you have time to approve, and so what Codex shows when you don’t isnxtrm’s line. - When you are away. With no answer in 25 seconds,
nxtrmstops with “nxtrm: GITHUB_TOKEN is waiting for your approval in Next Term. Allow it there, then restart this command.”, and the server doesn’t start. The request waits in Next Term as a card that says “This MCP server stopped waiting.”, with Allow for 15 minutes chosen; for a key that asks for Touch ID every time, Allow once, whose one use then waits 2 minutes for the restart. Allow it, then restart the server: in Claude Code,/mcp, then Reconnect. A card goes after 30 minutes, or when the Mac locks. - Next Term must be open. A server whose config runs
nxtrm secret runstarts only while Next Term is running with “Let agents control Next Term” on.nxtrmnever opens Next Term by itself. - Starting it again. For a server you restart often, untick Ask for Touch ID every time it’s used in its key’s Edit… sheet, then choose Allow this command until the Mac locks: until you lock the Mac, it starts with no panel.
If an MCP server won’t start
Section titled “If an MCP server won’t start”The server’s error, in /mcp in Claude Code or in Codex’s startup message, is nxtrm’s one line. The same lines show in a tab that run_with_secrets opened, and the last two show only there:
| The line begins | What to do |
|---|---|
nxtrm: Next Term is not running, or “Let agents control Next Term” is off | Open Next Term, turn on “Let agents control Next Term” in Settings › Editor › Agents, then restart the server. |
nxtrm: GITHUB_TOKEN is waiting for your approval | Allow it in Next Term’s panel, then restart the server. |
nxtrm: Next Term quit before it answered | Open Next Term and restart the server. |
nxtrm: Next Term declined | You declined. Restart the server to be asked again. |
nxtrm: you declined this a few minutes ago | Wait until the time it names, or restart the server after it. |
nxtrm: you chose Decline and Stop Asking | Resume it under Paused in Settings › Secrets. |
nxtrm: Next Term is pausing key requests | Wait, or click Resume Asking in Settings › Secrets. |
nxtrm: too many key requests are waiting | Answer the panels in Next Term, then restart the server. |
nxtrm: this command has asked too often | Resume it in Settings › Secrets, or restart Next Term. |
nxtrm: no key named GITHUB_TOKEN | Add the key in Settings › Secrets, or fix the name in the config. |
nxtrm: the command holds a stored key’s value | The config still has the key itself in its arguments. Put the key’s name in nxtrm secret run instead. |
nxtrm: Next Term can’t open Secrets on this Mac | See Settings › Secrets (Where your keys are). |
nxtrm: the program at Next Term’s socket is not this copy of Next Term | Another copy of Next Term is running, or something else is at its socket. Quit it and open the copy nxtrm links to. |
nxtrm: Next Term refused this request. | The request failed one of Next Term’s checks. Run the config’s command through the nxtrm link, unchanged. |
nxtrm: npx: command not found | The command after -- isn’t on the PATH the agent gives its servers: write its full path. |
nxtrm: npx: can't run it | The command after -- was found but isn’t a program you can run, such as a folder or a file without execute permission. Fix the path, or chmod +x your own script. |
usage: nxtrm secret run … | The arguments are wrong: names first, then --, then the command. |
nxtrm: this run’s approval expired or was used already | In a run_with_secrets tab: the line ran a second time, or more than 30 seconds after Next Term typed it. Ask the agent to call run_with_secrets again. |
nxtrm: no answer in Next Term within 10 minutes | In a run_with_secrets tab: nobody answered the panel. Ask the agent to call run_with_secrets again. |
In Claude Code, /mcp shows each server’s state and its error, and Reconnect starts it again. Claude Code waits up to 30 seconds for a server to start.
Turning off agent control
Section titled “Turning off agent control”Turning off “Let agents control Next Term”, in Settings › Editor › Agents, closes Next Term’s MCP server, and with it Secrets for agents and MCP servers: the five tools are gone, and servers that start through nxtrm secret run get no keys and don’t start. Settings › Secrets says so while it is off and a key is stored. Your keys stay in the vault, and you can still manage them. Import From › A File… still works; MCP Configs…, 1Password…, Bitwarden… and Bitwarden Secrets Manager… say they need it on, since keys reach Next Term through it, and so does nxtrm secret import.
Bring keys in
Section titled “Bring keys in”Import From, a button under the list and an item in the Secrets menu, brings keys in from where they are now:
- A File…: keys from a file you choose (From a file).
- MCP Configs…: keys in your agents’ MCP settings, moved out of them (Move keys out of your MCP configs).
- 1Password…, Bitwarden… and Bitwarden Secrets Manager…: keys from your own command-line tool for each, run in a tab you can see (From 1Password or Bitwarden).
Each import shows what it found first, copies only the keys you tick, with one Touch ID, and can be undone for 30 days. A key keeps no tie to where it came from: change it there later, and Secrets keeps the copy it took.
From a file
Section titled “From a file”Import From › A File… opens a file picker, and Next Term reads only the file you choose. It tells the format by the content, not only the name:
.envfiles:.env,.env.local,.env.*,*.env, and.envrc, whoseexport NAME=valuelines are read and which is never run.NAME: valuelines count too, as Node’s dotenv and Compose read them.- JSON, with comments or not: a flat object of names and values, nested objects, whose path names the key (
{"openai": {"apiKey": …}}givesOPENAI_API_KEY), and lists ofnameandvaluepairs (orkeyandvalue), as Azure App Service, ECS, Kubernetesenvlists andbws secret listwrite them. - TOML: Codex’s
config.toml, Streamlit’ssecrets.toml, Cargo’scredentials.toml, and any other by its paths. - YAML: a Compose file’s
environment, a KubernetesSecret(itsstringData, and itsdata, decoded from base64) orConfigMap, goose’s and Continue’s configs, and any other by its paths. - Agents’ configs that MCP Configs… doesn’t rewrite: Zed’s
context_servers, Claude Code’s settings’env, OpenCode’s providers, goose, Continue, and a file ofmcpServerssuch as Devin Desktop’smcp_config.json. Each key is copied with a note that the file still holds it. An MCP config that MCP Configs… rewrites opens that import for the file instead, since it changes the file, and then offers the keys outside its servers here. - Password managers’ exports: Bitwarden’s unencrypted
.jsonexport, 1Password’s.1puxexport andop item get --format jsonoutput, and the CSV exports of Bitwarden, 1Password, KeePassXC and LastPass. - Other CSV: saved passwords from Chrome, Firefox and Apple Passwords, and a plain file with a
name(orkey) and avaluecolumn.
A file can be up to 5 MB: 512 KB for a .env file, 2 MB for TOML or YAML, and 50 MB for a .1pux export.
The preview, Import from a File, names the file and how it was read, then lists one row for each key found: a tick, Name (agents see it), Never show, Kind, Last 4, Found at, Page and Note.
- Ticked only when the row looks like a key: the source marks it secret (a Bitwarden hidden field or password, a 1Password concealed field, a Kubernetes
Secret, abwssecret), or the value has a key’s shape, a secret word in its name, or a random look. - Listed, unticked, with a note: a name that sends its value to browsers (
NEXT_PUBLIC_,VITE_and the like), with “This name ships the value to browsers.” when the value looks like a key; a publishable key, “Safe to expose; usually not worth storing”; a short-lived token, “Expires within hours”; a website’s sign-in password with no key’s shape; and a name you have already, “Replaces your key”, which you tick yourself if you mean it. - Not listed: references such as
${OPENAI_API_KEY}orop://…, placeholders, numbers, booleans and other settings. The footer counts them, such as “14 other values aren’t keys.”, and Show Them lists the first 20 by name, place and reason, such as “CHUNK_SIZE, line 6: A setting”, then how many more, such as “And 12 more.”, never a value. - Never ticked, with the reason: a value with line breaks, over 16 KB or with a NUL; a whole credential file, such as a Google Cloud service-account key, “A credential file; not importable yet”; a value encrypted by sops or dotenvx. An encrypted Bitwarden export can’t be read at all: “This export is encrypted. Export again as .json (unencrypted), or use Import from Bitwarden…”
- Names come from the file and can be edited in the row. A key’s name is kept as it is; a JSON path or an item’s title becomes one, so
apiKeygivesAPI_KEY, and a name made from a title says “From the item’s title; agents will see this name.” A name set twice in one file lists both, ticking the last, as dotenv reads it. - Last 4 shows a value’s last four characters, or dots for one under 12 characters, and nothing more of it.
- Never show starts as the New keys default, and Page is the link the file gives or one Next Term suggests (Where a key came from).
The preview lists at most 1,000 rows. Websites’ sign-in passwords, which start unticked, get the room the other rows leave, and the rest of them are counted, not listed, so a password manager’s vault of thousands of sign-ins still lists its keys. The footer says so, such as “200 more website passwords aren’t listed: Next Term lists 1,000 at most.” A file with more than 1,000 rows besides those lists none, and the preview says “It holds more than 1,000 keys, more than Next Term lists at once. Import them in smaller parts.”
Import… asks first: “Copy 3 keys into Secrets?”, with the keys, any key a row replaces (“Undo can’t put back what it held.”), and that the file stays as it is. Copy Keys then asks for Touch ID once, in a sheet that reads: “Next Term” is trying to copy 3 keys from .env. What each key is for is set from the file’s name and folder, such as “From .env in rag-api”, or the app’s name, such as “From Bitwarden”: never an item’s title or a note, since agents see it.
Next Term never writes the file. When the keys are in, the sheet says what still holds them:
- A
.envfile: it still holds the keys. The sheet gives the line to start the project with instead, such asnxtrm secret run OPENAI_API_KEY PINECONE_API_KEY -- <your command>, then says to delete the keys from.env(the nxtrm command). - Another file: it still holds the keys; delete them from it once nothing reads them there. An agent’s config: “Next Term can’t rewrite it yet.”
- A password manager’s export: Move Export to the Trash, ticked: “Its keys are in Secrets now. Moving it to the Trash doesn’t erase backups of it.” Done moves it to the Trash, where you can still take it back.
- A file in a git repository: the advice to rotate, since git’s history keeps the keys, with Rotate… beside each key.
From 1Password or Bitwarden
Section titled “From 1Password or Bitwarden”Import From › 1Password…, Bitwarden… and Bitwarden Secrets Manager… each open a sheet that says what will run, shows its line, and links the tool’s download page. Open in a New Tab opens a tab in your home folder, titled “Import from 1Password” (or Bitwarden, or Bitwarden Secrets Manager), and types the line there without running it: “The line is in a new tab, not run. Read it there, then press Return.” You press Return and unlock at the tool’s own prompt. What it prints goes by pipe into nxtrm secret import, which hands it to Next Term, never to the screen, and the preview opens. The tab shows only a count, such as “Next Term: 7 keys found. Choose them in the Import window.”, and the tool’s own prompts.
The lines, with nxtrm written out as the full path of Next Term’s own command:
-
1Password, which you unlock with Touch ID through the 1Password app. It reads your API Credential and Password items:
Terminal window op item list --categories "API Credential,Password" --format json | op item get - --format json --reveal | nxtrm secret import --from 1password - -
Bitwarden, which asks for your master password at
bw’s own prompt, and locks again at the end:Terminal window BW_SESSION="$(bw unlock --raw)" bw list items | nxtrm secret import --from bitwarden -; bw lock -
Bitwarden Secrets Manager, which reads your access token from
BWS_ACCESS_TOKENin the tab’s environment. If you keep that token in Secrets, putnxtrm secret run BWS_ACCESS_TOKEN --in front ofbwsyourself; the line never does it for you.Terminal window bws secret list --output json | nxtrm secret import --from bws -
What each reads: from Bitwarden, each login’s password, its hidden fields, and NAME=value lines in a secure note; from 1Password, concealed fields and passwords; from Bitwarden Secrets Manager, each secret’s key and value. Never usernames, one-time-code seeds, password history, cards, identities, the rest of a note’s text, or a bws note. A website’s password starts unticked unless it has a key’s shape or its item’s title names a key, such as “Groq token”. What each key is for reads “From 1Password”, “From Bitwarden” or “From Bitwarden Secrets Manager”.
- You run it. Next Term types the line and never presses its Return, never runs these tools by itself, installs nothing and checks for nothing. A tool you don’t have fails in the tab with your shell’s own message.
- Next Term keeps no master password, session key or access token. Your master password is typed into a Next Term tab, so its keystrokes pass through Next Term, as through any terminal; Next Term keeps no copy.
- Agents keep out of that tab. From the moment the tab opens until it closes, agents can’t type in it, press its keys, answer in it or close it.
- Afterwards, the sheet says how the session ends, going by the line the tab ran: “Bitwarden is locked again.” when it ended with
bw lock; for 1Password, “Close this tab to end 1Password’s authorization for it.”, with Close Tab. - It needs “Let agents control Next Term” on, since the keys reach Next Term through it. While it is off, the sheet says so and opens no tab.
From other tools
Section titled “From other tools”nxtrm secret import reads what any tool prints, piped in, in the formats the file import reads (The nxtrm command). Type the line yourself in a Next Term tab, such as for Infisical or Doppler:
infisical export | nxtrm secret import --from env -doppler secrets download --no-file --format=env | nxtrm secret import --from env -Next Term types and runs nothing for these tools: you type the line, as with any command, and you unlock the tool as it asks.
Who ran nxtrm secret import
Section titled “Who ran nxtrm secret import”The preview names where piped keys came from: the tab, and the line, when it is the one a preset typed. It warns “An agent ran this command. It may have seen these keys.” when an agent runs above nxtrm secret import, or when the command runs in a tab an agent drives: one it opened with new_tab or ran a command in with run_with_secrets, or typed or pressed keys in with send_to_tab, press_keys or answer_agent, until you type or paste in that tab yourself. When no agent is above it and no Next Term tab ran it, such as a command that left its tab’s processes, as ( … &), nohup or setsid can make it, or one run in another terminal app, it warns: “Next Term couldn’t tell which tab ran this command. If you didn’t run it, an agent may have, and may have seen these keys.”
- Your own run in a Next Term tab brings Secrets forward with the keyboard. Given an MCP config that MCP Configs… rewrites, it opens that import for the file, as A File… does.
- An agent’s run, or one from no tab Next Term knows, is handled as an agent’s
import_secretsis: Secrets comes forward without taking the keyboard or making Next Term the active app, and Import… answers only a click, with no Return default; ⎋ still cancels. - Such a run given an MCP config is refused before anything opens or comes forward, since moving its keys out rewrites the file, and only you start that. The command prints “nxtrm: this is an agent’s MCP config, and moving its keys out rewrites it, so nothing opened. Choose Secrets › Import From › MCP Configs… in Next Term to move them.” and exits with 1.
A run inside tmux or herdr can’t be traced to a Next Term tab, so its sheet needs a click, and MCP configs are imported from Secrets › Import From › MCP Configs….
An agent can also open the import on your Mac with import_secrets; for a password manager, you still press Return in the tab yourself.
Undo an import
Section titled “Undo an import”Recent imports, under the list, shows each import of the last 30 days, such as “15 Oct: OPENAI_API_KEY and PINECONE_API_KEY, copied from .env in rag-api”, with Undo…:
- Keys copied in from a file or a password manager: Undo… asks first, “Delete 2 keys this import copied in?”, with the keys and “The file they came from isn’t changed.”, then deletes them after Touch ID. A key you changed since the import stays, and so does one that replaced a key you had, since the old value is gone: Undo can’t put it back. A key that a later MCP-config import used, instead of storing a second copy of the same value, stays too while the entries it rewrote start with it, and the confirm says so, such as “OPENAI_API_KEY stays in Secrets: entries an MCP-config import rewrote start with it. Once that import is undone, Undo here deletes it.” The copy-in stays in Recent imports for that key, and while nothing else of it can be deleted, Undo… says “Nothing to delete yet”, with OK. Once that MCP-config import is undone, the copy-in’s Undo… deletes it.
- Keys moved out of your MCP configs: Undo… puts them back into the files (below).
Move keys out of your MCP configs
Section titled “Move keys out of your MCP configs”Import From › MCP Configs…, under the list or in the Secrets menu, finds keys sitting in plain text in your agents’ MCP settings and moves the ones you tick into Secrets. It reads the configs of the agents Next Term sets itself up in (Claude Code, Codex, Gemini CLI, Qwen Code, Cursor, OpenCode, Copilot CLI, Amp, Junie, Command Code and the Claude desktop app), VS Code’s mcp.json, and in each project open in Next Term its .mcp.json, .cursor/mcp.json, .vscode/mcp.json, .gemini/settings.json, .codex/config.toml and opencode.json. Import From › A File… on one of these files opens this import for that file alone. It works only while “Let agents control Next Term” is on, since the servers get their keys through it.
- What it finds: values in a server’s
env(OpenCode’senvironment) named like a key (TOKEN,SECRET,PASSWORD,KEY,AUTH,CREDENTIAL,PAT), or shaped like one. References such as${GITHUB_TOKEN},{env:X}orop://…are left alone. Each row shows the agent, the file, the server, the variable and the key’s last four characters, never more, and a Never show box that starts as the New keys default. - One key for one value: the same token in three servers is one row, “Same value in 3 servers”, with a suggested name you can change. A row whose value a key from an earlier import already holds, moved or copied in, and not never shown, uses that key: “Already in Secrets with this value, from an earlier import: the move uses that key and stores no second copy.”
- Not movable, with the reason: a key in a header (“takes its key in a header; Next Term can’t wrap it yet. Use the server’s sign-in if it has one.”), a key in the command’s arguments, a shape the edit doesn’t handle, and the Claude desktop app’s file while the app is open.
- Rows start unticked. A row from a file in a git repository warns that the change reaches everyone who pulls, that they need Next Term, and that git history keeps the old key.
Move Selected… asks first, naming every file it will change, then asks for Touch ID once. Next Term then saves the keys, and rewrites each ticked server to start through nxtrm secret run, keeping the rest of the file as it was. Codex servers also get startup_timeout_sec = 30. Comments inside a rewritten server’s entry are lost, and its row says so. Claude Code’s ~/.claude.json is edited under Claude Code’s own lock, so the two never write it at once.
Undo. Next Term keeps a copy of each changed entry for 30 days, with every detected key replaced by a marker, so the copy holds no key in plain text. Recent imports, under the list, shows them, each with Undo…, which puts the keys back into the files as plain text after a confirm and Touch ID. It puts an entry back only if it is still what Next Term wrote; one you changed since is left, and named. A checkbox in the confirm also deletes the keys from Secrets. An entry that holds a key never shown is left as it is.
After the move:
- Rotate the keys. They sat in plain text, and Time Machine and git history keep old copies. Select each key in the list and use Rotate…, which opens its page, or asks where it came from when it has none. Do it if this Mac ever ran an untrusted install, or if a file is in git.
- Restart the servers:
/mcpin Claude Code, or restart the agent. The first time an agent starts each of them, approve it in Next Term. Restart any you missed. - From then on these servers start only while Next Term is open, and each start asks you to approve, unless you unticked Ask for Touch ID every time it’s used for its key and an approval you gave earlier covers it.
What is masked, and what masking misses
Section titled “What is masked, and what masking misses”Next Term replaces the exact value of each stored key with •••: as stored, in base64 and base64url, and percent-encoded. It does so in:
- everything its MCP tools return, such as a tab’s screen in
read_tabandwait_for_tab, the fields oflist_tabs, and the file, search and git tools; - the editor selection it sends to Claude Code and the other agents it links with;
- its notifications;
- the scrollback it saves for an update or a quit, the text it sends to Write with Agent and Suggest a Command, and the log.
It works from launch, before you unlock anything: when you save a key, Next Term makes a fingerprint of each of its forms with mask.key and keeps those in vault.json, and masking compares text against the fingerprints, never against the keys. The screen you see is not masked: a tab shows what its program prints.
Masking is best effort. It misses:
- keys under 12 characters, in any form, since a short value would match ordinary text;
- a key changed before it is printed: split, reversed, in hex, or inside a longer run of base64;
- what the program prints elsewhere, such as a file it writes, or output an agent reads with its own tools rather than through Next Term.
The fingerprints have limits of their own:
- Someone who copies the Secrets folder,
mask.keyincluded, can test guesses of a key against its fingerprints, with no unlock, which works for a key short or simple enough to guess. A long random key, such as services issue, is not practical to guess. - A program that can write to the Secrets folder can turn masking off, by deleting
mask.keyor the fingerprints, since they are read before any unlock. A deletedmask.keyis made again, with new fingerprints, the next time you approve a use or a change. Such a program can read the keys a tab prints anyway.
Where your keys are
Section titled “Where your keys are”Secrets keeps its files in ~/Library/Application Support/Next Term/Secrets.noindex/, a folder only you can open (mode 0700), with files only you can read (0600). The .noindex name keeps Spotlight out. In it:
vault.json: every value, each sealed with AES-GCM, and beside them, in plain text, the list: the names, what each key is for, who added it, whether it is never shown and whether it asks for Touch ID every time, its page link and reminder, its dates, and its fingerprints, with an old value’s for 30 days after a rotation. Any program running as you can read that list, and so can a backup of the file. The key that opens the values is wrapped by a key in your Mac’s Secure Enclave, which works only after Touch ID or your password. The list is signed with a key that only the vault’s key gives, so a change to it is found when the vault is next opened: if the file was changed outside Next Term, no key is released, and Settings › Secrets says “Secrets’ file was changed outside Next Term, so no key is released. Start Over, or restore the file from a backup.”mask.key, the fingerprints’ key, used for masking;audit.log, the log, andimports/, what Undo uses: copies of the MCP-config entries an import changed, with the keys taken out, and for each copy-in the names of its keys, with no value.
Next Term’s MCP file tools refuse this folder, even when your home folder is open as a project. Settings › Secrets says what protects it:
- “Protected by this Mac’s Secure Enclave. Using a key needs your approval with Touch ID or your password.”
- On a Mac without a Secure Enclave (an Intel Mac without a T2 chip): “Less protected: this Mac has no Secure Enclave. Next Term asks for Touch ID or your password, but the key that opens Secrets is in your login keychain.” The check is then Next Term’s, not the hardware’s. After an update, macOS may ask once for your login password before Next Term can read that key again; Next Term shows “Waiting for the keychain…” meanwhile.
Backups. Time Machine copies vault.json: the values stay sealed, and the list in it stays readable. Next Term marks mask.key, the log and imports/ so that backups skip them, but macOS can reset that mark, so this keeps them out of most backups, not all. Next Term never syncs or uploads your keys.
Start Over. When the vault can’t be opened on this Mac, Settings › Secrets says “These keys were saved on another Mac, or this Mac’s Secure Enclave was reset, so they can’t be opened here.” and offers Start Over…. It keeps the old folder, renamed Secrets-<date>.old.noindex, and starts an empty Secrets. MCP servers that use those keys won’t start until you add them again.
Macs without Touch ID
Section titled “Macs without Touch ID”Every approval asks for your Mac password instead, in the same sheet. A Mac with a Touch ID keyboard, or a MacBook with its lid open, can use either.
On a new Mac
Section titled “On a new Mac”Keys don’t move to a new Mac, by Migration Assistant or by a backup. Keep your own copy of each key, or plan to make new ones.
This holds for “less protected” Macs too. A vault restored on another Mac shows the can’t-open line and Start Over….
What Secrets protects, and what it doesn’t
Section titled “What Secrets protects, and what it doesn’t”Secrets keeps keys out of config files and out of your tabs’ environments. Each value on disk is sealed, no MCP tool returns a key’s value, and a key is used only after you approve, or under an approval you gave earlier.
It does not protect a key from the command you approve:
- A command you approve gets the key and can print it or send it anywhere. That command can be an agent, and a project file, such as a
.envwith a base URL, can send the key somewhere else. The panel warns about the cases it can see. - While a command you approved runs, any other program or agent running as you can read its environment, with no approval. That includes agents in other tabs and the command’s own child processes.
- A command’s exit code can carry a little of a key. Masking can’t hide that.
- An unpinned package can change between restarts inside an approval:
npx -yanduvxfetch the newest version each time. The panel warns when a package has no pinned version. - Masking is best effort (what it misses). A key set to never show is never shown or copied again; a command you allow still gets it.
- Secrets can’t stop malware already running as you.
How the parts check each other:
nxtrm secret runand the app check each other by code signature before a key is sent: the app answers only its own program, run asnxtrm secret run, andnxtrmsends nothing until it has checked the app. This relies on the hardened runtime that Next Term’s releases are signed with.- Approvals live in memory, and each holds only its own keys. They end at the latest when the Mac locks or Next Term quits.
- The log records command lines with keys masked, and never a value.
Known limits
Section titled “Known limits”- Links and keys aren’t checked. Next Term doesn’t check that a link is the right page, or that a key works. It suggests a page from the key’s value or name, and you decide.
- Rotating doesn’t revoke. Next Term doesn’t rotate keys through providers’ APIs: you make the new key on the provider’s page. Rotate… keeps the old value masked for 30 days; deleting or deactivating the old key there is up to you.
- A
.envfile keeps its keys. Copying keys in leaves the file as it was. Until the project starts throughnxtrm secret run, the file still holds the keys, and git history and Time Machine keep old copies. - An export is on disk until you delete it. A password manager’s export is unencrypted until then, and moving it to the Trash doesn’t erase backups of it.
- 1Password stays unlocked in its tab for a while. After a 1Password import,
op’s authorization for that tab lasts until the tab closes or 10 minutes pass with no use, as 1Password’s documentation says. Agents can’t type into that tab, but anything you type there meanwhile can use it. - Bitwarden’s session key is in one process’s environment while it runs. While
bw list itemsruns, its environment holds the session key, and any process running as you, an agent’s shell included, can read another process’s environment then.bw lockends the key right after. - A master password typed into a tab passes through Next Term, as through any terminal. Next Term keeps no copy.
- A pipe an agent ran may have shown it the keys. An agent that ran
op,bwor another tool in its own shell, or typed the line into a tab it drives, may have seen the values before they reached Next Term. The preview says so, and says when Next Term couldn’t tell which tab ran it (Who rannxtrm secret import). - An older Next Term drops what it doesn’t know. A version from before these features still opens the vault and keeps its keys, but the next time it saves the vault it drops their links, reminders, rotation dates, the masking of old values after a rotation, and any Ask for Touch ID every time it’s used you unticked, so those keys ask every time again.
Remote connections
Section titled “Remote connections”Coming later When agents outside your Mac can connect to Next Term, they will see your keys’ names only if you allow “List secret names”. A key used from a remote connection will always ask for Touch ID or your password at the Mac, with only Allow once, and no earlier approval will cover it. Values never cross the connection, and a remote agent can’t add, store or import a key.
The log
Section titled “The log”audit.log, in the Secrets folder, has one line per event: a use, an add, edit, replace, rotation or delete, a key set never to show, a reveal or copy, an import and its Undo, a decline, a pause, an approval and its end, a refused request, a changed file, and Start Over. Each line holds the time, the key names, who asked, the command (masked, at most 300 characters), the folder and how it was approved. It never holds a value. Last used in the list comes from it. At 1 MB it becomes audit.log.1, replacing the one before.
