rrxiv 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. rrxiv-0.1.0/.dockerignore +19 -0
  2. rrxiv-0.1.0/.env.example +79 -0
  3. rrxiv-0.1.0/.github/workflows/release.yml +79 -0
  4. rrxiv-0.1.0/.github/workflows/test.yml +24 -0
  5. rrxiv-0.1.0/.gitignore +28 -0
  6. rrxiv-0.1.0/CHANGELOG.md +102 -0
  7. rrxiv-0.1.0/CITATION.cff +23 -0
  8. rrxiv-0.1.0/CLAUDE.md +26 -0
  9. rrxiv-0.1.0/CODE_OF_CONDUCT.md +31 -0
  10. rrxiv-0.1.0/Dockerfile +53 -0
  11. rrxiv-0.1.0/LICENSE +21 -0
  12. rrxiv-0.1.0/PKG-INFO +177 -0
  13. rrxiv-0.1.0/README.md +134 -0
  14. rrxiv-0.1.0/SECURITY.md +20 -0
  15. rrxiv-0.1.0/docker/entrypoint.sh +38 -0
  16. rrxiv-0.1.0/docs/internal/plan-cli-sigs-server.md +99 -0
  17. rrxiv-0.1.0/docs/internal/plan-fill-gaps.md +314 -0
  18. rrxiv-0.1.0/fly.toml +58 -0
  19. rrxiv-0.1.0/openapi.yaml +2795 -0
  20. rrxiv-0.1.0/pyproject.toml +111 -0
  21. rrxiv-0.1.0/scripts/build-seed-pdfs.py +456 -0
  22. rrxiv-0.1.0/scripts/dump_openapi.py +112 -0
  23. rrxiv-0.1.0/scripts/enrich-seed.py +329 -0
  24. rrxiv-0.1.0/scripts/regen_models.sh +62 -0
  25. rrxiv-0.1.0/scripts/sync_schemas.sh +84 -0
  26. rrxiv-0.1.0/seed/README.md +61 -0
  27. rrxiv-0.1.0/seed/active-replication.cir.json +119 -0
  28. rrxiv-0.1.0/seed/active-replication.pdf +0 -0
  29. rrxiv-0.1.0/seed/active-replication.source.tar.gz +0 -0
  30. rrxiv-0.1.0/seed/active-replication.tex +86 -0
  31. rrxiv-0.1.0/seed/agents-as-editors.cir.json +86 -0
  32. rrxiv-0.1.0/seed/agents-as-editors.pdf +0 -0
  33. rrxiv-0.1.0/seed/agents-as-editors.source.tar.gz +0 -0
  34. rrxiv-0.1.0/seed/agents-as-editors.tex +59 -0
  35. rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.cir.json +69 -0
  36. rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.pdf +0 -0
  37. rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.source.tar.gz +0 -0
  38. rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.tex +50 -0
  39. rrxiv-0.1.0/seed/claim-graph-first-class.cir.json +127 -0
  40. rrxiv-0.1.0/seed/claim-graph-first-class.pdf +0 -0
  41. rrxiv-0.1.0/seed/claim-graph-first-class.source.tar.gz +0 -0
  42. rrxiv-0.1.0/seed/claim-graph-first-class.tex +78 -0
  43. rrxiv-0.1.0/seed/reproducibility-budgets.cir.json +112 -0
  44. rrxiv-0.1.0/seed/reproducibility-budgets.pdf +0 -0
  45. rrxiv-0.1.0/seed/reproducibility-budgets.source.tar.gz +0 -0
  46. rrxiv-0.1.0/seed/reproducibility-budgets.tex +68 -0
  47. rrxiv-0.1.0/seed/retraction-as-data.cir.json +80 -0
  48. rrxiv-0.1.0/seed/retraction-as-data.pdf +0 -0
  49. rrxiv-0.1.0/seed/retraction-as-data.source.tar.gz +0 -0
  50. rrxiv-0.1.0/seed/retraction-as-data.tex +58 -0
  51. rrxiv-0.1.0/seed/rrxiv-whitepaper.cir.json +114 -0
  52. rrxiv-0.1.0/seed/shrinkage-estimators.cir.json +76 -0
  53. rrxiv-0.1.0/seed/shrinkage-estimators.pdf +0 -0
  54. rrxiv-0.1.0/seed/shrinkage-estimators.source.tar.gz +0 -0
  55. rrxiv-0.1.0/seed/shrinkage-estimators.tex +58 -0
  56. rrxiv-0.1.0/src/rrxiv/__init__.py +3 -0
  57. rrxiv-0.1.0/src/rrxiv/_schemas/__init__.py +0 -0
  58. rrxiv-0.1.0/src/rrxiv/_schemas/agent_provenance.schema.json +175 -0
  59. rrxiv-0.1.0/src/rrxiv/_schemas/annotation.schema.json +123 -0
  60. rrxiv-0.1.0/src/rrxiv/_schemas/cir.schema.json +92 -0
  61. rrxiv-0.1.0/src/rrxiv/_schemas/citation.schema.json +72 -0
  62. rrxiv-0.1.0/src/rrxiv/_schemas/claim.schema.json +211 -0
  63. rrxiv-0.1.0/src/rrxiv/_schemas/figure.schema.json +43 -0
  64. rrxiv-0.1.0/src/rrxiv/_schemas/model_registry.schema.json +111 -0
  65. rrxiv-0.1.0/src/rrxiv/_schemas/orcid_signing_key.schema.json +53 -0
  66. rrxiv-0.1.0/src/rrxiv/_schemas/paper.schema.json +226 -0
  67. rrxiv-0.1.0/src/rrxiv/_schemas/paper_list_item.schema.json +95 -0
  68. rrxiv-0.1.0/src/rrxiv/_schemas/pulse_snapshot.schema.json +356 -0
  69. rrxiv-0.1.0/src/rrxiv/_schemas/reproducibility_manifest.schema.json +151 -0
  70. rrxiv-0.1.0/src/rrxiv/_schemas/revision_diff.schema.json +220 -0
  71. rrxiv-0.1.0/src/rrxiv/_schemas/section.schema.json +55 -0
  72. rrxiv-0.1.0/src/rrxiv/_schemas/submission_request.schema.json +55 -0
  73. rrxiv-0.1.0/src/rrxiv/_schemas_manifest.txt +26 -0
  74. rrxiv-0.1.0/src/rrxiv/annotations/__init__.py +40 -0
  75. rrxiv-0.1.0/src/rrxiv/annotations/load.py +48 -0
  76. rrxiv-0.1.0/src/rrxiv/annotations/payloads.py +243 -0
  77. rrxiv-0.1.0/src/rrxiv/auth/__init__.py +69 -0
  78. rrxiv-0.1.0/src/rrxiv/auth/agent.py +298 -0
  79. rrxiv-0.1.0/src/rrxiv/auth/anonymous.py +110 -0
  80. rrxiv-0.1.0/src/rrxiv/auth/orcid.py +303 -0
  81. rrxiv-0.1.0/src/rrxiv/auth/refresh.py +55 -0
  82. rrxiv-0.1.0/src/rrxiv/cli/__init__.py +0 -0
  83. rrxiv-0.1.0/src/rrxiv/cli/annotation_post.py +567 -0
  84. rrxiv-0.1.0/src/rrxiv/cli/app.py +766 -0
  85. rrxiv-0.1.0/src/rrxiv/cli/auth.py +216 -0
  86. rrxiv-0.1.0/src/rrxiv/cli/conformance.py +230 -0
  87. rrxiv-0.1.0/src/rrxiv/cli/credentials.py +500 -0
  88. rrxiv-0.1.0/src/rrxiv/cli/login.py +542 -0
  89. rrxiv-0.1.0/src/rrxiv/cli/read_commands.py +497 -0
  90. rrxiv-0.1.0/src/rrxiv/cli/seed.py +489 -0
  91. rrxiv-0.1.0/src/rrxiv/cli/serve.py +124 -0
  92. rrxiv-0.1.0/src/rrxiv/cli/submit.py +385 -0
  93. rrxiv-0.1.0/src/rrxiv/client/__init__.py +53 -0
  94. rrxiv-0.1.0/src/rrxiv/client/async_client.py +330 -0
  95. rrxiv-0.1.0/src/rrxiv/client/auth.py +40 -0
  96. rrxiv-0.1.0/src/rrxiv/client/client.py +332 -0
  97. rrxiv-0.1.0/src/rrxiv/client/errors.py +120 -0
  98. rrxiv-0.1.0/src/rrxiv/client/retry.py +141 -0
  99. rrxiv-0.1.0/src/rrxiv/client/signatures.py +474 -0
  100. rrxiv-0.1.0/src/rrxiv/diff.py +293 -0
  101. rrxiv-0.1.0/src/rrxiv/doctor.py +288 -0
  102. rrxiv-0.1.0/src/rrxiv/graph.py +298 -0
  103. rrxiv-0.1.0/src/rrxiv/models/__init__.py +129 -0
  104. rrxiv-0.1.0/src/rrxiv/models/_generated/__init__.py +3 -0
  105. rrxiv-0.1.0/src/rrxiv/models/_generated/agent_provenance_schema.py +213 -0
  106. rrxiv-0.1.0/src/rrxiv/models/_generated/annotation_schema.py +124 -0
  107. rrxiv-0.1.0/src/rrxiv/models/_generated/cir_schema.py +77 -0
  108. rrxiv-0.1.0/src/rrxiv/models/_generated/citation_schema.py +245 -0
  109. rrxiv-0.1.0/src/rrxiv/models/_generated/claim_schema.py +236 -0
  110. rrxiv-0.1.0/src/rrxiv/models/_generated/figure_schema.py +34 -0
  111. rrxiv-0.1.0/src/rrxiv/models/_generated/model_registry_schema.py +89 -0
  112. rrxiv-0.1.0/src/rrxiv/models/_generated/orcid_signing_key_schema.py +41 -0
  113. rrxiv-0.1.0/src/rrxiv/models/_generated/paper_list_item_schema.py +71 -0
  114. rrxiv-0.1.0/src/rrxiv/models/_generated/paper_schema.py +184 -0
  115. rrxiv-0.1.0/src/rrxiv/models/_generated/pulse_snapshot_schema.py +260 -0
  116. rrxiv-0.1.0/src/rrxiv/models/_generated/reproducibility_manifest_schema.py +120 -0
  117. rrxiv-0.1.0/src/rrxiv/models/_generated/revision_diff_schema.py +190 -0
  118. rrxiv-0.1.0/src/rrxiv/models/_generated/section_schema.py +58 -0
  119. rrxiv-0.1.0/src/rrxiv/models/_generated/submission_request_schema.py +41 -0
  120. rrxiv-0.1.0/src/rrxiv/parser/__init__.py +29 -0
  121. rrxiv-0.1.0/src/rrxiv/parser/bibliography.py +174 -0
  122. rrxiv-0.1.0/src/rrxiv/parser/build.py +1222 -0
  123. rrxiv-0.1.0/src/rrxiv/parser/clean.py +277 -0
  124. rrxiv-0.1.0/src/rrxiv/parser/sidecar.py +287 -0
  125. rrxiv-0.1.0/src/rrxiv/parser/source_map.py +258 -0
  126. rrxiv-0.1.0/src/rrxiv/parser/tex.py +679 -0
  127. rrxiv-0.1.0/src/rrxiv/scaffold.py +271 -0
  128. rrxiv-0.1.0/src/rrxiv/server/__init__.py +33 -0
  129. rrxiv-0.1.0/src/rrxiv/server/annotations/__init__.py +0 -0
  130. rrxiv-0.1.0/src/rrxiv/server/annotations/router.py +421 -0
  131. rrxiv-0.1.0/src/rrxiv/server/annotations/threads.py +115 -0
  132. rrxiv-0.1.0/src/rrxiv/server/app.py +230 -0
  133. rrxiv-0.1.0/src/rrxiv/server/auth/__init__.py +1 -0
  134. rrxiv-0.1.0/src/rrxiv/server/auth/router.py +957 -0
  135. rrxiv-0.1.0/src/rrxiv/server/auth/signature_middleware.py +224 -0
  136. rrxiv-0.1.0/src/rrxiv/server/auth/templates.py +179 -0
  137. rrxiv-0.1.0/src/rrxiv/server/authors/__init__.py +0 -0
  138. rrxiv-0.1.0/src/rrxiv/server/authors/router.py +356 -0
  139. rrxiv-0.1.0/src/rrxiv/server/claims/__init__.py +0 -0
  140. rrxiv-0.1.0/src/rrxiv/server/claims/replication.py +214 -0
  141. rrxiv-0.1.0/src/rrxiv/server/claims/router.py +290 -0
  142. rrxiv-0.1.0/src/rrxiv/server/deps.py +204 -0
  143. rrxiv-0.1.0/src/rrxiv/server/discovery/__init__.py +7 -0
  144. rrxiv-0.1.0/src/rrxiv/server/discovery/router.py +146 -0
  145. rrxiv-0.1.0/src/rrxiv/server/errors.py +150 -0
  146. rrxiv-0.1.0/src/rrxiv/server/ids.py +53 -0
  147. rrxiv-0.1.0/src/rrxiv/server/metrics.py +146 -0
  148. rrxiv-0.1.0/src/rrxiv/server/models/__init__.py +5 -0
  149. rrxiv-0.1.0/src/rrxiv/server/models/router.py +209 -0
  150. rrxiv-0.1.0/src/rrxiv/server/observability.py +271 -0
  151. rrxiv-0.1.0/src/rrxiv/server/pagination.py +152 -0
  152. rrxiv-0.1.0/src/rrxiv/server/papers/__init__.py +0 -0
  153. rrxiv-0.1.0/src/rrxiv/server/papers/claim_ids.py +94 -0
  154. rrxiv-0.1.0/src/rrxiv/server/papers/diff.py +369 -0
  155. rrxiv-0.1.0/src/rrxiv/server/papers/projection.py +170 -0
  156. rrxiv-0.1.0/src/rrxiv/server/papers/router.py +391 -0
  157. rrxiv-0.1.0/src/rrxiv/server/papers/scopes.py +113 -0
  158. rrxiv-0.1.0/src/rrxiv/server/papers/slug.py +126 -0
  159. rrxiv-0.1.0/src/rrxiv/server/search/__init__.py +0 -0
  160. rrxiv-0.1.0/src/rrxiv/server/search/router.py +331 -0
  161. rrxiv-0.1.0/src/rrxiv/server/settings.py +195 -0
  162. rrxiv-0.1.0/src/rrxiv/server/snapshots/__init__.py +0 -0
  163. rrxiv-0.1.0/src/rrxiv/server/snapshots/router.py +137 -0
  164. rrxiv-0.1.0/src/rrxiv/server/stats/__init__.py +10 -0
  165. rrxiv-0.1.0/src/rrxiv/server/stats/cache.py +43 -0
  166. rrxiv-0.1.0/src/rrxiv/server/stats/pulse.py +674 -0
  167. rrxiv-0.1.0/src/rrxiv/server/stats/router.py +64 -0
  168. rrxiv-0.1.0/src/rrxiv/server/store/__init__.py +58 -0
  169. rrxiv-0.1.0/src/rrxiv/server/store/memory.py +228 -0
  170. rrxiv-0.1.0/src/rrxiv/server/store/protocol.py +278 -0
  171. rrxiv-0.1.0/src/rrxiv/server/store/sqlite.py +676 -0
  172. rrxiv-0.1.0/src/rrxiv/server/submissions/__init__.py +0 -0
  173. rrxiv-0.1.0/src/rrxiv/server/submissions/router.py +692 -0
  174. rrxiv-0.1.0/src/rrxiv/snapshot.py +318 -0
  175. rrxiv-0.1.0/src/rrxiv/testing/__init__.py +36 -0
  176. rrxiv-0.1.0/src/rrxiv/testing/live_server.py +102 -0
  177. rrxiv-0.1.0/src/rrxiv/testing/mock_server.py +338 -0
  178. rrxiv-0.1.0/src/rrxiv/validate/__init__.py +0 -0
  179. rrxiv-0.1.0/tests/conftest.py +17 -0
  180. rrxiv-0.1.0/tests/fixtures/meaty/figures/fig-meaty.tex +9 -0
  181. rrxiv-0.1.0/tests/fixtures/meaty/meaty.rrxiv.aux +9 -0
  182. rrxiv-0.1.0/tests/fixtures/meaty/meaty.tex +57 -0
  183. rrxiv-0.1.0/tests/fixtures/meaty-multifile/chapters/chapter01.tex +22 -0
  184. rrxiv-0.1.0/tests/fixtures/meaty-multifile/figures/fig-stack.tex +10 -0
  185. rrxiv-0.1.0/tests/fixtures/meaty-multifile/main-flat.rrxiv.aux +7 -0
  186. rrxiv-0.1.0/tests/fixtures/meaty-multifile/main-flat.tex +57 -0
  187. rrxiv-0.1.0/tests/fixtures/meaty-multifile/main.tex +33 -0
  188. rrxiv-0.1.0/tests/fixtures/minimal/minimal.bib +7 -0
  189. rrxiv-0.1.0/tests/fixtures/minimal/minimal.rrxiv.aux +8 -0
  190. rrxiv-0.1.0/tests/fixtures/minimal/minimal.tex +60 -0
  191. rrxiv-0.1.0/tests/test_annotation_round_trip.py +314 -0
  192. rrxiv-0.1.0/tests/test_annotations.py +287 -0
  193. rrxiv-0.1.0/tests/test_async_client.py +193 -0
  194. rrxiv-0.1.0/tests/test_auth_flows.py +304 -0
  195. rrxiv-0.1.0/tests/test_authors_router.py +164 -0
  196. rrxiv-0.1.0/tests/test_bibliography.py +156 -0
  197. rrxiv-0.1.0/tests/test_build.py +281 -0
  198. rrxiv-0.1.0/tests/test_claim_neighborhood.py +105 -0
  199. rrxiv-0.1.0/tests/test_clean.py +202 -0
  200. rrxiv-0.1.0/tests/test_cli_conformance.py +25 -0
  201. rrxiv-0.1.0/tests/test_cli_login.py +255 -0
  202. rrxiv-0.1.0/tests/test_cli_submit.py +338 -0
  203. rrxiv-0.1.0/tests/test_client.py +206 -0
  204. rrxiv-0.1.0/tests/test_client_retry.py +223 -0
  205. rrxiv-0.1.0/tests/test_conformance_e2e.py +214 -0
  206. rrxiv-0.1.0/tests/test_credentials.py +160 -0
  207. rrxiv-0.1.0/tests/test_diff.py +152 -0
  208. rrxiv-0.1.0/tests/test_doctor.py +73 -0
  209. rrxiv-0.1.0/tests/test_graph.py +248 -0
  210. rrxiv-0.1.0/tests/test_ids.py +49 -0
  211. rrxiv-0.1.0/tests/test_live_server_fixture.py +29 -0
  212. rrxiv-0.1.0/tests/test_meaty_claims.py +284 -0
  213. rrxiv-0.1.0/tests/test_metrics_endpoint.py +62 -0
  214. rrxiv-0.1.0/tests/test_mock_server.py +145 -0
  215. rrxiv-0.1.0/tests/test_models.py +190 -0
  216. rrxiv-0.1.0/tests/test_models_registry.py +159 -0
  217. rrxiv-0.1.0/tests/test_orcid_key_binding.py +357 -0
  218. rrxiv-0.1.0/tests/test_orcid_key_client.py +246 -0
  219. rrxiv-0.1.0/tests/test_pagination.py +107 -0
  220. rrxiv-0.1.0/tests/test_papers_projection_and_discovery.py +580 -0
  221. rrxiv-0.1.0/tests/test_refresh_and_rotate.py +254 -0
  222. rrxiv-0.1.0/tests/test_revision_diff_validation.py +55 -0
  223. rrxiv-0.1.0/tests/test_scaffold.py +93 -0
  224. rrxiv-0.1.0/tests/test_search_default_match.py +189 -0
  225. rrxiv-0.1.0/tests/test_search_filters.py +185 -0
  226. rrxiv-0.1.0/tests/test_seed_canonicalise.py +224 -0
  227. rrxiv-0.1.0/tests/test_seed_cli.py +456 -0
  228. rrxiv-0.1.0/tests/test_sentry_init.py +76 -0
  229. rrxiv-0.1.0/tests/test_server_auth_render.py +525 -0
  230. rrxiv-0.1.0/tests/test_server_cross.py +378 -0
  231. rrxiv-0.1.0/tests/test_server_submissions.py +535 -0
  232. rrxiv-0.1.0/tests/test_sidecar.py +225 -0
  233. rrxiv-0.1.0/tests/test_signatures.py +248 -0
  234. rrxiv-0.1.0/tests/test_smoke.py +7 -0
  235. rrxiv-0.1.0/tests/test_snapshot.py +130 -0
  236. rrxiv-0.1.0/tests/test_srv_phase2.py +845 -0
  237. rrxiv-0.1.0/tests/test_stats_pulse.py +415 -0
  238. rrxiv-0.1.0/tests/test_store_backends.py +326 -0
  239. rrxiv-0.1.0/tests/test_tex.py +165 -0
  240. rrxiv-0.1.0/uv.lock +1724 -0
