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.
- rrxiv-0.1.0/.dockerignore +19 -0
- rrxiv-0.1.0/.env.example +79 -0
- rrxiv-0.1.0/.github/workflows/release.yml +79 -0
- rrxiv-0.1.0/.github/workflows/test.yml +24 -0
- rrxiv-0.1.0/.gitignore +28 -0
- rrxiv-0.1.0/CHANGELOG.md +102 -0
- rrxiv-0.1.0/CITATION.cff +23 -0
- rrxiv-0.1.0/CLAUDE.md +26 -0
- rrxiv-0.1.0/CODE_OF_CONDUCT.md +31 -0
- rrxiv-0.1.0/Dockerfile +53 -0
- rrxiv-0.1.0/LICENSE +21 -0
- rrxiv-0.1.0/PKG-INFO +177 -0
- rrxiv-0.1.0/README.md +134 -0
- rrxiv-0.1.0/SECURITY.md +20 -0
- rrxiv-0.1.0/docker/entrypoint.sh +38 -0
- rrxiv-0.1.0/docs/internal/plan-cli-sigs-server.md +99 -0
- rrxiv-0.1.0/docs/internal/plan-fill-gaps.md +314 -0
- rrxiv-0.1.0/fly.toml +58 -0
- rrxiv-0.1.0/openapi.yaml +2795 -0
- rrxiv-0.1.0/pyproject.toml +111 -0
- rrxiv-0.1.0/scripts/build-seed-pdfs.py +456 -0
- rrxiv-0.1.0/scripts/dump_openapi.py +112 -0
- rrxiv-0.1.0/scripts/enrich-seed.py +329 -0
- rrxiv-0.1.0/scripts/regen_models.sh +62 -0
- rrxiv-0.1.0/scripts/sync_schemas.sh +84 -0
- rrxiv-0.1.0/seed/README.md +61 -0
- rrxiv-0.1.0/seed/active-replication.cir.json +119 -0
- rrxiv-0.1.0/seed/active-replication.pdf +0 -0
- rrxiv-0.1.0/seed/active-replication.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/active-replication.tex +86 -0
- rrxiv-0.1.0/seed/agents-as-editors.cir.json +86 -0
- rrxiv-0.1.0/seed/agents-as-editors.pdf +0 -0
- rrxiv-0.1.0/seed/agents-as-editors.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/agents-as-editors.tex +59 -0
- rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.cir.json +69 -0
- rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.pdf +0 -0
- rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/citation-vs-knowledge-graphs.tex +50 -0
- rrxiv-0.1.0/seed/claim-graph-first-class.cir.json +127 -0
- rrxiv-0.1.0/seed/claim-graph-first-class.pdf +0 -0
- rrxiv-0.1.0/seed/claim-graph-first-class.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/claim-graph-first-class.tex +78 -0
- rrxiv-0.1.0/seed/reproducibility-budgets.cir.json +112 -0
- rrxiv-0.1.0/seed/reproducibility-budgets.pdf +0 -0
- rrxiv-0.1.0/seed/reproducibility-budgets.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/reproducibility-budgets.tex +68 -0
- rrxiv-0.1.0/seed/retraction-as-data.cir.json +80 -0
- rrxiv-0.1.0/seed/retraction-as-data.pdf +0 -0
- rrxiv-0.1.0/seed/retraction-as-data.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/retraction-as-data.tex +58 -0
- rrxiv-0.1.0/seed/rrxiv-whitepaper.cir.json +114 -0
- rrxiv-0.1.0/seed/shrinkage-estimators.cir.json +76 -0
- rrxiv-0.1.0/seed/shrinkage-estimators.pdf +0 -0
- rrxiv-0.1.0/seed/shrinkage-estimators.source.tar.gz +0 -0
- rrxiv-0.1.0/seed/shrinkage-estimators.tex +58 -0
- rrxiv-0.1.0/src/rrxiv/__init__.py +3 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/agent_provenance.schema.json +175 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/annotation.schema.json +123 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/cir.schema.json +92 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/citation.schema.json +72 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/claim.schema.json +211 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/figure.schema.json +43 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/model_registry.schema.json +111 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/orcid_signing_key.schema.json +53 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/paper.schema.json +226 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/paper_list_item.schema.json +95 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/pulse_snapshot.schema.json +356 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/reproducibility_manifest.schema.json +151 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/revision_diff.schema.json +220 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/section.schema.json +55 -0
- rrxiv-0.1.0/src/rrxiv/_schemas/submission_request.schema.json +55 -0
- rrxiv-0.1.0/src/rrxiv/_schemas_manifest.txt +26 -0
- rrxiv-0.1.0/src/rrxiv/annotations/__init__.py +40 -0
- rrxiv-0.1.0/src/rrxiv/annotations/load.py +48 -0
- rrxiv-0.1.0/src/rrxiv/annotations/payloads.py +243 -0
- rrxiv-0.1.0/src/rrxiv/auth/__init__.py +69 -0
- rrxiv-0.1.0/src/rrxiv/auth/agent.py +298 -0
- rrxiv-0.1.0/src/rrxiv/auth/anonymous.py +110 -0
- rrxiv-0.1.0/src/rrxiv/auth/orcid.py +303 -0
- rrxiv-0.1.0/src/rrxiv/auth/refresh.py +55 -0
- rrxiv-0.1.0/src/rrxiv/cli/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/cli/annotation_post.py +567 -0
- rrxiv-0.1.0/src/rrxiv/cli/app.py +766 -0
- rrxiv-0.1.0/src/rrxiv/cli/auth.py +216 -0
- rrxiv-0.1.0/src/rrxiv/cli/conformance.py +230 -0
- rrxiv-0.1.0/src/rrxiv/cli/credentials.py +500 -0
- rrxiv-0.1.0/src/rrxiv/cli/login.py +542 -0
- rrxiv-0.1.0/src/rrxiv/cli/read_commands.py +497 -0
- rrxiv-0.1.0/src/rrxiv/cli/seed.py +489 -0
- rrxiv-0.1.0/src/rrxiv/cli/serve.py +124 -0
- rrxiv-0.1.0/src/rrxiv/cli/submit.py +385 -0
- rrxiv-0.1.0/src/rrxiv/client/__init__.py +53 -0
- rrxiv-0.1.0/src/rrxiv/client/async_client.py +330 -0
- rrxiv-0.1.0/src/rrxiv/client/auth.py +40 -0
- rrxiv-0.1.0/src/rrxiv/client/client.py +332 -0
- rrxiv-0.1.0/src/rrxiv/client/errors.py +120 -0
- rrxiv-0.1.0/src/rrxiv/client/retry.py +141 -0
- rrxiv-0.1.0/src/rrxiv/client/signatures.py +474 -0
- rrxiv-0.1.0/src/rrxiv/diff.py +293 -0
- rrxiv-0.1.0/src/rrxiv/doctor.py +288 -0
- rrxiv-0.1.0/src/rrxiv/graph.py +298 -0
- rrxiv-0.1.0/src/rrxiv/models/__init__.py +129 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/__init__.py +3 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/agent_provenance_schema.py +213 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/annotation_schema.py +124 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/cir_schema.py +77 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/citation_schema.py +245 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/claim_schema.py +236 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/figure_schema.py +34 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/model_registry_schema.py +89 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/orcid_signing_key_schema.py +41 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/paper_list_item_schema.py +71 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/paper_schema.py +184 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/pulse_snapshot_schema.py +260 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/reproducibility_manifest_schema.py +120 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/revision_diff_schema.py +190 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/section_schema.py +58 -0
- rrxiv-0.1.0/src/rrxiv/models/_generated/submission_request_schema.py +41 -0
- rrxiv-0.1.0/src/rrxiv/parser/__init__.py +29 -0
- rrxiv-0.1.0/src/rrxiv/parser/bibliography.py +174 -0
- rrxiv-0.1.0/src/rrxiv/parser/build.py +1222 -0
- rrxiv-0.1.0/src/rrxiv/parser/clean.py +277 -0
- rrxiv-0.1.0/src/rrxiv/parser/sidecar.py +287 -0
- rrxiv-0.1.0/src/rrxiv/parser/source_map.py +258 -0
- rrxiv-0.1.0/src/rrxiv/parser/tex.py +679 -0
- rrxiv-0.1.0/src/rrxiv/scaffold.py +271 -0
- rrxiv-0.1.0/src/rrxiv/server/__init__.py +33 -0
- rrxiv-0.1.0/src/rrxiv/server/annotations/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/annotations/router.py +421 -0
- rrxiv-0.1.0/src/rrxiv/server/annotations/threads.py +115 -0
- rrxiv-0.1.0/src/rrxiv/server/app.py +230 -0
- rrxiv-0.1.0/src/rrxiv/server/auth/__init__.py +1 -0
- rrxiv-0.1.0/src/rrxiv/server/auth/router.py +957 -0
- rrxiv-0.1.0/src/rrxiv/server/auth/signature_middleware.py +224 -0
- rrxiv-0.1.0/src/rrxiv/server/auth/templates.py +179 -0
- rrxiv-0.1.0/src/rrxiv/server/authors/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/authors/router.py +356 -0
- rrxiv-0.1.0/src/rrxiv/server/claims/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/claims/replication.py +214 -0
- rrxiv-0.1.0/src/rrxiv/server/claims/router.py +290 -0
- rrxiv-0.1.0/src/rrxiv/server/deps.py +204 -0
- rrxiv-0.1.0/src/rrxiv/server/discovery/__init__.py +7 -0
- rrxiv-0.1.0/src/rrxiv/server/discovery/router.py +146 -0
- rrxiv-0.1.0/src/rrxiv/server/errors.py +150 -0
- rrxiv-0.1.0/src/rrxiv/server/ids.py +53 -0
- rrxiv-0.1.0/src/rrxiv/server/metrics.py +146 -0
- rrxiv-0.1.0/src/rrxiv/server/models/__init__.py +5 -0
- rrxiv-0.1.0/src/rrxiv/server/models/router.py +209 -0
- rrxiv-0.1.0/src/rrxiv/server/observability.py +271 -0
- rrxiv-0.1.0/src/rrxiv/server/pagination.py +152 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/claim_ids.py +94 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/diff.py +369 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/projection.py +170 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/router.py +391 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/scopes.py +113 -0
- rrxiv-0.1.0/src/rrxiv/server/papers/slug.py +126 -0
- rrxiv-0.1.0/src/rrxiv/server/search/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/search/router.py +331 -0
- rrxiv-0.1.0/src/rrxiv/server/settings.py +195 -0
- rrxiv-0.1.0/src/rrxiv/server/snapshots/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/snapshots/router.py +137 -0
- rrxiv-0.1.0/src/rrxiv/server/stats/__init__.py +10 -0
- rrxiv-0.1.0/src/rrxiv/server/stats/cache.py +43 -0
- rrxiv-0.1.0/src/rrxiv/server/stats/pulse.py +674 -0
- rrxiv-0.1.0/src/rrxiv/server/stats/router.py +64 -0
- rrxiv-0.1.0/src/rrxiv/server/store/__init__.py +58 -0
- rrxiv-0.1.0/src/rrxiv/server/store/memory.py +228 -0
- rrxiv-0.1.0/src/rrxiv/server/store/protocol.py +278 -0
- rrxiv-0.1.0/src/rrxiv/server/store/sqlite.py +676 -0
- rrxiv-0.1.0/src/rrxiv/server/submissions/__init__.py +0 -0
- rrxiv-0.1.0/src/rrxiv/server/submissions/router.py +692 -0
- rrxiv-0.1.0/src/rrxiv/snapshot.py +318 -0
- rrxiv-0.1.0/src/rrxiv/testing/__init__.py +36 -0
- rrxiv-0.1.0/src/rrxiv/testing/live_server.py +102 -0
- rrxiv-0.1.0/src/rrxiv/testing/mock_server.py +338 -0
- rrxiv-0.1.0/src/rrxiv/validate/__init__.py +0 -0
- rrxiv-0.1.0/tests/conftest.py +17 -0
- rrxiv-0.1.0/tests/fixtures/meaty/figures/fig-meaty.tex +9 -0
- rrxiv-0.1.0/tests/fixtures/meaty/meaty.rrxiv.aux +9 -0
- rrxiv-0.1.0/tests/fixtures/meaty/meaty.tex +57 -0
- rrxiv-0.1.0/tests/fixtures/meaty-multifile/chapters/chapter01.tex +22 -0
- rrxiv-0.1.0/tests/fixtures/meaty-multifile/figures/fig-stack.tex +10 -0
- rrxiv-0.1.0/tests/fixtures/meaty-multifile/main-flat.rrxiv.aux +7 -0
- rrxiv-0.1.0/tests/fixtures/meaty-multifile/main-flat.tex +57 -0
- rrxiv-0.1.0/tests/fixtures/meaty-multifile/main.tex +33 -0
- rrxiv-0.1.0/tests/fixtures/minimal/minimal.bib +7 -0
- rrxiv-0.1.0/tests/fixtures/minimal/minimal.rrxiv.aux +8 -0
- rrxiv-0.1.0/tests/fixtures/minimal/minimal.tex +60 -0
- rrxiv-0.1.0/tests/test_annotation_round_trip.py +314 -0
- rrxiv-0.1.0/tests/test_annotations.py +287 -0
- rrxiv-0.1.0/tests/test_async_client.py +193 -0
- rrxiv-0.1.0/tests/test_auth_flows.py +304 -0
- rrxiv-0.1.0/tests/test_authors_router.py +164 -0
- rrxiv-0.1.0/tests/test_bibliography.py +156 -0
- rrxiv-0.1.0/tests/test_build.py +281 -0
- rrxiv-0.1.0/tests/test_claim_neighborhood.py +105 -0
- rrxiv-0.1.0/tests/test_clean.py +202 -0
- rrxiv-0.1.0/tests/test_cli_conformance.py +25 -0
- rrxiv-0.1.0/tests/test_cli_login.py +255 -0
- rrxiv-0.1.0/tests/test_cli_submit.py +338 -0
- rrxiv-0.1.0/tests/test_client.py +206 -0
- rrxiv-0.1.0/tests/test_client_retry.py +223 -0
- rrxiv-0.1.0/tests/test_conformance_e2e.py +214 -0
- rrxiv-0.1.0/tests/test_credentials.py +160 -0
- rrxiv-0.1.0/tests/test_diff.py +152 -0
- rrxiv-0.1.0/tests/test_doctor.py +73 -0
- rrxiv-0.1.0/tests/test_graph.py +248 -0
- rrxiv-0.1.0/tests/test_ids.py +49 -0
- rrxiv-0.1.0/tests/test_live_server_fixture.py +29 -0
- rrxiv-0.1.0/tests/test_meaty_claims.py +284 -0
- rrxiv-0.1.0/tests/test_metrics_endpoint.py +62 -0
- rrxiv-0.1.0/tests/test_mock_server.py +145 -0
- rrxiv-0.1.0/tests/test_models.py +190 -0
- rrxiv-0.1.0/tests/test_models_registry.py +159 -0
- rrxiv-0.1.0/tests/test_orcid_key_binding.py +357 -0
- rrxiv-0.1.0/tests/test_orcid_key_client.py +246 -0
- rrxiv-0.1.0/tests/test_pagination.py +107 -0
- rrxiv-0.1.0/tests/test_papers_projection_and_discovery.py +580 -0
- rrxiv-0.1.0/tests/test_refresh_and_rotate.py +254 -0
- rrxiv-0.1.0/tests/test_revision_diff_validation.py +55 -0
- rrxiv-0.1.0/tests/test_scaffold.py +93 -0
- rrxiv-0.1.0/tests/test_search_default_match.py +189 -0
- rrxiv-0.1.0/tests/test_search_filters.py +185 -0
- rrxiv-0.1.0/tests/test_seed_canonicalise.py +224 -0
- rrxiv-0.1.0/tests/test_seed_cli.py +456 -0
- rrxiv-0.1.0/tests/test_sentry_init.py +76 -0
- rrxiv-0.1.0/tests/test_server_auth_render.py +525 -0
- rrxiv-0.1.0/tests/test_server_cross.py +378 -0
- rrxiv-0.1.0/tests/test_server_submissions.py +535 -0
- rrxiv-0.1.0/tests/test_sidecar.py +225 -0
- rrxiv-0.1.0/tests/test_signatures.py +248 -0
- rrxiv-0.1.0/tests/test_smoke.py +7 -0
- rrxiv-0.1.0/tests/test_snapshot.py +130 -0
- rrxiv-0.1.0/tests/test_srv_phase2.py +845 -0
- rrxiv-0.1.0/tests/test_stats_pulse.py +415 -0
- rrxiv-0.1.0/tests/test_store_backends.py +326 -0
- rrxiv-0.1.0/tests/test_tex.py +165 -0
- rrxiv-0.1.0/uv.lock +1724 -0
rrxiv-0.1.0/.env.example
ADDED
|
@@ -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
|
rrxiv-0.1.0/CHANGELOG.md
ADDED
|
@@ -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.
|
rrxiv-0.1.0/CITATION.cff
ADDED
|
@@ -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.
|