# Open source

Estravon is built on two public repositories. You can use just estravon-plugin or both.

## estravon-plugin (`tiberavonltd/estravon-plugin`)
The Zotero plugin. Works with both the hosted service (api.estravon.com) and a self-hosted local backend. Install from the XPI file — no developer tools needed.
- [View on GitHub →](https://github.com/tiberavonltd/estravon-plugin)
- [Download estravon-0.5.1.xpi →](https://github.com/tiberavonltd/estravon-plugin/releases/download/v0.5.1/estravon-0.5.1.xpi)

## estravon-backend (`tiberavonltd/estravon-backend`)
The vanilla Python backend. Runs on your machine and serves results to the plugin over localhost. Use your own API key with Mistral, Replicate, or Datalab — or the MinerU engine, which runs entirely on your own hardware with no key and no third party in the loop.
- [View on GitHub →](https://github.com/tiberavonltd/estravon-backend)
- [Install guide →](/install)

## Quickest start: pip install

You don't need to clone the repository to run the backend. Install directly from PyPI, create a working folder with a .env file containing your Mistral API key (or skip the key entirely — see below), and start:

```
pip install estravon-backend
estravon --port 7766
```

Get a pay-as-you-go API key at Mistral / Replicate, or a subscription API key at Datalab — or run the MinerU engine locally and pay no one. Full step-by-step at  [estravon.com/install](https://www.estravon.com/install)

## Two ways to use the plugin

### Option A — Hosted service
Install the plugin, point it at api.estravon.com (the default), and buy a credit pack. No local setup, we take care of everything else.

### Option B — Self-hosted backend
Install the plugin and the backend. Point the plugin at http://localhost:7766. Configure your backend. Your machine sends your PDF straight to Mistral, Replicate, or Datalab — or, with the MinerU engine, to nowhere at all.

## Updating the plugin

Zotero checks for plugin updates automatically once every 24 hours. The check is driven by a background timer — restarting Zotero does not reset the countdown. When a new version is available, Zotero installs it silently if "Allow automatic updates" is enabled in Tools → Plugins.

To update immediately without waiting 24 hours:

- Open Zotero → Tools → Plugins
- Click the gear icon (⚙) next to Estravon
- Choose Check for Updates

This calls the update check directly and bypasses the timer. The plugin downloads and installs the new XPI in place.

## For developers: integrating with Estravon

Two ways to drive extraction programmatically, depending on what you're building.

### Call the backend directly over HTTP
Any tool can POST a PDF to /process and read back Markdown — no plugin needed. The local backend responds synchronously; the hosted service queues the job and you poll GET /jobs/{id}. The backend returns Markdown and images only — attaching results to a Zotero item isn't part of this contract; that's what the hook below is for. Full endpoint contract, including the two-step Markdown fetch, is in docs/API.md (with an OpenAPI spec alongside it) in the backend repo.
```
# Local backend — synchronous
const form = new FormData();
form.append("pdf_file", pdfBlob, "section.pdf");
form.append("section_name", "chapter_01");
form.append("page_range", "14-40");

const res = await fetch("http://localhost:7766/process", {
  method: "POST",
  body: form,
});
const { files } = await res.json();  // [{label, md_url, image_urls}, ...]
const markdown = await (await fetch("http://localhost:7766" + files[0].md_url)).text();
```
```
# Hosted backend — async, poll until done
const res = await fetch("https://api.estravon.com/process", {
  method: "POST",
  headers: { "X-API-Key": apiKey },
  body: form,
});
const { job_id } = await res.json();  // HTTP 202 — queued

let result;
while (true) {
  const poll = await fetch(`https://api.estravon.com/jobs/${job_id}`,
    { headers: { "X-API-Key": apiKey } });
  result = await poll.json();
  if (result.status === "done" || result.status === "error") break;
  await new Promise(r => setTimeout(r, 3000));
}
```
[docs/API.md on GitHub →](https://github.com/tiberavonltd/estravon-backend/blob/main/docs/API.md)

### Trigger extraction through the Estravon plugin
If you're building a Zotero plugin yourself, call Zotero.Estravon.extract() and let Estravon handle backend routing and attaching results for you — no HTTP details, no re-implementing the attach/log-note logic.
```
const result = await Zotero.Estravon.extract({
  itemID,
  pageRange: "112-148",
  sectionName: "chapter_04",
  attach: true,   // false: resolve with markdown only, you handle filing
  silent: true,   // false: also show Estravon's own error dialog on failure
});
// => { status: "done"|"error", jobId?, markdown?, attachmentIDs?, error? }
```
[Full reference in the estravon-plugin DEVELOPMENT.md →](https://github.com/tiberavonltd/estravon-plugin/blob/main/DEVELOPMENT.md)

### Compare engines on your own PDF
estravon-backend-benchmarks runs the same PDF through MinerU, Mistral, Datalab, and Replicate and shows the results side by side — timing, cost, and the actual Markdown output. Drives a running estravon-backend over HTTP; it's an evaluation aid, not a leaderboard.
```
from estravon_bench.compare import compare

result = compare("your.pdf", "1-4", engines=["mineru", "mistral"])
print(result.to_markdown_table())
```
[estravon-backend-benchmarks on GitHub →](https://github.com/tiberavonltd/estravon-backend-benchmarks)