@@ -0,0 +1,19 @@
1
+ .git
2
+ .gitignore
3
+ .venv
4
+ __pycache__
5
+ *.pyc
6
+ *.pyo
7
+ .mypy_cache
8
+ .ruff_cache
9
+ .pytest_cache
10
+ tests/
11
+ build/
12
+ dist/
13
+ *.egg-info
14
+ node_modules
15
+ .DS_Store
16
+ .envrc
17
+ .env
18
+ .claude
19
+ docs/
@@ -0,0 +1,79 @@
1
+ # rrxiv-python — reference server config.
2
+ #
3
+ # Copy this file to `.env` (gitignored) and edit. Or set the same vars
4
+ # via your shell / Fly.io secrets / docker -e flags.
5
+ #
6
+ # All vars are read by ServerSettings.from_env() at startup. Booleans
7
+ # accept 1/true/yes/on (any other value is False). Defaults shown here
8
+ # match `src/rrxiv/server/settings.py`.
9
+
10
+ # ---- Public-facing base URL the server is reachable at -----------------
11
+ # Used in OAuth redirect_uri construction and OpenAPI doc links.
12
+ # Local dev: http://127.0.0.1:8000/api/v0
13
+ # Local dev preview (.claude/launch.json): http://127.0.0.1:8765/api/v0
14
+ # Production (primary): https://api.rrxiv.com/api/v0
15
+ # rrxiv.org redirects to rrxiv.com via a Cloudflare page rule.
16
+ RRXIV_API_BASE=http://127.0.0.1:8000/api/v0
17
+
18
+ # ---- Storage --------------------------------------------------------
19
+ # memory:// volatile, default for tests
20
+ # sqlite:///./rrxiv.db file-backed, single-process
21
+ # sqlite:////data/rrxiv.db Fly.io persistent volume path
22
+ RRXIV_STORE_URL=sqlite:////tmp/rrxiv-dev.db
23
+
24
+ # ---- Dev mode -------------------------------------------------------
25
+ # True → ORCID + hCaptcha verification is stubbed, Ed25519 still real.
26
+ # False → real ORCID OAuth + real hCaptcha required for writes.
27
+ RRXIV_DEV_MODE=1
28
+
29
+ # ---- ORCID OAuth (production only when DEV_MODE=0) ------------------
30
+ # Register an app at https://orcid.org/developer-tools (free).
31
+ # Redirect URI must EXACTLY match what you register.
32
+ # RRXIV_ORCID_CLIENT_ID=APP-XXXXXXXXXXXXXXXX
33
+ # RRXIV_ORCID_CLIENT_SECRET=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
34
+ # RRXIV_ORCID_REDIRECT_URI=https://rrxiv.com/login/orcid-callback
35
+ # RRXIV_ORCID_AUTHORIZE_URL=https://orcid.org/oauth/authorize
36
+ # RRXIV_ORCID_TOKEN_URL=https://orcid.org/oauth/token
37
+ # RRXIV_ORCID_DEV_ID=0000-0001-0000-DEV1 # dev-mode iD returned for stubbed auth
38
+
39
+ # ---- hCaptcha (production anonymous submissions) --------------------
40
+ # Register at https://www.hcaptcha.com/ (free for non-enterprise).
41
+ # RRXIV_HCAPTCHA_SECRET=0x0000000000000000000000000000000000000000
42
+ # RRXIV_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
43
+
44
+ # ---- CORS allowlist -------------------------------------------------
45
+ # Comma-separated origins. Empty = `*` (dev mode only).
46
+ # Production: lock to the deployed web origin(s).
47
+ RRXIV_CORS_ORIGINS=
48
+
49
+ # RRXIV_ENABLE_CORS=1
50
+ # RRXIV_SIGNATURE_CLOCK_SKEW_SECONDS=300
51
+
52
+ # ---- Rate limits (requests per minute) ------------------------------
53
+ # These are advisory in v0.1 (no enforcement middleware yet); the limits
54
+ # are exposed via /version so clients know what to expect.
55
+ # RRXIV_RATE_LIMIT_ANONYMOUS_READ_RPM=120
56
+ # RRXIV_RATE_LIMIT_ORCID_READ_RPM=240
57
+ # RRXIV_RATE_LIMIT_AGENT_READ_RPM=600
58
+ # RRXIV_RATE_LIMIT_ORCID_WRITE_RPM=30
59
+ # RRXIV_RATE_LIMIT_AGENT_WRITE_RPM=30
60
+
61
+ # ---- Token / challenge lifetimes (seconds) --------------------------
62
+ # RRXIV_CHALLENGE_TTL_SECONDS=300
63
+ # RRXIV_TOKEN_TTL_SECONDS_ORCID=86400
64
+ # RRXIV_TOKEN_TTL_SECONDS_AGENT=2592000
65
+ # RRXIV_TOKEN_TTL_SECONDS_ANONYMOUS=3600
66
+ # RRXIV_IDEMPOTENCY_WINDOW_SECONDS=86400
67
+
68
+ # ---- Logging --------------------------------------------------------
69
+ # debug | info | warning | error
70
+ # RRXIV_LOG_LEVEL=info
71
+
72
+ # ---- Sentry (optional error tracking) -------------------------------
73
+ # Create a project at https://sentry.io ; pick "FastAPI" as the platform
74
+ # and copy the DSN. Init is a no-op if SENTRY_DSN is unset.
75
+ # SENTRY_DSN=https://<key>@o<org>.ingest.sentry.io/<project>
76
+ # SENTRY_ENVIRONMENT=production
77
+ # SENTRY_RELEASE=rrxiv-python@0.1.0 # defaults to rrxiv-python@<__version__>
78
+ # SENTRY_TRACES_SAMPLE_RATE=0.05 # 5% of transactions traced
79
+ # SENTRY_PROFILES_SAMPLE_RATE=0.0 # CPU profiling off by default
@@ -0,0 +1,79 @@
1
+ name: release
2
+
3
+ # Hands-off PyPI releases: publish whenever the version in pyproject.toml
4
+ # reaches a value that has no matching git tag yet. To cut a release you
5
+ # only bump `version` in pyproject.toml (in a normal PR) and merge — this
6
+ # job then builds, publishes to PyPI via Trusted Publishing (OIDC, no
7
+ # token), tags the commit, and creates the GitHub Release. No manual
8
+ # release step, no stored secrets.
9
+ #
10
+ # Guard: every push to main runs this, but it exits early (cheap) unless
11
+ # the current version has no `v<version>` tag. Re-running after a
12
+ # successful publish is a no-op because the tag now exists.
13
+ #
14
+ # One-time maintainer setup (external to this repo):
15
+ # PyPI Trusted Publisher for project "rrxiv" → owner random-walks,
16
+ # repo rrxiv-python, workflow release.yml, environment (blank).
17
+ on:
18
+ push:
19
+ branches: [main]
20
+ workflow_dispatch: {}
21
+
22
+ concurrency:
23
+ group: release
24
+ cancel-in-progress: false
25
+
26
+ jobs:
27
+ release:
28
+ name: Publish to PyPI on version bump
29
+ runs-on: ubuntu-latest
30
+ permissions:
31
+ id-token: write # PyPI Trusted Publishing (OIDC)
32
+ contents: write # push the tag + create the GitHub Release
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ with:
36
+ fetch-depth: 0 # need existing tags for the guard
37
+
38
+ - name: Resolve version + tag
39
+ id: v
40
+ run: |
41
+ version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
42
+ echo "version=$version" >> "$GITHUB_OUTPUT"
43
+ echo "tag=v$version" >> "$GITHUB_OUTPUT"
44
+ if git rev-parse "refs/tags/v$version" >/dev/null 2>&1; then
45
+ echo "already released v$version — nothing to do"
46
+ echo "release=false" >> "$GITHUB_OUTPUT"
47
+ else
48
+ echo "v$version has no tag — will release"
49
+ echo "release=true" >> "$GITHUB_OUTPUT"
50
+ fi
51
+
52
+ - uses: astral-sh/setup-uv@v3
53
+ if: steps.v.outputs.release == 'true'
54
+ with:
55
+ python-version: "3.11"
56
+
57
+ - name: Build sdist + wheel
58
+ if: steps.v.outputs.release == 'true'
59
+ run: |
60
+ rm -rf dist
61
+ uv build
62
+
63
+ # Publish BEFORE tagging: if the upload fails, no tag is written, so
64
+ # a fixed re-run can retry the same version. PyPI only consumes a
65
+ # version on a successful upload.
66
+ - name: Publish to PyPI (Trusted Publishing)
67
+ if: steps.v.outputs.release == 'true'
68
+ uses: pypa/gh-action-pypi-publish@release/v1
69
+
70
+ - name: Tag + GitHub Release
71
+ if: steps.v.outputs.release == 'true'
72
+ env:
73
+ GH_TOKEN: ${{ github.token }}
74
+ run: |
75
+ git tag "${{ steps.v.outputs.tag }}"
76
+ git push origin "${{ steps.v.outputs.tag }}"
77
+ gh release create "${{ steps.v.outputs.tag }}" dist/* \
78
+ --title "${{ steps.v.outputs.tag }}" \
79
+ --notes "Automated release of rrxiv ${{ steps.v.outputs.version }} on PyPI. See CHANGELOG.md for details."
@@ -0,0 +1,24 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: astral-sh/setup-uv@v3
14
+ with:
15
+ python-version: "3.11"
16
+ - run: uv sync --all-extras
17
+ - run: uv run ruff check .
18
+ - run: uv run mypy src/
19
+ - run: uv run pytest
20
+ # The committed openapi.yaml must match the reference server's
21
+ # generated OpenAPI. Fails the build if the API surface drifted from
22
+ # the docs (regenerate with: python scripts/dump_openapi.py).
23
+ - name: OpenAPI doc is in sync with the server
24
+ run: uv run python scripts/dump_openapi.py --check
rrxiv-0.1.0/.gitignore ADDED
@@ -0,0 +1,28 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ dist/
9
+ build/
10
+ .coverage
11
+ htmlcov/
12
+
13
+ # uv
14
+ .venv/
15
+
16
+ # Editor
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+ .DS_Store
21
+
22
+ # Generated
23
+ *.log
24
+
25
+ # Secrets — copy .env.example to .env and edit. Never commit .env.
26
+ .env
27
+ .env.local
28
+ .env.*.local
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ All notable changes to `rrxiv-python` are recorded here. The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows [SemVer](https://semver.org/spec/v2.0.0.html) once it ships its first stable release. While in pre-1.0, breaking changes can land at any minor version.
4
+
5
+ ## [0.1.0] — unreleased
6
+
7
+ First public release. Everything below ships in 0.1.0 (the package was
8
+ developed under `[Unreleased]` and had never been published to PyPI, so
9
+ all of it — the initial scaffolding through the productionization
10
+ sprint — lands together in the first tagged release).
11
+
12
+ ### Added
13
+
14
+ #### SRV sprint (May 2026)
15
+
16
+ - **`rrxiv submit` CLI** ([RRP-0016](https://github.com/random-walks/rrxiv/blob/main/proposals/0016-submission-request-schema.md), [RRP-0017](https://github.com/random-walks/rrxiv/blob/main/proposals/0017-revision-flow-and-diff.md)). New Typer subcommand `rrxiv submit <cir.json> <bundle.tar.gz>` posts to `/api/v0/submissions`. Resolves identity from the keyring (ORCID bearer or agent keypair via `rrxiv login`), computes the SHA-256 bundle hash, and supports `--revision-of <prior_paper_id>`, `--revision-summary <text>` (or `--revision-summary-file`), `--dry-run`, and `--identity {orcid|agent}`. Output: id_slug, paper_id, retrieval_uri, view URL; for revisions also a one-line diff summary (claims +N/-M/~K plus abstract flag). Supports `--json` for machine consumption. New module `rrxiv.cli.submit`.
17
+ - **Submission `dry_run` + `client_compile_hash`** ([RRP-0016](https://github.com/random-walks/rrxiv/blob/main/proposals/0016-submission-request-schema.md)). `POST /api/v0/submissions` now accepts `dry_run`, `client_compile_hash`, and `revision_summary` form fields. Dry-run validates + compiles + parses + diffs without persisting (returns 200 + `would_persist`); real submissions return 201 with extended body (`version`, `previous_version`, `id_slug`, `revision_diff`, `dry_run: false`). Hash mismatch → 400 `bundle_hash_mismatch`. Unknown `previous_version` → 400 `previous_version_not_found`.
18
+ - **Revision diff endpoint** ([RRP-0017](https://github.com/random-walks/rrxiv/blob/main/proposals/0017-revision-flow-and-diff.md)). New `GET /api/v0/papers/{id}/diff?from=<prior>` returns a `RevisionDiff` (added/removed/modified claims with word-level statement + proof hunks, abstract/topics deltas). Lineage check rejects unrelated papers with 400 `papers_not_in_same_lineage`. Same diff is attached inline to revision submission responses. New `rrxiv.server.papers.diff` module (pure functions: `compute_revision_diff`, `claim_local_id`, `papers_in_same_lineage`).
19
+ - **Revision summary annotation synthesis** ([RRP-0017](https://github.com/random-walks/rrxiv/blob/main/proposals/0017-revision-flow-and-diff.md)). When `revision_summary` form field is provided on a revision submission, the server synthesises a `revision_summary` annotation attached to the new paper, authored by the submitting identity. Author may supersede with richer structured highlights.
20
+ - **Errata listing** ([RRP-0017](https://github.com/random-walks/rrxiv/blob/main/proposals/0017-revision-flow-and-diff.md) companion). New `GET /api/v0/papers/{id}/errata` returns paginated erratum annotations for a paper, newest first.
21
+ - **Annotation threads** ([RRP-0018](https://github.com/random-walks/rrxiv/blob/main/proposals/0018-annotation-threads.md)). Annotations accept an `in_reply_to` field. Server validates: target exists (else 400 `in_reply_to_not_found`); same paper artefact (else `in_reply_to_artefact_mismatch`); not self-reply (`in_reply_to_self`). New `GET /api/v0/annotations/{id}/replies` returns direct children oldest-first. New `rrxiv.server.annotations.threads` module.
22
+ - **Server-derived `replication_status`** ([RRP-0019](https://github.com/random-walks/rrxiv/blob/main/proposals/0019-reproducibility-manifests.md), [RRP-0020](https://github.com/random-walks/rrxiv/blob/main/proposals/0020-author-claim-retraction.md)). On every claim read path the server recomputes `claim.replication_status` from accumulated annotations: independent-replication count ≥ per-discipline quorum → `replicated`; contradicts ≥ supports → `contradicted`; any non-superseded non-lifted `claim_retraction` → `retracted` (highest precedence). Quorum defaults: 1 math/formal, 2 algo/crypto, 3 ML/experimental, 5 behavioural. v0.x compromise: with zero annotations the persisted author-set value is honoured (Euclid corpus stays working without backfill). New `rrxiv.server.claims.replication` module.
23
+ - **New annotation types**: `revision_summary` (RRP-0017) and `claim_retraction` (RRP-0020) — both surface through the generic `POST /annotations` flow with structured payloads. Retraction's lift convention (`comment.in_reply_to + structured_payload.lifts_retraction=true` from the same author identity) reverts derivation to the normal rule.
24
+ - **Refined `replication` annotation payload** (RRP-0019): `reproduction_kind` discriminates `fresh_replication` from `reproduction_from_artifacts`; optional `confidence_interval`, `reproducibility_manifest_uri`, `reproducibility_manifest_hash` fields; backward-compat default of `fresh_replication` for pre-RRP annotations.
25
+ - **Author claim reproducibility manifest** (RRP-0019): `Claim.reproducibility = { manifest_uri, manifest_hash }` available end-to-end through the regenerated `rrxiv.models.ClaimReproducibility`.
26
+ - **Schema sync** (auto): submitted_request, revision_diff, reproducibility_manifest schemas mirrored from rrxiv into `src/rrxiv/_schemas/` + pydantic models regenerated under `src/rrxiv/models/_generated/`. `rrxiv.models` re-exports `SubmissionRequest`, `RevisionDiff`, `ReproducibilityManifest`, `VersionRef`, `AddedClaim`, `RemovedClaim`, `ModifiedClaim`, `DiffHunk`, `TextDiff`, `ClaimReproducibility`, `ExpectedOutput`, plus supporting enums.
27
+ - **Test coverage** (`tests/test_srv_phase2.py`): 21 new tests for diff computation, diff endpoint, errata listing, in_reply_to validation, replies endpoint, retraction derivation, retraction lift, quorum lookup, dry-run, hash mismatch, and revision_summary synthesis.
28
+
29
+ #### Earlier in [Unreleased]
30
+
31
+ - **Paper list-item projection** ([RRP-0012](https://github.com/random-walks/rrxiv/blob/main/proposals/0012-paper-list-item-projection.md)). New `rrxiv.server.papers.projection` module computes the aggregate `stats` block (claims, replicated, contradicted, contested, untested counts plus paper-level rollup status) from claims + annotations. `GET /api/v0/papers` and `GET /api/v0/papers/{id}?include=stats` return the `PaperListItem` shape. New `rrxiv.models.PaperListItem`, `Stats`, `PaperStatus` regenerated from the new schema.
32
+ - **Server-minted `id_slug`** ([RRP-0013](https://github.com/random-walks/rrxiv/blob/main/proposals/0013-id-slug.md)). `POST /api/v0/submissions` mints a `rrxiv:YYMM.NNNNN` slug for each new paper; revisions inherit. Paper detail endpoints accept both the UUIDv7 `id` and the slug. New `rrxiv.server.papers.slug` module with `mint_slug`, `is_slug`, `find_paper_by_slug`.
33
+ - **Discovery endpoints**: `GET /api/v0/scopes`, `GET /api/v0/topics`, `GET /api/v0/claims/top`. Scopes are instance metadata (the five UI-discovery slices: `active`/`agent`/`human`/`contested`/`fresh` plus `all`). Topics are derived from the union of `paper.topics[]`. Top claims is a v0.1 stub ranking pending real telemetry.
34
+ - **Per-paper read endpoints**: `GET /api/v0/papers/{id}/claims`, `GET /api/v0/papers/{id}/related`, `GET /api/v0/papers/{id}/stats`. Related uses topic-overlap Jaccard for v0.1.
35
+ - **Filtered list endpoints**: `GET /api/v0/papers?scope=<id>&topic=<topic>`. `GET /api/v0/search/papers` accepts the full filter set (`scope`, `topic`, `author`, `status`, `claims_min`, `submitted_from`, `submitted_to`, `sort`).
36
+ - **Configurable CORS allowlist** via `RRXIV_CORS_ORIGINS=https://...,https://...`. Empty (dev default) → `*`. Production deployments lock down.
37
+ - **`rrxiv seed-store --from <dir> --store <url>` CLI** for bulk-loading CIRs into a store, bypassing `/submissions`. Idempotent on rebuild. Used by the production Dockerfile to bake the seed corpus into the image.
38
+ - **Dockerfile + fly.toml** for Fly.io deployments. SQLite on a persistent volume mount at `/data`. First-boot entrypoint seeds the database from the bundled `seed/` directory.
39
+ - `rrxiv conformance <server-url>` — CLI subcommand running the canonical 9-step end-to-end story (enroll → submit → list → search → annotate → snapshot → verify) against any rrxiv server. Useful for validating other-language client+server pairs.
40
+ - `rrxiv.testing.live_server` — pytest fixture spinning up a real uvicorn-backed reference server on an ephemeral 127.0.0.1 port. Importable by downstream client packages.
41
+ - **SQLite persistent store** ([RRP-0011](https://github.com/random-walks/rrxiv/blob/main/proposals/0011-sqlite-store.md)). New `rrxiv.server.store.SqliteStore` implementing the `Store` Protocol; configured via `RRXIV_STORE_URL=sqlite:///path/to/db.sqlite` or `rrxiv serve --store sqlite:///...`. `memory://` remains the default. Same routers, swappable backend.
42
+ - **Agent Ed25519 key rotation** ([RRP-0010](https://github.com/random-walks/rrxiv/blob/main/proposals/0010-agent-key-rotation.md)). `POST /auth/agent/{handle}/rotate-key` requires both an old-key transport signature and an inline new-key signature (proof of possession). Server atomically replaces the registered public key. New `rrxiv.auth.agent.{rotate_agent_key, build_rotation_payload}` helpers.
43
+ - **Bearer-token refresh** ([RRP-0009](https://github.com/random-walks/rrxiv/blob/main/proposals/0009-refresh-tokens.md)). `POST /auth/refresh` exchanges a still-valid bearer for a fresh one. Old token revoked atomically. New `rrxiv.auth.refresh_bearer` client helper. Anonymous tokens cannot be refreshed.
44
+ - **Server-side render endpoints** for the paste-back flows (RRP-0006 follow-up): `GET /auth/orcid/render` (mints a one-time paste code and shows it inline) and `GET /auth/anonymous/render` (hosts the hCaptcha widget). Closes the v0.1 UX gap where the CLI told the user to open a URL that 404'd.
45
+ - **Real ORCID OAuth + real hCaptcha** integrations on the reference server. Server-side httpx call to `orcid_token_url` and `api.hcaptcha.com/siteverify` when `dev_mode=False`. Tests mock the httpx call. Configuration via `RRXIV_ORCID_CLIENT_ID`, `RRXIV_ORCID_CLIENT_SECRET`, `RRXIV_ORCID_REDIRECT_URI`, `RRXIV_HCAPTCHA_SECRET`.
46
+ - **Submissions + sources + versions + search + snapshot creation** API routes, completing the OpenAPI surface: `POST /submissions` (multipart cir+bundle), `GET /papers/{id}/source`, `GET /papers/{id}/versions`, `GET /search/papers`, `GET /search/claims`, `POST /snapshots`, `GET /snapshots/{id}/blob`. Submissions are validated against `rrxiv.models.CIR` (strict). Anonymous identities forbidden from writes.
47
+ - **Strict annotation validation**: `POST /annotations` validates against `rrxiv.models.Annotation` (which embeds the per-type `structured_payload` validators). Cross-paper claim ID validation: `target_type=claim` requires `<paper_id>:c<n>` shape and existence; `target_type=paper` requires the paper to exist.
48
+ - **Signature-verification ASGI middleware** (RRP-0007 hardening). Replaces the dependency-based body-consume-and-restore with an ASGI middleware that runs before FastAPI's body parser. Multipart uploads (`POST /submissions`) now work with agent signatures.
49
+ - **End-to-end conformance fixture** (`tests/test_conformance_e2e.py`) — the canonical 9-step story against a live uvicorn server.
50
+ - `rrxiv login {orcid|agent|anonymous}` CLI subcommands per [RRP-0006](https://github.com/random-walks/rrxiv/blob/main/proposals/0006-cli-login.md). ORCID flow uses RFC 8252-style loopback OAuth with `--no-browser` paste fallback for SSH/container envs. Agent flow generates an Ed25519 keypair locally, signs the canonical enrollment payload, and persists both the bearer and the private key. `rrxiv login status` lists stored identities; `rrxiv logout` clears them.
51
+ - `rrxiv.cli.credentials` — OS-native keyring storage (macOS Keychain / Windows Credential Locker / Linux Secret Service) via `keyring`, with a 0600-file fallback at `~/.config/rrxiv/credentials.json` for headless environments. Multi-server, multi-identity-type slots so a developer can be logged in to multiple instances simultaneously. Override via `RRXIV_CRED_BACKEND={keyring|file}` and `RRXIV_CRED_DIR`.
52
+ - `rrxiv.client.signatures` — HTTP Message Signatures (RFC 9421) for agent writes per [RRP-0007](https://github.com/random-walks/rrxiv/blob/main/proposals/0007-message-signatures.md). `AgentSigningKey` wraps an Ed25519 keypair + handle; `AgentSigningAuth(httpx.Auth)` signs outgoing writes in place. Wired into `RrxivClient` and `AsyncRrxivClient` via `agent_signing_key=` constructor param. Server-side verifier (`verify_request_signature`) is reused by the reference server.
53
+ - `rrxiv.server` — FastAPI **reference server** per [RRP-0008](https://github.com/random-walks/rrxiv/blob/main/proposals/0008-reference-server.md). Per-domain layout (`auth/`, `papers/`, `claims/`, `annotations/`, `snapshots/`, `store/`), in-memory storage, dev-mode stubs for ORCID OAuth + hCaptcha, real Ed25519 verification (no shortcuts on the crypto path). RFC 9457 problem-details on errors. Idempotency-Key dedup. Sliding-window rate limiting. Cross-tests drive `RrxivClient` against the FastAPI app via Starlette's TestClient transport — proves wire compatibility end-to-end.
54
+ - `rrxiv serve` CLI subcommand — start the reference server with uvicorn. Defaults: `127.0.0.1:8000`, dev mode on.
55
+ - New `[agent]`, `[cli]`, `[server]` optional extras. `[dev]` includes all three so tests run against the full stack.
56
+ - New doctor checks: `[agent]`/`[cli]`/`[server]` extras present, keyring backend usable.
57
+ - `rrxiv.auth` module — token-acquisition flows for ORCID OAuth, agent enrollment (Ed25519), and anonymous attestation (hCaptcha) per [RRP-0005](https://github.com/random-walks/rrxiv/blob/main/proposals/0005-token-acquisition.md). Includes helpers (`build_orcid_authorization_url`, `exchange_orcid_code`, `enroll_agent`, `request_anonymous_challenge`, `verify_anonymous_challenge`) and request/response dataclasses for each flow. Optional `[agent]` extra pulls in `cryptography` for keypair signing.
58
+ - `rrxiv.client.errors.raise_for_status` — public status-code-to-exception mapper, shared by the sync client, async client, and auth flow helpers.
59
+ - `MockRrxivServer` handlers for the five `/auth/*` endpoints (orcid callback, agent enroll, anonymous challenge/verify) so client tests can exercise wire shape without a real server.
60
+ - TeX parser now uses `pylatexenc.latexwalker` (AST-based) per [RRP-0004](https://github.com/random-walks/rrxiv/blob/main/proposals/0004-tex-parser-ast.md) — comments and math mode no longer leak into extractors; multi-optional-arg macros parse correctly.
61
+ - `rrxiv doctor` — health check for the package + environment.
62
+ - `rrxiv diff` — semantic diff between two CIRs (claims, edges, citations, annotations, top-level fields).
63
+ - `rrxiv init` — scaffold a new rrxiv paper directory with a bundled `rrxiv.cls`.
64
+ - Standalone `Section` and `Figure` pydantic models (was: `$defs` of `cir.schema.json`).
65
+ - `AsyncRrxivClient` — `httpx.AsyncClient`-backed mirror of the sync client, with `async for paper in client.iter_papers(...)` style helpers.
66
+ - Configurable opt-in retry policy on the HTTP client; default = 3 retries / 30s total budget / no 5xx retry. `Retry-After` honoured; jittered exponential backoff otherwise.
67
+ - `rrxiv.annotations` module with per-type `structured_payload` validators per `spec/0006-annotations.md`. `rrxiv annotation validate <file>` CLI subcommand.
68
+ - `rrxiv.graph.ClaimGraph` — typed directed multigraph with traversal, cycle detection, Mermaid / DOT / JSON output. `rrxiv graph` CLI.
69
+ - TeX-to-text cleaning of CIR title/abstract/claim statements; the canonical fields no longer carry `\Large`, `\\[0.2em]`, `\texttt{...}` etc.
70
+ - `\bibitem` extraction from inline `thebibliography` blocks (the rrxiv whitepaper's bibliography now resolves into the CIR).
71
+ - Sidecar parser back-compat: accepts both the post-rename `RRXIV:` prefix and the pre-rename `RRVIX:` prefix (with a `DeprecationWarning`).
72
+
73
+ ### Changed
74
+
75
+ - **Project renamed: `rrvix` → `rrxiv`.** Every Python identifier (`from rrvix import ...` → `from rrxiv import ...`), the CLI command name (`rrvix parse` → `rrxiv parse`), the package name on PyPI (when published) all moved. The legacy `RRVIX:` sidecar prefix and `.rrvix.aux` extension are still parsed, with a deprecation warning.
76
+ - HTTP client errors now follow [RFC 9457 Problem Details](https://datatracker.ietf.org/doc/rfc9457/) for the parsed body when the server provides one.
77
+ - TeX parser now applies the TeX-to-text cleaner to title, abstract, and claim statements; CIRs read like prose.
78
+ - Edge marker delimiter in the cls is `|` (was `:`); the parser accepts both, with a deprecation warning on the colon form (per [RRP-0002](https://github.com/random-walks/rrxiv/blob/main/proposals/0002-edge-marker-delimiter.md)).
79
+
80
+ ### Fixed
81
+
82
+ #### Productionization sprint (July 2026)
83
+
84
+ - **Server-side paper-id minting (security)** ([RRP-0029](https://github.com/random-walks/rrxiv/blob/main/proposals/0029-paper-id-uuidv7.md)). `POST /api/v0/submissions` now always mints the UUIDv7 `paper.id` server-side for new (non-revision) submissions and ignores any client-supplied CIR `id`. Previously a submitter could silently overwrite **any** existing paper — including the seeded corpus — by echoing its id, because both stores upsert without an existence check and `rrxiv parse` always emits an id (the tex file stem, e.g. `main`). Revisions still legitimately target an existing id via `previous_version`.
85
+ - **ORCID login `redirect_uri` threading** (OAuth [RFC 6749 §4.1.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3)). The CLI loopback and paste flows now thread the `redirect_uri` they authorized with all the way into the ORCID token exchange — `exchange_orcid_code` gained a `redirect_uri` param, and `/auth/orcid/render` exchanges the code with its own URL — instead of falling back to the server's `RRXIV_ORCID_REDIRECT_URI`. Fixes the 401 that mismatch caused against production.
86
+ - **`rrxiv submit` view link** now points at the web host (`rrxiv.com/papers/<slug>`), not the API host (`api.rrxiv.com`), which 404'd. Client-side host mapping only; no server change.
87
+ - **`rrxiv seed-store --preserve-community`** — new opt-in flag (mutually exclusive with `--reset`) to reseed the canonical corpus on a live instance while leaving every externally submitted paper and **all** annotations intact. Replaces only the incoming seed papers via the new `Store.replace_seed_papers` (sqlite + memory). `--reset` keeps its full-clear behaviour for dev use.
88
+
89
+ #### Parser / schema
90
+
91
+ - DOI regex in `citation.schema.json` now allows lowercase letters (real-world DOIs are mixed case).
92
+ - Citation field URL strips a wrapping `\url{...}` macro so it passes pydantic `AnyUrl` validation.
93
+
94
+ ### Initial scaffolding
95
+
96
+ The starting point 0.1.0 was built on:
97
+
98
+ - Pydantic v2 models generated from the rrxiv JSON Schemas (paper, claim, annotation, citation, cir).
99
+ - TeX parser v0: regex-based walker, sidecar reader, build_cir() producing a validated CIR.
100
+ - `rrxiv parse` and `rrxiv validate` CLI commands.
101
+ - Schema sync mechanism (`scripts/sync_schemas.sh`) and codegen mechanism (`scripts/regen_models.sh`).
102
+ - MIT license.
@@ -0,0 +1,23 @@
1
+ cff-version: 1.2.0
2
+ message: >-
3
+ If you use rrxiv-python in academic work, please cite it. This entry
4
+ tracks the repository; for release-specific details see CHANGELOG.md.
5
+ title: "rrxiv-python: reference Python implementation of the rrxiv protocol"
6
+ abstract: >-
7
+ Reference Python implementation of the rrxiv protocol — parser, client
8
+ SDK, FastAPI reference server, conformance test suite, and CLI.
9
+ type: software
10
+ authors:
11
+ - family-names: Albis-Burdige
12
+ given-names: Blaise
13
+ email: albisburdige@protonmail.com
14
+ orcid: "https://orcid.org/0009-0002-0561-6499"
15
+ license: MIT
16
+ repository-code: "https://github.com/random-walks/rrxiv-python"
17
+ keywords:
18
+ - rrxiv
19
+ - scholarly-communication
20
+ - claim-graph
21
+ - preprint-server
22
+ - fastapi
23
+ - python-sdk
rrxiv-0.1.0/CLAUDE.md ADDED
@@ -0,0 +1,26 @@
1
+ # rrxiv-python — parser + SDK + reference server + CLI
2
+
3
+ Complements the workspace rules in `../../CLAUDE.md`. This is the Python side of
4
+ the protocol: the TeX→CIR parser, the client SDK, the FastAPI **reference
5
+ server** (ground truth for API behaviour), and the `rrxiv` CLI.
6
+
7
+ ## Toolchain (use `uv`, never `pip`)
8
+
9
+ - `uv run pytest` · `uv run ruff check .` · `uv run mypy src` (strict). Python 3.11+.
10
+ - All four must pass before a PR: ruff, mypy, pytest, and the OpenAPI drift-gate `uv run python scripts/dump_openapi.py --check`.
11
+
12
+ ## Structure
13
+
14
+ - `src/rrxiv/parser/` — TeX + sidecar → CIR (`build.py`).
15
+ - `src/rrxiv/client/` — SDK (incl. `AgentSigningKey`, RFC-9421 signing).
16
+ - `src/rrxiv/server/` — FastAPI reference server. Its generated OpenAPI is authoritative over the prose in `rrxiv/spec` / `schema/api.openapi.yaml`.
17
+ - `src/rrxiv/cli/` — the `rrxiv` CLI (`login`, `auth`, `submit`, `seed-store`, …).
18
+ - `src/rrxiv/_schemas/` — schemas **vendored** from `../rrxiv/schema` (byte-for-byte). `src/rrxiv/models/_generated/` — pydantic models from those schemas.
19
+
20
+ ## Schema sync (don't hand-edit generated files)
21
+
22
+ Edit canonical schemas in `../rrxiv/schema`, then here: `scripts/sync_schemas.sh` → `scripts/regen_models.sh` → tests. Revert timestamp-only churn in generated files; commit only meaningful diffs.
23
+
24
+ ## Identifier model gotcha (RRP-0029)
25
+
26
+ `paper.id` is a server-minted **UUIDv7** (the storage PK; `server/ids.py:uuid7`). But **claims and annotations are keyed off `id_slug`, NOT `paper.id`** — `claim.id` is `<id_slug>:<local_label>` and `claim.paper_id` is the `id_slug`. Every "claims/annotations for this paper" filter uses `claim_owner_key(paper)` (= `id_slug or id`), while blob/PK lookups (`get_paper`/`load_source`/…) use `paper.id`. Claim-prefix canonicalisation lives in `server/papers/claim_ids.py` and runs on **both** ingest paths (seed-store + `POST /submissions`). There's a regression test pinning slug-keyed resolution + cross-paper edge survival — keep it green when touching claim/annotation queries.
@@ -0,0 +1,31 @@
1
+ # Code of Conduct
2
+
3
+ This project adopts the [Contributor Covenant 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) as its code of conduct.
4
+
5
+ ## Our Pledge
6
+
7
+ We pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Standards
10
+
11
+ Examples of behavior that contributes to a positive environment include demonstrating empathy and kindness, being respectful of differing opinions and experiences, giving and gracefully accepting constructive feedback, accepting responsibility for mistakes and learning from them, and focusing on what is best for the overall community.
12
+
13
+ Unacceptable behavior includes the use of sexualized language or imagery, trolling or insulting comments, public or private harassment, publishing others' private information without explicit permission, and other conduct that could reasonably be considered inappropriate in a professional setting.
14
+
15
+ ## Enforcement
16
+
17
+ Project maintainers are responsible for clarifying and enforcing standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
18
+
19
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainers at the contact email below. All complaints will be reviewed and investigated promptly and fairly. All maintainers are obligated to respect the privacy and security of the reporter.
20
+
21
+ **Reporting contact:** Blaise Albis-Burdige — <albisburdige@protonmail.com>.
22
+
23
+ ## Scope
24
+
25
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces.
26
+
27
+ ## Attribution
28
+
29
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct/. Translations and the full enforcement guidelines are available at the same URL.
30
+
31
+ [Contributor Covenant text](https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md) is licensed [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
rrxiv-0.1.0/Dockerfile ADDED
@@ -0,0 +1,53 @@
1
+ # rrxiv reference server — example production image.
2
+ #
3
+ # Builds a single-process FastAPI app behind uvicorn, with SQLite as the
4
+ # persistent store. The bundled `seed/` corpus is baked into the image
5
+ # as a starting point; first-boot seeds the database if it's empty.
6
+ #
7
+ # Target: Fly.io with a persistent volume mounted at /data — but the
8
+ # image is portable to any container host.
9
+ #
10
+ # This Dockerfile is a REFERENCE for parties wanting to run their own
11
+ # rrxiv instance. The canonical instance at rrxiv.com is deployed from
12
+ # a separate private repo (rrxiv-instance) that extends this image with
13
+ # instance-specific seed data, paper-repo ingestion, and deploy config.
14
+ # See `.env.example` in this repo for the env vars the server reads;
15
+ # set them at deploy time via `fly secrets set …` or your platform's
16
+ # equivalent.
17
+
18
+ FROM python:3.12-slim
19
+
20
+ WORKDIR /app
21
+
22
+ # System deps — minimal. Python 3.12 includes sqlite3.
23
+ RUN apt-get update \
24
+ && apt-get install -y --no-install-recommends ca-certificates \
25
+ && rm -rf /var/lib/apt/lists/*
26
+
27
+ # Copy project metadata first for better Docker layer caching.
28
+ COPY pyproject.toml README.md ./
29
+ COPY src/ ./src/
30
+
31
+ # Install with the server extra. Use pip rather than uv because the
32
+ # Fly.io builder expects a self-contained image without external tools.
33
+ RUN pip install --no-cache-dir ".[server]"
34
+
35
+ # Bake the seed corpus into the image.
36
+ COPY seed/ /seed/
37
+
38
+ # Entrypoint script handles first-boot seeding then exec's uvicorn.
39
+ COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
40
+ RUN chmod +x /usr/local/bin/entrypoint.sh
41
+
42
+ # /data is the persistent volume mount point on Fly. The entrypoint
43
+ # initialises /data/rrxiv.db from /seed/ on first boot.
44
+ VOLUME ["/data"]
45
+
46
+ EXPOSE 8080
47
+
48
+ # Healthcheck — Fly's [checks] block will hit this independently, but
49
+ # the Docker HEALTHCHECK is useful for local testing.
50
+ HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
51
+ CMD python -c "import urllib.request,sys;urllib.request.urlopen('http://127.0.0.1:8080/api/v0/version').read()" || exit 1
52
+
53
+ ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
rrxiv-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Blaise Albis-Burdige
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.