OpenInterpretability
  • Ekbasis
  • Guide
  • Pricing
  • Research
  • Lab
  • Tools
  • Notes
  • Registry
  • Manifesto
Sign inGet API keyAPI key
OpenInterpretability

Ekbasis API: know what an action will do before your agent runs it. From an independent lab for AI-agent safety. Open weights, Apache-2.0.

Get your API key
Ekbasis API
  • Console · sign in
  • Getting started
  • Pricing
  • For agents (agents.md)
  • Results and models
  • Client and CLI
  • Cookbook
The lab
  • About the lab
  • Open tools
  • Observatory
  • ProbeBench
  • InterpScore
  • Academy
Research
  • Manifesto
  • Roadmap
  • Papers & posts
  • Docs
Community
  • GitHub org
  • Notebooks
  • SDK (openinterp)
  • HuggingFace
  • Twitter / X
© 2026 OpenInterpretability — Apache-2.0 for code, CC-BY 4.0 for docs.Built in public.
Back to EkbasisGETTING STARTED · FOR FIRST-TIME USERS

How to use Ekbasis

Ekbasis does not chat. You give it a state (what the world looks like now), an action and a few typed questions, and it answers each question with calibrated probabilities, in one forward pass. This page shows how to think about it, how to write a state it can use, and what it really answers.

It is not only for code. It works for anything you can describe in the state — git and the shell, payments, databases, email, calendars, cloud and Kubernetes — and has been measured in 21 domains. See real answers by domain.

Every answer on this page is a real response of Ekbasis-27B-INT4 on the hosted API, recorded on 2026-10-10 (34 calls), unless it is marked as measured in the cookbook.

Building an agent? Point it at openinterp.org/ekbasis/agents.md — the same guide written for language models: exact formats, when to consult, decision rules, failure handling.

What it isThe mental model3 ways to use itWriting a good stateQuestion typesReading the answerExamples by domainStrong and weakCostFAQ

What it is, and what it is not

It is a forecaster

You describe the world as it is now and the action about to happen. It tells you what the world will look like afterwards, as probabilities over answers you defined.

It is not a chatbot

It never writes text. There is no conversation, no "hello", no explanation. Every request is a state plus typed questions; every answer is numbers.

It is not a judge

It does not decide what is allowed or good. It says what will happen; your code (or a person) decides what to do with that, using thresholds you choose.

The closest everyday analogy is a weather forecast: it does not argue with you or tell you to stay home. It says “70% rain”, and you decide about the umbrella. Ekbasis says “99%: this loses uncommitted work”, and your tool decides whether to stop. Because nothing is generated, you pay only for the input tokens it reads.

The mental model

State

The facts now: files, balances, branches, rules, what the screen shows.

+
Action

What is about to happen, usually written into the state (“About to: …”).

+
Questions

Typed, with the possible answers listed: choice, yes/no, score.

Answers

A probability for every option, the pick and its confidence. One pass, no text.

The smallest useful request

request: POST $EKBASIS_URL/v1/systemone
{
  "state": "Checking account available balance: $180.20. About to: transfer $250.00 to a friend.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens with the transfer?",
      "options": [
        "transfer succeeds",
        "transfer fails: insufficient funds"
      ]
    }
  }
}
  • state: the facts and the action, in plain English. The numbers are there.
  • questions: a map of names to questions; you pick the names (here q).
  • type: choice with options: the answer is always one of these.
response (real, 0.94 s, 131 input tokens)
{
  "answers": {
    "q": {
      "type": "choice",
      "choice": "transfer fails: insufficient funds",
      "probabilities": {
        "transfer succeeds": 0.03514484688639641,
        "transfer fails: insufficient funds": 0.964855134487152
      },
      "confidence": 0.964855134487152
    }
  },
  "usage": {
    "input_tokens": 131,
    "output_tokens": 0
  }
}

choice: What happens with the transfer?

  • transfer succeeds3.5%
  • transfer fails: insufficient funds96.5%

confidence 0.965

Read it as: “the transfer fails, 96.5% sure”. The state decided the outcome ($180.20 < $250.00), so the model is confident.

Three ways to use it, fastest first

All three use the same hosted endpoint and key. Get the key in the console (sign in, create a key, buy credits).

a · The git guard, in 60 seconds

The CLI reads your repository with read-only git commands, writes the state for you and asks the trained git questions. You write no state at all.

terminal
# 1. get a key: https://openinterp.org/console → Create API key (then buy credits)
# 2. install the client (Python ≥ 3.9, no dependencies)
pip install ekbasis

# 3. point it at the hosted API
export EKBASIS_URL=https://openinterp.org/api/v1
export EKBASIS_API_KEY=ekb_...          # the key from the console
ekbasis health                         # setup check: free, needs no key

