Skip to content

Configuration reference

papio loads TOML from ~/.config/papio/config.toml (on Windows, %APPDATA%\papio\config.toml) unless the global --config <path> option selects another file. Configuration is layered over the built-in defaults; unknown TOML fields are rejected. papio init writes a validated user-only config file and papio doctor reports readiness.

The tables below list every decoded key in internal/config. Paths beginning with ~/ are expanded when papio loads them.

Top-level keys

Key Type Default Effect and constraints
access_mode string empty Required before acquisition. Allowed values are conservative, assisted, and delegated; a fresh guided papio init chooses conservative. Conservative records institutional OpenURL availability without opening a handoff; assisted and delegated can route eligible exhaustion to browser handoff.
email string empty Contact identity for polite API pools. Doctor fails when enabled Unpaywall has no email; enabled OpenAlex also requires an email and API key.
data_dir path string ~/.local/share/papio (Windows: %LOCALAPPDATA%\papio) Private writable data directory for the database, artifacts, socket, and default browser-adoption directory.

[fetch]

Key Type Default Effect and constraints
max_bytes integer bytes 104857600 (100 MiB) Maximum artifact-download size. It must be at least 1048576 (1 MiB).
timeout_seconds integer seconds 120 Fetch deadline. It must be at least 5 seconds.
allow_http_loopback boolean false Development and test override that permits HTTP loopback. Doctor warns while it is enabled; production policy is HTTPS-only.

[pdf]

Key Type Default Effect and constraints
ocr_enabled boolean true Enables the OCR fallback. If it is enabled, doctor requires both pdftoppm and tesseract; disabling it makes image-only papers require review.
min_text_chars integer 400 Minimum extracted-text threshold used by PDF validation before OCR fallback is relevant.
max_ocr_pages integer 4 Maximum pages processed by the OCR fallback.
title_match_threshold number 0.6 PDF title-match threshold. It must be greater than 0 and no greater than 1.

[browser]

Key Type Default Effect and constraints
extension_id string empty The Chrome extension ID allowed to use the native host. It must be 32 characters from a through p; an empty value disables the bridge.
extension_ids list of strings empty Additional Chrome-family extension IDs allowed to reach the native host alongside extension_id — e.g. an Edge Add-ons store copy or a second keyed build, which carry different IDs than the Chrome Web Store package. Each is 32 chars ap. The manifest's allowed_origins lists extension_id plus every entry here.
firefox_extension_id string empty The Firefox (Gecko) add-on ID allowed to use the native host — papio@orgmentem.com for the built extension. Accepts an email-style ID or a braced GUID; an empty value disables the Firefox bridge.
openurl_base_url string URL empty Legacy/default institutional OpenURL resolver base. It must use https://; an empty value prevents default-profile institutional routing. Existing query parameters are preserved when papio adds citation metadata. Prefer the institution's direct-link-enabled endpoint so a single electronic service bypasses the resolver menu.
shibboleth_entity_id string URL empty Default institution's Shibboleth IdP entityID (https://). When set, a provider login wall is auto-routed to this IdP (skipping the WAYF selector). Empty disables federated login-routing for the default profile.
proquest_account_id string digits empty Default institution's ProQuest account id (digits, max 64). When set, papio appends ?accountid=<id> to unlock the institution's ProQuest link-resolver without a manual sign-in. Empty disables the append. During papio init you may paste a ProQuest URL containing accountid= instead of the bare id.
download_adoption_root path string empty Root for browser-download adoption. When empty, the effective value is <data_dir>/adoptions; adoption is confined to a job subdirectory beneath this root.
action_expiry_seconds integer seconds 1800 Maximum open time for one browser handoff. It must not be negative.

papio init can derive openurl_base_url from a pasted library discovery URL (--institution-url) or from the resolver configured in Zotero.

The browser path uses the user's ordinary Chrome session. It is not configured with passwords, MFA, CAPTCHA tokens, or publisher credentials.

