Follow one job through Tinfirm.

A ticket asks for a docs update, an agent writes it, a pull request comes out. Watch where each guard sits on the way. Everything inside the dashed line runs on your servers.

Outside your networkOutside your networkYour networkTinfirmRunner hostSandboxfresh for this runYour systemsDOCS-58Jira, GitHub, GitLab,Slack, Teams and yourMCP serverstrigger rulestool permissionsallow · ask · denywaits for approvalaudit log#2041 9c1e…a7#2042 47ab…10#2043 d03f…5cno system keysWRWriter is working12m 20s · 40 tool calls12m 34s · 41 tool calls12m 51s · 42 tool calls13m 02s · 43 tool calls13m 15s · 44 tool calls13m 24s · 45 tool calls13m 31s · 47 tool callsReadrefunds.mdEditedrefunds.md+12 −3Ranlink checkrunningfailedEditedrefunds.md+2 −1Ranlink checkrunningpassedEditedfaq.md+8 −1Editedrefunds.md+2 −1Ranlink checkpassedEditedfaq.md+8 −1Usedjira_get DOCS-58Usedgithub_label DOCS-58Approval needed: add a labelagent/docs-58+12 −3+14 −4+22 −5model proxyadds the keybranch pushown branch, no forceModel providersees the promptsGitHub or GitLaba person mergesDOCS-58DOCS-58dummy keyreal keyjira_getjira_getgithub_labelagent/docs-58agent/docs-58entry #2043
OutsideOutside your networkYour networkTinfirmRunner hostSandboxnew each runYoursystemsDOCS-58Jira, GitHub,Slack, Teams,MCP serverstrigger rulestoolpermissionswaits for approvalaudit log#2042#2043WRWriterno system keys12m 20s · 40 calls12m 34s · 41 calls12m 51s · 42 calls13m 02s · 43 calls13m 15s · 44 calls13m 24s · 45 calls13m 31s · 47 callsReadrefunds.mdReadrefunds.mdReadrefunds.mdEditedrefunds.mdEditedrefunds.mdEditedrefunds.mdRanlink checkRanlink checkRanlink checkEditedrefunds.mdEditedrefunds.mdEditedrefunds.mdRanlink checkRanlink checkEditedfaq.mdUsedjira_getUsedgithub_labelApproval neededagent/docs-58+12 −3+14 −4+22 −5branch pushmodel proxyGit hostyou mergeModelproviderDOCS-58DOCS-58dummy keyreal keyjira_getjira_getgithub_labelagent/docs-58agent/docs-58entry #2043

1/7A ticket or a message starts it

Only your trigger rules start work, with limits on floods. Tinfirm polls your trackers, so it needs no inbound port, and marks outside text as data for the agent.

At a glance

Every guard, in one place.

The walkthrough follows one job. Here are the guards it passes, side by side, and each is explained further down.

A fresh sandbox for every run

A new container or microVM: non-root, no capabilities, its own network, gone when the run ends.

The sandbox

No system keys inside

Tools run on Tinfirm's server, git goes through a fetch-only proxy, and model calls carry a dummy key.

Where credentials live

Only the tools you allowed

Each tool reads or writes, writes ask a person by default, and settings can only narrow that.

Tools in three layers

Hard limits

Time, tool calls, loops and silence are capped, and any credential in the output stops the run.

The limits

Its own branch, no force

The runner host pushes the agent's branch with a lease check, after scanning the commits. A person merges.

Connections

Companies kept apart

Row-level security is forced in PostgreSQL on every company table, and tested.

Identity and access

A hash-chained audit log

Append only, verified every day, exported for checks on your own machine.

Audit

Nothing calls in

Runner hosts connect out, trackers are polled, and the model gateway has no ingress.

Network

The sandbox

A fresh sandbox for every run.

Every run starts in a new container, or a new microVM, on one of your runner hosts. The agent works there on a fresh copy of the repositories it may use, and the sandbox is gone when the run ends.

  • Locked down by default: a non-root user, every Linux capability dropped, no new privileges, Docker's default seccomp profile and a network of its own.
  • A VM per run, if you want one: with Kata Containers and Cloud Hypervisor, each run gets its own microVM and its own kernel. The container tier shares the host's kernel.
  • Hard limits on every run: 60 minutes and 200 tool calls by default; the same call five times in a row, or 20 minutes without a word, stops it too.
  • Credentials are redacted from events, summaries and logs. Output or a commit that holds one stops the run, and nothing is pushed or saved.