# 4. ask before you run
cd your-repo
ekbasis git-check -- "git reset --hard"
ekbasis git-check -- "git checkout main" && git checkout main   # only runs if exit 0
first line of the output, repo with 3 modified files (measured, cookbook guard battery) · exit 2
Ekbasis: RISKY  (lose uncommitted work: 99%)
exitmeaningdo
0no risk foundrun it
2RISKY: may lose work or file contentstop, read the reason, decide
3cannot foresee (server unreachable, no key, no credits, unreadable repo, parts it cannot evaluate)treat as risky
1usage errorfix the command

Also: ekbasis shell-check -- "rm -r build/" (prototype) and ekbasis preflight -- "sqlite3 app.db < migrations/0012.sql" (which step of a multi-step change fails first). --fail-open turns exit 3 into 0; do not use it for anything destructive.

b · For agents: hook or MCP

Claude Code hook. Before every Bash call that runs git, the hook asks Ekbasis. If the command may lose work, Claude Code asks you to confirm, with the reason. Put EKBASIS_URL and EKBASIS_API_KEY in your shell profile, then add to ~/.claude/settings.json:

~/.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "ekbasis-claude-hook", "timeout": 30 }]
      }
    ]
  }
}

EKBASIS_GUARD_MODE=deny blocks instead of asking; EKBASIS_SHELL_GUARD=1 also checks non-git lines that change files (prototype). On top of the model the hook adds a rule layer for committed work (e.g. git branch -D of a branch with unique commits). When it cannot foresee, it asks.

MCP (any MCP client). Three tools: check_git_commands, predict_consequences (your own rules, state, actions and questions) and preflight_command.

terminal
pip install "ekbasis[mcp]"   # Python ≥ 3.10
claude mcp add --scope user ekbasis \
  -e EKBASIS_URL=https://openinterp.org/api/v1 -e EKBASIS_API_KEY=ekb_... -- ekbasis-mcp

To teach an agent when to ask (and when not to: over-asking trains people to click through), give it the ekbasis-guard skill.

c · Python, for your own app

client.ask(state, questions) sends every question about one state in one request. The helpers write the question dicts for you: yes_no, choice, number, and world_state for the rules / state / actions layout.

python
from ekbasis import Ekbasis, choice, yes_no, world_state

client = Ekbasis("https://openinterp.org/api/v1", api_key="ekb_...")  # or EKBASIS_URL / EKBASIS_API_KEY
state = world_state(
    rules="A card purchase is approved only if the available balance covers it. "
          "A refund counts only once it has arrived.",
    state="Card available balance: $15.00. A $40 refund was issued yesterday; it arrives in 3-5 business days.",
    actions=["Pay a purchase of $18.00 today."],
)
ans = client.ask(state, {
    "approved": yes_no("Is the $18.00 purchase approved?"),
    "balance": choice("What is the available balance afterwards?", ["$15.00", "$55.00", "-$3.00"]),
})
for name, a in ans.items():
    print(name, a.value, round(a.confidence, 3), {k: round(v, 3) for k, v in a.probabilities.items()})
output (real, 0.49 s)
approved False 0.981 {'yes': 0.019, 'no': 0.981}
balance $15.00 0.974 {'$15.00': 0.974, '$55.00': 0.003, '-$3.00': 0.023}

Each answer has .value (True/False or the chosen label), .confidence, .probabilities and, for yes/no, .p_yes. Errors raise CannotJudge (a subclass of ekbasis.EkbasisError): catch it and treat the action as risky. Any language works: it is one JSON POST (see the curl under every example below).

How to write a good state

This is where first answers go wrong. The model knows how actions behave; it does not know your world. Whatever decides the outcome has to be in the text. Three pairs, same question, real answers:

Numbers, not adjectives

Weak state

Vague: 'a bit low'

state · Account balance is a bit low. About to: transfer $250.00 to a friend.

choice: What happens with the transfer?

  • transfer succeeds24.5%
  • transfer fails: insufficient funds75.5%

confidence 0.755

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Account balance is a bit low. About to: transfer $250.00 to a friend.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens with the transfer?",
      "options": [
        "transfer succeeds",
        "transfer fails: insufficient funds"
      ]
    }
  }
}'

Better state

Bank transfer larger than the balance

state · Checking account available balance: $180.20. About to: transfer $250.00 to a friend.

choice: What happens with the transfer?

  • transfer succeeds3.5%
  • transfer fails: insufficient funds96.5%

confidence 0.965

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Checking account available balance: $180.20. About to: transfer $250.00 to a friend.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens with the transfer?",
      "options": [
        "transfer succeeds",
        "transfer fails: insufficient funds"
      ]
    }
  }
}'

Same pick, but 75.5% instead of 96.5%: with “a bit low” it is guessing, and the confidence says so.

Say what is backed up

Weak state

Nothing said about copies

state · About to: rm ~/thesis/draft.docx

choice: What happens to the thesis draft?

  • lost permanently93.6%
  • recoverable from the cloud version history6.4%

confidence 0.936

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "About to: rm ~/thesis/draft.docx",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the thesis draft?",
      "options": [
        "lost permanently",
        "recoverable from the cloud version history"
      ]
    }
  }
}'

