What resmon is
Connected app status
Open Connected app details in the header to inspect the runtime ID, last observation, process/start/version and saved schema version. The header checks every 15 seconds; pending or failed checks label the previous observation stale. A changed runtime requires Use this running app and a fresh successful observation. This acceptance lives in this mounted header only; reload starts a new observation. Other desktop requests are not pinned.
The runtime ID identifies one serving process and changes on restart. It is not a corpus, installation or build fingerprint; corpus and build identity remain unknown. Advanced Settings separately polls every 5 seconds and retains its attached-app versus daemon status.
MCP health and get_execution alone accept optional expected_runtime_id. Read health’s
identity.runtime_id, compare it with the app you intend to inspect, then supply it on
those reads. A mismatch refuses the read; other 16 reads and 7 confirmed writes keep their
existing behavior. Runtime matching does not authenticate a hostile local service.
Overview
resmon is an automated, customizable literature surveillance platform that monitors open-access scholarly repositories, aggregates newly published papers against user-defined criteria, and compiles chronological reading reports. By default it functions as a streamlined metadata and abstract extractor; an optional AI-powered pipeline can generate concise, customized summaries of abstracts, methodologies, results, and discussions. The desktop application surfaces this capability through a dashboard of active routines and recent activity, manual Deep Dive and Deep Sweep runs, scheduled Automated Deep Sweeps, a calendar of upcoming and past executions, and a local-first architecture that keeps every report and credential on the user’s own machine.
Key Features
-
Multi-repository ingestion across 26 scholarly sources — unified metadata normalization over arXiv, bioRxiv, CORE, CrossRef, DataCite, DBLP, DOAJ, Dryad, ERIC, EuropePMC, GovInfo, HAL, INSPIRE-HEP, medRxiv, NASA ADS, NDL Search, NIST Resource Metadata Management, OpenAIRE, OpenAlex, Open Library, OSTI.GOV, PLOS, PubMed, Semantic Scholar, Springer Nature, and Zenodo. Each client enforces per-source rate limiting, exponential backoff, and graceful degradation when a single source fails mid-sweep.
-
Three operational modes:
- Targeted Deep Dive — a focused, manual query against a single repository within a defined date range, with support for an ephemeral per-execution API key that never persists to disk.
- Broad Deep Sweep — a cross-repository manual query that applies Deep Dive parameters to every selected repository in one run.
- Automated Deep Sweep (Routine) — a background-scheduled Deep Sweep that runs on a cron expression, emits progress events, and optionally triggers email notifications on completion.
-
AI-powered summarization — optional dual-path LLM integration covering remote commercial APIs (BYOK) and local/open-weight model inference, with token-aware chunking and customizable summarization prompts applied to abstracts, methodologies, results, and discussions. API keys are stored per provider in the OS keyring (one slot for each of
anthropic,openai,google,xai,meta,deepseek,alibaba,local, andcustom), and every per-execution AI override panel on Deep Dive, Deep Sweep, and Routines exposes the full Settings → AI control set (Provider, Model, Length, Tone, Temperature, Extraction Goals) with per-field merge semantics so a single override never clobbers persisted defaults. -
Corpus analytics — a dedicated Analytics page computed entirely from papers already stored locally, so it makes no repository requests and costs no API quota. It reports which repositories contribute papers nothing else found (and which only duplicate others), the median discovery lag between a paper’s publication date and the moment resmon first saw it — a figure no repository publishes about itself — per-routine health with an explicit signal when a routine stops finding anything new, and publication volume over time by source or subject category. Counts are always shown; medians and percentages are withheld until the sample is large enough to mean anything, with the sample size displayed either way.
-
The watchdog — silent-failure detection — a literature monitor fails silently: a dead source and a quiet field produce the same empty inbox. resmon compares every source and routine against baselines drawn from its own execution history and reports where reality has departed from them, in two deliberately separate grades. Broken is a recorded fact — a source that failed to answer on three consecutive runs, a required API key that is not configured, an active routine overdue against its own observed cadence (which is what catches the background service being down). Failing to answer covers three things and not a fourth: a query that raised, an endpoint that could not be reached, and a reply resmon could not read. The middle two raise nothing and are recorded as completed runs returning zero, because every source client degrades rather than failing a sweep — before v1.8.6 they were indistinguishable here from a quiet field. A window the source cannot answer at all is deliberately excluded: ERIC and Open Library refusing a sub-year window is correct behaviour, not a fault. Looks unusual is an inference from the user’s own baseline — a source that reliably returned papers returning none for four runs running — and is always worded as a prompt to check, with the innocent explanation stated alongside it. Thresholds are conservative and published in the interface; findings can be muted individually, and a mute is dropped automatically once its condition clears. What cannot yet be judged is listed too, so a watchdog silent for want of data is never mistaken for a clean bill of health. Cadence advice derived from discovery lag rounds it out, carrying the honest caveat that the lag was measured through the user’s own polling interval.
-
Why nothing came back — a source that returns zero results says why, at the moment you are looking at the zero, and says not recorded where resmon does not know. Every client degrades rather than raising when an upstream fails, so until 1.8.6 a source whose endpoint answered 503, a source whose reply would not parse, and a source in a genuinely quiet field were the same row —
ok, zero results — on the monitor, in the report, and in the search record. resmon now records the reason at the HTTP boundary itself, so no source client has to be edited for an outage to be captured, and clients speak up only for what a status code cannot say: a window the source cannot answer at all, a reply that would not read, records dropped because their rights statement is not one resmon may store. The rule has no third state — a zero reason is a recorded fact with a named source of truth, or it is not recorded — and there is no backfill, because a reason reconstructed for a run nobody observed is a guess wearing a fact’s clothes. The reproducible search record stops counting an outage as a database that answered, the watchdog stops calling an outage the last successful run, and a baseline of what a source normally returns is no longer built from runs where it never answered. -
Corpus-wide Explorer — search and filter every paper resmon has collected, across all executions and routines, by free text over titles and abstracts, author, source, subject category, and publication date. Filters live in the URL, so a filtered view can be bookmarked, shared, or reached from the Analytics page by clicking a source or category. Built for scale: free text runs against an FTS5 index rather than a substring scan, authors and categories are filtered through normalized indexed tables rather than parsed at query time, and pagination seeks by sort key rather than counting rows — a page at row 90,000 of 100,000 costs 0.30 ms.
-
Semantic search over your own corpus — with an embedding model configured, the Explorer gains a Sort control that orders results by how close each paper is to what you typed rather than by date, and every result gains Papers like this one, its nearest neighbours in your corpus with the distance and the source of each. Closest ranks the corpus, not a keyword slice of it. Your other filters — source, category, author, date — still choose what is in scope and are untouched by the sort; the phrase you type is what distance is measured from rather than a word filter, so Closest finds papers that contain none of your words and the two sorts can return different numbers of papers. That is deliberate and it was bought with a measurement: on a real 15,707-paper corpus, ranking inside the text filter returned an empty page for eleven of twenty plain-English queries, while the same vectors unfiltered found a relevant paper for almost all of them. Papers resmon has not embedded yet are listed last and marked not ranked rather than given a large distance — they have not been judged distant, they have not been judged — and the count is shown. Vectors live in a plain
document_embeddingstable any SQLite can read; thesqlite-vecvirtual table over it is an index, rebuildable and never the only copy. The whole feature is absent, with a reason, when it cannot work — no embedding model, or a build that cannot load the extension — rather than present and inert. A local model is free and recommended (ollama pull nomic-embed-text): nothing leaves the machine and there is no key. An Anthropic key cannot do this — Anthropic does not offer an embeddings API — and neither agent CLI can either, so a subscription that covers your summaries does not cover semantic search; both are listed in the provider menu, disabled, with the reason, rather than quietly omitted. Embedding runs automatically after each sweep for the papers it found; everything already collected needs the one-off backfill, which can be stopped and restarted freely because it resumes from what is missing rather than from where it stopped. -
The same paper, found twice — resmon links records that look like the same work reaching it from two places, and never merges them. Insert-time deduplication catches an identical record arriving twice; it cannot catch an arXiv preprint and its Crossref publication, which differ in date, author formatting and punctuation. A pair is linked when they share a DOI, or when their titles are near-identical and their text is close and both actually have an abstract — that last condition is not fussiness: a paper with no abstract is embedded from its title alone, so “similar titles and close vectors” would be one signal counted twice, and on a real corpus that produced 27 false links out of 30 (journal front matter, where every
Editorial Boardrecord matches every other). With it, hand-graded precision on two fresh samples is 83% and 87%. Both records are always kept, both counts stay, and collapsing is a per-view toggle that starts off — the corpus is worth having because it keeps what each source actually said. The link names its evidence, because “same DOI” is an identifier and “near-identical title” is an inference. -
Is this routine finding what you meant? — a coverage audit per routine, comparing its results against a plain-English intent you write for it (or, failing that, its keyword string — and it says which, because comparing keywords against results those keywords produced is measuring a query against itself). Two lists: the results sitting furthest from the intent, and papers already in your corpus that this routine never returned. The cutoff is drawn from the routine’s own distribution rather than an absolute distance, because a fixed number means different things on different models and subjects, and below a dozen embedded results it declines to draw one at all. The governing limitation is stated everywhere the audit appears rather than in a footnote: resmon can only compare against papers it already holds — “missed” means missed by this routine and found by something else, never missed by resmon. You write the intent in the routine editor (What this routine is really looking for), and each list is a page of 25 that says “Showing 25 of N” when there are more.
-
Watch a person, not just a set of words — a watch profile is somebody you point a routine at: their name, the identifiers you have for them, where they work. A routine can then follow them for new papers or for retractions on the papers already matched to them, instead of you guessing a keyword query that catches them and nothing else. Profiles export and import as JSON on one documented shape, and a curated starter set ships in the repository — every ORCID in it checked against the public ORCID record on the day it was added, with the citation stored beside it, and one candidate dropped because his record has no public name to check against. The rule the whole feature is arranged around: an author match is a string match unless the source gave an identifier, and resmon says so on every paper. Every match records its basis —
ORCID match,name + affiliation, orname only— and the badge is on the paper, in the Explorer, on the profile page, in the run’s report and on the MCP surface; aname_onlymatch is never presented as the person, and the string the source actually returned is shown beside it so you can judge the match yourself. A profile with no ORCID is told, while you are typing it, that every match it can ever produce will be name-only. Nineteen of resmon’s twenty-six sources can be asked about an author at all, each by a syntax established against a live record and cited in the catalog; the seven that cannot say so on the run’s own source row rather than returning a bare zero. And a source’s author search is treated as a candidate generator, never a verdict — every record that comes back is re-checked against the profile locally, and the ones that do not survive are counted in the run rather than silently dropped. Theretractionsmode adds no provider and invents no finding: it is the lifecycle check you already have, restricted to that person’s papers, reporting notices resmon already holds — and the basis rides on every finding, because a retraction attached to a name is not a retraction attached to a person. All of that is measured, and the numbers are in the app. Four profiles across all twenty askable sources produced 1,369 matches on 7 September 2026: 90% were name-only, 9% carried an ORCID, and 0.7% — nine matches in all — reached name + affiliation, so the basis rule is in practice binary. Hand-graded: 30 of 30 ORCID matches were the right person; of the thirty name-only matches, seventeen named somebody whose identity could be checked and fifteen were right, one was wrong — a library catalogue that lists a molecular biologist as co-author of a book on Coleridge, which resmon matched faithfully and labelled name only, which is exactly the right amount of confidence to have had. The nine name + affiliation matches span at least five different researchers with one common name at one large institution, all labelled identically: the affiliation raises the evidence, it does not identify a person. Matching now preserves initials and surname particles and compares affiliations as complete token runs (MIT does not match SUMMIT). Exact mononyms or shared single-token names remain visibly ambiguous candidates. Conflicting ORCIDs are displayed as counterevidence and prevent affiliation promotion; equal ORCIDs retain the identifier basis. Older saved matches and papers are kept unchanged and labeled Historical match — not rechecked under the current matching policy in Profiles and Explorer. The field-test counts above describe the earlier policy, not measured accuracy of these corrections. -
A reading queue, for the papers you actually meant to read — a run’s Papers tab lists what that execution found, one paper at a time with its own Save button, instead of leaving you to read ids out of a Markdown report. Saved papers go to a Reading queue page with two states, To read and Read, and the same Why am I seeing this? evidence every other view shows, because it is the same stored record rather than a copy of one. Membership is keyed on the paper’s stored id, so a later run that rediscovers the same record keeps the state you gave it — saving it again never puts something you have read back on the pile — and two records that merely look like the same work stay two entries, on the same rule that makes resmon link near-duplicates rather than merge them. Removing an entry removes membership and nothing else: the paper, its authors and every run that found it are untouched, and the only thing in resmon that deletes a paper is still the Danger Zone you operate yourself. Tick papers and export them as BibTeX, RIS or CSV through the same exporter Results & Logs uses — over exactly the papers ticked on the page in view, so a paper that has left that page cannot end up in the file. It holds no notes, no PDFs, no reminders and ranks nothing — each of those is a decision rather than a gap, and an existing install starts the queue empty because resmon never observed which papers you meant to read before it existed.
-
Match transparency — “why am I seeing this?” — every result in the Explorer can say which of the keywords from the runs that found it actually appear in it, and in which field: title, abstract, subject categories, or author list. Matching is on whole words, so
AIdoes not matchsaid, and a quoted phrase must appear as a phrase. The same arithmetic run corpus-wide gives per-keyword marginal contribution on the Analytics page: how many papers each keyword found that no other keyword of yours did, so a term that only duplicates another becomes visible and can be retired. The feature is defined as much by what it refuses to claim: resmon stores no full text, and most sources are relevance-ranked rather than literal keyword filters, so a paper containing none of your keywords is expected rather than a fault. Every explanation carries those limits alongside the evidence rather than behind a tooltip, and names the source’s own documented keyword semantics. bioRxiv and medRxiv are the two exceptions at the API boundary: resmon retrieves date-range records and applies its own keyword filter instead of relying on an opaque upstream ranking. -
Reference-manager exports — selected executions’ papers export to BibTeX, RIS, or CSV as one file, alongside the Markdown, PDF, and LaTeX report bundle. Repeated stored document IDs appear once; distinct records remain separate. Papers are ordered by publication date, then internal ID, descending, independently of run selection order. BibTeX citation keys are allocated across the complete file; collision suffixes depend on its contents/order and are not permanent paper IDs. DOI-bearing entries are journal articles, others generic records, and BibTeX special characters are escaped. JSON remains API/MCP-only with CSV’s public fields and missing values as
null. Execution JSON can explicitly requestinclude_ids=truefor internal corpus IDs; MCP execution results use that option so a returned paper ID can be passed toexplain_match. Default JSON and CSV columns are unchanged. -
A Danger Zone that tells the truth — every destructive action states exactly what it does and does not remove, including whether your collected papers are affected. A dedicated Erase the paper corpus action removes every paper resmon has collected along with its lifecycle records; Erase all app data and Factory reset now include the corpus too. Before 1.7.0 nothing in the Danger Zone deleted a single paper — a “factory reset” left the entire corpus in place, which on a real install meant tens of thousands of papers surviving a reset that claimed to erase everything.
-
Corpus lifecycle — papers change after you find them — a monitor’s corpus is frozen at discovery time and silently goes stale. resmon unfreezes it: retractions and expressions of concern through Crossref, which has distributed the Retraction Watch database openly since 2023; preprints that have since reached a journal, via the bioRxiv API’s link to the published DOI; and newer versions of what you hold, on both bioRxiv and arXiv. Discovering after submission that a paper you built on was retracted is a career-grade problem, and no other literature monitor checks for any of it. The governing rule is enforced in code, not left to callers: resmon never asserts a lifecycle event on its own authority — every finding carries a resolvable link to the notice behind it, and one that cannot produce a link is refused at the database layer rather than recorded. The publisher’s own wording is stored and displayed verbatim, an expression of concern is graded below a retraction and said to be weaker, and a correction is treated as the routine scholarly upkeep it is rather than colored like an alarm. The check makes outbound requests, so it runs only when asked, bounded and resumable, and coverage is always reported alongside the findings — “no retractions found” means nothing if only a fifth of the corpus has been looked at, and papers with no usable identifier are counted separately rather than silently treated as clean.
-
Cross-platform desktop notifications — routine and manual completions raise a native OS notification on macOS, Linux, and Windows. The notification dispatcher is invoked both from the foreground app and from the headless
resmon-daemon, so completions fire even when the Electron UI is closed. -
Calendar scheduling — a calendar view of scheduled routines and historical executions, driven by the scheduler service and the
/api/calendar/eventsendpoint. -
Email notifications — per-routine and global SMTP-based notifications on routine completion, including attachment of the execution bundle produced by the shared export pipeline.
-
Backup and restore — the database, the Library vault and the reports in one hashed bundle, written into a folder you choose; Google Drive for desktop’s folder keeps it in Drive with no sign-in inside resmon.
-
Coverage before conclusions — opening a Results run shows answered, recorded non-answer and unknown sources above its six existing tabs. Counts use saved selected sources when that selection is established; otherwise they describe recorded sources and the full selection stays unknown. A selected source without an outcome is unknown. Genuine empty answers are included in answered; failed requests, unreadable replies and missing history never become evidence that no papers exist. View source details opens the Search record. Explicit report ZIP exports include generated
search-record.jsonandsearch-record.mdcompanions per execution; these describe saved facts at export time and leave the original report/log and existing PDF behavior unchanged. Routine mail bundles retain their existing behavior. -
The reproducible search record — every systematic review’s methods section requires the same account of a search: exact terms, sources queried, per-database retrieval counts, deduplication figures, date executed, and software version. It is assembled by hand in spreadsheets essentially everywhere. resmon exports the available saved facts for each execution as JSON or as a Markdown document ready for a methods section, in the shape a PRISMA 2020 flow diagram needs. The design work is in the labeling, not the layout: resmon’s counters do not map cleanly onto PRISMA’s boxes, and a record that printed them under PRISMA headings would publish a claim resmon cannot support. So each figure names the box it belongs in — or says it has none. resmon flags cross-source duplicates and keeps both rows, so the record reports duplicates found, never duplicates removed. “Already held from an earlier run” is an artefact of monitoring over time with no PRISMA equivalent, and is labeled as such. A figure that was never measured reads not recorded, never
0, because a reviewer reads0as a measurement. Sources that were selected but contributed nothing — a missing key, a failing endpoint — stay in the record, because a strategy listing them as searched would overstate its coverage. This ships the search log and deliberately does not chase Covidence: screening is a different product, and the search-and-document half is the part nobody does. -
Configuration export and import — serialize any Deep Dive, Deep Sweep, or routine configuration to JSON and re-import it on the same or another device for reproducible surveillance setups.
-
Local-first storage and logging — SQLite-backed state, per-execution log files, and a configurable export directory for report and artifact bundles. Data leaves the machine only for a feature in use: each query to the source it searches, paper text to an AI summary or embedding service you configure, DOIs to Crossref, bioRxiv and arXiv when papers are checked for retractions and newer versions, reports by email or webhook, and the check for an app update.
-
In-app About resmon page — a dedicated top-level page hosting four self-contained tabs: Tutorials (25 written walk-throughs covering the full app, every page, and every Settings sub-tab, with a shared Tutorial deep-link button rendered next to every page header and every Settings sub-panel header; 17 of them also have a video, linked rather than embedded — the tab renders no third-party frame and sends no request to YouTube, and every link opens in your own browser through the Electron shell), Issues (a credentials-free form that builds either a
mailto:link to the maintainer or a pre-populated GitHub issue deep link — the app never posts the report itself), Blog (an in-app reader for the public resmon blog athttps://ryanjosephkamp.github.io/resmon/, fed by the GitHub Pages source underdocs/_posts/and rendered through an origin-locked Electron<webview>), and About App (build version, recent-update notes, license, privacy notice, author links — relocated out of Settings).