Quickstart

Install oboro, wire the Claude Code hooks in, and clean and restore a document by hand for everything else.

Install

Pick whichever fits. The install script is the quickest.

Install script

Downloads the prebuilt binary for macOS or Linux and verifies it against the release checksums:

curl -fsSL https://m.canouil.dev/oboro/install.sh | bash

It installs into /usr/local/bin when that is writable, otherwise into ~/.local/bin. Pin a version with --version, choose a directory with --dir, and pass --help for the rest. The script needs bash and curl. On a minimal distribution such as Alpine, install them first with apk add bash curl.

To also detect names you have not listed, install the ner build and fetch its model:

curl -fsSL https://m.canouil.dev/oboro/install.sh | bash -s -- --features ner
oboro models pull   # about 348 MB, once

On Linux, the ner binary needs glibc 2.39 or newer (Ubuntu 24.04+, Debian 13+). The default binary is static and runs anywhere.

Windows

Downloads the prebuilt binary and verifies it against the release checksums:

powershell -ExecutionPolicy ByPass -c "irm https://m.canouil.dev/oboro/install.ps1 | iex"

This installs into %LOCALAPPDATA%\Programs\oboro\bin. You do not need administrator rights. It adds that directory to your user PATH if the directory is missing; open a new terminal for that to take effect. irm | iex cannot take arguments. To pin a version, use the parameterised form instead:

& ([scriptblock]::Create((irm https://m.canouil.dev/oboro/install.ps1))) -Version 0.7.0

-Dir picks an install directory, and -Help lists the rest. OBORO_VERSION, OBORO_INSTALL_DIR, OBORO_SKIP_CHECKSUM and OBORO_VERIFY_PROVENANCE do the same jobs as their install.sh counterparts. No prebuilt Windows build offers ner or ocr yet. Both stay a source build there, the same as everywhere else.

Docker

You need no toolchain. The image is one static binary on distroless. The vault volume is not optional, because the placeholder mapping lives in it:

docker volume create oboro-vault
docker run --rm \
  -v oboro-vault:/vault \
  -v "$PWD":/work -w /work \
  --user "$(id -u):$(id -g)" \
  ghcr.io/mcanouil/oboro:latest clean contract.docx

See the Docker page for restore and the volume in full, and for the ghcr.io/mcanouil/oboro:ner variant, which detects names with the recognition model already inside the image.

Prebuilt binary, by hand

Download the archive for your machine and SHA256SUMS from the releases page, then verify and install it:

VERSION=0.1.0
TARGET=x86_64-unknown-linux-musl   # or the row that matches your machine

curl -fsSLO "https://github.com/mcanouil/oboro/releases/download/${VERSION}/oboro-${VERSION}-${TARGET}.tar.gz"
curl -fsSLO "https://github.com/mcanouil/oboro/releases/download/${VERSION}/SHA256SUMS"
sha256sum --ignore-missing --check SHA256SUMS   # shasum -a 256 on macOS
tar -xzf "oboro-${VERSION}-${TARGET}.tar.gz"
install -m 0755 oboro /usr/local/bin/oboro

With Rust already installed

cargo install --git https://github.com/mcanouil/oboro

From source

This is required for ocr, since no prebuilt binary or image carries it. It is also required for a ner build on a Linux distribution older than the prebuilt one’s glibc:

git clone https://github.com/mcanouil/oboro.git
cd oboro
cargo build --release

The binary lands in target/release/oboro.

That plain build depends on nothing but Rust. Two capabilities are optional. Reading images needs the Tesseract system libraries. Recognising names links ONNX Runtime, which the build fetches over the network. The ner build also ships prebuilt, from the install script and as a Docker image. The ocr build does not; you compile it yourself.

To detect names you have not listed, you need the recognition model:

cargo build --release --features ner   # downloads ONNX Runtime while building
oboro models pull

The build downloads ONNX Runtime. So --features ner needs network access, and a fully offline build fails here. models pull then fetches the model, about 348 MB once, checked against pinned hashes. Once you have built it, models pull is the only command that touches the network.

Reading images and scanned documents needs Tesseract. It also needs trained data for the languages your documents are in:

# Debian and Ubuntu; add a tesseract-ocr-<language> package per language
sudo apt-get install tesseract-ocr tesseract-ocr-eng libtesseract-dev libleptonica-dev

# macOS; tesseract-lang installs the trained data for every language
brew install tesseract tesseract-lang

cargo build --release --features ocr

Oboro uses whatever trained data is installed, so you do not need to declare a language. Set ocr_languages to pick among the installed languages.

cargo build --release --features "ner,ocr"

In a devcontainer

For building or contributing with nothing on the host but Docker. The container carries the pinned Rust toolchain, Tesseract and the OCR libraries. So cargo build --release --features "ner,ocr" runs inside it, with no host toolchain to set up. The binary lands in target/release/oboro, as usual. In Visual Studio Code or a GitHub Codespace, reopen the folder in the container when prompted. See Development for the manual docker invocation.

Check what your build can do:

oboro doctor

However you installed it, oboro completions --install puts a completion script where your shell reads it. It takes the shell from $SHELL unless you name one:

oboro completions --install

Without --install, the script goes to standard output, and where it belongs goes to standard error. So a redirect captures the script alone. See Shells for every destination, and how to do it by hand.

Wire it into Claude Code

Do this first if you use Claude Code. The agent reads files itself. So cleaning a copy by hand and pasting it in protects nothing: the agent already read the original.

Two hooks put Oboro in that path. The plugin installs both hooks, and brings the skill that explains their placeholders:

/plugin marketplace add mcanouil/oboro
/plugin install oboro@oboro

The plugin cannot install the binary its hooks run. Install that first, with any of the ways above. Without the binary, Oboro withholds every matching tool result and refuses every matching write, and the message says what to install. The plugin fails closed, rather than leaving you unprotected while looking installed.

If you would rather keep both halves in your own files, not in a plugin, one command writes them:

oboro skill install --with-hooks

It asks whether to cover this project or every project, and shows both files it would write before writing them. Pass --project or --user to skip the question. Pass --dry-run to see the settings without writing them. Oboro plans both halves before it writes either one. So if a scope refuses one half, through a symbolic link or settings it cannot merge into, Oboro installs neither.

Either half installs on its own. Drop --with-hooks for the skill alone. Run oboro hook install for the hooks alone. Avoid installing the hooks alone: the model then sees placeholders with nothing to explain them.

This is what it adds, next to whatever your settings already hold:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Read|Grep|Bash|WebFetch",
        "hooks": [{ "type": "command", "command": "oboro hook post-tool-use" }]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "oboro hook pre-tool-use" }]
      }
    ]
  }
}