Sandboxrun 7f3a · runner-02Running
ContainerKata microVM
User
Not root: uid 1000
Capabilities
All dropped, no new privileges
System calls
Docker's default seccomp profile
Network
A namespace of its own
Limits
4 GiB memory, 2 CPUs, 1024 processes by default
Workspace
A fresh clone, on branch agent/docs-58
Mounts
Attachments and skills read only; /output for documents
Keys inside
A dummy model key
Credentials

Your systems' keys stay out of the sandbox.

Tinfirm's server runs the tools that reach your systems, the runner host pushes and fetches git, and the host's model proxy carries the model calls. The agent gets results, not keys.

  • An encrypted vault. Every secret version is sealed with AES-256-GCM under its own random key, which is wrapped under a master key kept outside the database. Rotate by adding a key and rewrapping.
  • Write-only. No API hands a secret back, and every use is recorded in the audit log with its purpose.
  • Yours stay yours. The secrets of a member's own accounts belong to that member: admins and Owners can't list, reveal or replace them, and they go when the member leaves.
Where each credential lives
  • Jira, GitHub, GitLab, chat and MCP tokensTinfirm's vault. The server runs the tools with them.Never inside
  • A run's git tokenThe runner host's git proxy, which lets the sandbox fetch and never push.Never inside
  • Each person's gateway keyTinfirm's vault. The server relays the model calls.Never inside
  • Your model provider's keysLiteLLM, inside your deployment.Never inside
  • Model access in the sandboxA dummy key. The host's model proxy swaps in a token valid for this run only.Dummy key only
  • A member's own subscriptionOnly on their own runner host, for their own work.In their sandbox
Network

Nothing on your side has to accept a call.

Runner hosts connect out to Tinfirm, Tinfirm polls your trackers, and your model gateway stays inside the deployment. Every connection, and which way it goes:

  • No inbound port for runner hosts or sandboxes. A host's token is stored only as a hash on the server.
  • Calls to your systems go over https only, follow no redirects and refuse loopback, link-local and private addresses.
  • An allowlist when you want it: turn on the egress proxy and a sandbox reaches only the model, your package mirrors and the hosts people approved; loopback and cloud metadata addresses are always refused.
Connectionswho calls whom
  • Runner host → TinfirmA WebSocket the host opens; its token is stored only as a hash
  • Tinfirm → your trackersPolls every minute by default, over https, with no redirects
  • Sandbox → model proxyModel API paths only; credential headers dropped; WebSockets refused
  • Sandbox → git proxyFetch only: a push is refused
  • Your software → webhookSigned with HMAC-SHA256; replays and stale timestamps dropped
  • Slack, Teams → TinfirmEach request's signature checked
  • Tinfirm → LiteLLMInside the deployment; nothing else may reach it
Identity and access

Who can sign in, and what they can reach.

People sign in through your identity provider, and every request is checked against their role, their company and, for an AI client, the scopes a person granted.

  • Companies kept apart by the database. Row-level security is forced on every company table and denies by default; the app's own role can't bypass it, and isolation tests check it.
  • Nothing to probe: what you may not see answers 404, never 403. Logins and the AI client endpoint are rate limited.
  • A strict web app: a Content Security Policy with no inline scripts and no framing, CSRF tokens on every change, HttpOnly session cookies.
Sign-in and tokens
Sign-in
Your identity provider over OIDC with PKCE, or local accounts hashed with Argon2id
Sessions
An HttpOnly cookie: 8 hours idle, 7 days at most, a new ID at every sign-in
Roles
Owner, Admin, Member; only an Owner makes an Owner
AI clients
OAuth 2.1 with PKCE; read, write and decide scopes, decide only when a person grants it; access tokens last an hour
Personal tokens
Up to 25 per person, stored as a hash, with an expiry if you set one, revocable
Companies
Forced row-level security in PostgreSQL
Audit

Evidence your auditors can check.

Every action that changes something is written to an audit log in which each entry carries the hash of the one before. Change or remove an entry and the chain no longer adds up.

  • What's in it: every change, every tool call with its system, tool, account and mode (never its arguments or results), every run with who paid and which host ran it, grants, budgets, secret uses and logins.
  • Checked every day, including for a missing tail, and exported as NDJSON that you can verify offline.
  • Append only: the app's database role can add and read entries, never change or delete them. Personal data stays out of the hashed fields, so erasure still works.
Audit logcompany chainChain verified today
 Export NDJSONVerify the chain on your own machine.
Your data

Kept as long as you decide, and no longer.

