What is BaseInst?
An agent given a task works out a plan, calls tools, reads what comes back, and answers. Give it the same task tomorrow with one value changed — a different region, a different customer, a different week — and it works the same plan out again from nothing.
The tokens, the latency and the tool calls are all spent a second time to arrive at a conclusion the first run already reached. BaseInst records those runs, derives the plan from one of them, and runs the plan directly the next time the same question comes round.
The loop
Three steps. Each is a thing you can see in the console, and each can be inspected before you trust the next.
The agent works normally. Every tool call, its arguments, what came back, how long it took and what it cost is stored — along with the prompt that started it and the answer it reached.
Produces · a runThe transcript becomes logic: an intent, a parameter schema naming what was incidental to that particular run, and one body per step that recomputes that step's arguments.
Produces · a scenarioA prompt that means the same thing as a recorded one runs the logic instead of being solved again. The tools are really called; only the rediscovery is skipped.
Produces · a savingRecord
The runner sits in the agent's execution path and stores each run. Nothing about the model's behaviour changes — the recorder executes the calls the model was going to make anyway and returns the results unaltered.
A run with three tool actions or fewer is not kept in the searchable pool and is eventually pruned. Below that threshold there is no plan to derive, only a single call and its answer. Each finished run gets a title of at most 40 characters, written by a model from its transcript, so the list reads as a list of tasks rather than a list of ids.
fleet tool, and answers. Nothing about the agent changed; the runner recorded it from the side, and a run like this is what a recording is made of.Calculate
A recorded run is a transcript: what happened, once, with those exact values. A calculated scenario is what BaseInst derives from it — an intent, a parameter schema, and one logic body per step.
Press Calculate yourself, or let the service choose: whenever a prompt comes back it takes the recording with the most to save — among those whose prompt has come back at least three times and that passed end to end, the run finishing without an error and no tool call in it failing — and calculates it. It never replaces a scenario you already have. Would rather it were a different recording? Prefer that one: it is calculated at once, taking the place of an automatic scenario (or, after you confirm, one you calculated by hand), and nothing evicts it in turn until you withdraw the preference.
The transcript says what the agent did. The scenario says what the agent was doing, which is the only half worth running again.
fleet and id — are what may change next time. The last execution says what one reuse cost and what it saved.
eu-west-1 fleet. return { fleet: parameters.fleet } sends whatever fleet the new prompt names; in this example the output logic sorts what comes back by risk and hands the worst device to the next step. Nothing is remembered — everything is recomputed. A plan that had memorised one device id would look identical today and be wrong the day the data changed.Replay
Matching is on the prompt text, embedded and compared by
similarity — not the transcript and not the answer. Above the detection
threshold (default 0.9) the service recognises a match; above
the steering threshold (default 0.92) the runner acts on it.
- Fully wrapped — every step is a tool the proxy already holds a connection to. The sequence runs through those connections with zero model tokens; the model's only job is to read the results.
- Mixed — some steps are built-in tools the proxy cannot execute. The live session is steered instead: each step runs as normal, with its arguments pinned to what the scenario computed.
The savings ledger, and why it is careful
The headline is verified saved. It is deliberately smaller than the number BaseInst could report, and the gap is the point.
Money we watched move. Costs measured on both sides — what the agent's run cost, what the replay cost — reported by the process that spent them. This is the number the dashboard leads with.
Booked from costs nobody measured end to end. Real rows, honest arithmetic, partial inputs. Shown beside the headline and never added into it.
The distinction matters because of how the arithmetic fails when a cost is
missing. An unmeasured replay usually has only its parameter-derivation call
priced, so baseline − cost books nearly the whole baseline as
profit. A ledger that summed those would report a large, confident, wrong
number.
Two more rules in the same spirit:
- A baseline drawn from one run is labelled as one run. Every saving on a row is measured against what the agent costs on that task. If that figure comes from a single sample it is a guess, and the console says so rather than presenting a statistic.
- A scenario can lose money, and it will say so. If recent replays cost more than the agent does, the row is flagged as not paying off. Savings are not clamped at zero — a scenario quietly costing you money is exactly the thing you need to be told.
Two run types are counted but save nothing, and are reported separately. A dry replay evaluates stored logic against recorded outputs — a check, not work. A not-steered hit is a matched prompt the runner declined to replay, which becomes another baseline sample instead.
Scenario caching, not semantic caching
AI gateways such as TrueFoundry's and Gravitee's offer semantic caching. The gateway keeps the model's reply to each prompt, keyed by an embedding of that prompt. When a new prompt is close enough to one it has seen, above a similarity threshold you set, it returns the stored reply and the model is never called. For what it is built for it is a good tool: the same question asked many times in slightly different words, where the same answer is still right. A help desk answering the password-reset question is the textbook case.
BaseInst starts the same way and then does something different. It also embeds the request and looks for a close match among your earlier ones, with a stricter threshold and more checks before anything runs. But what it keeps and reuses is not the answer. It is the scenario: the plan the agent worked out, with the values that vary taken out of it, and a rule per step for recomputing that step's arguments from the new request and from what the previous step returned. On a match the tools are really called, against today's data, and the model reads the results and writes today's answer. What is skipped is the working-out, not the work. The honest name for it is scenario caching: it caches the plan.
| Semantic cache | BaseInst: scenario caching | |
|---|---|---|
| What is stored | The model's reply, keyed by the prompt's embedding | The plan: an intent, the parameters, one rule per step |
| What a hit returns | The stored reply, without calling the model | A fresh run of the plan: live tool calls, a new answer |
| Right when | The same question deserves the same answer | The same task comes back with different values: another fleet, another customer, another week |
| Wrong when | The world moved since the reply was stored, or the near-match was a different question | The task is exploratory, so there is no plan to reuse |
| Where it sits | In front of the model, in the gateway | Beside the agent, on the path to its tools |
| What it saves | The whole model call, on questions that repeat word for word or nearly | The planning turns, on tasks that repeat in shape |
| What it reports | A hit rate | A saving: what the agent costs on the task, minus what the replay measurably cost, with verified kept apart from projected |
| The failure to watch for | A hit that should have been a miss returns the wrong answer, confidently. Both vendors' own documentation warns of it | A step that remembers a value instead of computing it. The free dry run exists to catch that |
The two are not rivals for the same request. A question with a fixed answer belongs in a semantic cache, and BaseInst would save nothing on it: there are no tool calls to plan. A task that calls tools, whose answer depends on what those tools return today, cannot be cached at all. Only its plan can, and that is what BaseInst caches.
What BaseInst is not
| Not this | Because |
|---|---|
| A caching layer, or a semantic cache | A cache returns a stored answer. A scenario re-runs the work with new arguments — the tools are really called, against live data, and the answer is about today. The side-by-side is under Scenario caching, not semantic caching, above. |
| A prompt library | Nothing here changes what the model is told. Tool descriptions are never edited to steer the model's choices. |
| An observability product | It records runs and you can read them, but the recording exists to become a scenario. If you only want traces there are cheaper things to run. |
| Automatic replay | A run is recorded automatically, and a scenario is calculated automatically once its prompt keeps coming back — or because you asked, or preferred it. Replay happens only once you have turned it on and said which servers may be called unattended. |
Who should use it?
BaseInst pays off on a narrow, common shape of work: a task you give an agent more than once, where what changes between runs is the values and not the plan.
If that describes something you do weekly, read on. If it does not, the honest answer is that BaseInst will record your work faithfully and save you nothing — and you should know that now rather than in three weeks.
The three-question test
All three have to be yes.
-
Will I run this again?
Not "could someone"; will you. A task run once is a task whose plan is worth nothing to derive.
-
Does the plan stay the same when the values change?
"Check the eu-west-1 fleet and tell me which device is most at risk" and "check us-east-2" are one plan with a parameter moved. "Figure out why the build broke" is a different investigation every time, even when the words are identical.
-
Is it more than three tool calls?
Below four actions there is no plan to capture, and the service will not keep the run in its searchable pool. A one-call task is already as cheap as it gets.
The shapes that work — and the ones that don't
Worth recording
- Scheduled sweeps and reports. Fleet health, cost reports, open-PR triage, error-budget checks. Same steps, new window, run on a cadence.
- Per-entity investigations on a fixed path. The customer changes; the four tools and their order do not.
- Fan-out over a list. Record once against one repository, region or account; the other thirty-nine are the same scenario with a different parameter. This is where the arithmetic gets loud.
- Runbooks with a stable spine. Nobody should be paying a model to rediscover the opening moves at 3am.
Not worth it
- Genuinely exploratory work. Debugging, code review, research — anywhere the second tool call depends on what the first said in a way you could not have predicted. The plan is the work.
- Non-MCP tools without hooks. You get a partial recording, correctly labelled as one.
- Anything that must not run unattended. If that sentence makes you uneasy about a particular tool, do not put it on the allow-list — that instinct is correct.
- High-variance, low-frequency work. Something you run twice a year, where the environment has moved on by the time it comes round again, so the plan the scenario holds is no longer the plan.
Who, in job terms
| You are | What you get out of it |
|---|---|
| Platform / SRE | Scheduled sweeps and runbook openings. The highest-yield case: fixed spine, changing scope, run constantly |
| Data / analytics engineering | Report pipelines where the shape is stable and the window moves |
| Support & success engineering | Per-customer investigations that always start the same way |
| Agent and tooling teams | A measured answer to "is this agent worth what it costs", from the ledger, before you touch replay at all |
| Engineering leadership | The verified-savings figure — deliberately conservative, and therefore quotable |
Recording is useful on its own, and it is the whole of what runs out of the box. A team that never calculates a single scenario still ends up with a priced, searchable record of what its agents did and what each run cost. For some teams that is the entire value, with replay as a later question.
What it costs you to find out
An afternoon, and no money. Recording, calculating and replaying are all free, there is no limit on how many scenarios you hold, and none of them expires. Take the task you repeat most, record it, calculate it (or repeat it three times and let the service calculate it for you), dry-replay it, and read the ledger after a week. If the number is unconvincing on your best candidate, it will not improve on your second-best.
If it is convincing, the question that follows is usually not about price but about where the recordings live. That is what running it in-house is for.
The honest limits
None of these is a bug to be fixed later.
- A saving is only counted when it is measured. The verified figure needs the runner to report what a run cost. A session that ends without reporting loses you the measurement, and the saving lands under projected instead — the ledger stays conservative either way.
- Calculation can be paused. Working a scenario out costs us several model calls, so the free service calculates one scenario at a time per account and stops for the day if the whole service has spent its budget. An ordinary account never meets the second one.
- A match is a match on the prompt. Phrase the first prompt of a reusable task as though someone else will have to phrase it again next month — because that is exactly what has to happen.
- Standalone recording is partial. Without the hook binary there is no prompt, no final answer and no built-in steps, so there is nothing for a match to compare against and replay cannot arm.
- Calculation needs a model. Deriving a scenario, and deriving parameters at replay time, both call Anthropic. A service without a key configured will refuse to calculate.
Using the console
The console is where recordings, scenarios and money are visible. What follows is a tour of the screens and, more usefully, of what each number is actually claiming.
Getting an account
Register
With an email and password, or sign in with Google.
Verify the address
Registration does not create a session; the link in the email does. That link is the proof of the inbox, and it may be opened on a different device from the one you registered on.
Sign in
You land on the dashboard. If a verification link has gone stale, ask for another from the sign-in screen — a new link supersedes the old one.
A new account sees Getting started on the dashboard where the tiles would be. The next step is to install the runner — about ten minutes — and the first recording lands there.
Getting around
The menu is on the left and stays there. It is grouped, because the destinations are about different things.
| Section | Screens |
|---|---|
| — | Dashboard, Recordings — and, while you are inside Recordings, its saved views — then Set up the runner |
| Account | Account, Sign-in history |
| Admin | Users, Invoices — admin accounts only |
Pinned to the bottom of the rail:
- Documentation — this handbook, where the deployment has been given a docs URL.
- The account chip — name and role, with account settings, the light/dark toggle and sign out.
⌘K opens search from anywhere. It searches the menu, and offers one row that hands your query to the recordings search. It does not search across everything — there is no index behind that, and a palette pretending otherwise would be worse than one that is clear about its scope.
At the foot of every page: API status, Help and support, Feedback. The status light is a single call on load — it does not poll, because a status indicator that hammers a service during an incident is a second outage stacked on the first.
Recordings — the columns
The list, and the only screen with thirteen columns. Everything about it lives in the URL — sort, page, search, filters — so a view worth building can be bookmarked or sent to somebody.
| Column | Reading |
|---|---|
| Run | The generated title, ≤40 characters. Falls back to the first line of the prompt before a title exists. Calculated rows carry a green edge |
| Started | When the agent ran |
| Steps | Actions the agent took. Not every stored row — a tool's response and the recorder's own bookkeeping are not steps the agent took |
| Status | done, failed, or running |
| LLM $/run | The baseline: what this task costs when the agent does it. Its ⓘ says how many runs the baseline is drawn from |
| Scenario $/run | What a replay costs. Marked est. when it is a pre-flight estimate rather than an observed mean — "we measured this" and "we guessed this" must not look alike |
| Saved %/run | How much cheaper one calculated run is than one agent run: (LLM $/run − Scenario $/run) ÷ LLM $/run, from the two columns beside it. The bar under the figure fills to the same percentage, in a green that deepens as the saving grows; red only when a replay costs more than the agent. Marked est. while the scenario cost is still an estimate; — without a ready scenario or a baseline |
| Original time | Wall-clock for the agent's run |
| Calculated? | yes, no, calculating… or failed. A scenario whose recent replays cost more than the agent is flagged not paying off here too. auto beside the badge means the service calculated it by itself. The ★ Preferred and ⊘ Switched off badges under a title are the two things you can tell the service about a recording |
| Calculated runs | Replays of this scenario that did work — the same count as the tile above. Its ⓘ breaks out unmeasured, dry and not-steered runs |
| Similar hits | Prompts matched against this recording |
| Saved $ | Verified savings for this row, signed. — until a measured replay has actually happened. Projected savings, when there are any, sit beneath it and are never added in |
Every column with a figure in it sorts, Saved %/run included. Calculated recordings sort to the top of every column by default; the note above the table says so and lets you turn it off. Runs with three actions or fewer are hidden, with a link saying how many — hidden rather than deleted, because they are also below the threshold for being kept at all.
Recordings — the detail page
Everything stored for one run: the prompt, each step with its arguments and
output, the final answer, and the scenario if there is one. This is where you
press Calculate, read what calculation produced, and run the
dry replay that is worth doing before you trust anything. There is no
Recalculate button: a calculation that failed is run again by preferring the
recording, which resets it and starts a fresh job. From the command line it is
still bir scenario calc --force.
check the us-east-2 fleet; the agent's answer; each tool call with what it sent and what came back. On the right, the scenario calculated from it, pictured up close in section One.It is also where you overrule the service's pick, in either direction. ☆ Prefer this recording calculates this one straight away, and takes nothing from anybody: you may hold as many scenarios as you like, so nothing has to be replaced to make room. A preferred recording is also first in line whenever the service looks for something to calculate on its own, whatever its hit count, and is kept however short it is.
Switch off calculation and replay is the opposite instruction: the service never picks that recording on its own, however often its prompt comes back and however much it has at stake — and while it is off, a matching prompt is not replayed from its scenario either: the agent runs again and nothing is saved. The scenario is kept, and comes back when you switch calculation and replay on again. The two are one switch, so each button is offered only while the other flag is not set: a recording is preferred, switched off, or neither. Two badges beside them are about the server rather than the recording — server cannot calculate when the deployment has no Anthropic key and nothing can be calculated on it at all, no automatic calculation when the key is there and only the picking is switched off, which leaves Prefer working.
What it costs
The service here is free. Recording, calculating, replaying, as many scenarios as you like, and none of them expires. There is no card, no balance and no fee. Your recordings are stored on our server in Germany, and the page on security and privacy says exactly what that means.
That last sentence is what we sell. A recording holds the request you typed, every tool call, and everything those tools returned — file contents, query rows, whatever a browser fetched. Plenty of companies cannot keep that on somebody else’s server, whatever the terms say. For them there is BaseInst In-house: the same service as a container you run inside your own network, at $5 per person per month. A person is someone with an account on your instance, however many machines they record from. There is no minimum, and a 30-day trial key needs no card. If you would rather not run it yourself, we will host a separate instance for you under your own address, at the same price.
One thing to be exact about, because it is the part people assume away: an
in-house instance still sends data to Anthropic, on your own
key and under your own account. The whole recording when it calculates a
scenario; a short excerpt when it writes a recording’s title; the new request
and the scenario’s parameters when a replay works out what to pass. By default
the only address the instance connects to is api.anthropic.com, on
your key. Everything else — your mail relay, sign-in with Google, licence
renewal, certificates — is something you switch on.
To talk about running it in-house, write to support@bi2202.com.
Reading the numbers honestly
Lead with verified
It is the smaller number and the defensible one. If you need the larger one for a slide, say which half is projected.
Check the baseline's sample count
Before quoting a saving. One run is a guess, and every saving on that row is measured against it.
Believe a negative
A scenario flagged as not paying off is costing you money on its recent runs. The two useful responses are to retire it (delete the recording) or to replace it by preferring a different recording; both beat looking away.
How to record
Everything starts with a recorded run. The recorder is
BaseInstRunner — bir — which lives in its own
repository, BaseInstRunnerMCP. Getting
started installs it in about ten minutes.
Three files there are the source of truth for everything below:
README.md (the operator's half),
docs/BaseInstRunner.md (the design), and
docs/calculatedReplayGuide.md (the replay runbook in more
detail than this page goes into). Where they disagree with this page, they
are right — they sit next to the code.
Getting started — install the runner
The beginner's path: one computer, Claude Code, and the runner installed from npm. Allow about ten minutes, most of it waiting for downloads. Nothing here needs a copy of the runner's source code. Signed in to the console, the same six steps are on its Set up the runner page, with your account filled in and a line that turns green when your first recording arrives.
Five things must be true. The steps below do none of them for you.
- You have a BaseInst account. Register at bi2202.com, open the link in the verification email, and sign in once. Stay signed in — step 4 approves the runner from that same browser session, and that is how the runner learns which account it belongs to. There is no account name or identifier to type anywhere.
- Claude Code is installed and starts when you type
claude. The runner wires itself into Claude Code's settings; it does not install Claude Code. - Something to record. The runner records MCP tool calls, so it needs a project with at least one MCP server — a
.mcp.jsonin the project folder, or servers listed in~/.claude.json. If you do not have one, that is fine: step 2 builds you a small, real one in about a minute. You do not need to go and find a project first. - Node 20 or newer. Type
node -vin a terminal.v20.or higher is fine. Anything lower, or an error, means: install the LTS build from nodejs.org, close the terminal, open a new one, and check again. - On Windows, use PowerShell — not
cmd.exe. Every Windows command on this page is PowerShell. Incmd,#does not start a comment (it is passed to the program, sonpm install … # notefails withEINVALIDTAGNAME) and$env:VAR = "…"is not valid at all. Open Windows PowerShell or Terminal from the Start menu.
Install the runner
Two lines. The first installs the runner. The second gives it the address of BaseInst, once, for every terminal you open from now on.
npm install -g @basein/runner setx BIR_AUTH_URL https://api.bi2202.comnpm install -g @basein/runner echo 'export BIR_AUTH_URL=https://api.bi2202.com' >> ~/.zprofile
You should see one line from the first command, added 1 package. The second printsSUCCESSon Windows and nothing on macOS. That is the whole install.Then close the terminal and open a new one before going on. The address only reaches a terminal opened after it was set.
This puts four commands on your PATH:
bir,bir-hooks,bir-proxyandbir-scenario. You only ever type the first two.Windows: if this says "running scripts is disabled on this system", PowerShell is blocking npm's own launcher. Allow scripts for your account with
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, answerY, and run the install again.Make something to record
The runner records MCP tool calls, so it needs a project with at least one MCP server in it. If you already have one — a
.mcp.jsonin the folder, or servers listed in~/.claude.json— use that project and go on to the next step.If you do not have one, build a small one now. It takes a minute, needs nothing but Node, and gives you a task worth recording rather than a toy that records nothing.
mkdir fleet-demo cd fleet-demoSave this into it as
fleet-server.js:#!/usr/bin/env node /** * A tiny, real MCP server over stdio — the thing `bir-proxy` will wrap. * * Two tools, chosen so a scenario built from them has to thread state: * list_devices(fleet) -> the fleet's devices, with risk scores * device_detail(id) -> one device's detail * * Step 2 of a scenario can only compute its `id` argument from step 1's output, * which is exactly what `respParams` threading is for. */ const FLEETS = { "eu-west-1": [ { id: "dev_88f1", name: "edge-01", risk: 0.94, firmware: "3.1.2" }, { id: "dev_2c30", name: "edge-02", risk: 0.31, firmware: "3.2.0" }, { id: "dev_9a05", name: "edge-03", risk: 0.62, firmware: "3.1.9" }, ], "us-east-2": [ { id: "dev_4411", name: "core-01", risk: 0.77, firmware: "2.9.4" }, { id: "dev_7b22", name: "core-02", risk: 0.12, firmware: "3.2.0" }, ], }; const TOOLS = [ { name: "list_devices", description: "List the devices in a fleet, with their risk scores.", inputSchema: { type: "object", properties: { fleet: { type: "string", description: "Fleet id, e.g. eu-west-1" } }, required: ["fleet"], }, }, { name: "device_detail", description: "Full detail for one device.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Device id, e.g. dev_88f1" } }, required: ["id"], }, }, ]; function callTool(name, args) { if (name === "list_devices") { const devices = FLEETS[args.fleet] ?? []; const worst = [...devices].sort((a, b) => b.risk - a.risk)[0]; return { fleet: args.fleet, count: devices.length, devices, worstDeviceId: worst ? worst.id : null, }; } if (name === "device_detail") { for (const devices of Object.values(FLEETS)) { const found = devices.find((d) => d.id === args.id); if (found) return { ...found, lastSeen: "2026-08-31T09:12:00Z", status: "online" }; } return { error: `unknown device: ${args.id}` }; } throw new Error(`unknown tool: ${name}`); } // ── minimal MCP stdio plumbing (newline-delimited JSON-RPC) ───────────────── let buf = ""; process.stdin.on("data", (chunk) => { buf += chunk.toString("utf8"); let nl; while ((nl = buf.indexOf("\n")) >= 0) { const line = buf.slice(0, nl).trim(); buf = buf.slice(nl + 1); if (line) handle(JSON.parse(line)); } }); const send = (msg) => process.stdout.write(JSON.stringify(msg) + "\n"); const reply = (id, result) => send({ jsonrpc: "2.0", id, result }); function handle(msg) { const { id, method, params } = msg; if (id === undefined || id === null) return; // notification switch (method) { case "initialize": return reply(id, { protocolVersion: params?.protocolVersion ?? "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "fleet", version: "1.0.0" }, }); case "tools/list": return reply(id, { tools: TOOLS }); case "ping": return reply(id, {}); case "tools/call": { try { const out = callTool(params.name, params.arguments ?? {}); // A normal MCP result: a text block holding JSON. return reply(id, { content: [{ type: "text", text: JSON.stringify(out) }], isError: false }); } catch (err) { return reply(id, { content: [{ type: "text", text: String(err.message) }], isError: true }); } } default: return send({ jsonrpc: "2.0", id, error: { code: -32601, message: `no method ${method}` } }); } } // Diagnostics go to stderr. A stray byte on stdout corrupts the JSON-RPC stream. process.stderr.write("fleet-server: ready\n");That is a real MCP server, not a mock: 111 lines, no dependencies, speaking newline-delimited JSON-RPC over stdio exactly as any other MCP server does. It holds two fleets of pretend devices and offers two tools:
list_devices(fleet) → the fleet's devices, with risk scores device_detail(id) → one device's detailTwo tools chosen for one reason: step 2 of a scenario can only work out its
idargument from step 1's output. That makes it the smallest honest test of whether scenario calculation really works, rather than the smallest test that passes.Now tell Claude Code the server is there. Save this as
.mcp.jsonin the same folder:{ "mcpServers": { "fleet": { "command": "node", "args": ["fleet-server.js"] } } }You should end up with those two files and nothing else. Do not start the server yourself — Claude Code launches it when it needs it, and after the next step
birsits in between.Wrap the project
In a terminal opened after step 1, go to the project folder and run one command:
cd path\to\your\project bir install --globalcd ~/path/to/your/project bir install --globalA healthy run ends like this:
+ chrome-devtools -> bir-proxy (project scope, upstream: npx) + hooks -> .../.claude/settings.json Wrapped 1 server.bir installrewrites the project's MCP entries in place — every server keeps its name, so nothing the model sees changes — and wires Claude Code's hooks. It keeps a copy of every file it touches, andbir uninstallputs them back byte for byte. If it printsNo MCP servers found for this directory. Nothing to wrap., you are in a folder with no MCP servers configured — go back to step 2 and build the demo one, orcdto a project that already has them.
The same command on a project wrapped once before, so fleetreads= already wrappedand the count is 0; on a first run that line starts with+and the count is 1. The two lines after Next: are the point — runbir-hooks, or only tool calls are recorded.Sign in
bir loginThis opens your browser at bi2202.com/activate and shows a short code in the terminal, like
FQSB-JCNQ. Check that the code on the page matches the one in your terminal, approve it, and the terminal finishes by itself. You never type a password into the terminal. The code is good for ten minutes; if it expires, runbir loginagain.This is the moment the runner is tied to your account. There is no account name or identifier to type anywhere: whichever account is signed in in the browser that presses Approve is the account this terminal signs in as. The page says so, under Will sign in as, and the terminal confirms it with
Signed in to … as you@example.com.From then on every recording from this computer goes to that account, until you runbir logout. If the page names the wrong account, press Deny, sign out of the site, sign in as the right one, and runbir loginagain.Prefer no browser step? Signed in to the console, its Set up the runner page makes a one-time setup token that is already tied to your account. Paste it within ten minutes, in the project folder:
bir login --token <the token from the page>It needs runner 0.2.1 or newer, and it is used once.
On a machine with no browser — a server over SSH, a container — use
bir login --no-browser. It prints the address and the code instead of opening anything, so you can approve it from your laptop.You are not asked again; the session is cached under
~/.baseinstrunner, andbir logoutrevokes it.
The code in the terminal and the code on the page are the same — that is the check. Press Approve and the terminal finishes by itself. The blurred line is the machine's public address, shown so you can tell your own request from someone else's; if you did not just run bir login, press Deny.Record
Two terminals, both in the project folder. That is how the two halves of the recorder find each other; a different folder in one of them means your prompts are not recorded.
What the recorder sends, and what it strips out first, is under Security and privacy, section Five.
Terminal A — leave this running the whole time:
bir-hooks
Three lines, then it waits: signed in, replay armed for this folder, listening for the session. Leave the window open. The session itself looks like the run pictured under Record, in section One. Terminal B — your normal work, now recorded:
claudeIf you built the demo in step 2, ask for "check the eu-west-1 fleet and tell me which device is most at risk". In the sample data the answer is
dev_88f1—edge-01, risk 0.94, on firmware 3.1.2: two tool calls, plus the model's turns around them.Otherwise, give the agent a real task — one that takes at least four tool calls. A run with three or fewer is kept but not listed, because there is no plan worth reusing in a couple of calls. When the run finishes, terminal A prints a line like:
[bir] … run.finish run=run_… steps=14 durationMs=48210 lossy=false recorded=trueNow open Recordings at bi2202.com. The run is there, with its title, its step count and what it cost. The demo task is a small one and sits near the four-step minimum — if it does not appear, that is why: ask a second thing in the same session ("and check us-east-2 as well") and it will clear the threshold comfortably. When you are done for the day, Ctrl+C in terminal A. It does not start itself after a reboot — start it again next time, in the project folder.
Check it
With
bir-hooksstill running, in a third terminal in the project folder:bir doctorThis is the one command worth remembering. It does not read a settings file and tell you what should happen; it asks the running system what is happening, and says so loudly when something is broken.
bir statusshows the configuration without needing anything running.
Healthy: bound (Tier 1)andRecording: yes. The one problem listed only says that no agent session was open at that moment; startclaudeand it goes away.
Not healthy: not runningandstandalone (Tier 2)meanbir-hooksis not running in this folder. Tool calls would be recorded, your prompt would not, and nothing could ever match.
If something is wrong
| What you see | What it means |
|---|---|
bir is not recognised / bir: command not found | The terminal predates the install. Close it and open a new one. |
| running scripts is disabled on this system (Windows) | PowerShell is blocking npm's launcher. Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, then try again. |
BIR_AUTH_URL not set — nothing is recorded in bir status | The address from step 1 has not reached this terminal. Close it and open a new one. If a terminal inside an editor still says this on macOS, add the same export line to ~/.zshrc as well; on Linux, or in bash, use ~/.bashrc instead of ~/.zprofile. |
not signed in — run bir login | Step 4 was skipped, or the session expired. Run bir login again. |
| The run was recorded, but it is not in your Recordings | The runner is signed in as a different account: whichever one pressed Approve in step 4. Run bir logout, sign in to the site as the right account, and run bir login again. |
No MCP servers found for this directory | This folder has no .mcp.json and no servers in ~/.claude.json. Build the demo server in step 2, or cd to a project that already has one. |
| Tools are recorded, but not what you typed | bir-hooks is not running, or is running in a different folder. Stop it, cd to the project folder, start it again. |
| The run finished but is not in Recordings | Three tool calls or fewer. Give the agent a bigger task. |
| Anything else | Run bir doctor and keep the output. It names the broken link in the chain. |
Undoing it. In the project folder, bir uninstall
puts every file it touched back exactly as it was;
npm rm -g @basein/runner removes the software as well.
Guide
What the recorder is
bir is an MCP proxy. It registers under your
existing MCP server keys, so the model still emits
mcp__chrome-devtools__navigate_page and still gets exactly what
chrome-devtools-mcp would have returned. The difference is that
bir executed the call on the client's behalf, and wrote down
what happened.
Interception is a configuration fact, not a prompting one. The model is never asked to cooperate, and tool descriptions are never edited to steer its choices.
An MCP server sees only MCP traffic addressed to it. Bash, Read, Edit, Grep are not MCP. Neither is your prompt, the model's reasoning, or the final answer.
Which is why there are two halves. bir-proxy records MCP calls;
bir-hooks is a hook receiver that records everything else and
owns the run.
The two tiers
| Tier | When | What ends up in the recording |
|---|---|---|
| 1 — Bound | bir-hooks is discoverable for this working directory | Built-ins and MCP, in one ordered step stream, with the prompt and the final answer |
| 2 — Standalone | No control server found within five seconds | MCP calls only. No prompt, no final answer, no built-in steps |
Tier 2 is not a degraded bug — it is the honest ceiling of what a proxy alone can observe, and a run records which tier produced it so nothing downstream mistakes a partial trace for a complete one. For anything beyond raw recording you want Tier 1: a match is a match on the prompt, and a standalone proxy never sees one.
Install
The beginner's path is Getting started above:
npm install -g @basein/runner, set BIR_AUTH_URL,
bir install --global, bir login. Working on the
runner itself? From a checkout of BaseInstRunnerMCP, build it
and point the install at the checkout instead of the published package:
npm install; npm run build
$env:BIR_AUTH_URL = "https://api.bi2202.com"
node dist/bin/bir.js install --local # this checkout's proxy, not npx
node dist/bin/bir.js login
npm install && npm run build
export BIR_AUTH_URL=https://api.bi2202.com
node dist/bin/bir.js install --local # this checkout's proxy, not npx
node dist/bin/bir.js login
For a fleet of machines, the runner repository ships one-command installers
in scripts/ that do the whole of Getting started in one go; see
its docs/quickstart.md and docs/installRun.md.
bir install rewrites your MCP entries in place,
keeping every key — so mcp__<server>__<tool> names
never change — and wires Claude Code's hooks. It stashes the original text of
every file first, and bir uninstall puts it back byte for byte.
Terminal A — leave running:
bir-hooks
Terminal B — your session, now recorded:
claude
And in a third terminal, whenever you want to know where you stand:
bir status # what is wrapped, and which config scope won
bir doctor # is it working right now — exits non-zero if not
doctor is the one to trust. status tells you what is
configured; doctor tells you whether a proxy can currently reach
a control server — which is the difference between Tier 1 and Tier 2.
For other MCP clients, nothing in bir knows where Cursor or Codex
keep their settings, and it does not guess:
bir install --config <path>, or
bir wrap --server-name my-server -- node my-server.js to print an
entry to paste. Those clients have no hooks, so they record at Tier 2.
Record a run worth keeping
Work normally. Two server-side facts decide whether the run becomes reusable, and both bite quietly.
A run needs more than three tool actions. Below that it is not embedded, does not enter the similarity pool, does not appear in the list, and is eventually pruned. A two-tool run will never match anything however often you repeat it.
The prompt is what gets embedded — the text you typed, not the transcript and not the answer. Write the first prompt of a reusable task as though someone else will have to phrase it again next month, because that is exactly what has to happen.
Watch it land on the audit stream:
[bir] … run.start run=run_… sess=birsess_… tier=bound
[bir] … run.finish run=run_… steps=14 durationMs=48210 lossy=false recorded=true
recorded=false means the service declined the run — usually
because it matched something you already have, which is similarity detection
working. Every decision is one key=value line on
stderr, never stdout, which belongs to the host's JSON-RPC
stream. There is no log file to configure; persistence is the shell's job:
cmd /c "bir-hooks 2>&1" | Tee-Object -Append -FilePath $HOME\.baseinstrunner\audit.log
bir-hooks 2>&1 | tee -a ~/.baseinstrunner/audit.log
Do not simplify the Windows one to bir-hooks 2>&1 | ….
Windows PowerShell wraps every stderr line from a native program in a
NativeCommandError record, so the log fills with
At line:1 char:1 blocks instead of bir's
key=value lines. Letting cmd merge the two streams
first means PowerShell only ever sees stdout, and both the console and the
file stay clean.
Calculate, then check
bir scenario list # what you have
bir scenario calc run_5f3a… # start the background job
bir scenario show run_5f3a… # poll until state leaves `calculating`
failed carries an error field; fix what it points at
and recalculate with --force. Or do all of this from the console's
recording detail page, which is the same API with the results laid out — with
one exception: the console has no Recalculate button, so a failed calculation is
run again there by preferring the recording.
Or do nothing. Once a prompt has come back three times
(SCENARIO_AUTO_MIN_HITS) and the run it keeps matching passed end
to end — it finished without an error and no tool call in it failed — the
service calculates the recording with the most to save by itself, and the
console marks it auto. A run has to have been quiet for ten
minutes first (SCENARIO_AUTO_SETTLE_MS): a session that has
just finished is often still sending steps. It never deletes anything and
never retries a failed calculation, and it works through one calculation at
a time per account.
Or say which ones to leave alone: press Disable
calculation on a recording's page and the picker never chooses it,
however often its prompt comes back — though you can still calculate it
yourself. Or say which one you want: press ☆ Prefer
this recording on its page. It is calculated straight away and takes
nothing from anything else you hold. SCENARIO_AUTO_CALCULATE=false
leaves calculation entirely to the button.
Dry-replay before you trust it. It derives parameters from a new prompt and evaluates the stored logic against the recorded outputs — no real tool is called, nothing is billed, and it is the cheapest way to find out that step 3 computes an argument you did not expect.
bir scenario replay scn_9c1b… --prompt "check the us-east-2 fleet" --dry
Turning replay on
Recording is the default and the whole of what runs out of the box. Replay takes two switches, deliberately:
bir install --replay # adds the `bir` MCP server
$env:BIR_REPLAY = "1"; bir-hooks # and this is the actual switch
bir install --replay # adds the `bir` MCP server
BIR_REPLAY=1 bir-hooks # and this is the actual switch
A scenario is a pre-approved list of tool calls with computed
arguments. A steered step is answered
permissionDecision: "allow", so a pinned Bash or
Edit runs without the prompt you would otherwise have seen. A
directly executed step never reaches the permission system at all.
Set BIR_REPLAY_ALLOW_SERVERS to the servers you are content to
have called unattended. Leaving it unset means every wrapped server
is eligible, and that is not the setting you want.
Configuration
| Variable | Effect |
|---|---|
BIR_AUTH_URL | The BaseInst service. Unset means nothing is recorded — everything else still works |
BIR_CONTROL_PORT | Preferred control-server port (default 53411) |
BIR_CORRELATION_DECISION | allow (default) or ask. Correlation auto-approves wrapped MCP calls; ask keeps the prompt, at the cost of lossier matching |
BIR_HOME | Where state lives (default ~/.baseinstrunner) |
BIR_VERBOSE=1 | Per-message detail on stderr |
BIR_REPLAY=1 | Enable replay. Nothing else about replay matters until this is set |
BIR_REPLAY_ALLOW_SERVERS | Server keys eligible for direct execution. Set this |
BIR_MIN_STEER_SIMILARITY | Minimum match similarity to replay (default 0.92) |
ANTHROPIC_API_KEY | Enables parameter derivation at replay time. Without it, replay uses the scenario's recorded sample values — free, and often still correct |
When it goes wrong
The governing rule is that a host session never fails because of BaseInstRunner. Every failure degrades to passthrough or to not-recording.
| Symptom | Cause | Fix |
|---|---|---|
| Nothing appears in the console | BIR_AUTH_URL unset, or not signed in | bir login; check bir doctor |
tier: standalone | bir-hooks not running, or not for this cwd | Start it in the project directory |
| Recorded, but not in the list | Three actions or fewer | Nothing to fix — below the threshold on purpose |
recorded=false | The service matched it to an existing run | Working as intended |
| No prompt ever matches | Similarity detection off, or the prompts really are different | Check SIMILARITY_DETECTION_ENABLED; re-read the prompt you wrote |
| Calculate returns 503 | No Anthropic key on the server | Server-side configuration |
Sample — the fleet demo
A complete run-through against a real MCP server, on your machine, with no
cloud resources and no money spent. About ten minutes. It uses the
fleet-demo folder you built in step 2 of
Getting started: a tiny but genuine MCP server over
stdio, with two tools.
list_devices(fleet) → the fleet's devices, with risk scores
device_detail(id) → one device's detail
Step 2 can only compute its id argument from step 1's
output. That makes this the smallest honest test of whether scenario
calculation actually works, rather than the smallest test that passes.
The task: "check the eu-west-1 fleet and tell me which device is most at
risk" — which resolves, in the sample data, to dev_88f1:
edge-01, risk 0.94, firmware 3.1.2.
Set up the project
This is the
fleet-demofolder from step 2. If you have not built it yet, go back and do that first — two files and a minute.BIR_AUTH_URLis already set permanently, from step 1, so there is nothing to export here.cd fleet-demo bir install --global --replay bir login--replayadds the scenario server now, so step 7 needs no second install.--globalinvokes the installed package by absolute path instead of going throughnpx— no registry round trip each time a server starts, and one less thing to go wrong.bir installrewrites.mcp.jsonso thefleetserver runs throughbir-proxy, and writes.claude/settings.jsonfor the hooks; both are generated for your machine, absolute paths and control-server port included. It keeps a copy of everything it touches, andbir uninstallputs it all back byte for byte.Start the control server
In its own terminal, from the same directory:
bir-hooks. Watch fortier=bound. If it saysstandalone, the control server and the session disagree about either the working directory or the port — the only two things discovery matches on.Record the run
In a second terminal, in the same folder, start Claude Code and ask it the task:
claudeAsk for "check the eu-west-1 fleet and tell me which device is most at risk". The two tool calls go past in the control-server terminal, then a
run.finishline.Look at it in the console
Recordings holds the run with its generated title, step count and cost. This is the baseline: what the task costs when the agent does it.
Calculate the scenario — and read it
bir scenario list # find the run id bir scenario calc run_… bir scenario show run_…Read what comes back before going further. You are looking for three things:
- an intent that describes the task, not the specific fleet;
fleetas a parameter witheu-west-1as its sample value — if the region is baked into a step's logic, the scenario only ever answers one question;- step 2's logic reading step 1's output to pick the highest-risk id. A scenario that hard-codes
dev_88f1looks identical on a green run and is wrong the moment the data moves.
Dry-replay against a different fleet
bir scenario replay scn_… --prompt "check the us-east-2 fleet" --dryThe derived parameter should be
us-east-2. Since a dry run evaluates against recorded outputs, step 2 still picks from the eu-west-1 devices — correct behaviour, and the reason those lines are markedResult (recorded):. What you are checking is the parameter derivation and the step logic, not the answer.Replay it for real
Stop
bir-hookswith Ctrl+C and start it again with replay turned on:$env:BIR_REPLAY = "1"; $env:BIR_REPLAY_ALLOW_SERVERS = "fleet" bir-hooksBIR_REPLAY=1 BIR_REPLAY_ALLOW_SERVERS=fleet bir-hooksThen in the other terminal start
claudeagain and ask for "check the us-east-2 fleet and tell me which device is most at risk".Now the tools are really called.
us-east-2's highest-risk device isdev_4411—core-01, risk 0.77. That is the proof the scenario computed the answer rather than remembering it.Read the ledger
Back in the console, check three things: Saved $ is populated and the baseline's ⓘ says how many runs it is drawn from (on a first pass, one — and one sample is a guess); the headline moved under verified, not projected; and the scenario is not flagged as not paying off. Saved %/run is the same verdict per run at a glance — a green bar is a cut, a red one is a loss. On a two-step task against a local server the arithmetic is tight — on real work with real latency it is not.
The second sample. There is another demo of the same shape
with a longer chain — list_services,
service_metrics, error_logs,
recent_deploys, dependency_graph. It lives in the
runner repository, which is not public, so it is not something you can fetch
today; the point of it survives without it. Extend the fleet server
above until it has five tools, at least one of them needing an
argument computed from an earlier step's output. That is worth doing once the
fleet demo works, because a five-step scenario is where the interesting
failures start: a step whose logic reads the wrong earlier output
looks fine on two steps and falls apart on five.
Videos
One of the four is recorded. Video 2, the install, was filmed on 2026-09-12 and is embedded at the top of Getting started — 7 min 54 s, silent, captioned. The other three are still shot lists and narration, written so somebody can sit down and record them without having to invent the structure first.
Common conventions, so the four feel like one set:
- 1280×720, terminal at a size legible on a phone.
- Light theme in the console — it is the default and it screenshots better.
- No music. Voice-over or captions, not both.
- Real data from the fleet sample. Never a mock-up of a screen that does not exist.
- Cut every wait longer than two seconds. Calculation takes about ten; nobody needs to watch it.
| Time | Shot | Narration |
|---|---|---|
| 0:00 | Terminal: the same task run twice, cost visible both times | "Your agent solved this yesterday. Today it paid full price to solve it again." |
| 0:15 | The record → calculate → replay diagram | "BaseInst records the run, derives the plan, and runs the plan next time." |
| 0:35 | Console: the recording, then its scenario — intent and parameters | "A transcript says what the agent did. A scenario says what it was doing." |
| 0:55 | Dashboard, verified tile, ⓘ open | "And it measures what that saved. The number it leads with is the one it watched move." |
| 1:15 | Recordings list, the savings column | "It is free to use. What we sell is running the whole thing inside your own network, when the recordings cannot leave it." |
The guide, executed. One terminal, one browser window.
Recorded — the film in Getting started. It follows this list loosely: the demo project and one run of the task by hand come first, then the install, and it runs on past the first recording to the scenario being calculated and the saving being verified. It is silent, with the step being done named in the corner, so it was cut without narration; the shot list below is kept as written, for a retake.
npm install -g @basein/runner,bir install --global,bir login— say whatinstallrewrites, and thatuninstallreverses it byte for byte.bir-hooksin a second terminal. Point attier=boundand say what Tier 2 would mean.bir doctor. Explain why this is the command to trust, notstatus.- Run the fleet task. Watch
run.startandrun.finishgo past. - Console → Recordings → the row. Name the columns: Steps, LLM $/run, and the baseline's sample count.
- Close on the threshold: "fewer than four actions and it is not kept — there is no plan in a single call."
The video that earns its place, because this is where people get burned.
scenario calc, thenscenario show. Read the intent aloud.- Stop on the parameters.
fleet: eu-west-1— a sample value, not a constant. - Stop on step 2's logic. Show it reading step 1's output. Then show, by hand, what a hard-coded
dev_88f1would look like: identical on this run, wrong on the next. - Dry replay against
us-east-2. ExplainResult (recorded):and why a dry run still shows eu-west-1 devices. - Real replay.
dev_4411. "It computed that. It did not remember it." - Close on the ledger row and the not-paying-off flag: "a scenario can cost you money, and this is where it says so."
- The sidebar: the sections, and what each one is for.
- ⌘K, including the recordings-search row. Say plainly that it does not search everything.
- Recordings: verified versus projected, with the ⓘ open. The segment worth over-explaining.
- The Saved %/run bars: read one row's two $/run figures against its percentage, then show a red one.
autoin the origin column, and the ★ Preferred and ⊘ Switched off badges under two titles: a row the service calculated by itself, one the user preferred over it, and one they switched off.- The delete confirmation, naming the savings that will leave the headline.
They live in videos/ beside this page, deployed with it, and are
embedded beside the section each one covers; the console embeds them from this
address, VITE_DOCS_URL. The install film is there. Until the others
exist, their written sections stand on their own — which is why they were
written to.
Security and privacy
What BaseInst sees, where it goes, how long it stays, and what it does without asking you. This section describes what the software does today, in plain words.
The formal version is the Privacy Policy on the website; where the two differ, the policy is the one that counts. Terms of service will be published separately.
The runner records what your agent did: the request you typed, every tool call with its arguments and results, the model's reasoning before each call, the final answer, and what it all cost. It strips the secrets it recognises, then sends the rest over HTTPS to your BaseInst account, where only you can see it. Working out a plan from a recording sends that recording to Anthropic. Nothing is sold, nothing is used for advertising, and no analytics run anywhere. You can delete a recording at any time, and everything derived from it goes with it.
What is recorded
One recording is one run of your agent: from the request you typed to the answer it gave. For each run the runner keeps:
- The request you typed, word for word.
- Every tool call: the tool's name, the arguments the agent sent, and what came back, or the error if it failed. Calls to the project's MCP servers pass through the runner itself. The agent's built-in tools, such as reading a file or running a shell command, are seen through Claude Code's hooks, which is why
bir-hookshas to be running. - The model's reasoning written just before each tool call. The runner reads it from the session's transcript file on your own machine and attaches it to the step.
- The final answer, the model used, token counts, timings and the cost.
- Run details: which recorder made it, whether the trace is complete or MCP-only, the host application, the names of the wrapped servers, whether any steps were dropped, the session id, and the path of the project folder, which can include your username. An MCP-only run also carries your computer's name.
A recording can therefore contain anything a tool read or returned: the contents of a file the agent opened, the rows a query returned, a page a browser tool fetched. Record only what you are comfortable storing.
What is not recorded. Your password: you never type it into the terminal, because bir login opens the browser, where you approve a code, or takes a one-time setup token made in the console. Anything the agent did not do: the runner does not scan your disk or watch other programs, and sees only what passed through a wrapped server or a hook, in the folder where bir-hooks runs. Sessions in other folders: each project is recorded only while its own bir-hooks is running there.
What is stripped before sending. Redaction runs on your machine, on the copy that is about to be sent, before it is even queued:
- By name. A value stored under a name that looks secret is replaced with
[redacted], whatever it is:token,secret,password,api_key,authorization,cookie,session_id,credential,private_key,access_key,client_secretand their variants. - Every value under an
envblock, because MCP server configurations put credentials there under any name at all. - By shape, wherever it appears, including inside free text: bearer tokens, JWTs,
sk-keys of the OpenAI and Anthropic kind, GitHub, Slack and Google API keys, and AWS access key ids. - Size. A single string is cut at 8 KB and a step at 64 KB. A file a tool returned in full arrives as its first 8 KB.
This is pattern matching, not understanding. A secret stored under an unusual name, or shaped like ordinary text, goes through. Treat a recording the way you would treat a log file from your own machine, and do not wrap a tool that handles data you are not allowed to keep.
Where it goes
| Who | What they receive | When |
|---|---|---|
| BaseInst's server | The recording, as described above, over HTTPS | Every recorded run |
| Anthropic | The whole recording: reasoning, every tool input and output, and the final answer | When a scenario is calculated, by you or automatically |
| Anthropic | The first 1,200 characters of the request, the names of the tools used, and a short excerpt of the first reasoning. Tool outputs and the final answer are deliberately left out | When a run finishes, to write its title |
| Anthropic, from your own machine | The new request, the scenario's intent and its parameter samples | On a replay, only if you set ANTHROPIC_API_KEY on the runner. It uses your key, not ours |
| PayPal | The invoice reference, the amount and the service's name. You pay on PayPal's own pages; BaseInst never sees a card number | Only if you buy an in-house licence. Nothing is charged for using the service here |
| That you signed in here; BaseInst receives your Google id, email address and name | Only if you sign in with Google | |
| Resend | Your email address and the one-time link | Verification and password-reset emails |
| Google Fonts | The IP address and browser of whoever opens the page | This handbook site only. The console loads nothing from anyone else |
- In transit. The runner talks to
api.bi2202.comover HTTPS only, and refuses to follow a redirect to a different site while carrying your token. - At rest. Recordings are stored in a database on one server, rented from Hetzner in Germany. The application does not encrypt them itself; they are protected by the server and its disk.
- Who can read them. You, signed in to your own account. There is no administrator screen that shows another account's recordings. The person who operates the server can read the database and the logs directly.
- Recognising a repeated request is done with a small open-source model that runs on BaseInst's own server. Nothing leaves for that.
- No analytics, no tracking pixels, no advertising scripts, in the console, the runner or this handbook.
How long it stays, and how to delete it
- A recording stays until you delete it, from its own page in the console. Deleting it also deletes its steps, its fingerprint, its scenario and its replay history, and the savings it booked leave your totals with it.
- Very short runs, a couple of tool calls, that were never calculated and never matched are pruned automatically.
- A scenario does not expire. It stays with its recording, and steers a matching prompt, until the recording is deleted or its owner switches calculation and replay off.
- Sign-in records keep the time, the method, the IP address and the browser or runner for every sign-in attempt, successful or not. They are kept for as long as the service runs, and your own are shown on your Sign-in history page. A
bir loginapproval shows the runner's address on the approval page and is pruned a day later; a setup token made in the console is the same kind of record. - Sessions last thirty days from their last use. An access token lasts a day, a verification link a day, a password-reset link an hour, and a sign-in code ten minutes.
- Closing your account, from Account settings, removes your profile, your password, your Google link and your sessions. It does not yet remove your recordings, scenarios, replay history or sign-in records. Delete your recordings first, and write to us for the rest.
- On your own machine the runner keeps your session in
~/.baseinstrunner/credentials.json, readable by your user only, and a copy of every configuration file it changed, so thatbir uninstallcan put them back.bir logoutrevokes the session. - Server logs hold, for every request, the method, path, status, duration, IP address and, when signed in, your user id. Secrets are masked before they are written. Backups, when they are taken, can hold deleted data until they expire.
What runs without asking you
Three things happen without a prompt, and each is a switch you own.
Wrapped tool calls are approved automatically while recording
To attach a call id to a tool call, the runner answers Claude Code's permission hook with allow. So a call to a wrapped MCP server that you would otherwise have been asked about is not asked about. Start the recorder with
BIR_CORRELATION_DECISION=askto keep the prompts, at the cost of less exact matching.Replay is off until you turn it on
Nothing is replayed unless
bir-hooksruns withBIR_REPLAY=1. When it is on, a steered step is approved for the agent, and a step the runner executes directly never reaches the permission system at all. Name the servers you are content to have called that way inBIR_REPLAY_ALLOW_SERVERS, and keep anything destructive, or anything that must not run unattended, off that list.Calculation can start by itself
Once the same request has come back three times, the service works out the plan for the best candidate without anyone pressing the button, which sends that recording to Anthropic as described above. Every recording's page has a switch that turns calculation and replay off for that recording.
How the service is protected
- Passwords are stored as argon2id hashes. Session, verification and reset tokens are random, stored only as hashes, and rotated when used.
- An email address must be verified before it can sign in. Sign-in and account routes are rate-limited per address.
- Everything is served over TLS, with strict transport security. The console loads scripts from its own origin only, and the API accepts browser requests from the console's origin only.
- The API runs as an unprivileged user in a hardened system service that can write to one directory.
- The runner package has no third-party dependencies at run time, so nothing else is loaded into your agent's process tree.
Your side of it
- You choose what is recorded: which project, which servers, and when
bir-hooksruns. Do not wrap a tool that returns secrets or personal data you are not allowed to store. - Delete what you no longer need. A recording is as sensitive as the tools it called.
- Keep replay off destructive tools. A replayed scenario is a list of tool calls you recorded, run again with computed arguments. What those calls do is yours.
- On a shared machine, run
bir logoutwhen you finish.
Reporting a security problem
Write to support@bi2202.com, the address on the Privacy Policy. Describe what you found and how to reproduce it, and please do not post it publicly before we have replied.
This section describes the runner at version 0.2.0 and the service as deployed on 2026-09-11. It is a statement of practice, not a contract; the Privacy Policy governs, and terms of service will be published separately. When the software changes in a way that matters here, this section changes with it.