Better state

The backup written down

state · File ~/thesis/draft.docx is synced to cloud storage with version history (two-way sync on). About to: rm ~/thesis/draft.docx

choice: What happens to the thesis draft?

  • lost permanently5.3%
  • recoverable from the cloud version history94.7%

confidence 0.947

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "File ~/thesis/draft.docx is synced to cloud storage with version history (two-way sync on). About to: rm ~/thesis/draft.docx",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the thesis draft?",
      "options": [
        "lost permanently",
        "recoverable from the cloud version history"
      ]
    }
  }
}'

Without the fact it assumes the usual case, rm of a file nobody backed up: 93.6% lost. It cannot see your cloud sync until you write it down; with it, 94.7% recoverable.

Write out the hidden rule

Weak state

Hidden rule left out

state · Trip booked as one round-trip ticket: outbound flight Friday, return flight Sunday. About to: cancel the outbound flight on Friday (the traveller will drive there instead).

choice: What happens to the return flight on Sunday?

  • it stays booked59.3%
  • it is cancelled too40.7%

confidence 0.593

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Trip booked as one round-trip ticket: outbound flight Friday, return flight Sunday. About to: cancel the outbound flight on Friday (the traveller will drive there instead).",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the return flight on Sunday?",
      "options": [
        "it stays booked",
        "it is cancelled too"
      ]
    }
  }
}'

Better state

Hidden rule written out

state · Trip booked as one round-trip ticket: outbound flight Friday, return flight Sunday. Airline rule for this fare: if the outbound flight is cancelled or not flown, every later flight on the same ticket is cancelled automatically. About to: cancel the outbound flight on Friday (the traveller will drive there instead).

choice: What happens to the return flight on Sunday?

  • it stays booked3.5%
  • it is cancelled too96.5%

confidence 0.965

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Trip booked as one round-trip ticket: outbound flight Friday, return flight Sunday. Airline rule for this fare: if the outbound flight is cancelled or not flown, every later flight on the same ticket is cancelled automatically. About to: cancel the outbound flight on Friday (the traveller will drive there instead).",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the return flight on Sunday?",
      "options": [
        "it stays booked",
        "it is cancelled too"
      ]
    }
  }
}'

Without the fare rule it leans on the common case at 59.3%, close to a coin flip. With the rule written in the state, 96.5% the other way.

Checklist

Put the numbers in the state

Write "balance: $180.20, transfer: $250.00", not "the balance is a bit low". Amounts, counts, sizes, times and limits are what decide most outcomes.

Say what is irreplaceable, and what is backed up

The model cannot see your disk. "The only copy, never backed up" and "synced with version history" lead to opposite answers for the same rm.

Write out the hidden rules

If the consequence comes from a rule that is not visible on the screen (a fare that cancels the return flight, a folder link that shares subfolders), state the rule. This is the #1 reason a first answer looks wrong.

Send the slice the action touches

3–8 concrete facts beat three pages of context. States over ~32k tokens are refused (HTTP 422); the git client caps itself at 8 files and 12 branches.

Make the options exhaustive and distinct

Each option should be a different outcome that you would act on differently. The probabilities are spread over your options only.

Separate rules, state and actions

The client helper world_state(rules, state, actions) writes the layout the model was trained on. To make a rule count, repeat it right before the question with recap() (the client docs measure the gain).

The question types

A question is {type, instructions, …}. You always define the possible answers; the model spreads its probability over them.

choice · pick one of options (a list) or criteria (label → description)

choice: pick among outcomes

state · The organizer creates a meeting for 9:00 AM New York time (Eastern). An attendee is in San Francisco (Pacific, 3 hours behind).

choice: What time does the San Francisco attendee see?

  • 6:00 AM their local time99.5%
  • 9:00 AM their local time0.5%

confidence 0.995

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "The organizer creates a meeting for 9:00 AM New York time (Eastern). An attendee is in San Francisco (Pacific, 3 hours behind).",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What time does the San Francisco attendee see?",
      "options": [
        "6:00 AM their local time",
        "9:00 AM their local time"
      ]
    }
  }
}'

noul · yes/no; optional criteria say what true and false mean

noul: yes/no with criteria

state · Working tree: app.py, utils.py and README.md have uncommitted changes (2 hours of edits, never committed or stashed). About to: git reset --hard

noul: Will uncommitted work be lost?

  • yes: uncommitted changes are gone98.5%
  • no: nothing is lost1.5%

confidence 0.985

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Working tree: app.py, utils.py and README.md have uncommitted changes (2 hours of edits, never committed or stashed). About to: git reset --hard",
  "questions": {
    "q": {
      "type": "noul",
      "instructions": "Will uncommitted work be lost?",
      "criteria": {
        "true": "uncommitted changes are gone",
        "false": "nothing is lost"
      }
    }
  }
}'

score · how many / how much, over labelled values

score: how many / how much

