{"id":51181547,"url":"https://github.com/heidihelena/fullvahti","last_synced_at":"2026-06-27T07:03:24.614Z","repository":{"id":365003542,"uuid":"1267393714","full_name":"heidihelena/fullvahti","owner":"heidihelena","description":"FullVahti = Fetch + Write Zotero plugin for Vahtian tools","archived":false,"fork":false,"pushed_at":"2026-06-24T21:02:55.000Z","size":183,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T21:14:54.011Z","etag":null,"topics":["citations","fetch-pdf","write","zotero-plugin"],"latest_commit_sha":null,"homepage":"https://vahtian.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heidihelena.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T13:54:26.000Z","updated_at":"2026-06-24T20:35:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/heidihelena/fullvahti","commit_stats":null,"previous_names":["heidihelena/fullvahti"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/heidihelena/fullvahti","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heidihelena%2Ffullvahti","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heidihelena%2Ffullvahti/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heidihelena%2Ffullvahti/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heidihelena%2Ffullvahti/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heidihelena","download_url":"https://codeload.github.com/heidihelena/fullvahti/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heidihelena%2Ffullvahti/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34844350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["citations","fetch-pdf","write","zotero-plugin"],"created_at":"2026-06-27T07:03:23.938Z","updated_at":"2026-06-27T07:03:24.594Z","avatar_url":"https://github.com/heidihelena.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FullVahti\n\n**Find open-access full-text PDFs for your Zotero references — in two clicks, with a tidy report of what's still missing.**\n\nFullVahti is a [Zotero](https://www.zotero.org) plugin from the [Vahtian](https://vahtian.com) tool family. You select references (or tag them), FullVahti looks each one up on [Unpaywall](https://unpaywall.org) and [PubMed Central](https://pmc.ncbi.nlm.nih.gov), attaches the free, legal PDF when one exists, and labels every item with what happened:\n\n| Tag | Meaning |\n|---|---|\n| `fulltext:pdf-found` | PDF attached (or one was already there) |\n| `fulltext:pdf-missing` | No open-access copy exists — request it via your library |\n| `fulltext:check-needed` | Something went wrong or no DOI/PMID — worth a human look |\n\nAfter each run you also get **one** report note in your library (tagged `fullvahti:report`) listing everything *not* found and why — exactly the list you need for interlibrary loan requests or a PRISMA flow diagram. FullVahti never creates per-item notes: the attached PDF is its own record.\n\n**Open access only.** FullVahti never bypasses a paywall. Paywalled papers are honestly reported as missing. PubMed Central downloads are restricted to the [PMC Open Access Subset](https://pmc.ncbi.nlm.nih.gov/tools/openftlist/) via its official OA service — articles that are merely *readable* in PMC are reported for your library to request, not scraped. The report records the license of every PDF it attaches.\n\n## For PhD students \u0026 systematic reviewers\n\nThe whole point. A typical pass over a screening set, all from inside Zotero:\n\n1. **Collect** your references in Zotero (from a database export, a search, or [CiteVahti](https://vahtian.com)). Tag the set you're working — e.g. `cite:closer-look`.\n2. **Get the full texts** → *Tools → FullVahti: Find OA PDFs for tagged items.* Free legal PDFs get attached; everything else is labelled and listed in one report note — your ready-made interlibrary-loan / [PRISMA](https://www.prisma-statement.org/) \"to retrieve\" list. Set your library's OpenURL resolver and each missing item gets a one-click **Find in my library** link.\n3. **Catch retractions** → *Tools → FullVahti: Check tagged items for retractions.* Cross-checks PubMed **and** Crossref/Retraction Watch, so a retracted paper can't quietly stay in your review — including the 2024–2025 mass retractions PubMed doesn't index.\n4. **Sanity-check the metadata** → *Tools → FullVahti: Check citation metadata for tagged items.* Compares each record to Crossref and flags missing fields and mismatches (a mismatch often means the wrong DOI is attached). Read-only — it never edits your records.\n5. **Record decisions** (optional) — if you use CiteVahti for claim-by-claim appraisal, FullVahti is its safe write-back door: confirmed review tags land in Zotero only after a preview, and every write is audited and undoable.\n\nNothing leaves your machine except the DOI/PMID lookups to the open services named below. No accounts, no analytics.\n\n## Install (no technical skills needed)\n\n1. Download `fullvahti-x.y.z.xpi` from the [latest release](https://github.com/heidihelena/fullvahti/releases/latest) — right-click the file and choose **Save Link As…** if your browser tries to open it.\n2. In Zotero: **Tools → Plugins**, click the gear ⚙️ in the top-right, choose **Install Plugin From File…**, and pick the downloaded file.\n3. That's it. Updates install themselves.\n\n## Use\n\n- **For a few papers:** select them in your library, right-click → **FullVahti: Find Open-Access PDFs**.\n- **For a whole screening batch:** tag the references `cite:closer-look` (or your own tag — change it in Settings → FullVahti), then **Tools → FullVahti: Find OA PDFs for tagged items**.\n\nThe first run asks for your email once — Unpaywall and PubMed Central are free services that ask for a contact address. It is sent only to them, never to us.\n\nFullVahti works politely, one paper at a time, so a big batch takes a few minutes. You can keep using Zotero meanwhile.\n\n## Check for retractions\n\nA retracted paper is exactly what a screening workflow must catch — and with well over a hundred thousand retractions issued in 2024–2025 alone (the Hindawi/Wiley, Sage and IEEE sham-paper sweeps), many of them **never indexed in PubMed**, this matters more than ever. FullVahti flags retracted references using two sources:\n\n- **PubMed** (precise — the \"Retracted Publication\" publication type), and\n- **Crossref**, which now carries the [Retraction Watch database](https://www.crossref.org/blog/retraction-watch-retractions-now-in-the-crossref-api/) — this catches the mass retractions PubMed doesn't list.\n\nHow to run it:\n\n- **For a few papers:** select them, right-click → **FullVahti: Check for Retractions**.\n- **For a batch:** tag them and use **Settings → FullVahti → Check tagged items for retractions**, or **Tools → FullVahti: Check items tagged … for retractions**.\n\nEvery item is labelled, and you get a report note that calls out the retracted ones (with where the flag came from):\n\n| Tag | Meaning |\n|---|---|\n| `retraction:retracted` | PubMed or Crossref/Retraction Watch records this as retracted |\n| `retraction:none` | No retraction recorded in either source |\n| `retraction:check-needed` | No record found, or the lookup failed — worth a human look |\n\nFullVahti only *reads* the status from PubMed and Crossref — it never decides retraction itself. For each DOI it checks PubMed first, then Crossref; for a DOI-only item it resolves a PMID via PubMed's search before falling back to Crossref.\n\n## Check citation metadata\n\nZotero records imported from databases often have missing or wrong fields — and a wrong field can mean the **wrong DOI is attached to the record**. FullVahti compares each reference against [Crossref](https://www.crossref.org) and tells you what's off, **without changing anything**:\n\n- **For a few papers:** select them, right-click → **FullVahti: Check Citation Metadata**.\n- **For a batch:** tag them and use **Settings → FullVahti → Check citation metadata**, or the Tools menu.\n\n| Tag | Meaning |\n|---|---|\n| `citation:ok` | Matches Crossref |\n| `citation:incomplete` | Crossref has fields your record is missing |\n| `citation:mismatch` | A field disagrees with Crossref — **check before editing**, it may be the wrong DOI |\n| `citation:check-needed` | No DOI, or Crossref didn't have the record |\n\nThe report note lists every discrepancy side by side (*your value* vs *Crossref's*). **FullVahti is read-only here** — it never edits a field and never auto-\"fixes\" a mismatch, because a mismatch needs a human: it can mean the wrong paper is attached, and only you should decide. (Comparisons fold accents and formatting, and only flag fields Crossref actually provides, to avoid noise.)\n\n## Getting paywalled papers through your library\n\nFullVahti only ever downloads open-access copies — it never fetches paywalled PDFs, even on your university network, because automated downloading through institutional credentials can trip publishers' \"systematic download\" terms and get your *whole institution's* access cut off.\n\nInstead, it can hand the citation to your library. In **Settings → FullVahti**, paste your library's **OpenURL resolver** address. After that, every item with no open-access copy gets a **\"Find in my library\"** link in the report note — one click takes you to your library's resolver, where you fetch the licensed copy yourself. FullVahti does no paywalled downloading; it just builds the link.\n\n(Your library's resolver URL is usually on its website under \"link resolver\", \"OpenURL\", \"SFX\", or \"find it\" — ask a librarian if unsure.)\n\n## For CiteVahti users (advanced, off by default)\n\nFullVahti is [CiteVahti](https://vahtian.com)'s Zotero companion. It does the Zotero-side work CiteVahti deliberately stays out of — finding open-access PDFs, and writing **confirmed** review decisions back into your library — while CiteVahti keeps sole ownership of claim verification and the human-first rating workflow. FullVahti never verifies claims, never decides whether a paper supports a claim, never rates anything, and never receives manuscript text: it only ever sees an item key and a short list of allowlisted tags.\n\nWrite-back goes through a token-guarded endpoint on Zotero's local server (`127.0.0.1:23119` — nothing leaves your machine):\n\n1. Zotero → Settings → FullVahti → tick **Allow CiteVahti to write tags**, click **Generate new token**.\n2. Give that token to CiteVahti.\n\n### The safety invariant: no silent Zotero writes\n\nEvery write is **previewed, confirmed, audited, and undoable**. The door is closed unless you open it, only allowlisted tags can be written, and the endpoints enforce this regardless of what a caller sends:\n\n| Endpoint | Method | Purpose |\n|---|---|---|\n| `/fullvahti/ping` | GET | Availability + advertised `capabilities` and `allowedTagPrefixes` |\n| `/fullvahti/tag` | POST | Preview or apply a tag change |\n| `/fullvahti/audit` | GET | List recorded writes (token in query string) |\n| `/fullvahti/undo` | POST | Reverse a recorded write by its audit id |\n\n**Allowlisted tags only.** Even with a valid token, only tags beginning `cite:`, `fulltext:`, `retraction:`, `citation:`, `GRADE:`, `RoB2:`, `ROBINS-I:`, or `Quality:` are accepted; anything else is refused and nothing is written.\n\n**1 — Preview (dry run).** CiteVahti shows you exactly what would change before anything is written:\n\n```\nPOST /fullvahti/tag\n{ \"token\": \"…\", \"itemKey\": \"ABCD1234\", \"add\": [\"GRADE:high\"], \"remove\": [\"fulltext:pdf-missing\"], \"dryRun\": true }\n→ { \"ok\": true, \"dryRun\": true,\n    \"preview\": { \"before\": [...], \"after\": [...], \"willAdd\": [\"GRADE:high\"], \"willRemove\": [\"fulltext:pdf-missing\"],\n                 \"alreadyPresent\": [], \"alreadyAbsent\": [] } }\n```\n\n`willAdd` / `willRemove` are the *effective* change — tags already present (or already absent) are dropped, so the preview is exactly what will happen.\n\n**2 — Apply (after you confirm).** Same call without `dryRun`. The response returns what was applied and an audit id:\n\n```\n→ { \"ok\": true, \"itemKey\": \"ABCD1234\", \"applied\": { \"added\": [\"GRADE:high\"], \"removed\": [\"fulltext:pdf-missing\"] },\n    \"audit\": { \"id\": \"abc-123\", \"ts\": \"2026-06-24T…\" } }\n```\n\n**3 — Audit.** Every applied write is recorded locally (the token is never stored). CiteVahti can read it with `GET /fullvahti/audit?token=…`, and you can read it yourself — no token, no curl — via **Tools → FullVahti: Show CiteVahti write-back audit log**, which also lets you clear the history (clearing never changes tags already written).\n\n**4 — Undo.** Any recorded write can be reversed by its audit id (preview the reversal with `dryRun: true` first):\n\n```\nPOST /fullvahti/undo\n{ \"token\": \"…\", \"auditId\": \"abc-123\" }\n→ { \"ok\": true, \"undoOf\": \"abc-123\", \"applied\": { \"added\": [\"fulltext:pdf-missing\"], \"removed\": [\"GRADE:high\"] }, … }\n```\n\nCiteVahti's job is to obtain *your* confirmation for each verified decision; FullVahti's job is to make that write previewable, auditable, and reversible. Neither side bypasses the other.\n\n## Privacy\n\n- Lookups go directly from your computer to Unpaywall, NCBI/Europe PMC, and Crossref (for retraction status and citation-metadata checks), carrying the paper's DOI/PMID; your contact email goes only to the APIs that ask for it (Unpaywall, NCBI, Crossref), never to the sites PDFs are downloaded from. Nothing else, to no one else.\n- No analytics, no accounts, no Vahtian servers involved.\n\n## Development\n\nPlain JavaScript, no build step — the plugin **is** the repository. To make an `.xpi` by hand: zip the files with `manifest.json` at the zip root. Releases are built automatically by GitHub Actions when a `v*` tag is pushed.\n\nCompatibility: Zotero 7–9. When a new Zotero major lands, bump `strict_max_version` in [manifest.json](manifest.json) and re-release.\n\nTwo [Claude Code](https://claude.com/claude-code) skills live in `.claude/skills/`:\n`zotero-9-plugin-dev` (building/debugging/releasing a bootstrapped Zotero 7/8/9\nplugin — including the Zotero 8/9 preference-pane \"dead object\" pitfall) and\n`fullvahti-for-agents` (the write-back API contract for an integrating LLM agent\nsuch as CiteVahti).\n\n## License\n\n[Apache 2.0](LICENSE) © Heidi Andersén / Vahtian\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheidihelena%2Ffullvahti","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheidihelena%2Ffullvahti","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheidihelena%2Ffullvahti/lists"}