papio init collects extension_id and firefox_extension_id during setup (Firefox defaults to the built add-on's papio@orgmentem.com), or set them non-interactively with --extension-id / --firefox-extension-id, so the native messaging host installs on the first run.

[browser.resolvers]

Named resolver profiles are per-institution tables keyed by a lowercase alphanumeric name. Each carries its own OpenURL base and, optionally, the same shibboleth_entity_id and proquest_account_id login fields as the default [browser] institution — so a multi-institution user routes each job's login to the right library instead of inheriting the default's identity:

[browser.resolvers.campus]
openurl_base_url = "https://library.example.edu/discovery/openurl?institution=EXAMPLE"
shibboleth_entity_id = "https://idp.example.edu/idp/shibboleth"  # optional
proquest_account_id = "12345"                                     # optional

A profile may also be written as a bare string — campus = "https://library.example.edu/discovery/openurl?institution=EXAMPLE" — which is shorthand for a table with only openurl_base_url set. This keeps older single-base configs valid; add the table form when a profile needs its own login identity.

Select one with papio acquire --resolver campus, papio acquire --batch works.json --resolver campus, or the corresponding MCP field. The selected name is snapshotted in the job policy, so re-opened actions cannot silently fall back to another institution.

On a tracked Alma/Primo resolver page, the extension may follow the first same-origin resolveService link selected by the institution's Online Services order. This emulates resolver direct linking without accepting provider terms or initiating physical-item, scan, or interlibrary-loan requests. Script access remains constrained by extension/manifest.json host permissions; an unlisted custom resolver origin stays in assisted mode.

[zotio]

Key Type Default Effect and constraints
executable path or command string zotio zotio executable papio invokes at the Zotero boundary. Optional: an empty value disables the deep Zotero integration (auto-import, plan/apply, queue); ownership lookup then classifies every work as not-owned. Required only when auto_import = true.
timeout_seconds integer seconds 120 zotio command deadline. It must be between 5 and 600 seconds inclusive.
attachment_mode string stored zotio attachment mode. Allowed values are stored and linked-file.
auto_import boolean false Default acquisition policy for automatic zotio plan-and-apply after a job is ready. An acquire --auto-import request can opt in per job.
auto_enrich boolean true After the first applied auto-import, enables the conservative scoped zotio enrichment of missing DOI and abstract fields for the imported parent.
exception_tags boolean false Enables the reconciled exception-tag ledger: papio maintains papio:needs-action and papio:unavailable as Zotero automatic tags on provenance-confirmed personal-library items, reconciling job state with current attachment state (papio zotio tags reconcile runs one pass on demand). Requires executable and zotio ≥ 0.13.0. Lifecycle states are never tagged; a same-name manual tag is never retyped or removed. After a daemon reload, turning this off makes the next pass remove papio-owned tags.
unavailable_recheck_days integer days 14 How long an unavailable outcome parks an item before backfill re-checks it (open-access availability drifts upward). Must be between 1 and 365 inclusive.

papio invokes zotio but does not read or store Zotero credentials. Manual mutation remains preview-first: papio zotio plan returns immutable plans and papio zotio apply requires the exact confirmation SHA-256.

[hooks]

Key Type Default Effect and constraints
on_ready shell command string empty When set, runs once via the system shell (/bin/sh -c; cmd /C on Windows) each time a job reaches ready (validated artifact). Job metadata arrives as PAPIO_* environment variables. Fire-and-forget: a failing hook is recorded as a hook.on_ready job event but never fails or retries the job. Empty disables it. See the hooks guide.
timeout_seconds integer seconds 120 Deadline for one hook run. Validated (5..600) only when on_ready is set.

[notify]

Key Type Default Effect and constraints
enabled boolean true Enables best-effort local desktop notifications from the daemon. The daemon coalesces park and applied-import notices in a 60-second window.
webhook_url string URL empty When set, every notification is also delivered as a JSON POST ({source, event, message, watch_id, watch_label, count, sent_at}; plain notices carry only source, message, sent_at). Independent of enabled, which governs only the desktop channel. Must be an absolute http(s) URL. Delivery is best-effort and never fails the triggering work.
webhook_secret string empty Sent as Authorization: Bearer <secret> on webhook posts. Requires webhook_url.

[discovery]

Key Type Default Effect and constraints
sources string array empty (= ["openalex"]) Discovery backends for papio search and watches, in merge-preference order. Valid entries: openalex, semanticscholar (each at most once). Results merge with DOI-then-title deduplication; earlier backends win ties. Per-backend API keys live under [sources.<name>] (e.g. sources.semanticscholar.api_key, optional — Semantic Scholar works keyless at public rate limits).

[sources.<name>]

[sources] is a map of resolver policies. The supported built-in names are arxiv, europepmc, unpaywall, openalex, openalex_content, core, crossref_tdm, and semanticscholar (discovery-only; see [discovery]). Each named section accepts these keys:

Key Type Default Effect and constraints
enabled boolean source-specific; see below Enables the resolver policy.
api_key string empty Credential or token for a source that requires one. Doctor requires it for enabled openalex, core, and crossref_tdm; enabled OpenAlex also needs email.
rate_per_sec number source-specific; see below Per-source request-rate budget.
burst integer source-specific; see below Per-source burst budget.
max_cost_usd number 0 Monthly budget for paid sources.
base_url_for_dev string URL empty Test/development endpoint override. If set, it must start with http://127.0.0.1 or http://localhost; do not use it for a remote production endpoint.

Built-in source defaults

Source name enabled rate_per_sec burst
arxiv true 1 1
europepmc true 2 2
unpaywall true 1 1
openalex false 2 2
openalex_content false 0 0
core false 0.4 1
crossref_tdm false 1 1

Watch configuration

There is no [watch] section or watch-specific key in papio's TOML config. Watch query, year filters, OA filter, collection, cadence, and per-run cap are stored with each watch created by papio watch add or the corresponding MCP tool. Use papio watch list to inspect them and papio watch remove <id> to remove one.

Validation and file permissions

papio validates configuration when loading and saving it. It writes the config file with mode 0600 and its config directory with mode 0700; doctor reports a configuration permission failure when group or other read bits are present. Use papio doctor rather than weakening these permissions to diagnose a setup problem.