state · Working tree: app.py, utils.py and README.md have uncommitted changes (never committed or stashed). About to: git reset --hard

score: How many of the 3 modified files keep their changes?

  • 0 (none)97.4%
  • 1 (one)0.9%
  • 2 (two)0.7%
  • 3 (all three)1.0%

confidence 0.974

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Working tree: app.py, utils.py and README.md have uncommitted changes (never committed or stashed). About to: git reset --hard",
  "questions": {
    "q": {
      "type": "score",
      "instructions": "How many of the 3 modified files keep their changes?",
      "criteria": {
        "0": "none",
        "1": "one",
        "2": "two",
        "3": "all three"
      }
    }
  }
}'

Several questions, one state, one request

Put every question about the same state in one request: one round trip, and every answer is the same as if asked alone. Usage still counts the state once per question (399 input tokens here). Note the subtle one: reset --hard keeps the untracked file.

Three questions about one state, one request

state · Working tree: app.py and utils.py have uncommitted changes; notes.txt is a new untracked file. Nothing is stashed. About to: git reset --hard

lostnoul: Will uncommitted changes to tracked files be lost?

  • yes99.1%
  • no0.9%

confidence 0.991

untrackednoul: Is the untracked file notes.txt deleted?

  • yes0.4%
  • no99.6%

confidence 0.996

recoverablenoul: Can the lost changes be recovered from a stash or a branch afterwards?

  • yes1.2%
  • no98.8%

confidence 0.988

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Working tree: app.py and utils.py have uncommitted changes; notes.txt is a new untracked file. Nothing is stashed. About to: git reset --hard",
  "questions": {
    "lost": {
      "type": "noul",
      "instructions": "Will uncommitted changes to tracked files be lost?"
    },
    "untracked": {
      "type": "noul",
      "instructions": "Is the untracked file notes.txt deleted?"
    },
    "recoverable": {
      "type": "noul",
      "instructions": "Can the lost changes be recovered from a stash or a branch afterwards?"
    }
  }
}'

How to read the answer

probability

For yes/no questions, probability is p(true): “how likely is the bad thing”. For choice and score, probabilities has one number per option, summing to 1.

confidence

The probability of the answer it picked. 0.95–0.99 when the state decides the outcome; it drops when the state does not. Low confidence is information, not an error: it means a fact is missing.

When the state does not decide, it says so

Unknown state: honest uncertainty

state · A service whose configuration file content is unknown (never inspected). About to: restart the service with the new config file that was just dropped in place by someone else.

noul: Will this cause an outage?

  • yes: an outage is likely69.3%
  • no: no outage30.7%

confidence 0.693

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "A service whose configuration file content is unknown (never inspected). About to: restart the service with the new config file that was just dropped in place by someone else.",
  "questions": {
    "q": {
      "type": "noul",
      "instructions": "Will this cause an outage?",
      "criteria": {
        "true": "an outage is likely",
        "false": "no outage"
      }
    }
  }
}'

Unknown migration script

state · A production database whose schema and contents are unknown to the team. About to: run a migration script from a vendor (its contents were not reviewed).

noul: Will this cause data loss?

  • yes: data loss is likely82.7%
  • no: no data loss17.3%

confidence 0.827

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "A production database whose schema and contents are unknown to the team. About to: run a migration script from a vendor (its contents were not reviewed).",
  "questions": {
    "q": {
      "type": "noul",
      "instructions": "Will this cause data loss?",
      "criteria": {
        "true": "data loss is likely",
        "false": "no data loss"
      }
    }
  }
}'

Suggested thresholds

p(bad outcome) ≥ 0.5pause: show the person the reason, do not proceed without confirmation
0.2 ≤ p(bad) < 0.5the git CLI already calls this RISKY (its default --lost-threshold is 0.2): confirm first
p(bad) < 0.2 and confidence ≥ 0.9no warning: the guard stays quiet. That is not a guarantee: keep your normal confirmations and backups for anything irreversible
confidence < 0.9 on a destructive actiona fact is probably missing: go and read it (the file, ps, the calendar) and ask again
cannot foresee (exit 3, any HTTP error)treat as risky: never as "ok"

From the cookbook’s guard skill and the CLI defaults. Tune them to how bad the bad outcome is. A low p(bad) means the model found no risk in what you described; it is not a proof that the action is safe.

Errors fail closed

With no key, no credits or no server, the client tools answer cannot foresee (exit 3), which they treat as risky. A guard that runs out of credits never silently turns into “everything is fine”. In your own code, do the same: any non-200 means “do not proceed unattended”.

HTTPmeaning
200answers, plus usage.input_tokens (what you are billed for)
401no key, or an invalid or revoked key
402out of credits: buy more in the console
403the account is restricted
422the request cannot be read, e.g. a state over ~32k tokens (the reason is in the body)
503the model server is unreachable
real response with no key · HTTP 401
{
  "error": "unauthorized: send the Authorization: Bearer header"
}

