Skip to content

Doc0x1/Rust-Geolocation-Microservice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rust-geolocation-api

Rust port of a Flask geolocation microservice. Accepts image uploads, runs GeoCLIP ML inference via ONNX Runtime to predict GPS coordinates, and reverse-geocodes results to city/region/country strings.

Project is a WIP but aims to be a production-ready backend API for geolocation tasks.

Architecture

Client
  │
  ▼
Axum HTTP server
  ├── tower-http: CORS, request size limit, tracing
  ├── governor: per-IP rate limiting
  ├── API key middleware (X-API-Key header)
  └── CSRF middleware (X-CSRF-Token, HMAC-SHA256)
       │
       ├── POST /api/v1/geolocate
       │     ├── image decode (base64 or multipart)
       │     ├── CLIP preprocessing (resize 224×224, normalize)
       │     ├── ONNX Runtime (ort) → clip_encoder.onnx
       │     ├── cosine similarity vs gps_gallery.npy
       │     └── reverse_geocoder (offline GeoNames)
       │
       ├── GET /api/v1/csrf-token
       └── GET /api/v1/health

No Python at runtime. GeoCLIP model exported to ONNX once via scripts/export_geoclip_onnx.py.

Prerequisites

  • Rust 1.75+
  • Python 3.10+ with geoclip, torch installed (model export only)
  • models/ directory populated by export script (see Setup)

Setup

1. Export GeoCLIP model (one-time)

pip install geoclip torch numpy
python scripts/export_geoclip_onnx.py

Produces:

  • models/clip_encoder.onnx — CLIP ViT-L/14 vision encoder
  • models/gps_gallery.npy — GPS coordinate gallery [N, 2]
  • models/gps_embeddings.npy — pre-computed normalized embeddings [N, 512]

2. Configure environment

cp .env.example .env
# Edit .env — set API_KEY, CSRF_SECRET, NEXTJS_DOMAIN

3. Build and run

cargo build --release
cargo run --release

Server starts on PORT (default 3000).

Environment Variables

Variable Required Default Description
API_KEY Yes Secret key sent in X-API-Key header
CSRF_SECRET Yes HMAC signing key for CSRF tokens
NEXTJS_DOMAIN No http://localhost:3000 Allowed CORS origin
PORT No 3000 HTTP listen port
MODEL_PATH No models/clip_encoder.onnx Path to ONNX encoder
GALLERY_PATH No models/gps_gallery.npy Path to GPS gallery
EMBEDDINGS_PATH No models/gps_embeddings.npy Path to gallery embeddings
RUST_ENV No development Set to production to disable debug logging
RUST_LOG No warn Tracing filter (e.g. info, debug)

API Reference

All responses use envelope { "data": {...} } on success or { "error": "message" } on failure.

GET /api/v1/health

No authentication required.

Response 200:

{ "data": { "status": "healthy" } }

GET /api/v1/csrf-token

Headers: X-API-Key: <key>

Rate limit: 10 req/min per IP

Response 200:

{ "data": { "csrf_token": "<token>" } }

POST /api/v1/geolocate

Headers:

  • X-API-Key: <key>
  • X-CSRF-Token: <token from /csrf-token>

Rate limit: 10 req/min per IP

Max body: 5 MB

Body (multipart/form-data):

  • image — file field (png, jpg, jpeg, webp)

Or body (application/x-www-form-urlencoded):

  • image_data — base64 data URI (data:image/jpeg;base64,...)

Response 200:

{
  "data": {
    "predictions": [
      { "location": "Paris, Île-de-France, FR", "confidence": 0.72 },
      { "location": "Lyon, Auvergne-Rhône-Alpes, FR", "confidence": 0.18 },
      { "location": "Marseille, Provence-Alpes-Côte d'Azur, FR", "confidence": 0.10 }
    ],
    "coordinates": [
      { "lat": 48.8566, "lon": 2.3522 },
      { "lat": 45.7485, "lon": 4.8467 },
      { "lat": 43.2965, "lon": 5.3698 }
    ]
  }
}

Error responses:

Status Body
400 { "error": "No image provided" }
400 { "error": "Invalid file type" }
400 { "error": "Invalid image data" }
401 { "error": "Unauthorized" }
403 { "error": "CSRF token invalid or expired" }
413 { "error": "File too large. Maximum upload size is 5MB." }
429 { "error": "Rate limit exceeded" }
500 { "error": "Internal server error" }

Security

  • API key: All protected endpoints require X-API-Key header (constant-time comparison)
  • CSRF: Stateless HMAC-SHA256 tokens with 30-minute expiry
  • CORS: Restricted to NEXTJS_DOMAIN and https://hacknexus.io
  • Rate limiting: 10 req/min per IP on /csrf-token and /geolocate
  • Body limit: 5 MB enforced by tower-http layer
  • No secrets in logs: IP addresses logged at WARN level only on auth failures

Development

cargo check          # fast type check
cargo clippy         # lints
cargo fmt            # format
cargo test           # unit tests
RUST_LOG=debug cargo run  # verbose logging

About

Rust port of my geoclip-service repository. GeoCLIP model ported with ONNX.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors