Repository navigation
Choose the collector origin in one place, from a fixed set of trusted origins - #29
Merged
Merged
Conversation
This was referenced Oct 20, 2026
anth-volk
force-pushed
the
claude/configurable-collector-origin
branch
from
October 22, 2026 17:39
5c4c86f to
40e0073
Compare
… origins Every service module now resolves its collector origin through core/collector_origin.py. A new `collector_origin` module setting may name any origin in TRUSTED_COLLECTOR_ORIGINS and is refused otherwise; the set holds only the current production collector, so nothing changes until a release adds the collector's next origin. No environment variable chooses the destination, and the development URL still must be on loopback. The Orrery module now refuses a bad origin before it opens its queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`configured_collector_origin` treats an empty `development_collector_url` as unset, and the shared collector session and the Orrery module follow it. The telemetry module passes the raw setting to `CollectorDelivery`, which treats an empty string as a development URL: on its own it is refused as not loopback, and with `collector_origin` it is refused as setting both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The telemetry module now passes `development_collector_url` to `CollectorDelivery` with the same empty-means-unset rule as `configured_collector_origin`, so its delivery URL and the shared session's origin come from one rule, as the Orrery module's already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
claude/configurable-collector-origin
branch
from
October 22, 2026 18:07
40e0073 to
13c5235
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Hugging Face module's collector submissions should resolve their origin like the telemetry and Orrery modules: a trusted `collector_origin` is used for the `artifacts:submit` session and the submission client, the default is the production collector, and an untrusted origin, or one set together with a development URL, turns submissions off at start. Fails before the fix: 4 failed, 1 passed (the default case). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Hugging Face module chose its collector origin with its own expression, so it ignored `collector_origin` and could disagree with the telemetry and Orrery modules. It now calls `configured_collector_origin`, like they do: a trusted origin is used for the `artifacts:submit` session and the submission client, and an untrusted one turns submissions off at start with the existing warning. With no setting, submissions still go to the production collector, so behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `collector_origin` or `development_collector_url` that isn't a string, such as `true`, `1` or a list, should be refused with `ValueError` like any other unusable collector setting, so the Hugging Face module turns collector submissions off at start instead of stopping the emitter host. Fails before the fix: 3 failed, 21 passed. Each new case raises from the module's `initialize` (`AttributeError` for a boolean or a number, `TypeError` for a list), because `urlsplit` receives a value that isn't text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`resolve_collector_origin` passed any given setting to `urlsplit`, so a `collector_origin` or `development_collector_url` such as `true`, `1` or a list raised `AttributeError` or `TypeError`. The Hugging Face module catches only `ValueError`, so such a value stopped the whole emitter host instead of turning collector submissions off. The resolver now raises `ValueError` for any given setting that isn't a string, so every module applies its existing rule for a refused setting: telemetry and Orrery fail to start, and the Hugging Face module turns submissions off with its one warning. After the fix: `tests/test_collector_origin.py` 24 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove six tests that repeat others or check nothing new: a constant compared with itself, two module tests that the every-module test covers, an environment test that only set invented variable names (the existing `test_production_collector_cannot_be_replaced_by_environment` covers that), a loopback test that repeats `test_development_collector_must_be_on_loopback`, and a delivery test that only read attributes. Merge the three "without a setting" tests into one that starts the telemetry, Orrery and Hugging Face modules together and checks that each uses the production collector. Drop the `https://` prefix check, since resolving the origin already requires HTTPS. `tests/test_collector_origin.py`: 24 passed before, 16 passed after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The telemetry module's `CollectorDelivery` refused an empty `development_collector_url` until this branch, so "as modules have always treated" was inaccurate. Only the Orrery and Hugging Face modules did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #28
Summary
The emitter now chooses the collector origin (the address that receives the build's Hugging Face token) in one place,
core/collector_origin.py. A newcollector_originmodule setting is accepted only when it names an origin in a fixed set,TRUSTED_COLLECTOR_ORIGINS.It changes no behaviour. The set holds only
PRODUCTION_COLLECTOR_URL, the current default, so every build sends to the same collector as before. It adds no environment variable.Plan item C3 moves the emitter to the collector's own hostname. The decisions it waited on are made:
This PR doesn't add that hostname. The follow-up (tracker task 5.1 · 2) adds it to
TRUSTED_COLLECTOR_ORIGINSand makes it the default.What changes
One resolver,
core/collector_origin.py, with two functions:resolve_collector_origin(development_collector_url=..., collector_origin=...);configured_collector_origin(configuration).With neither setting, it returns
PRODUCTION_COLLECTOR_URL. Every module that sends requests to the collector now calls it:TelemetryModule(telemetry/service/module.py), for the shared collector session;CollectorDelivery(telemetry/service/collector.py), for run registrations and event batches;OrreryModule(orrery/service/module.py), for the shared session;HuggingFaceModule(huggingface/service/module.py), for Submit staged bundles' calibration diagnostics and Orrery inputs to the collector #27's collector submissions. That covers theartifacts:submittoken exchange and the requests that create, complete and check submissions.A fixed set of trusted origins,
TRUSTED_COLLECTOR_ORIGINSincore/constants.py. It holds onlyPRODUCTION_COLLECTOR_URL.A
collector_originmodule setting. It's accepted only when it is an HTTPS origin in the trusted set. Setting it together withdevelopment_collector_urlis also refused, and so is a value of either setting that isn't a string (such astrue,1or a list), which the resolver refuses withValueError. When a setting is refused:ValueErrorat start;OrreryModule.initializechecks the origin before it opens its queue, so a refused origin no longer leaves the queue open.An empty setting counts as unset in every module, for both
development_collector_urlandcollector_origin.What stays the same:
test_production_collector_cannot_be_replaced_by_environmentstill passes.Collector submissions ignored the setting before this update
After
main(with #27, released as 0.8.0) was merged in, the Hugging Face module still chose its origin with its own expression:_development_collector_url(...) if ... else _collector_origin(PRODUCTION_COLLECTOR_URL).This description earlier predicted the shared session would then raise "Provider modules must use the same collector origin." It wouldn't have: the Hugging Face module opens its own
artifacts:submitsession, not the shared one. Instead, the module ignoredcollector_origin:PRODUCTION_COLLECTOR_URL;Nothing sets
collector_originor turns submissions on today, so no build was affected.I searched
src/forPRODUCTION_COLLECTOR_URL,_collector_origin(and_development_collector_url(. Outside the resolver, they now appear only where they're defined, incore/constants.pyandcore/auth.py.Two other kinds of request use credentials the collector issues, but they aren't sent to the collector, so they don't use the resolver:
publication_origin.The interface package
On
main,microcosm-emitter-interfacestill has nocollector_originfield:TelemetrySettingsandOrrerySettingseach have one collector setting,development_collector_url.OrrerySettingsalso haspublication_origin, the run viewer's address for graph publications, which isn't a collector setting.HuggingFaceSettingscarries no collector setting at all, not evencollector_submissions.Nothing that exists today needs the field, so this PR doesn't add it. Microcosm would need it only to select a trusted origin other than the default.
Changing the default (tracker task 5.1 · 2)
Both edits go in one emitter PR:
TRUSTED_COLLECTOR_ORIGINS.PRODUCTION_COLLECTOR_URLat it.Keep the old origin in the set while the old address still serves. Then, if the new address has to be rolled back, a build can still select the old one with
collector_origin.test_every_trusted_origin_is_a_bare_https_originchecks the new entry.Which builds pick that release up:
microcosm-emitter==0.2.1keep the old address until Microcosm moves off that pin. Microcosm'spackages/microcosm-build/pyproject.tomlonmainstill has that pin. The old address has to keep answering until the pin moves. If it doesn't, those builds' telemetry stays on their build machines, where the emitter keeps retrying and keeps events for seven days.Release
The only changelog fragment is
changelog.d/trusted-collector-origins.added.md. Its type isadded, sotools/bump-version.pymakes the next release a minor version. Merging now releases 0.9.0 (after 0.8.0) of bothmicrocosm-emitterandmicrocosm-emitter-interfaceto PyPI.tools/check-changelog.shprints0.8.0 -> 0.9.0.Other open PRs
mainand already conflict with it inREADME.md,telemetry/service/collector.pyandtelemetry/service/module.py. This PR changes how those two modules choose the origin, which adds a little to resolving those conflicts.Commits and tests
9b31f08tests/test_collector_origin.py(new, 14 tests):ImportErrorformicrocosm_emitter.core.collector_origin, so 0 of 14 rantests/telemetry/test_delivery.py: 29 passed30046a1test_modules_treat_an_empty_development_url_as_unset: 2 failed13c5235498427fmain(46 commits, up to 0.8.0) into this branch, with no conflictstests/test_collector_origin.pyandtests/submissions/test_delivery.py: 62 passed12f72601a3b7cbtests/test_collector_origin.py: 21 passeda84d457test_collector_submissions_refuse_an_unusable_origin_at_start: 3 failed, 21 passed. The Hugging Face module'sinitializeraisedAttributeError(a boolean or a number) orTypeError(a list), which would stop the emitter host.c2ba970ValueErrortests/test_collector_origin.py: 24 passedf0d3037https://checktests/test_collector_origin.py: 24 passed before, 16 passed after3261983The results for the first three commits were recorded when they were written. The first commit then had the hash
5c4c86f; the branch has been rebased since.After the last commit, I ran these locally:
-m "not collector"as CI runs them: 448 passed (453 before the test clean-up inf0d3037). The files weretests/test_collector_origin.py,tests/submissions/,tests/huggingface/,tests/telemetry/test_delivery.py,tests/telemetry/test_module.py,tests/orrery/test_client.py,tests/orrery/test_publication.pyandtests/interface/test_protocol_fixtures.py.tests/test_boundaries.pyandtests/test_collector_contract.py: 11 passed. Their 9collector-marked tests need PostgreSQL, and CI'scollectorjob runs them.tools/check-quality.sh(Ruff check and format) andtools/check-changelog.sh: both pass.CI on
3261983passed all 10 checks:collectortests against PostgreSQL;This PR changes no workflow, deployment code or setting, and adds no environment variable.
🤖 Generated with Claude Code