latlng

Secure latlng with scoped JWTs

Give every tracker, dashboard, and operator a token that can do exactly what it needs — and nothing else. You'll enable JWT auth, mint scoped tokens with latlng-cli, and check the boundaries.

Intermediate · about 10 minutes · tested against latlng v0.2.0

Before you start

  • latlng-server and latlng-cli from a release archive, Homebrew, or Docker.
  • Collections named with a shared prefix, such as fleet-hamburg.

01 Plan who needs what

A tracking system has very different clients. A tracker in a truck should only write its own fleet's positions. A dispatch dashboard reads and subscribes. An operator manages hooks. latlng expresses this with permissions scoped to collection name patterns — which is why naming fleets fleet-hamburg, fleet-berlin, and so on pays off.

02 Generate a secret and configure the server

latlng-cli can generate an HMAC secret and mint tokens for self-hosted setups. require_auth makes the server refuse to start without a verifier, and disable_bearer_token turns off the static all-powerful token.

Terminal
latlng-cli token secret > .latlng-jwt-secret
latlng.toml
require_auth = true
disable_bearer_token = true
jwt_secret = "<contents of .latlng-jwt-secret>"
jwt_algorithm = "HS256"
jwt_issuer = "https://id.example.com"
jwt_audience = "latlng"

[storage]
type = "aof"
path = "./data/appendonly.aof"
Terminal
latlng-server --config ./latlng.toml

curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:7421/collections/fleet-hamburg
# 401

03 Mint tokens with presets

Presets bundle common action sets. writer can read and write objects, readonly can read and query, dashboard can query and subscribe to live events, and hooks-admin / channels-admin manage geofences. Each is limited to the collections you pass with --collection, which accepts glob patterns.

Terminal
# Short-lived admin token for setup
ADMIN=$(latlng-cli token create --config ./latlng.toml --subject ops --ttl 15m --admin)

# A tracker that may only write to Hamburg-style fleets
TRACKER=$(latlng-cli token create --config ./latlng.toml \
  --subject truck-17 --ttl 24h --preset writer --collection 'fleet-*')

# A dashboard that reads and subscribes, but can't write
DASHBOARD=$(latlng-cli token create --config ./latlng.toml \
  --subject dispatch-ui --ttl 8h --preset dashboard --collection 'fleet-*')

04 Check what each token can do

Create two collections as admin, then try the tracker token against both. Writes inside fleet-* succeed; anything else is rejected with 403.

Requests
curl -s -X POST http://127.0.0.1:7421/collections/fleet-hamburg -H "Authorization: Bearer $ADMIN"
curl -s -X POST http://127.0.0.1:7421/collections/billing       -H "Authorization: Bearer $ADMIN"

curl -s -X POST http://127.0.0.1:7421/collections/fleet-hamburg/objects/truck-17 \
  -H "Authorization: Bearer $TRACKER" -H 'content-type: application/json' \
  -d '{"object":{"Point":{"lat":53.55,"lon":9.99,"z":null}}}'
# {"ok":true,"stored":true}

curl -s -X POST http://127.0.0.1:7421/collections/billing/objects/x \
  -H "Authorization: Bearer $TRACKER" -H 'content-type: application/json' \
  -d '{"object":{"Point":{"lat":53.55,"lon":9.99,"z":null}}}'
# {"error":"forbidden"}

05 Inspect and verify tokens

token inspect decodes a token without checking its signature; token verify checks it against your config. The permissions live in the latlng_permissions claim.

latlng-cli token inspect "$TRACKER" (claims)
{
  "aud": "latlng",
  "iss": "https://id.example.com",
  "sub": "truck-17",
  "latlng_permissions": [
    {
      "collections": ["fleet-*"],
      "actions": [
        "collections:list", "collections:inspect",
        "objects:read", "objects:write", "objects:delete",
        "queries:read"
      ]
    }
  ]
}

06 Use tokens from the SDK

Pass the token to the client; it's sent with every HTTP request and used to authenticate WebSocket subscriptions.

TypeScript
import { LatLngClient } from "@latlng/sdk";

const client = new LatLngClient({
  leaderUrl: "https://latlng.internal.example.com",
  token: process.env.LATLNG_TOKEN,
});

latlng serves plain HTTP. In production, terminate TLS at a reverse proxy, load balancer, or ingress in front of it, so tokens never cross the network unencrypted. To use an existing identity provider instead of HMAC secrets, configure jwks_url — see authentication modes.

What you built

  • A server that rejects unauthenticated requests.
  • Tracker, dashboard, and admin tokens with different permissions.
  • Collection-scoped access using name patterns like fleet-*.