Everything Tinfirm stores lives in your PostgreSQL. The company's Owner sets how long each kind of data is kept; open conversations and legal holds are never purged.

  • No product telemetry. Error reports stay local by default, and if your operator turns automatic delivery on, they're scrubbed of URLs, emails, IDs, addresses, paths and tokens first.
  • Uploads scanned for credentials, archives included: a file that holds one of your vault's secrets is refused, and a key in a known format needs an admin to let it through, on the record.
  • Prompts aren't kept by the gateway: the bundled LiteLLM doesn't store prompts or answers in its spend logs, and Tinfirm's relay stores neither.
  • When someone leaves, their accounts, secrets, personal MCP servers, gateway key and personal runner hosts are deleted, their OAuth grants revoked and their triggers paused.
Retentionset by the Owner
Run events90 days
Chat content30 days
Other content365 days
Files365 days
Memory notes730 days
Legal holdErasureOpen conversations kept
How it's built

Shipped with care, deployed locked down.

Pinned dependencies

The server's base images are pinned by digest and the CI actions by commit, with lockfiles committed and updates proposed weekly. The runner's tools are pinned and checked against their checksums, with auto-update off.

Scanned on every change

Static analysis, secret scanning and dependency CVE checks run on every pull request, on every push to the main branch, before every release and once a week.

Locked-down defaults on Kubernetes

The server runs as a non-root user on a read-only root filesystem with every capability dropped and seccomp on. A network policy admits only your ingress, and the management port is never exposed.

The gateway stays inside

The bundled LiteLLM has no ingress and its admin UI is off; on Kubernetes only the server's pods may reach it. Runner hosts never reach it: the server relays their model traffic.

Limits

What we tell you up front.

Every product has edges. These are Tinfirm's today, from its threat model, so you can weigh them before you decide, not after.

The network allowlist is opt-in.Without the egress proxy a sandbox can reach any host. With it on, it reaches only the model, package mirrors and approved hosts; services on the host's bridge address still need a firewall.
The container tier shares the host kernel.For a kernel per run, use the Kata microVM tier. Access to a host's Docker socket equals root on that host.
A personal subscription runs in its owner's sandbox.A member's own Claude or ChatGPT token is inside the sandboxes of their own runs, on their own runner host.
Code host tokens reach the runner host.A run’s git token goes to the host that runs it, never into the sandbox, and lasts until the run ends.
Company runner hosts see run content.A host runs the work, so whoever controls it can see what runs there.
Local accounts have no second factor.Use your identity provider's multi-factor sign-in over OIDC, and switch local sign-in off.
Prompt injection can't be ruled out.Outside text is marked as data, which lowers the risk. Whoever can write where a shared trigger listens can drive it with the trigger's accounts.
Your model provider sees the promptsyou send it, under your terms with them.
Full database access could rewrite the audit logand its hashes together. Anchoring the chain outside the database is still open.
Owners can read personal conversations.For support and audit. Each read is recorded and shown to the person.

Questions your security team will ask

Something we missed? Ask us.

Where does our data live?

On your own servers. There is no Tinfirm cloud.

You run the app, its PostgreSQL database and the runner hosts. Runner hosts connect out to Tinfirm, and Tinfirm polls your trackers, so neither needs an inbound port. The model provider you pick sees the prompts you send it.

Can the agent see our keys?

Not your system keys. They stay with Tinfirm and the runner host.

Inside the sandbox the agent has a dummy model key and a token that works for one run only. Tool calls run in Tinfirm with the connection's credentials, the git proxy can only fetch, and output with a credential in it stops the run. One exception: when a person brings their own Claude or ChatGPT subscription, that login runs inside their own sandbox, on their own runner host.

Where are secrets kept?

In an encrypted vault, and no API ever hands one back.

Each secret is sealed with AES-256-GCM under its own fresh key. The master keys live outside the database, and you can rewrap and rotate them. Every use of a secret is logged.

What if a ticket tells the agent to misbehave?

It reads the ticket as data, and it has little to give away.

Text from outside is marked as data before the agent sees it. That lowers the risk; no one can promise to prevent prompt injection. So the guards above still hold: no system keys to leak, tools only as far as you allowed them, no force push, and a person merges.

Can one company see another's data?

No. The database itself keeps them apart.

PostgreSQL row-level security is forced, not left to the app, and isolation tests check it. A person's own work uses their own account, with no fallback to a shared one, and read-only accounts cannot write.

Can Tinfirm trigger itself in a loop?

No. Its own writes never start new work.

A comment or label Tinfirm writes back to your tracker is recognised as its own, so it cannot set off another run. Floods of outside events are limited too.

NextPricingThree editions from one image: Community free, Pro per seat, Enterprise negotiated. Never a fee per run or per token.

Run it on your own infrastructure.

Request early access