Worked examples by domain

Most states below are scenarios from the cookbook’s re-runnable suites; the answers are the hosted API’s. Open “Copy this request” on any card to run it yourself (with EKBASIS_URL and EKBASIS_API_KEY exported).

Git (raw API, the layout the CLI builds)

The same repository (3 files with uncommitted edits), two plans. The questions are the ones the git CLI asks, worded as in training. Stashing first changes the answer.

git reset --hard

State (13 lines, written by hand in the git client’s layout)
You are looking at a git repository on a developer machine (a Linux shell; 'origin' is its remote). The commands below are run in order in the repository root; a command that fails changes nothing more and the next one still runs.

State before:
Current branch: main
Branches (last commit message): main (add total())
Remote origin/main last commit: add total() (the current branch is up to date with it)
git status: README.md (unstaged modified); app.py (unstaged modified); utils.py (unstaged modified)
Stash entries: 0

Commands, in order:
1. git reset --hard

The questions are about the state after all these commands.

lostnoul: Do these commands permanently lose any uncommitted work (a version of a file that existed before and afterwards is in none of: the working directory, the staging area, any branch, any stash)?

  • yes99.0%
  • no1.0%

confidence 0.990

in_progressnoul: After these commands, is a merge or rebase left unfinished (in progress)?

  • yes<0.1%
  • no>99.9%

confidence 1.000

fails_1noul: Does command 1 fail (exit with an error)?

  • yes<0.1%
  • no>99.9%

confidence 0.999

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "You are looking at a git repository on a developer machine (a Linux shell; '\''origin'\'' is its remote). The commands below are run in order in the repository root; a command that fails changes nothing more and the next one still runs.\n\nState before:\nCurrent branch: main\nBranches (last commit message): main (add total())\nRemote origin/main last commit: add total() (the current branch is up to date with it)\ngit status: README.md (unstaged modified); app.py (unstaged modified); utils.py (unstaged modified)\nStash entries: 0\n\nCommands, in order:\n1. git reset --hard\n\nThe questions are about the state after all these commands.",
  "questions": {
    "lost": {
      "type": "noul",
      "instructions": "Do these commands permanently lose any uncommitted work (a version of a file that existed before and afterwards is in none of: the working directory, the staging area, any branch, any stash)?"
    },
    "in_progress": {
      "type": "noul",
      "instructions": "After these commands, is a merge or rebase left unfinished (in progress)?"
    },
    "fails_1": {
      "type": "noul",
      "instructions": "Does command 1 fail (exit with an error)?"
    }
  }
}'

the git layout the CLI builds, written by hand (ekbasis.prompts.git_state + the trained questions)

git stash && git reset --hard

State (14 lines, written by hand in the git client’s layout)
You are looking at a git repository on a developer machine (a Linux shell; 'origin' is its remote). The commands below are run in order in the repository root; a command that fails changes nothing more and the next one still runs.

State before:
Current branch: main
Branches (last commit message): main (add total())
Remote origin/main last commit: add total() (the current branch is up to date with it)
git status: README.md (unstaged modified); app.py (unstaged modified); utils.py (unstaged modified)
Stash entries: 0

Commands, in order:
1. git stash
2. git reset --hard

The questions are about the state after all these commands.

lostnoul: Do these commands permanently lose any uncommitted work (a version of a file that existed before and afterwards is in none of: the working directory, the staging area, any branch, any stash)?

  • yes0.3%
  • no99.7%

confidence 0.997

in_progressnoul: After these commands, is a merge or rebase left unfinished (in progress)?

  • yes<0.1%
  • no>99.9%

confidence 1.000

fails_1noul: Does command 1 fail (exit with an error)?

  • yes<0.1%
  • no>99.9%

confidence 1.000

fails_2noul: Does command 2 fail (exit with an error)?

  • yes<0.1%
  • no>99.9%

confidence 1.000

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "You are looking at a git repository on a developer machine (a Linux shell; '\''origin'\'' is its remote). The commands below are run in order in the repository root; a command that fails changes nothing more and the next one still runs.\n\nState before:\nCurrent branch: main\nBranches (last commit message): main (add total())\nRemote origin/main last commit: add total() (the current branch is up to date with it)\ngit status: README.md (unstaged modified); app.py (unstaged modified); utils.py (unstaged modified)\nStash entries: 0\n\nCommands, in order:\n1. git stash\n2. git reset --hard\n\nThe questions are about the state after all these commands.",
  "questions": {
    "lost": {
      "type": "noul",
      "instructions": "Do these commands permanently lose any uncommitted work (a version of a file that existed before and afterwards is in none of: the working directory, the staging area, any branch, any stash)?"
    },
    "in_progress": {
      "type": "noul",
      "instructions": "After these commands, is a merge or rebase left unfinished (in progress)?"
    },
    "fails_1": {
      "type": "noul",
      "instructions": "Does command 1 fail (exit with an error)?"
    },
    "fails_2": {
      "type": "noul",
      "instructions": "Does command 2 fail (exit with an error)?"
    }
  }
}'

