HomeBlog › ##snat list and ##snat get: you keep the keys
Blog

##snat list and ##snat get: you keep the keys

Jul 19, 20265 min readBy the Rysh team

Your AI saw sk_live_SNAT000001. You can see the real value — locally, on demand. Asymmetry is the whole point.

In the last article I showed how SecretNAT — rysh's NAT-for-credentials layer — swaps real secrets for look-alike tokens before anything reaches the model provider, and how we grepped the actual wire to prove the real key count was zero.

This one is about the other side of that asymmetry: the mapping is yours. The model sees tokens forever. You can inspect and reverse them any time, locally, with two commands. That asymmetry — provider never, you always — is what "keeping the keys" actually means in practice.

The three commands

rysh is an agentic terminal multiplexer, and its system commands live behind a ## prefix. SecretNAT's surface is three of them (with ##rst — "ReSet" — as the short alias).

##snat status — what's on, and what's watching:

> ##snat status
[snat] SecretNAT / ReSet — reversible secret translation
  session default : on
  mode            : semantic
  known secrets   : 1
  detectors       : anthropic, stripe, github, slack, google, aws,
                    aws-secret, pem, openai, jwt, dburl, bearer, envkv

##snat list — every token that's been swapped in, with its detector and hit count. Tokens and types only — never values:

> ##snat list
  sk_live_SNAT000001   stripe   hits:5

##snat get — the local reveal. When you need the real value:

> ##snat get sk_live_SNAT000001
  = sk_live_51H8x…realkey
  [detected-tier · revealed locally — the model only ever saw the token]

That reveal happens on your machine, from the in-memory mapping, on your explicit request. It is not a model round-trip. The provider was not consulted, and couldn't be — it never had the value to give back.

Two tiers, one habit worth building

SecretNAT translates secrets in two ways, and knowing the difference tells you where to put your trust.

The detected tier is zero-setup: a registry of format-preserving detectors scans outbound text and mints tokens for anything credential-shaped — Stripe keys, GitHub tokens, AWS keys, JWTs, PEM blocks, database-URL passwords, bearer tokens, KEY=value assignments, and more, plus your own regexes via config. It's what caught the Stripe key in the wire test without being told anything.

But detection is a heuristic. It catches common shapes; it can miss exotic ones.

The known tier is deterministic. Register a secret with rysh's secret store and it becomes a stable named token — your Stripe key goes over the wire as ${STRIPE_KEY}, every time, across restarts. No pattern-matching involved.

The habit: let detection be your safety net, not your strategy. For the five secrets your team actually cares about, register them. Exact substitution beats clever substitution.

The defaults are opinionated (here's why)

You can flip SecretNAT per pane or per session (##snat on|off), switch modes (##snat mode private hides even the credential type from the provider), and configure it in a snat: block:

snat:
  enabled: true          # on by default
  mode: semantic         # or: private
  restore_display: false # display keeps tokens too
  custom_detectors:
    - { name: acme, pattern: "acme_[A-Za-z0-9]{32}", prefix: "acme_" }

Two defaults worth defending:

On by default. A protection you have to remember to enable is a protection that's off during the incident. The wire test in the previous article ran on default config — no flags, no setup.

restore_display: false. When AI output is rendered in your pane, the tokens stay tokens on screen. Why? Because the pane buffer is persisted, and panes can be shared to teammates. Auto-restoring real values into the display would re-leak them into scrollback and screen-shares. So the rule is: your tools get real values just-in-time; your screen keeps tokens unless you deliberately opt in — and when you need to eyeball one value, that's what ##snat get is for. One value, one deliberate act, no ambient exposure.

That's the design philosophy in miniature: every path a secret could travel defaults to the token, and every restoration is either just-in-time-for-a-tool or explicit-by-a-human.

Why "you keep the keys" isn't a slogan

Compare the two parties' capabilities at the end of a session:

The model provider You
Saw sk_live_SNAT000001 everything
Can reverse it never — no mapping exists on their side ##snat get, any time
Where the mapping lives nowhere they can reach your machine's memory

The mapping table is in-memory only, and its serializer is deliberately built to fail if any code path ever tries to persist it — a leak into a KV bucket becomes a loud error, not a silent file.

Honesty, restated from the previous article because it bears repeating: this is a translation layer, not a vault. No encryption-at-rest, no rotation, no HSM — pair it with your actual secrets manager. Restored values do exist in memory at tool-execution time; that's by design, and it's why the bounded claim is precisely: the provider never receives the value. And rysh itself runs on Claude with your own Anthropic key — self-hostable on your infra, not open source.

But within that boundary, the asymmetry is total. The model reasons about your production key at full fidelity and could not print it if you begged. You can print it with one command. That's what owning the mapping feels like.


rysh is in private beta — self-hostable, your keys, your data. If your security team's first question about AI tools is "and where do the credentials go?", bring them the wire test and join the design-partner program: free access, direct line to the founder, your threat model on the roadmap. Apply here.

Related: SecretNAT: your AI needs the shape of a secret, not its value · Self-hostable, your keys, your data

Try Rysh

Every pane is a shell and an AI agent — install takes one command.

Get started free →