Oboro never moves, reorders or removes anything already in the file. A hook Oboro finds already named oboro hook is left exactly as you wrote it. You can write it by hand instead if you prefer: the command is a convenience, not the only way in.

Then check it took:

oboro doctor
PostToolUse .claude/settings.local.json (Read|Grep|Bash|WebFetch, reachable)
PreToolUse  .claude/settings.local.json (Write|Edit, reachable)

Installed as a plugin, the hooks live in the plugin’s own files, not in your settings. doctor says so:

plugin      /home/you/.claude/settings.json (oboro@oboro, enabled)
PostToolUse not in your settings; the plugin carries it
PreToolUse  not in your settings; the plugin carries it
skill       /home/you/project/.claude/skills/oboro/SKILL.md (not installed here; the plugin carries its own)
skill       /home/you/.claude/skills/oboro/SKILL.md (not installed here; the plugin carries its own)

Avoid installing them again in your settings on top of the plugin: both copies would then run on every matching tool call. oboro hook install warns you when it finds the plugin enabled.

Ask the agent to read a file holding a phone number, and it will tell you about [[PHONE_1]]. Ask it to write that back, and the real number lands in the file.

Install both halves. oboro hook install installs both. With only the first half, the model sees placeholders and nothing puts them back. So [[PHONE_1]] ends up in your source.

Read the reachable field. A hook that names a binary not on PATH is configured but useless, and it fails closed on every matching tool call.