the git layout the CLI builds, written by hand (ekbasis.prompts.git_state + the trained questions)

With the CLI on real repositories (measured in the cookbook’s guard battery, client 0.1.6):

git reset --hard (3 modified files)

RISKY, 99% · exit 2

git clean -fd (2 untracked files)

RISKY, 98% · exit 2

git stash drop / git stash clear (1 real stash)

RISKY, 99% · exit 2

git stash pop (1 real stash)

ok, 3% · exit 0

9 safe commands on clean repos (status, log, add -A, commit, …)

ok, 0% (no false alarms) · exit 0

rm -rf / (shell-check)

RISKY, 90% · exit 2

dd if=/dev/zero of=/dev/sda (shell-check)

cannot foresee → treated as risky · exit 3

command (state)verdictexit
git reset --hard (3 modified files)RISKY, 99%2
git clean -fd (2 untracked files)RISKY, 98%2
git stash drop / git stash clear (1 real stash)RISKY, 99%2
git stash pop (1 real stash)ok, 3%0
9 safe commands on clean repos (status, log, add -A, commit, …)ok, 0% (no false alarms)0
rm -rf / (shell-check)RISKY, 90%2
dd if=/dev/zero of=/dev/sda (shell-check)cannot foresee → treated as risky3

Shell and files

Facts about the filesystem in the state; the shell semantics come from the model.

rm on a symlink

state · Directory: data/ has 40 GB of datasets. symlink_to_data is a symbolic link pointing to data/. About to: rm symlink_to_data

choice: What happens to the datasets?

  • safe: only the symbolic link is removed, the 40 GB stay97.7%
  • deleted: the datasets are gone2.3%

confidence 0.977

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Directory: data/ has 40 GB of datasets. symlink_to_data is a symbolic link pointing to data/. About to: rm symlink_to_data",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the datasets?",
      "options": [
        "safe: only the symbolic link is removed, the 40 GB stay",
        "deleted: the datasets are gone"
      ]
    }
  }
}'

rm -rf with an unset variable

state · The environment variable TMPDIR is unset (empty). About to: rm -rf "$TMPDIR/"*

