Privacy
An API spec and a response body are among the most sensitive things a developer handles. This page is the specific accounting.
By default: nothing
Section titled “By default: nothing”Out of the box, with no settings changed, Tinspec sends nothing anywhere except the requests you explicitly make to your own APIs.
There is no telemetry, no analytics, and no account. The only call Tinspec makes to a Tinspec
server is the update check against a static JSON file on tinspec.dev.
Two background calls do go out, and both go where you pointed them:
- A live-bound spec source re-fetches its document on an interval — 10 s by default, changed under Settings → General → Spec refresh interval. That request goes to the spec URL you configured, which is the whole point of the binding staying live.
- A file-backed spec is watched on disk instead, by comparing an
mtime:sizestamp across the root document and every local$refit reaches. No network, no file contents read unless the stamp changed. Remote$refs cannot be watched this way and are deliberately left to the ordinary poll.
Everything that could send your data somewhere else is off by default and behind its own consent switch.
Where your data lives
Section titled “Where your data lives”| Thing | Where |
|---|---|
| Collections, chains, auth definitions, providers, route matchers, overlays | Plain YAML in your project folder |
| Request pre/post hooks | Inline in the collection YAML, as block scalars — the reviewed text is the executed text |
| Reusable snippets and standalone scripts | Plain .js in .tinspec/lib/ and .tinspec/functions/ |
| Run history, spec cache, search index | .tinspec/tinspec.db — local SQLite, rebuildable |
| Secrets | OS keychain, or a machine-local AES-256-GCM encrypted store (chosen per secret, at save time) |
| Your Anthropic API key (if AI assist is on) | The encrypted store — never handed back to the UI |
| The agent-access token | The encrypted store |
Secrets are never written to the YAML files or the database. Auth definitions reference
secrets by name — including the newer kinds, where an OAuth client secret, a refresh token and
an AWS secret access key are all stored as …Ref names rather than values. There is no
secret_get command for the UI to call. This is what makes the project folder safe to commit
and safe to sync.
Scripts — an escape hatch you own
Section titled “Scripts — an escape hatch you own”A request can carry pre and post hooks, and an auth provider can be a script. They run in
the engine’s sandboxed QuickJS runtime, and the ts.* standard library deliberately hands them
real power: ts.secret(name) reads a secret value, and ts.fetch(url, opts) makes a network
call.
So the honest statement is: a script can read a secret and send it wherever it likes. That is what signing a body or minting a token requires. Treat a collection you pulled from a teammate the way you would treat any code you are about to run.
Two things make that auditable rather than alarming:
- There is no module loader and no
require. Nothing is fetched at run time, so the script that runs is exactly the text in the YAML that a reviewer read..tinspec/libfiles are snippets the editor inserts into the request;.tinspec/functionsfiles are scripts you run yourself, not on a send. - You can run a hook without sending anything, from the Scripts tab, so a signing hook is testable against nothing rather than against production.
The contract is on the auth script reference.
AI assist — what leaves
Section titled “AI assist — what leaves”AI assist is off by default, and its surfaces stay hidden until you turn it on and store your own Anthropic key. With it on:
What is sent: a minimal OpenAPI subset — the operations of the endpoints you are working with, their parameters and schemas. Nothing else.
What is never sent:
- Response bodies. Whatever your API returned stays on the machine.
- Environment values.
- Secrets, tokens, or keychain contents.
- Run history, your project folder, or any file.
How to verify it rather than trust it: the AI panel has a collapsible “context sent with prompts” preview showing the exact payload, verbatim, before it goes. Open it once.
Requests go directly from your machine to Anthropic with your key. There is no Tinspec-hosted inference tier, and nothing routes through Tinspec’s servers.
Agent access — what leaves
Section titled “Agent access — what leaves”Agent access is off by default, behind a consent switch separate from AI assist.
With it on, the app serves MCP on 127.0.0.1 only — never a network interface — behind a
bearer token, and refuses requests without it.
What an agent can read: through 21 tools and four kinds of resource — the open project’s endpoints, saved requests, chains, run history, contract-drift findings, environment variable names, the identities it can send as, and — this is the important one — response bodies from requests it sends.
That last point deserves to be stated plainly rather than buried: response bodies do reach whichever model your agent uses. That is the purpose of the feature, and it is why it is off by default and why you approve sends one at a time.
What never crosses:
- Environment values. Keys only.
- Auth tokens. A sensitive request header is masked to
Bearer ****in what the agent sees. - The keychain. Not reachable through any tool.
- Credentials and secret names, from the identity tools.
list_auth_providersandlist_sendersemit identities — name, kind, tenant, which sender is the default, whether a sender is usable — and nothing else. No provider config, nosecretRefnames, no script source. A sender that is missing its password is reported as unusable, without naming the secret it wants, let alone its value.
What needs your approval: every tool that sends traffic or writes a file —
send_request, run_chain, save_request, save_chain — plus set_environment, the one
otherwise-read-adjacent tool that changes app state. Read-only tools run immediately and never
prompt. Safe-method sends can be auto-allowed with a setting; everything else always asks.
If you publish your own API as tools: a published endpoint arrives at the agent as a tool
like createOrder, carrying a provider and sender resolved here. The agent is never told
which identity it is calling as and the tool schemas contain no credential field, so it cannot
learn the JWT. Publishing nothing is the default. Deprecated and x-internal endpoints are
never published, and each published call goes through the same approval toast as any other
send.
What is recorded: every agent send lands in run history tagged mcp with the client name,
alongside your own runs. The Agent activity panel shows every tool call, its outcome, and each
approval decision live.
The mock server — a local listener
Section titled “The mock server — a local listener”A spec-bound mock serves your API’s declared shapes back to you. It is a server, so it is worth saying where it listens and what it serves.
- It binds loopback by default. Putting a mock of an internal API on the LAN is something you would have to type.
- There is no TLS, and no state: a
POSTdoes not make the nextGETreturn the row. - Response bodies can come from your own run history — real 2xx payloads the API actually returned, scrubbed of secrets by the same path the Body autofill uses. That data never leaves the machine; it is read out of the local SQLite history and served back on loopback.
- Nothing auto-starts. Options persist between launches, the server does not, and mocks stop when you switch project.
Signing in to your own identity provider
Section titled “Signing in to your own identity provider”An OAuth 2.0 / OIDC provider signs in against your authorization server, never a Tinspec one.
The browser half is the only part the app owns: it opens one URL in your system browser and
captures the redirect on a 127.0.0.1 listener on an ephemeral port. The state nonce is
validated before the code is exchanged. The device grant is the same flow with no listener at
all.
A refresh token is kept in memory only — never written to disk by the engine, and
deliberately never exposed as a substitutable {{variable}}, because a variable can end up in a
URL or a body and a refresh token has no business in either.
Cloud sync — what would leave
Section titled “Cloud sync — what would leave”Not available to sign up for at this release. When it is:
What syncs: collections, chains, auth provider definitions, API providers, route matchers.
What does not: secret values, ever. Auth definitions carry secret names; the values
stay on the machine that holds them. A teammate who pulls a synced auth provider gets the
flow and must supply their own credential. Endpoint overlays and the .tinspec/lib and
.tinspec/functions script files are not synced kinds at this release either.
In CI — what the runner leaves behind
Section titled “In CI — what the runner leaves behind”The tinspec CLI has no account, no sign-in and no Tinspec URL. It reads
secrets from a --secrets-file (refused outright if it lives inside the workspace, so it cannot
be committed by accident) or from a --secrets-command you supply — the escape hatch for
vault kv get, op read, aws secretsmanager. tinspec secrets list prints reference names,
never values.
The reports it writes are files that get uploaded to a CI server and attached to a merge request, so what goes into them is a security decision rather than a formatting one:
- No request header, no request body and no credential ever enters a report. That is enforced by what the report builders collect, not by hiding fields at render time.
- A response body is attached only for a failing case, truncated, and passed through a redaction list of the literal secret values the run was given.
The update check
Section titled “The update check”On startup the app fetches a static JSON file from tinspec.dev to see whether a newer
version exists. It is a plain file read — no identifiers, no usage data, nothing about your
project. The CLI has no update check at all; Homebrew handles that.
Summary
Section titled “Summary”| Feature | Default | What leaves |
|---|---|---|
| The app itself | On | Nothing but the requests you make |
| Spec refresh | On (10 s) | A fetch of the spec URL you configured — nothing to us |
| Update check | On | Nothing — a static file read |
| Request & auth scripts | None until a collection carries one | Whatever the script itself sends |
| Mock server | Off | Nothing — a loopback listener |
| AI assist | Off | An OpenAPI subset, previewable verbatim |
| Agent access | Off | Spec and response bodies, to your own agent, per approved send |
| Cloud sync | Off (not open) | Documents, never secret values |
| CI reports | Opt-in per run | Results, drift, and a failing case’s redacted body |