At 17 to 70 ms a call, this runs on every matching tool without being felt. See the timings. See the reference for what each half does, and what neither covers.

WarningYour own words are not covered

The hooks clean what tools read and write. What you type into the chat is never touched. The event that fires on a prompt can add context to it, but cannot rewrite it.

Paste a document instead of retyping what is in it, and the hook covers it.

Tell the agent what it is reading

With the hooks on, the agent reads [[PHONE_1]] without being told what that is. Left to guess, it reads a placeholder as a bug in your file, as a template to fill in, or as a redaction to work around.

The plugin above carries the skill, and so does oboro skill install --with-hooks. So this section is only for installing the skill on its own; installing it twice is not worth doing.

Install the skill that explains them:

oboro skill install

It asks whether to install for this project or for every project, and names the file it will write before it writes it:

1  this project    /home/you/project/.claude/skills/oboro/SKILL.md
2  every project   /home/you/.claude/skills/oboro/SKILL.md
Install the Oboro skill where? [1/2]

Pass --project or --user to skip the question. Pass --with-hooks to install the hooks in the same scope at the same time. oboro skill show prints the text without writing anything.

Then check it took, as with the hooks:

oboro doctor
skill       /home/you/project/.claude/skills/oboro/SKILL.md (current)
skill       /home/you/.claude/skills/oboro/SKILL.md (not installed)

The skill is carried in the binary. So it describes the version of the hooks you have, not the version documented when you installed. Edit the installed copy, and a later install leaves your edit alone. It writes what it would have installed to SKILL.md.oboro-proposed beside it instead.

For an agent other than Claude Code, the same skill is one command away:

npx skills add mcanouil/oboro

That reads skills/oboro/SKILL.md from this repository and installs it for whichever agents it finds. It installs the explanation, not the machine: no binary, no hooks, and nothing redacted until you add both. It also symlinks the skill by default. Oboro never writes through a symbolic link. So oboro skill install afterwards names the link and refuses to overwrite what you installed.

Clean a document

Everything below is the manual loop. It is the primary path in any tool without hooks. In Claude Code, it is the fallback for a document you would rather handle yourself.

oboro clean contract.docx

That writes contract.clean.md beside the input and tells you what it replaced:

contract.docx -> contract.clean.md (7 replaced: ADDRESS 2, EMAIL 1, IBAN 1, ORG 1, PERSON 1, PHONE 1)

Read it, then paste it into whichever model you are using.

Point clean at a directory to do a whole folder at once. Add --recursive to include its subfolders:

oboro clean contracts/ --recursive

Decide for yourself what gets hidden

clean redacts everything it finds. review shows you the list first and lets you reject anything it got wrong:

oboro review contract.docx
┌ contract.docx  —  6 of 7 will be redacted ─────────────────────┐
│ [x] PERSON      31%  Jean Dupont                               │
│ [x] ORG         52%  Acme Consulting SARL                      │
│ [ ] PERSON      22%  Le Havre                                  │
│ [x] IBAN       100%  FR14 2004 1010 0505 0001 3M02 606         │
└────────────────────────────────────────────────────────────────┘
 j/k move   space toggle   a accept all   n reject none   w write

Rejecting a detection leaves the value in the output. Oboro never records it anywhere.

Bring the answer back

The model replies using the placeholders. Save its answer and put the real values back:

oboro restore answer.md

Or keep it in a pipeline:

pbpaste | oboro restore

A whole round trip

oboro clean contract.docx --stdout | pbcopy   # paste into the model
pbpaste | oboro restore                       # read what it said, with real values

Uninstalling

One command removes everything Oboro wrote: the completion script, the agent hooks and skill, the vault, the recognition model, and the binary itself.

oboro uninstall

It prints everything that would go, and asks you to confirm. --dry-run prints the same report and stops there. Without --keep-vault, the vault and its key go too. After that, you can no longer restore anything cleaned earlier. A hook you pasted by hand into a project’s shared .claude/settings.json is named, not removed, since that file is committed and Oboro never wrote it. See the uninstall reference for the flags, and the Docker guide for the volume and image this command cannot reach.

Back to top