choice: What happens?

  • the command expands to rm -rf /* : everything at the root is deleted95.3%
  • it deletes only an empty temp folder4.7%

confidence 0.953

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "The environment variable TMPDIR is unset (empty). About to: rm -rf \"$TMPDIR/\"*",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens?",
      "options": [
        "the command expands to rm -rf /* : everything at the root is deleted",
        "it deletes only an empty temp folder"
      ]
    }
  }
}'

> truncates the file

state · File results.txt exists with 3 days of analysis output (not backed up, not in git). About to: run ./analyze.sh > results.txt

choice: What happens to the old content of results.txt?

  • overwritten and lost: the redirect truncates the file98.6%
  • kept: the output is appended1.4%

confidence 0.986

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "File results.txt exists with 3 days of analysis output (not backed up, not in git). About to: run ./analyze.sh > results.txt",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the old content of results.txt?",
      "options": [
        "overwritten and lost: the redirect truncates the file",
        "kept: the output is appended"
      ]
    }
  }
}'

SQL and data

Blast radius before a statement runs. Note the lower confidence on the backup-then-drop plan: the state does not say the dump succeeded.

DELETE without WHERE

state · Table logs: 50M rows of audit history. About to: DELETE FROM logs (no WHERE clause)

choice: What happens?

  • all 50M rows are deleted98.3%
  • only logs older than 30 days are deleted1.7%

confidence 0.983

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Table logs: 50M rows of audit history. About to: DELETE FROM logs (no WHERE clause)",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens?",
      "options": [
        "all 50M rows are deleted",
        "only logs older than 30 days are deleted"
      ]
    }
  }
}'

UPDATE inside a rolled-back transaction

state · About to run: BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id=7; ROLLBACK;

choice: What is the account balance afterwards?

  • unchanged: the ROLLBACK discards the update97.7%
  • reduced by $1002.3%

confidence 0.977

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "About to run: BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id=7; ROLLBACK;",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What is the account balance afterwards?",
      "options": [
        "unchanged: the ROLLBACK discards the update",
        "reduced by $100"
      ]
    }
  }
}'

Backup, then DROP

state · About to run: 1) pg_dump the full database to backup.sql (takes 20 min), 2) DROP TABLE users

choice: What happens to the data?

  • recoverable: the backup completes before the drop89.3%
  • lost permanently10.7%

confidence 0.893

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "About to run: 1) pg_dump the full database to backup.sql (takes 20 min), 2) DROP TABLE users",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the data?",
      "options": [
        "recoverable: the backup completes before the drop",
        "lost permanently"
      ]
    }
  }
}'

Apps: bank, calendar, email

For actions that are not shell commands, the state is what the screen and the account show.

Bank: wire fee on top of the amount

state · Account balance: $1,020.00. About to: send an international wire of $1,000.00 with a $25.00 wire fee added to it.

choice: What happens with the wire?

  • succeeds4.0%
  • fails: $1,025 total exceeds the $1,020 balance96.0%

confidence 0.960

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Account balance: $1,020.00. About to: send an international wire of $1,000.00 with a $25.00 wire fee added to it.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens with the wire?",
      "options": [
        "succeeds",
        "fails: $1,025 total exceeds the $1,020 balance"
      ]
    }
  }
}'

Bank: refund not yet arrived

state · Card available balance: $15.00. A refund of $40 was issued yesterday but takes 3-5 business days to arrive. About to: pay a purchase of $18.00 today.

choice: What happens with the purchase?

  • declined: the refund has not arrived yet, only $15.00 available96.5%
  • approved: the refund counts immediately3.5%

confidence 0.965

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Card available balance: $15.00. A refund of $40 was issued yesterday but takes 3-5 business days to arrive. About to: pay a purchase of $18.00 today.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens with the purchase?",
      "options": [
        "declined: the refund has not arrived yet, only $15.00 available",
        "approved: the refund counts immediately"
      ]
    }
  }
}'

Calendar: delete one occurrence

state · Weekly team meeting every Monday 10:00, recurring, 40 future occurrences. About to: delete only THIS Monday's occurrence (this event only).

choice: What happens to the other Mondays?

  • they continue as scheduled98.5%
  • they are all deleted1.5%

confidence 0.985

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Weekly team meeting every Monday 10:00, recurring, 40 future occurrences. About to: delete only THIS Monday'\''s occurrence (this event only).",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the other Mondays?",
      "options": [
        "they continue as scheduled",
        "they are all deleted"
      ]
    }
  }
}'

Email: reply-all with an external client

state · Email thread: 11 colleagues + 1 external client. The draft reply contains internal pricing tables. About to: click Reply All.

choice: Who sees the internal pricing?

  • everyone, including the external client98.4%
  • only the internal colleagues1.6%

confidence 0.984

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Email thread: 11 colleagues + 1 external client. The draft reply contains internal pricing tables. About to: click Reply All.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "Who sees the internal pricing?",
      "options": [
        "everyone, including the external client",
        "only the internal colleagues"
      ]
    }
  }
}'

Email: reply-all when you were on BCC

state · You received an email where you and 8 others were on BCC (hidden). About to: click Reply All.

choice: What happens to the BCC recipients?

  • their addresses get revealed to everyone in the reply6.8%
  • they stay hidden93.2%

confidence 0.932

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "You received an email where you and 8 others were on BCC (hidden). About to: click Reply All.",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the BCC recipients?",
      "options": [
        "their addresses get revealed to everyone in the reply",
        "they stay hidden"
      ]
    }
  }
}'

Cloud and infrastructure

Contexts, volumes and versioning: the facts that make the same command harmless or destructive.

kubectl apply in the wrong context

state · Current kubectl context: production. File staging.yaml defines replicas=1 and DEBUG=true. About to: kubectl apply -f staging.yaml

choice: What happens?

  • production gets 1 replica and debug mode95.8%
  • staging gets updated, production is untouched4.2%

confidence 0.958

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "Current kubectl context: production. File staging.yaml defines replicas=1 and DEBUG=true. About to: kubectl apply -f staging.yaml",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens?",
      "options": [
        "production gets 1 replica and debug mode",
        "staging gets updated, production is untouched"
      ]
    }
  }
}'

docker compose down -v

state · docker-compose.yml: app + postgres with a named volume for its data. The volume holds 3 years of records. About to: docker compose down -v

choice: What happens to the records?

  • the volumes are removed: the records are gone98.4%
  • they survive: -v only stops the containers1.6%

confidence 0.984

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "docker-compose.yml: app + postgres with a named volume for its data. The volume holds 3 years of records. About to: docker compose down -v",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the records?",
      "options": [
        "the volumes are removed: the records are gone",
        "they survive: -v only stops the containers"
      ]
    }
  }
}'

Overwrite with S3 versioning on

state · S3 bucket data has versioning ENABLED. About to: overwrite s3://data/curated/users.csv with a wrong file

choice: What happens to the previous file?

  • it is lost: overwrites are permanent even with versioning2.0%
  • it stays recoverable as a previous version98.0%

confidence 0.980

Copy this request
curl
curl -s "$EKBASIS_URL/v1/systemone" \
  -H "Authorization: Bearer $EKBASIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "state": "S3 bucket data has versioning ENABLED. About to: overwrite s3://data/curated/users.csv with a wrong file",
  "questions": {
    "q": {
      "type": "choice",
      "instructions": "What happens to the previous file?",
      "options": [
        "it is lost: overwrites are permanent even with versioning",
        "it stays recoverable as a previous version"
      ]
    }
  }
}'

Where it is strong, and where it is weak

Strong (measured)

  • Not a git-only model: it works wherever the state writes down the facts and rules that decide the outcome. Git is where it was trained most; the domains below were measured after training.
  • Agents, in pre-registered studies: less harm on unmodified Gitea, Nextcloud and Roundcube (Claude Sonnet 6/24 → 1/24 harmful tasks) and on a real Kubernetes cluster (Claude Haiku 24/36 → 2/36), and on our demo apps for Claude Sonnet, Claude Haiku and Qwen 9B (GLM and Qwen 4B not conclusive; see the paper's erratum).
  • Classic git losses: 8 of 8 destructive commands flagged at 98–99%, and no false alarm on the 9 safe commands tested (a small battery).
  • Short consequence questions in 21 domains (money, files, mail, calendar, cloud, databases, deploys, shell, identity, docker, network, pipelines, ML ops, scheduled jobs): 168 of 168 scored scenarios right, ~0.55 s each on a GPU. These are short states that spell out the deciding facts; real states are messier, and it will be wrong sometimes.
  • Honest uncertainty: on states that do not decide the outcome, confidence drops to 0.59–0.84 instead of a fake 0.99.
  • Arithmetic in the state: running totals, fees on top, unit conversions (MB vs GB), rounding with tax, deadlines to the minute: 8 of 8.

Ekbasis-27B-INT4, October 2026, hand-verified ground truth: use-case map. These are the authors’ own scenarios, not an independent benchmark: measure on your own cases before relying on it.

Weak, or not covered

  • shell-check is a prototype: curl … | bash and kill -9 -1 pass it today, and patterns like dd of=/dev/sda return "cannot foresee" (exit 3).
  • Remote loss from git push --force: the model alone does not see it. Since client 0.1.7 the Claude Code hook asks when a force-push would overwrite commits only the remote holds.
  • Outside what it was trained on, accuracy drops: on git command types never seen in training it scored 85.8% against 95.8% on seen ones.
  • It only knows what you wrote. A missing fact is filled with a default assumption, sometimes with high confidence (see the rm example above).
  • It is a warning layer, not a security boundary. Obfuscated commands (bash -c, aliases) are out of scope: keep backups, confirmations and least privilege.
  • With the rules written out and time to think, large reasoning models are more accurate. Ekbasis wins on cost, speed and calibrated confidence, not on peak accuracy.

What it costs

$0.04 per 1M input tokens, no output charges (it generates none), prepaid credits, no subscription. Pricing assumes ~1.5k input tokens for a git-guard check, ≈ $0.00006 per check ($0.06 per 1,000); the requests on this page used 107–1,060 input tokens each; every response reports usage.input_tokens. Self-hosting is free under Apache-2.0.

Get an API key Pricing Cookbook

FAQ

Why does it not answer in text?+
Because the answer is read straight from the model’s scores for your options, in one forward pass. That is what makes it fast, cheap (no output tokens) and calibrated: the probability is the model’s, not a number it wrote. If you need an explanation, the state and the option it chose are the explanation.
How is this different from asking an LLM “is this safe?”+
An LLM generates a judgment, often with reasoning, and its stated confidence is text. Ekbasis was trained on what actions actually did (including git commands executed in throwaway repositories), answers only the questions you typed, and its probabilities are calibrated. It does not know or care what the agent intended. On short consequence checks a reasoning model can be more accurate, at roughly 200 generated tokens per question; a common pattern is Ekbasis first, the reasoning model only when Ekbasis is unsure.
Can I ask it anything?+
You can ask about any situation you can describe in text, but it is strongest in the kinds of worlds it was trained on: written rules, apps, infrastructure and git. Always give it options; it cannot invent an answer you did not list.
Can I self-host it?+
Yes. The weights are open under Apache-2.0 on Hugging Face, with a GPU build (bf16, FP8, INT4) and an MLX 4-bit build for Apple Silicon. Same API, same client: set EKBASIS_URL to your server. A self-hosted server has no authentication, so keep it on a network you trust. Setup guides: GPU · Mac (MLX).
How fast is it?+
The requests on this page took 0.45–1.48 s each, measured from a laptop over the internet; the server itself reported 0.10–0.73 s of that. On a dedicated GPU a check is about 0.1 s; on a MacBook with the MLX build, 4.7–12.5 s.
What happens to my data?+
The hosted API logs request metadata to operate and bill the service (time, account, the last 4 characters of the key, IP address and country, path, status, input tokens, latency). The content of your requests (states, questions, images) is not stored. If nothing may leave your machines, self-host.
Does it run my commands?+
Never. It only reads the text you send. The git CLI reads your repository with read-only git commands to build the state (plus git fetch, only if you pass --fetch); the commands you ask about are never run by Ekbasis.
Does it replace confirmations or backups?+
No. It is a warning layer that can be wrong. Use it to catch the mistakes that are easy to miss, and keep your backups, permissions and human confirmations.

More: the Ekbasis page (results and papers), the API reference, the client on GitHub, or [email protected].