Architecture¶
System Overview¶
┌──────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │ │ ATAK/iTAK │ │ iOS App │ │ CLI │
│ (Next.js) │ │ (CoT XML) │ │ (SwiftUI) │ │ (Go, GPX) │
└──────┬───────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ HTTP/WS │ HTTP │ HTTP/WS │ HTTP/WS
│ │ │ │
┌──────▼────────────────────▼────────────────────▼──────┐
│ Load Balancer / Reverse Proxy │
│ (Caddy, ALB, Ingress — handles TLS) │
└──────────────────────┬────────────────────────────────┘
│
┌──────────────────────▼────────────────────────────────┐
│ Go API Service │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Handlers │→ │ Services │→ │ Repos │ │
│ │ (HTTP) │ │ (Logic) │ │ (SQL) │ │
│ └──────────┘ └──────────┘ └────┬─────┘ │
│ │ │ │
│ ┌────▼─────┐ ┌──────▼──────┐ │
│ │WebSocket │ │ Pub/Sub │ │
│ │ Hub │◄─────────────│ (Interface) │ │
│ └──────────┘ └─────────────┘ │
└────────┬──────────────────────┬──────────┬────────────┘
│ │ │
┌────▼─────┐ ┌─────▼────┐ ┌──▼─────┐
│PostgreSQL│ │ Redis │ │S3/Minio│
│+ PostGIS │ │(Pub/Sub) │ │(Files) │
└──────────┘ └──────────┘ └────────┘
The system is composed of three application services (Go API, Next.js web client, iOS client) backed by three infrastructure services (PostgreSQL with PostGIS, Redis, S3-compatible object storage). A reverse proxy (Caddy, ALB, or Ingress controller) sits in front for TLS termination and routing.
API Layered Architecture¶
The API follows a strict layered design. Each layer only depends on the one below it.
HTTP Request
│
▼
┌─────────────┐
│ Middleware │ CORS → MaxBodySize → RateLimit → Logging → Audit → Router
└──────┬──────┘
▼
┌─────────────┐
│ Handler │ Parse request, validate input, call service, write JSON response
└──────┬──────┘
▼
┌─────────────┐
│ Service │ Business logic, authorization, orchestration
└──────┬──────┘
▼
┌─────────────┐
│ Repository │ SQL queries via pgx, maps rows to domain models
└──────┬──────┘
▼
PostgreSQL
Layer Responsibilities¶
Middleware (internal/middleware/) — Cross-cutting concerns applied to every request:
- CORS — Validates
Originheader against configured allowed origins - MaxBodySize — Wraps request body with
http.MaxBytesReader - RateLimit — Per-IP token bucket rate limiting
- Logging — Structured JSON request/response logging
- Audit — Records API actions to the audit log
- Auth — Per-route via
authMW.Authenticate()orauthMW.RequireAdmin()
Handler (internal/handler/) — One file per domain. Handlers parse HTTP input, delegate to a service, and write JSON responses. They never contain business logic or SQL.
Service (internal/service/) — Business rules, authorization checks, and cross-domain orchestration.
Repository (internal/repository/) — Pure data access using pgx/v5 directly — no ORM.
Model (internal/model/) — Domain models, request/response DTOs, and a typed error system.
Authentication¶
Login:
Client → POST /api/v1/auth/login { username, password }
Server → Verify password (bcrypt, cost 12)
→ Generate JWT access token (15min, HMAC-SHA256)
→ Generate opaque refresh token (crypto/rand UUID)
→ Store SHA-256(refresh_token) in DB with expiry
→ Return { access_token, refresh_token, user }
Token Refresh:
Client → POST /api/v1/auth/refresh { refresh_token }
Server → SHA-256 hash the token, look up in DB
→ Delete old token (rotation — each token is single-use)
→ Issue new access + refresh token pair
Refresh tokens are never stored in plaintext. Token rotation means a stolen refresh token can only be used once.
Real-Time Architecture¶
Client A (writer) Client B (reader)
│ ▲
│ WS: location_update │ WS: location_update
▼ │
┌──────────┐ ┌─────┴──────┐
│ API Node │ │ API Node │
│ Hub │ │ Hub │
└────┬─────┘ └─────▲──────┘
│ │
│ PUBLISH group:123:location │ SUBSCRIBE group:123:location
▼ │
┌───────────────────────────────────────────────┐
│ Redis │
│ (Pub/Sub broker) │
└───────────────────────────────────────────────┘
Multiple API instances can run behind a load balancer. Redis pub/sub broadcasts messages across all nodes, enabling horizontal scaling without sticky sessions.
Pub/Sub Channels¶
| Channel Pattern | Purpose |
|---|---|
group:{id}:location |
Real-time location updates for group members |
group:{id}:messages |
New messages in a group |
user:{id}:direct |
Direct messages to a specific user |
global:admin |
Admin-level notifications |
Database Schema¶
PostgreSQL 16 with PostGIS for spatial operations. 17 tables across core, auth, MFA, messaging, spatial, and configuration domains.
users ──────< devices
│
├────────< group_members >──────── groups
│
├────────< messages ────────────── attachments
│
├────────< location_history
│
├────────< refresh_tokens
│
├────────< audit_logs
│
├────────< user_totp_methods
│
├────────< webauthn_credentials
│
├────────< recovery_codes
│
└────────< drawings
cot_events (Cursor on Target — ATAK/iTAK ingestion)
map_configs (tile source configurations)
terrain_configs (terrain/elevation sources)
server_settings (key-value server configuration)
Location fields use GEOMETRY(POINT, 4326) with GIST spatial indexes. All coordinates use WGS 84 (SRID 4326).
Migrations are embedded in the Go binary via //go:embed and run automatically on startup.
Multi-Factor Authentication¶
Three MFA methods supported, all optional per-user unless the admin enables server-wide enforcement:
- TOTP — Authenticator apps (Google Authenticator, Authy, 1Password)
- WebAuthn/FIDO2 — Security keys (YubiKey, Titan) and platform authenticators (Touch ID, Windows Hello)
- Recovery Codes — 8 one-time backup codes (bcrypt-hashed)
TOTP secrets are encrypted at rest using AES-256-GCM (HKDF-derived key from JWT_SECRET) or AWS KMS when MFA_KMS_KEY_ARN is set.
Repository Structure¶
vincenty/
├── services/api/ # Go API service
│ ├── cmd/server/ # Entrypoint, DI, route registration
│ ├── internal/
│ │ ├── auth/ # JWT + password hashing
│ │ ├── config/ # Env-based configuration
│ │ ├── database/ # Migration runner + embedded SQL
│ │ ├── handler/ # HTTP handlers (one per domain)
│ │ ├── middleware/ # CORS, auth, logging, rate limit, audit
│ │ ├── model/ # Domain models + typed errors
│ │ ├── pubsub/ # Pub/sub interface + Redis impl
│ │ ├── repository/ # SQL queries (pgx)
│ │ ├── service/ # Business logic
│ │ ├── storage/ # Object storage interface + S3 impl
│ │ └── ws/ # WebSocket hub + client management
│ └── Dockerfile
├── clients/web/ # Next.js web client
├── clients/cli/ # CLI track streamer (Go)
├── clients/ios/ # iOS client (SwiftUI, XcodeGen)
├── deploy/
│ ├── caddy/ # Reverse proxy config (production)
│ ├── k8s/ # Kubernetes manifests
│ ├── helm/vincenty/ # Helm chart
│ └── ecs/ # AWS ECS Fargate task definitions
├── docker-compose.yml # Development stack
├── docker-compose.prod.yml # Production stack (Caddy + TLS)
└── Makefile
Security Model¶
| Layer | Mechanism |
|---|---|
| Transport | TLS via reverse proxy (Caddy / ALB / Ingress) |
| Authentication | JWT access tokens (short-lived) + rotating refresh tokens |
| MFA | TOTP, WebAuthn/FIDO2, recovery codes |
| Authorization | Role-based: admin, group_admin, member. Per-group permissions |
| Password storage | bcrypt (cost 12) |
| Token storage | SHA-256 hashed refresh tokens in DB |
| TOTP secret storage | AES-256-GCM or AWS KMS envelope encryption |
| Rate limiting | Per-IP token bucket algorithm |
| Audit | Every API action logged with user, resource, IP, timestamp |
| Container security | Distroless base image, non-root user, no shell |