Skip to content

Promote edge to stable: XID participant identity, XID download behind sign-in, privacy fixes - #3078

Merged
colinmegill merged 2 commits into
stablefrom
edge
Oct 16, 2026
Merged

colinmegill merged 2 commits into
stablefrom
edge

Conversation

@colinmegill

Copy link
Copy Markdown
Member

XID participant identity follows the request, the owner's allow-list and the conversation start; the participant XID download requires an owner or moderator; no subscription address stored when subscriptions are off; referrer stored without its query string; identity parameters redacted in the request log. No schema change.

colinmegill and others added 2 commits October 16, 2026 00:28
…criptions, referrer and logs (#3076)

* server: the participant XID download requires an owner or moderator

GET /api/v3/xid/<uuid>-xid.csv returned the full participant-to-xid map to
anyone holding the UUID. Minting the UUID was already owner/moderator-only,
but a copied or logged link still exposed every xid. The download now takes
the same owner/moderator check: no sign-in is 401, anyone else 403
(polis_err_xid_report_auth); the owner gets the same bytes as before and an
unknown UUID is still 404.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

* client-admin: download the participant XID list with the signed-in account

The XID list download is now an authenticated request: the button fetches it
with the admin's token and saves the file, instead of a bare link, and shows
an error when the site refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

* server: no subscription address is stored when subscriptions are off

POST /api/v3/convSubscriptions stored the participant's email even when the
conversation had subscriptions turned off. It now refuses with 403
(polis_err_subscriptions_disabled) and stores nothing; unsubscribing is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

* server: store the participant referrer without its query string

The embedding page's URL was stored as the participant's referrer with its
query string, which can carry the participant's xid. The referrer is now
stored as origin and path only, at both places it is written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

* server: redact identity parameters from the request log

The access log and the error handler recorded full URLs, including xid,
email, token and invite or login code query parameters, next to the IP and
user agent. Those parameters are now logged as [redacted]; path, status,
timing, IP and user agent are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t and the start (#3077)

- A request whose xid differs from the xid bound to its participant token is
  treated as that xid's participant, with the conversation's XID gates applied
  to it, and receives a token bound to it; it no longer acts as the token's
  participant. An empty or blank xid is treated as absent.
- An XID token issued for another conversation carries no user or
  participant into this one; its xid is resolved here like a first visit.
- When the identity is an xid and the conversation uses the XID allow-list,
  the xid must be on the owner's list on every action (join, vote, comment,
  next statement, participation init), so removing it takes effect. The list
  is the owner's: an xid listed from any of the owner's conversations is
  admitted to each of them, and never-listed xids are refused. Replacing the
  list replaces the owner's list, and reading it returns the owner's list.
- The allow-list setting is fixed once a conversation has started: changing
  it after anyone from outside the owner's site has joined is refused with
  409 polis_err_xid_whitelist_fixed_after_start, and the update is not
  applied.


Claude-Session: https://claude.ai/code/session_018NVGBuYCk4EZmiz4csUv9s

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Delphi Coverage Report

File Stmts Miss Cover
polismath/init.py 2 0 100%
polismath/benchmarks/bench_pca.py 128 107 16%
polismath/benchmarks/bench_repness.py 81 65 20%
polismath/benchmarks/bench_update_votes.py 38 28 26%
polismath/benchmarks/benchmark_utils.py 34 0 100%
polismath/components/init.py 1 0 100%
polismath/components/config.py 165 70 58%
polismath/conversation/init.py 2 0 100%
polismath/conversation/conversation.py 1291 264 80%
polismath/conversation/group_vote_counts.py 38 2 95%
polismath/conversation/manager.py 131 42 68%
polismath/database/init.py 1 0 100%
polismath/database/dynamodb.py 392 176 55%
polismath/database/postgres.py 414 95 77%
polismath/delphi_storage/init.py 1 0 100%
polismath/delphi_storage/codec.py 295 22 93%
polismath/delphi_storage/embedding_spool.py 58 6 90%
polismath/delphi_storage/golden_corpus.py 60 9 85%
polismath/delphi_storage/import_report.py 51 3 94%
polismath/delphi_storage/legacy_import.py 171 16 91%
polismath/delphi_storage/postgres.py 49 22 55%
polismath/delphi_storage/production_import.py 612 131 79%
polismath/delphi_storage/resource.py 179 32 82%
polismath/delphi_storage/scan_pacing.py 77 0 100%
polismath/delphi_storage/writer.py 193 18 91%
polismath/empty_output.py 29 2 93%
polismath/engine_adapter.py 317 265 16%
polismath/job_child/init.py 356 52 85%
polismath/job_child/census.py 101 8 92%
polismath/pca_kmeans_rep/init.py 5 0 100%
polismath/pca_kmeans_rep/clusters.py 257 38 85%
polismath/pca_kmeans_rep/corr.py 98 17 83%
polismath/pca_kmeans_rep/group_k_smoother.py 28 0 100%
polismath/pca_kmeans_rep/legacy_kmeans.py 197 8 96%
polismath/pca_kmeans_rep/pca.py 162 19 88%
polismath/pca_kmeans_rep/repness.py 226 5 98%
polismath/poller/init.py 4 0 100%
polismath/poller/admission.py 318 15 95%
polismath/poller/backfill.py 936 32 97%
polismath/poller/capacity.py 485 27 94%
polismath/poller/capacity_queue.py 248 13 95%
polismath/poller/coordinator_bridge.py 317 317 0%
polismath/poller/math_writer.py 113 3 97%
polismath/poller/promotion.py 112 4 96%
polismath/poller/readiness.py 368 13 96%
polismath/poller/rebuild_child.py 133 5 96%
polismath/poller/service.py 665 52 92%
polismath/poller/worker_pool.py 140 0 100%
polismath/queue/executor.py 223 111 50%
polismath/regression/init.py 4 0 100%
polismath/regression/clojure_comparer.py 188 20 89%
polismath/regression/comparer.py 891 640 28%
polismath/regression/datasets.py 135 27 80%
polismath/regression/recorder.py 36 27 25%
polismath/regression/utils.py 130 85 35%
polismath/replay/init.py 2 0 100%
polismath/replay/attribution_capture.py 49 0 100%
polismath/replay/axis_continuity.py 347 24 93%
polismath/replay/certify.py 999 59 94%
polismath/replay/crosslang.py 128 2 98%
polismath/replay/decision_ties.py 55 8 85%
polismath/replay/diagnostics.py 50 7 86%
polismath/replay/driver.py 73 1 99%
polismath/replay/dsn_admission.py 36 1 97%
polismath/replay/equiv_query.py 10 0 100%
polismath/replay/event_ingress.py 95 1 99%
polismath/replay/fixture_bundle.py 815 62 92%
polismath/replay/fixture_config.py 266 11 96%
polismath/replay/fixture_extract.py 285 12 96%
polismath/replay/fixture_generate.py 103 1 99%
polismath/replay/fixture_samples.py 157 14 91%
polismath/replay/fixture_selection.py 119 0 100%
polismath/replay/fixture_survey.py 147 11 93%
polismath/replay/legacy_pca.py 29 17 41%
polismath/replay/polarity.py 319 25 92%
polismath/replay/poller_equiv.py 776 99 87%
polismath/replay/prodclone.py 193 2 99%
polismath/replay/real_data.py 146 10 93%
polismath/replay/schedule.py 205 10 95%
polismath/replay/served_math.py 159 4 97%
polismath/replay/shard_bench.py 189 65 66%
polismath/replay/stagecompare.py 890 161 82%
polismath/replay/stages.py 256 40 84%
polismath/replay/stepcompare.py 86 1 99%
polismath/replay/store.py 120 11 91%
polismath/replay/tie_capture.py 107 19 82%
polismath/replay/types.py 71 6 92%
polismath/run_math_pipeline.py 284 93 67%
polismath/tools/engine_rebuild.py 230 11 95%
polismath/types.py 22 0 100%
polismath/utils/cli_flags.py 15 0 100%
polismath/utils/clj_hash.py 44 0 100%
polismath/utils/env_flags.py 13 0 100%
polismath/utils/general.py 63 39 38%
polismath/utils/output_profile.py 66 2 97%
polismath/utils/serialization.py 10 5 50%
polismath/utils/vote_convention.py 159 5 97%
umap_narrative/501_calculate_comment_extremity.py 113 38 66%
umap_narrative/502_calculate_priorities.py 136 103 24%
umap_narrative/700_datamapplot_for_layer.py 507 507 0%
umap_narrative/701_static_datamapplot_for_layer.py 310 310 0%
umap_narrative/702_consensus_divisive_datamapplot.py 435 275 37%
umap_narrative/801_narrative_report_batch.py 666 455 32%
umap_narrative/803_check_batch_status.py 268 148 45%
umap_narrative/llm_factory_constructor/init.py 2 0 100%
umap_narrative/llm_factory_constructor/model_provider.py 234 107 54%
umap_narrative/narrative_data.py 178 65 63%
umap_narrative/numerical_stages.py 54 25 54%
umap_narrative/polismath_commentgraph/init.py 1 0 100%
umap_narrative/polismath_commentgraph/cli.py 268 268 0%
umap_narrative/polismath_commentgraph/core/init.py 3 3 0%
umap_narrative/polismath_commentgraph/core/clustering.py 108 108 0%
umap_narrative/polismath_commentgraph/core/embedding.py 104 104 0%
umap_narrative/polismath_commentgraph/lambda_handler.py 219 219 0%
umap_narrative/polismath_commentgraph/schemas/init.py 2 0 100%
umap_narrative/polismath_commentgraph/schemas/dynamo_models.py 160 9 94%
umap_narrative/polismath_commentgraph/tests/conftest.py 17 17 0%
umap_narrative/polismath_commentgraph/tests/test_clustering.py 74 74 0%
umap_narrative/polismath_commentgraph/tests/test_embedding.py 55 55 0%
umap_narrative/polismath_commentgraph/tests/test_storage.py 87 87 0%
umap_narrative/polismath_commentgraph/utils/init.py 9 7 22%
umap_narrative/polismath_commentgraph/utils/converter.py 283 237 16%
umap_narrative/polismath_commentgraph/utils/group_data.py 332 289 13%
umap_narrative/polismath_commentgraph/utils/storage.py 609 489 20%
umap_narrative/reset_conversation.py 162 53 67%
umap_narrative/run_pipeline.py 342 212 38%
umap_narrative/topic_naming.py 148 11 93%
Total 25760 8027 69%

@colinmegill
colinmegill merged commit 86dc0ec into stable Oct 16, 2026
50 of 56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant