{"id":30615647,"url":"https://github.com/alexfalkowski/standort","last_synced_at":"2026-06-06T08:02:39.195Z","repository":{"id":37821129,"uuid":"483755174","full_name":"alexfalkowski/standort","owner":"alexfalkowski","description":"Standort provides location based information.","archived":false,"fork":false,"pushed_at":"2026-04-02T07:24:34.000Z","size":44509,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-03T01:42:39.836Z","etag":null,"topics":["cucumber","golang","make","ruby"],"latest_commit_sha":null,"homepage":"https://alexfalkowski.github.io/standort","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexfalkowski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2022-04-20T17:42:41.000Z","updated_at":"2026-04-02T07:24:38.000Z","dependencies_parsed_at":"2026-04-09T05:01:06.484Z","dependency_job_id":null,"html_url":"https://github.com/alexfalkowski/standort","commit_stats":{"total_commits":454,"total_committers":2,"mean_commits":227.0,"dds":0.2929515418502202,"last_synced_commit":"74953d4b5729d9c5ab23e7b84c71775110581371"},"previous_names":[],"tags_count":1146,"template":false,"template_full_name":null,"purl":"pkg:github/alexfalkowski/standort","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexfalkowski%2Fstandort","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexfalkowski%2Fstandort/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexfalkowski%2Fstandort/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexfalkowski%2Fstandort/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexfalkowski","download_url":"https://codeload.github.com/alexfalkowski/standort/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexfalkowski%2Fstandort/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31586410,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cucumber","golang","make","ruby"],"created_at":"2025-08-30T08:06:17.908Z","updated_at":"2026-04-09T05:01:09.739Z","avatar_url":"https://github.com/alexfalkowski.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Gopher](assets/gopher.png)\n[![CircleCI](https://circleci.com/gh/alexfalkowski/standort.svg?style=shield)](https://circleci.com/gh/alexfalkowski/standort)\n[![codecov](https://codecov.io/gh/alexfalkowski/standort/graph/badge.svg?token=JJP65DPD1M)](https://codecov.io/gh/alexfalkowski/standort)\n[![Go Report Card](https://goreportcard.com/badge/github.com/alexfalkowski/standort/v2)](https://goreportcard.com/report/github.com/alexfalkowski/standort/v2)\n[![Go Reference](https://pkg.go.dev/badge/github.com/alexfalkowski/standort/v2.svg)](https://pkg.go.dev/github.com/alexfalkowski/standort/v2)\n[![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)\n\n# Standort\n\nStandort is a Go service that provides location-based information (country + continent) from:\n\n- an **IP address** (GeoIP2 database lookup), and/or\n- a **latitude/longitude point** (point-in-polygon lookup over embedded GeoJSON).\n\nIt exposes **two API versions (v1 and v2)** over **gRPC** and **HTTP**.\n\n---\n\n## What “location” means\n\nStandort returns:\n\n- `country`: ISO-3166 alpha-2 code (e.g. `US`, `DE`)\n- `continent`: two-letter continent code (e.g. `NA`, `EU`)\n\nLookups are performed using embedded assets:\n\n- `assets/geoip2.mmdb`: IP → country code (GeoIP2)\n- `assets/earth.geojson`: lat/lng → country + continent name (GeoJSON polygons), indexed with an R-tree\n\n---\n\n## API versions\n\n### v1\n\nv1 has separate RPCs for IP-based lookup and lat/lng-based lookup.\n\n- `GetLocationByIP`\n- `GetLocationByLatLng`\n\n### v2\n\nv2 combines both inputs into a single RPC:\n\n- `GetLocation`\n\nv2 supports passing inputs either directly in the request *or* via request metadata:\n\n- IP address can be derived from request metadata (commonly `X-Forwarded-For`).\n- Geolocation can be derived from a `Geolocation` header containing a `geo:` URI (RFC 5870).\n\nIf a lookup fails, v2 records error details into response `meta` attributes where possible, and only returns “not found” when neither IP nor GEO yields a location.\n\n---\n\n## Quickstart (development)\n\n### Prerequisites\n\n- Go (see `go.mod` for the required version)\n- Git submodules (this repo relies on a `bin/` submodule)\n- Ruby (used by the feature/benchmark harness under `test/`)\n\n### Bootstrap\n\nInitialize submodules and vendor dependencies:\n\n```sh\ngit submodule sync\ngit submodule update --init\nmake dep\n```\n\nNotes:\n- Many `make` targets run with `-mod vendor`.\n- If you see “inconsistent vendoring” errors, re-run `make dep`.\n\n### Build\n\nBuild the server binary:\n\n```sh\nmake build\n```\n\n### Run locally (dev config)\n\nThe repository includes a dev config at `test/.config/server.yml` with default addresses:\n\n- HTTP: `:11000`\n- gRPC: `:12000`\n\n#### Option A: dev mode (requires `air`)\n\n```sh\nmake dev\n```\n\nThis runs `air` to rebuild and restart using the dev config.\n\n#### Option B: run the binary directly\n\nIf you’ve built the binary (via `make build`), run:\n\n```sh\n./standort server -i file:test/.config/server.yml\n```\n\n---\n\n## Health endpoints\n\nWhen running with the dev config, the health HTTP observer endpoints are:\n\n- `GET http://localhost:11000/healthz`\n- `GET http://localhost:11000/livez`\n- `GET http://localhost:11000/readyz`\n\n(Exact behavior depends on the go-service/go-health wiring.)\n\n---\n\n## API usage examples\n\nThe service is gRPC-first; HTTP is wired by routing HTTP requests to the gRPC handlers. Exact HTTP routes and encoding are provided by the go-service RPC router.\n\n### gRPC examples\n\nYou can use `grpcurl` against the dev gRPC address (`localhost:12000`).\n\n\u003e Tip: you may need `-plaintext` for local development unless you’ve configured TLS.\n\n#### v1: lookup by IP\n\n```sh\ngrpcurl -plaintext \\\n  -d '{\"ip\":\"8.8.8.8\"}' \\\n  localhost:12000 \\\n  standort.v1.Service/GetLocationByIP\n```\n\n#### v1: lookup by lat/lng\n\n```sh\ngrpcurl -plaintext \\\n  -d '{\"lat\":52.5200,\"lng\":13.4050}' \\\n  localhost:12000 \\\n  standort.v1.Service/GetLocationByLatLng\n```\n\n#### v2: lookup with explicit IP\n\n```sh\ngrpcurl -plaintext \\\n  -d '{\"ip\":\"8.8.8.8\"}' \\\n  localhost:12000 \\\n  standort.v2.Service/GetLocation\n```\n\n#### v2: lookup with explicit point\n\n```sh\ngrpcurl -plaintext \\\n  -d '{\"point\":{\"lat\":52.5200,\"lng\":13.4050}}' \\\n  localhost:12000 \\\n  standort.v2.Service/GetLocation\n```\n\n#### v2: lookup using metadata (headers)\n\nv2 can fall back to metadata when request fields are omitted:\n\n- IP can come from forwarded IP metadata (commonly derived from `X-Forwarded-For`).\n- Point can come from a `Geolocation` header using a `geo:` URI.\n\nExample with a geo URI:\n\n```sh\ngrpcurl -plaintext \\\n  -H 'Geolocation: geo:52.5200,13.4050' \\\n  -d '{}' \\\n  localhost:12000 \\\n  standort.v2.Service/GetLocation\n```\n\nExample with a forwarded IP (exact metadata key handling is framework-dependent, but commonly derived from HTTP `X-Forwarded-For` in the gateway/proxy layer):\n\n```sh\ngrpcurl -plaintext \\\n  -H 'X-Forwarded-For: 8.8.8.8' \\\n  -d '{}' \\\n  localhost:12000 \\\n  standort.v2.Service/GetLocation\n```\n\n### HTTP examples\n\nHTTP runs on `localhost:11000` with the dev config.\n\nBecause HTTP routing is generated/wired via go-service RPC routing, the easiest accurate source of truth for HTTP paths is the service at runtime (logs) or the framework documentation/config. The mapping is based on the generated gRPC full method names:\n\n- v1:\n  - `standort.v1.Service/GetLocationByIP`\n  - `standort.v1.Service/GetLocationByLatLng`\n- v2:\n  - `standort.v2.Service/GetLocation`\n\nIf you want fully concrete `curl` examples for the HTTP endpoints (paths, methods, and JSON shapes), run the server and inspect the registered routes (or share the route output/logs), and I’ll add them verbatim.\n\n---\n\n## Development workflow\n\n### Format\n\n```sh\nmake format\n```\n\n### Lint\n\n```sh\nmake lint\n```\n\nTo apply automatic fixes where available:\n\n```sh\nmake fix-lint\n```\n\n### Unit/spec tests (Go)\n\n```sh\nmake specs\n```\n\nNote: `make specs` uses `gotestsum` directly. Install it if you don’t already have it.\n\n### Feature tests (Ruby harness)\n\n```sh\nmake features\n```\n\n### Benchmarks (Ruby harness)\n\n```sh\nmake benchmarks\n```\n\n---\n\n## Protobuf / API generation\n\nProtos live under `api/standort/v1` and `api/standort/v2`.\n\nTo lint and generate code:\n\n```sh\nmake proto-lint\nmake proto-format\nmake proto-generate\n```\n\nBreaking-change check:\n\n```sh\nmake proto-breaking\n```\n\n---\n\n## Repository layout (high level)\n\n- `main.go`: CLI entrypoint; registers the `server` command.\n- `internal/cmd`: DI composition and command wiring.\n- `internal/config`: service config composition.\n- `internal/health`: health registrations/observers.\n- `internal/location`: domain lookup logic (IP + point-in-polygon).\n- `internal/api/v1`, `internal/api/v2`: API modules and transports (gRPC + HTTP wiring).\n- `assets`: embedded runtime datasets (GeoJSON + GeoIP DB).\n- `api`: protobuf definitions + buf config.\n- `test`: Ruby feature/benchmark harness + example config.\n- `vendor`: vendored Go dependencies.\n\n---\n\n## Changelog\n\nSee `CHANGELOG.md`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexfalkowski%2Fstandort","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexfalkowski%2Fstandort","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexfalkowski%2Fstandort/lists"}