Security

An SSH client holds
the keys to everything.

So the interesting question isn't what we promise not to do — it's what the architecture makes impossible. This page is the list of "cannots", the honest list of "we don't have thats", and where to send a vulnerability.

Report a vulnerability: hello@definable.ai with Security in the subject

Security by design

Four decisions do most of the work here. Each one is a structural property of the code, not a policy someone could quietly relax.

Local-first Hosts, chat history, the command audit log and agent memory live in SQLite files under ~/.tryssh on your Mac. Our servers hold identity and billing rows — nothing else.
Keychain-only secrets Private keys, passphrases and host passwords go to the macOS Keychain and nowhere else. Databases hold public metadata: a hostname, a username, a port.
Channel isolation The agent gets its own SSH exec channel. Your interactive terminal is a separate PTY channel. Neither can observe the other.
Default-deny execution A command auto-runs only if it matches a read-only allowlist and contains no shell operators. Everything else stops and waits for a human.

What we can and cannot see

The distinction that matters: some things are private because of a promise, and some are private because there is no code path that would move them. These are the second kind.

Thing Reaches our servers? Why
SSH private keys and passphrases No Never Stored in the macOS Keychain under the service ai.definable.tryssh. Nothing reads them except the local SSH engine, on the machine, at connect time.
Host passwords No Never Same store, same path.
Your terminal session No Never The PTY stream goes from your Mac to your server and into the terminal emulator. Nothing in between records or forwards it.
Your host list and connection settings No Never Rows in the local SQLite file. There is no sync service.
Chat history and the command audit log No Not stored Persisted locally. The content of a live turn does transit our LLM gateway — see below — but nothing is written down on our side.
Agent chat content, while a turn is running Yes In transit Relayed through cloud.tryssh.com to Anthropic to generate the reply. This is the honest asterisk on "local-first" — the full accounting is here.
Account identity Yes Stored Email, name and avatar from Google or GitHub SSO, via Stytch.
Billing state Yes Stored Plan, credit balance, and a ledger row per agent turn. Card numbers go to Stripe; we never receive them.

How secrets are handled

They go to the OS, not to a file we invented

Every SSH secret — private key material, key passphrases, host passwords — is written to the macOS Keychain under the service name ai.definable.tryssh. The local SQLite database stores the reference, never the value. There is no "encrypted secrets table", because a secrets table you encrypt yourself is a key-management problem you invented; the Keychain already solved it, and it is the thing macOS asks you about, audits, and locks with your login.

The shipped app carries no server credentials

The backend that ships inside Tryssh has no database URL, no identity-provider secret, and no payment-provider key. It holds one thing: the bearer token minted for your session after you sign in, kept in the macOS Keychain. Everything privileged happens in the hosted service, behind that bearer. This is a hard rule in the codebase rather than a habit — the client is a binary anyone can unpack, so any secret in it is a public secret.

Command output is scrubbed before anything sees it

Host output passes through a redactor on its way out of the SSH channel — before it is shown to you, before it is written to the local database, and before it reaches the model. It rewrites the value side of anything shaped like a credential assignment (DB_PASSWORD=…, "api_key": "…", x-api-key: …) and a set of unmistakable vendor token shapes: AWS access key ids, GitHub and Slack tokens, Stripe secret keys, Google API keys, Anthropic and OpenAI-style keys, JWTs. Only the value is replaced; the surrounding line survives, because "connection refused for user deploy" is exactly the text you needed.

This is a mitigation, not a guarantee. A credential in a shape we don't recognise — an all-numeric PIN, a bare high-entropy string with no key name near it — will pass through. If you cat a secrets file, assume it left the machine.

Two channels, on purpose

The visible terminal is an SSH shell channel with a PTY, streamed byte-for-byte into a real emulator. The agent runs its commands on separate exec channels. They share an authenticated connection and nothing else — no shared buffer, no shared stream. The agent cannot read your scrollback, cannot see what you typed, and cannot type into your session. That is a property of the transport, not a rule we enforce in application code.

The approval gate

The gate is default-deny. A command the agent proposes runs unattended only when it matches an allowlist of read-only commands and contains no shell operators — no ;, &&, |, redirects, backticks, $(…). Everything else renders as a card with the exact command text and stops until you click.

Landing in the next release: a denylist of irreversible operations — wiping a system path, formatting a filesystem, writing to a raw block device, flushing firewall rules, stopping the SSH daemon you are connected through, dropping a database, force-pushing over a protected branch. Those will always ask, even on a host you have set to run everything automatically, and the "don't ask again" allowlist will not remember them. It is not in 0.1.6; we would rather say so than let you assume a protection you do not have yet.

Per-host autonomy is yours to raise: the default is ask, and you can move a host to run everything. That is a real setting with real consequences, and we would rather document it than pretend the product is safer than it is.

The actual rules, command by command →

What we don't have

Written down so nobody has to infer it from silence.

  • No SOC 2, ISO 27001, or any other certification. We are a small team. We have not been audited, and we will not imply that we have.
  • No third-party penetration test of the app or the hosted service to date.
  • No bug bounty programme. We cannot currently pay for reports. We will credit you publicly if you want that, and we will answer you.
  • No SSO/SAML for teams, no audit-log export, no device management. Those are enterprise controls we have not built.
  • No formal published retention window for hosted-service logs. See data use for exactly what those logs contain and what they deliberately exclude.

If one of these is a blocker for your organisation, tell us which one — hello@definable.ai. It is genuinely useful for deciding what to build next.

Update promptly. Tryssh auto-updates through Sparkle over HTTPS with EdDSA-signed releases. If you disable updates, you are opting out of security fixes too — 0.1.5, for one example, shipped a broken model id that we had to patch server-side while installs caught up.

Reporting a vulnerability

Email hello@definable.ai with Security in the subject. Include what you found, how to reproduce it, and the version you tested (Tryssh → About, or codesign -dv /Applications/Tryssh.app).

What you can expect from us:

  • An acknowledgement within 3 business days. A human reply, not an auto-responder.
  • An assessment within 10 business days — whether we agree it's a vulnerability, and what we intend to do.
  • Credit in the changelog when the fix ships, if you want it.

Those are deliberately modest numbers we can actually hit, rather than impressive ones we can't. Please don't test against infrastructure you don't own, don't run automated scanners against cloud.tryssh.com, and give us a reasonable window before publishing.

The same contact details are published in machine-readable form at /.well-known/security.txt, per RFC 9116.

FAQ

Questions, answered

Where does Tryssh store my SSH private keys?
In the macOS Keychain, under the service name ai.definable.tryssh. Private keys, passphrases, and host passwords are never written to any database — ours or the local one — and are never transmitted to our servers.
Can the Tryssh agent read what I type in my terminal?
No. The interactive terminal is an ssh2 shell channel with a PTY; the agent runs commands on a separate exec channel. The two do not share a stream, so the agent cannot read your terminal scrollback or type into it.
Is Tryssh SOC 2 certified?
No. We hold no security certifications, we have not been through a third-party audit or penetration test, and we do not run a bug bounty programme. We would rather say so than imply otherwise.

Read the rest before you trust us.

The data-use page is the uncomfortable one. Start there.