Skip to content

feat(cli): location auto-detect for client create (#84)#93

Draft
LukasWodka wants to merge 3 commits into
developfrom
feat/84-location-autodetect
Draft

feat(cli): location auto-detect for client create (#84)#93
LukasWodka wants to merge 3 commits into
developfrom
feat/84-location-autodetect

Conversation

@LukasWodka

Copy link
Copy Markdown
Contributor

Summary

The deferred fast-follow from #92: client create now pre-fills its location prompt with an auto-detected electricityMaps zone (the backend's ZONE_CHOICES), so a cloud-hosted client doesn't have to look up its own zone.

Stacked on #92 (feat/84-client-commands) — it wires into that PR's client create. Retarget to develop once #92 merges.

How detection works (internal/geo)

Detect(ctx) returns a best-effort Zone{Code, Source, Confidence} or nil:

  1. Cloud instance metadata first (high confidence) — AWS (IMDSv2 token, IMDSv1 fallback), GCP, Azure, probed concurrently under one short deadline (first to answer wins, so a real cloud host responds in one round-trip instead of waiting through the others' timeouts). The region maps to an ISO country via a curated AWS/GCP/Azure table.
  2. IP geolocation fallback (low confidence, flagged) — Cloudflare's cdn-cgi/trace loc= (HTTPS, no API key) → ISO country.

The output is always an ISO alpha-2 country code, which is always a valid top-level ZONE_CHOICES value. An unmapped cloud region falls through to GeoIP rather than suggest a string the backend would reject.

UX

  • The detected zone pre-fills the prompt default; the user confirms with Enter or overrides — still never silent, never empty (the RFC requirement).
  • Best-effort: offline / egress-restricted / bare metal → empty default (the prior behavior). Detection only runs interactively when --location is omitted; --location X skips it entirely.

Tests

internal/geo: each provider, GeoIP fallback, unmapped-region fallthrough, nothing-detected, region→country table — endpoints mocked via httptest (the metadata/GeoIP URLs are overridable package vars). internal/cli: detected zone accepted as the default, end-to-end. A detectZone seam keeps the command tests hermetic (no real probes). Full go build / vet / test ./... green.

Follow-up noticed (separate)

location is CharField(choices=ZONE_CHOICES) — the backend ChoiceField rejects any value not in the list. cli#92's free-text --location (and a user override here) can still send an invalid zone that only fails at create time as a raw 400. Worth vendoring ZONE_CHOICES into the CLI to validate / offer a picker — happy to file that.

🤖 Generated with Claude Code

LukasWodka and others added 3 commits June 20, 2026 11:24
RFC-0001 P1. Adds the `client` subtree the login flow hands off to:
provision a tracebloc client for this machine, list the account's clients,
and attach this machine to an existing one.

- internal/slug: Go port of RFC-0001 Appendix B (backend common/utils/slug.py)
  — DNS-1123 slugify (NFKD via x/text) + collision suffix + empty-slug guard,
  kept in lock-step with the backend that validates the result.
- internal/api: CreateClient / ListClients / ListClientAdmins against
  /edge-device/, Bearer-authed (backend#836).
- internal/cli/client.go: create (--name / --location / --yes), list (ls),
  use <id>. The derived namespace is shown for confirmation; location is
  required (never silent-empty); a 403 surfaces the ask-an-admin path
  (backend#836); the generated machine credential is printed once.

Location auto-detect (cloud-metadata / GeoIP suggested default) is a
fast-follow — this PR takes --location or prompts for it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eractive tests

Self-review follow-up on the client commands:

- api: ListClients now follows DRF `next` to the end (was page-1 only), so
  `list`, `use <id>`, and create-time collision detection see every client in
  the account, not just the first page.
- cli: `create` gathers name + location first, then shows one review + a single
  confirm (was confirm-mid-flow) — matches the dataset-push interactive flow.
- tests: committed slug golden-parity test (24 pairs verified byte-identical
  against the Python slugify_dns1123, incl. NFKD ligatures/fractions/roman/
  fullwidth); interactive create + cancel via the prompter seam; paginated
  list; collision-suffix end-to-end.
- slug: doc-note the redundant dash-collapse (mirrors slug.py) and the
  ""/None fallback divergence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deferred fast-follow from cli#92: pre-fill `client create`'s location
prompt with a detected electricityMaps zone (backend ZONE_CHOICES), so a
cloud-hosted client doesn't have to look up its own zone.

- internal/geo: Detect() probes cloud instance metadata first (AWS IMDSv2/v1,
  GCP, Azure — concurrently under one short deadline, first wins → high
  confidence), then Cloudflare IP geolocation (low confidence, flagged). Returns
  an ISO country code, always a valid top-level zone; cloud regions map via a
  curated AWS/GCP/Azure table, and an unmapped region falls through to GeoIP for
  a valid zone rather than suggest something the backend would reject.
- client create: the detected zone pre-fills the prompt default (the user
  confirms with Enter or overrides) — still never silent, never empty. A
  detectZone seam keeps the command tests hermetic.
- Best-effort: offline / egress-restricted / bare metal → empty default (the
  prior behavior). Only runs interactively when --location is omitted.

Tests: geo per-provider + GeoIP fallback + unmapped-region fallthrough +
nothing-detected + region→country table; cli accepts-detected-zone end-to-end.
go build/vet/test green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LukasWodka LukasWodka requested a review from saadqbal June 22, 2026 06:52
@saadqbal saadqbal marked this pull request as draft June 22, 2026 07:33
@LukasWodka LukasWodka changed the base branch from feat/84-client-commands to develop June 22, 2026 10:03
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