Easy EU region selection: ROBOFLOW_REGION, auth login --region, auth set-region - #513
imbgar-roboflow wants to merge 8 commits into
Conversation
|
CI note: the |
0e1d22a to
6ad02bc
Compare
…egion, auth set-region
One switch selects the Roboflow platform region (us default, eu):
resolved env var > config file > region default > existing US default,
so explicit API_URL/APP_URL-style overrides keep working unchanged.
- config.py: region registry + runtime resolve_url()/get_effective_region();
EU maps api/app to api,app.roboflow.eu, detection/instance-seg to
serverless.roboflow.eu, dedicated deployments to eu.roboflow.cloud.
Universe and semantic-seg stay global .com (no EU instance today).
- login(): browser auth flow prints and validates the token against the
region's app host at call time; forced re-login now merges the prior
config instead of clobbering it (preserves region + URL overrides).
- CLI: auth login --region {us,eu} (interactive + --api-key paths),
auth set-region, region + effective URLs in auth status (text/JSON),
root login alias forwards --region.
- Docs: CLI-COMMANDS.md region section, README 'Using Roboflow EU'.
- Tests: back-compat guard (no region = byte-identical US defaults),
precedence matrix, EU map, login persistence/merge, CLI paths.
INF-314
- auth login --region <other> now re-authenticates an existing user instead of returning "Already logged in" (stored creds belong to their region) - roboflow.login(region=...) rebinds the URL constants other modules imported at import time, so the SDK talks to the new region immediately - load_model / download_dataset accept app.roboflow.eu URLs - set-region only warns about stored credentials when switching region with credentials present - unknown-region fallback is a RegionWarning (warnings module), suppressed in --json mode; auth status reports it as a region_warning field instead - help-output test strips ANSI so it passes when Rich forces colors in CI - README: minimum Python is 3.10 (matches python_requires)
6ad02bc to
031c4d3
Compare
Classification, keypoint and VLM models hardcoded serverless.roboflow.com, so 'roboflow infer' in the EU region sent images to the US. Route them through a region-aware SERVERLESS_URL, default webcam() to the region's detection host, and refuse hosted semantic segmentation in EU (no EU deployment) unless SEMANTIC_SEGMENTATION_URL is set explicitly.
URL defaults now come from a (region, environment) matrix, so ROBOFLOW_REGION=eu ROBOFLOW_ENVIRONMENT=staging reaches EU staging here as it does in inference instead of EU production. Every service with a staging deployment follows it (API, app, serverless, Universe, dedicated deployments, US semantic segmentation); EU semantic segmentation still refuses. Explicit URL overrides keep precedence. Unknown values warn and fall back to prod rather than treating any non-prod value as staging. 'auth status' reports the environment.
|
/mina superduper sugg |
EU production async-serverless deployments set ROBOFLOW_ENVIRONMENT=production. Treat it as prod here, as the matching inference change does, so the value never needs a warning or selects the wrong hosts.
Out of scope for this PR; the EU prod values are fixed at the source in roboflow/async-serverless#461.
|
/jarbas review |
imbgar-roboflow
left a comment
There was a problem hiding this comment.
TL;DR
Needs work — 5 fixes before merge · 6 follow-ups.
Required before merge
- Fix before merge: [astra] Allow authentication when the config contains only a region —
roboflow/cli/handlers/auth.py:247
Exact replacement attached as an inline suggestion. - Fix before merge: [astra] Validate the semantic endpoint that will actually receive the image —
roboflow/models/semantic_segmentation.py:43
Exact replacement attached as an inline suggestion. - Fix before merge: [astra] Keep authentication and subsequent SDK requests on the same platform —
roboflow/__init__.py:145
Exact replacement attached as an inline suggestion. - Fix before merge: [astra] Store credential provenance separately from the region preference —
roboflow/cli/handlers/auth.py:135
Exact replacement attached as an inline suggestion. - Fix before merge: [fable-5.1] set-region writes a credential-less config that makes login() and auth login report already logged in —
roboflow/__init__.py:94
Exact replacement attached as an inline suggestion.
Follow-ups
- [astra] Accept the staging platform URLs introduced by the endpoint matrix —
roboflow/__init__.py:179 - [astra] Resolve the login host after handling an unreadable saved config —
roboflow/__init__.py:82 - [fable-5.1] Implicit forced re-login now deletes stored credentials before the token exchange can fail —
roboflow/__init__.py:108 - [fable-5.1] Region of the issuing platform is only recorded when passed explicitly; env-driven and env-overridden logins diverge —
roboflow/__init__.py:134 - [fable-5.1] Data-residency refusal is opt-in per call site; the EU constant still points at the US host —
roboflow/config.py:184 - [fable-5.1] No test covers set-region followed by login —
tests/cli/test_auth_region.py:106
How this verdict was reached
- Jury: superduper-trio (reviewer) · superduper-adversarial (cross_examiner) · superduper-glm (adjudicator)
- Reviewer seats: astra · fable-5.1 · kimi-k3
- Work: 20 raised · 20 accepted · 0 refuted · 0 unverifiable · 5 blocking
- Verifier: ran on blocking candidates
- Withheld: 8 take-or-leave notes (below the public evidence bar)
- Coverage: superduper-trio reviewed 16/16 files and 46/46 hunks · superduper-adversarial independently reviewed 16/16 files and 46/46 hunks
Failure architecture
Why does auth set-region eu on a fresh install make the next auth login report an existing session without credentials?
set-region saves a config holding only ROBOFLOW_REGION; both CLI and SDK login paths decide logged-in status purely from config-file existence, so a credential-less file is treated as an authenticated session and login never runs.
flowchart LR
D1N1["source: Fresh install: user runs auth set-region eu known"]
D1N2["component: Config file written containing ROBOFLOW_REGION but no credentials known"]
D1N3["decision: Login path decides logged-in status via os.path.isfile(conf_location) only known"]
D1N4["failure: CLI prints 'Already logged in' with workspace 'unknown'; no authentication occurs known"]
D1N5["outcome: roboflow.login() prints the same message and returns None unauthenticated known"]
D1N1 -->|"_set_region calls _save_config even when config has no workspaces known"| D1N2
D1N2 -->|"Config file exists on disk, so the existence-only check passes known"| D1N3
D1N3 -->|"File existence is misread as a usable session; region match also suppresses force known"| D1N4
D1N2 -->|"roboflow.login() applies the same existence-only check to the region-only config known"| D1N5
Scope: CLI and SDK login decisions after set-region writes a credential-less config (roboflow/cli/handlers/auth.py:247,368-372; roboflow/init.py:94,99)
Assumptions: The trace verifications cited in the adjudications were executed at head and reproduce the described behavior; No other code path forces reauthentication when the saved region already matches the requested one; The merge at init.py:99 only runs when force is True, as stated in P7
_Unresolved: Actual diff hunks were not visible to this pass; node truth rests on finding evidence and adjudication traces, not direct code inspection; Internal implementation lines of save_config and the isfile check beyond the cited anchors are unverified; Whether the new status test's unauthenticated classification was intended to gate a later login attempt is unresolved; Behavior of auth login --region eu when the saved region differs from the request is not traced here
How does a post-import SEMANTIC_SEGMENTATION_URL override send images to the US default endpoint despite the guard?
The guard reads the live override and permits prediction, but the model's self.api_url was fixed from an imported constant at construction, so the actual request still targets segment.roboflow.com and the residency intent is bypassed.
flowchart LR
D2N1["source: User sets os.environ 'SEMANTIC_SEGMENTATION_URL' to a custom endpoint after import known"]
D2N2["component: Guard reads SEMANTIC_SEGMENTATION_URL from current configuration known"]
D2N3["component: Model __init__ built self.api_url from an imported constant (US default) known"]
D2N4["decision: Guard sees the new override and permits prediction known"]
D2N5["failure: Image is submitted to segment.roboflow.com while the guard believed a custom endpoint was in force known"]
D2N6["outcome: Added test reproduces the setup but never asserts the request destination known"]
D2N1 -->|"Override is visible in current configuration at guard time known"| D2N2
D2N2 -->|"Non-default override present, so the guard allows prediction known"| D2N4
D2N3 -->|"self.api_url was captured at construction and ignores the later override known"| D2N5
D2N4 -->|"Permitted prediction submits the image to the static api_url known"| D2N5
D2N5 -->|"Test mocks parent prediction, so the wrong destination goes undetected known"| D2N6
Scope: Semantic segmentation request URL selection between the live guard and the statically captured api_url (roboflow/models/semantic_segmentation.py:40-46)
Assumptions: The imported constant resolves to segment.roboflow.com under EU defaults, as stated in the finding; TrainedModel exhibits the same live-guard versus static-returned-URL mismatch described in P2 but is not separately traced; The added test mocks the parent prediction call, so no real request is issued in the test itself
Unresolved: Diff text for semantic_segmentation.py was not visible to this pass; truth rests on the finding's trace verification; Exact guard and init implementation lines beyond the cited anchor are unverified; Whether other model classes share the imported-constant URL pattern is unresolved; The concrete line in tests/test_region_models.py that mocks the parent prediction is approximated, not pinned
Why do authentication and subsequent SDK requests end up on different platforms after an explicit-region login?
login() stores credentials using the explicit region argument, but refresh_region_urls() re-resolves without it so the environment wins; provenance is also read from the mutable region preference, so keys survive platform switches.
flowchart LR
D3N1["source: ROBOFLOW_REGION us exported, no saved credentials; user calls roboflow.login(region 'eu') known"]
D3N2["component: login resolves the app URL with the explicit eu argument and stores EU credentials known"]
D3N3["component: refresh_region_urls() resolves without the explicit argument; environment wins, API_URL stays US known"]
D3N4["failure: Credential/backend mismatch: EU key submitted to US API; US key retained under EU label known"]
D3N5["component: _stored_region() reads the mutable ROBOFLOW_REGION preference as the credentials' issuing region known"]
D3N6["outcome: After US login, set-region eu then login --region eu skips reauth; US key kept and config relabeled EU known"]
D3N1 -->|"Explicit eu argument drives app URL resolution and credential storage known"| D3N2
D3N2 -->|"refresh_region_urls() runs without the explicit region argument known"| D3N3
D3N3 -->|"API_URL remains US while the saved key is EU; Roboflow() submits it to the US API known"| D3N4
D3N2 -->|"Issuing platform is recorded only via the mutable region preference known"| D3N5
D3N5 -->|"set-region rewrites the preference without changing credentials, so reauth is skipped known"| D3N6
D3N6 -->|"US-issued key remains selectable and paired with the effective request target assumed"| D3N4
Scope: Region resolution and credential provenance across login, refresh_region_urls, and set-region (roboflow/init.py:145; roboflow/cli/handlers/auth.py:135)
Assumptions: The trace verification for P3 ran at head with ROBOFLOW_REGION=us exported and no saved credentials; The CLI API-key login path exhibits the equivalent mismatch on the next invocation, as stated but not separately traced; P4's mechanism is mechanically checkable against the diff per its adjudication, though no executing trace was cited for it
Unresolved: Diff hunks were not visible to this pass; P3 truth rests on its trace verification and P4 on adjudicated diff structure; Exact lines of refresh_region_urls and check_key beyond the cited anchor are unverified; Whether set-region's existing conflict reporting covers the login-time conflict is unresolved; The EU workspace merge into the US workspace map in the API-key path is described but not traced to a line
Reviewed commit bd0b6a5f3ed1124a72574696e6aa6870d579e6f3.
quota · @imbgar-roboflow · superduperduper 109/3 · superduper 9/6 · mina 71/15
…their platform - A config holding only a region (from auth set-region) no longer counts as a session in roboflow.login() or roboflow auth login. - Record the platform that issued the credentials (ROBOFLOW_CREDENTIALS_REGION) separately from the ROBOFLOW_REGION preference; re-authenticate when they differ, and drop the other platform's keys on API-key login. - Refuse an explicit region that an exported ROBOFLOW_REGION would override, so credentials and requests stay on one platform. - Forced login keeps the old credentials until the token exchange succeeds. - Semantic segmentation resolves its destination with the same lookup as the residency check, so an explicit override is where the image actually goes. - Accept staging app/universe URLs in load_model and download_dataset.
|
Follow-ups from the 2026-09-30 review (reviewed at Fixed
Partly addressed
Not changing
Local results: 1105 tests pass on Python 3.10 and 3.13; ruff and mypy are clean. The 18 new tests fail against |
imbgar-roboflow
left a comment
There was a problem hiding this comment.
Inline responses to the six follow-ups from the 2026-09-30 review.
Description
One switch points the SDK + CLI at the Roboflow EU data-residency platform (Linear INF-314), and a second selects staging, with the same
ROBOFLOW_REGION×ROBOFLOW_ENVIRONMENTmatrix as roboflow/inference#2701.How it works
config.pyresolves every default URL from a(region, environment)matrix. Precedence: explicit URL env var > explicit config-file key > region/environment default > US production default, soAPI_URL-style overrides keep working unchanged.packages/shared/environments/*and the dedicated-deployment ingresses; all verified live:api/app.roboflow.comapi/app.roboflow.oneapi/app.roboflow.euapi/app.roboflow-eu.oneserverless.roboflow.comserverless.roboflow.oneserverless.roboflow.euserverless.roboflow-eu.oneuniverse.roboflow.comuniverse.roboflow.oneuniverse.roboflow.comuniverse.roboflow.oneroboflow.cloudstaging.roboflow.cloudeu.roboflow.cloudeu.staging.roboflow.cloudsegment.roboflow.comlambda-semantic-segmentation.staging.roboflow.comserverless.roboflow.com(newSERVERLESS_URLkey), andwebcam()defaults to the region's host. Semantic segmentation has no EU deployment, so in EU it raises instead of sending images to the US, unlessSEMANTIC_SEGMENTATION_URLis set explicitly. The check and the request use the same URL lookup.login()resolves the app host at call time and rebinds the import-time URL constants, so the running process switches region immediately. A forced re-login keeps the old credentials until the token exchange succeeds.set-regiongoes through normal login.ROBOFLOW_CREDENTIALS_REGION, separately from theROBOFLOW_REGIONpreference. Login re-authenticates when the two differ, and API-key login on the other platform replaces the old workspace keys instead of merging them.--region/region=that an exportedROBOFLOW_REGIONwould override is refused before authenticating, so credentials and requests stay on one platform.--api-key --region euvalidates against the EU API.auth status(text and--json) reports region, environment and effective URLs.set-regionwarns only when stored credentials came from the other platform.app.roboflow.euand staging (app.roboflow.one,app.roboflow-eu.one,universe.roboflow.one) links are accepted byload_model/download_dataset.--jsonmode) and fall back tous/prod; they never raise at import.ROBOFLOW_ENVIRONMENTacceptsprodandstaging; any other value warns and falls back to prod.With nothing set, every URL constant is byte-identical to today, guarded by a test.
Testing
python -m unittest), including the full region × environment matrix, precedence, EU inference routing, semantic-segmentation refusal, login persistence/re-auth/credential provenance, CLI status/set-region, and invalid-value handlingmake check_code_quality(ruff + mypy) cleanDocs
CLI-COMMANDS.md: region flag,set-region,ROBOFLOW_REGION,ROBOFLOW_ENVIRONMENT, endpoint tableREADME.md: "Using Roboflow EU" section