Themis API Go Rewrite (UTM)
| Field | Value |
|---|---|
| Status | Draft |
| Owner | Eric Gesell |
| Contributors | TBD |
| Date | 2026-04-21 |
1. Executive Summary
Problem Statement:
The UTM Themis API (utm-themis-api) — DroneUp’s USS implementation — was written in Clojure by contractors no longer with the company. No current DroneUp engineer has Clojure expertise, making the service a maintenance liability. Every other service in the platform is written in Go.
Proposed Solution: Rewrite the Themis API as a Go service implementing the ASTM F3548-21 USS interface, with SDD v2.0.2 qualification as the MVP gate. A discovery spike will determine the implementation approach before full development begins.
Success Criteria:
- The Go service passes 100% of the
uss_qualifierSDD v2.0.2 “Basic SCD” suite (27 ASTM F3548-21 requirements) with zero failures. - A DroneUp Go engineer can build, run, and debug the service without Clojure knowledge.
- The service deploys to Kubernetes via the existing
utm-infrastructureHelm chart with only configuration changes. - DSS round-trip latency (create OI → receive subscription notification → notify peer USSs) is ≤ 5 seconds p95 in the dev environment.
- The existing PostgreSQL schema is preserved with no destructive migrations, maintaining production cutover compatibility.
Scope Clarity: B — compliance target is concrete and testable; implementation approach (DB layer, DSS client strategy, traffic cutover method) requires a short discovery spike before full development begins.
2. User Experience & Functionality
User Personas
- Go engineer (DroneUp) — reads, debugs, extends, and on-calls the service without Clojure knowledge
uss_qualifier(InterUSS test harness) — automated framework that exercises the SDD compliance surface via HTTP; its pass/fail result is the qualification gate- Peer USS (external operator) — sends
POST /uss/v1/operational_intentsnotifications; expectsGET /uss/v1/operational_intents/{uuid}to return current OI state - Mission Console / internal clients — calls the flight planning API to create, activate, and close flights on behalf of RPICs
User Stories & Acceptance Criteria
US-01: Flight plan creation triggers DSS deconfliction
As Mission Console, I want to create a flight plan so that the service registers an operational intent with the DSS and detects conflicts with other operators.
POST /api/flight-plans/v2accepts a valid flight plan body (geometry, altitude, start time, duration, aircraft ID).- Service queries the DSS for overlapping OIs in the 4D volume.
- No conflicts: creates OI in DSS with state
Accepted; returns201 Createdwith the flight plan and its OI ID. - Conflict found: returns
409 Conflictwith details of the conflicting OI. - OI is written to the
operational_intentPostgreSQL table.
US-02: Flight activation creates an Active OI in the DSS
As Mission Console, I want to activate a planned flight so that other USSs are notified of an Active operational intent.
POST /api/flight-plans/v2/activate/:idtransitions anAcceptedOI toActivatedin the DSS.- Peer USSs with overlapping subscriptions receive a notification within 5 seconds p95.
- Returns
200 OKwith the updated flight plan. - Returns
409 Conflictif a higher-priority OI conflicts at activation time.
US-03: Flight close removes the OI from the DSS
As Mission Console, I want to close a flight so that the DSS no longer shows an active OI for that volume.
POST /api/flight-plans/v2/close/:iddeletes the OI from the DSS and notifies subscribers.- Returns
200 OK. - Returns
404 Not Foundif the flight plan ID does not exist.
US-04: Peer USS can fetch an operational intent
As a peer USS, I want to retrieve an OI by UUID so that I can verify its current state.
GET /uss/v1/operational_intents/:uuidreturns the OI in ASTM F3548-21 format (volumes, state, priority).- Returns
404 Not Foundif UUID does not exist. - Response validates against the ASTM F3548-21 schema.
US-05: Service handles incoming USS-to-USS notifications
As the system, I want to receive OI notifications from peer USSs so that I can detect conflicts with managed flights.
POST /uss/v1/operational_intentsaccepts a valid InterUSS notification body and upserts the OI locally.- If the notified OI conflicts with an existing managed flight, a Slack alert is posted to
#scd-cm-alerts. - Returns
200 OKwithin 10 seconds of receipt. - Returns
400 Bad Requestfor malformed payloads. - JWT in
Authorization: Beareris validated against the correct Wings JWKS endpoint for the active authority.
US-06: USS version endpoint responds correctly
As
uss_qualifier, I want to query the USS version endpoint to confirm the service is reachable and returning the correct API version.
GET /uss/v1returns200 OKwith the correctapi_versionper ASTM F3548-21.
Non-Goals (MVP)
| Out of Scope | Rationale |
|---|---|
| Scheduler / pathfinder API | Not tested by SDD v2.0.2 |
Admin UI (/admin/*) | Not part of SDD test surface |
| WebSocket streaming endpoints | Not tested by SDD v2.0.2; deferred to v1.1 |
| Telemetry ingestion | Not tested by SDD v2.0.2 |
| Airspace tile endpoints | Not tested by SDD v2.0.2 |
| Charged airspace integration | Not tested by SDD v2.0.2 |
| DAA simulator | Dev tooling only |
| Production traffic cutover | MVP is a qualification-passing service running alongside the Clojure service |
Replacing utm-themis-altitude | Separate service; out of scope |
| Remote ID (ASTM F3411) | Not required for Basic SCD |
3. Regulatory & Compliance
ASTM F3548-21 (SDD v2.0.2)
The service must pass the InterUSS uss_qualifier SDD v2.0.2 “Basic SCD” suite — 27 requirements:
| Group | Requirements | Description |
|---|---|---|
| GEN | GEN0100, GEN0105, GEN0300, GEN0310, GEN0500 | HTTP conformance, timestamping, versioning |
| OPIN | OPIN0015–OPIN0040 | OI format and retrieval from the USS |
| USS | USS0005 | USS-to-USS notification protocol |
| SCD | SCD0005–SCD0095 | Deconfliction, state transitions, priority handling, conflict notification timing |
Test harness: interuss/monitoring:v0.28.0 (Docker). Config already exists at dev/interuss/sdd2_0_2_prod_fitness.yaml in utm-themis-api.
FAA 44807 Exemption (Wings Platform)
Themis is part of the Wings-qualified configuration. Replacing it is a configuration change requiring Wings re-qualification. The Go service must produce a passing uss_qualifier report before re-qualification can be submitted.
DO-178C / DO-278A: Not applicable — UTM ground software, not certified avionics.
Data Retention: External OIs received from peer USSs must be deleted within 1,080 hours (45 days) per SDD v2.0.2. The background cleanup job from the Clojure implementation must be preserved.
4. Technical Specifications
Architecture Overview
Mission Console / Internal Clients
│ REST (HTTP/JSON)
▼
utm-themis-go (Go)
│
├─ Flight Planning API (/api/flight-plans/v2/*)
│ │
│ ▼
│ DSS Client (ASTM F3548-21 HTTP)
│ │
│ ▼
│ Distributed System (DSS) [external — interuss/dss]
│
├─ InterUSS USS Interface (/uss/v1/*)
│ ← inbound peer USS notifications
│ → outbound peer USS notifications
│
├─ JWT Validator
│ (Wings OAuth JWKS — per authority, geographic routing)
│
├─ PostgreSQL + PostGIS
│ (flight_plan, operational_intent, dss_subscription)
│
└─ Slack webhook (conflict alerts → #scd-cm-alerts)Tech Stack
| Component | Choice | Notes |
|---|---|---|
| Language | Go | Consistent with rest of DroneUp platform |
| HTTP framework | TBD (discovery spike) | net/http + chi, or DroneUp standard |
| DB driver | pgx + raw SQL | No ORM — preserve existing schema exactly |
| Auth | JWKS-validated JWT | Multiple authorities; authority selected by iss claim + geographic bounds |
| Observability | OpenTelemetry → Honeycomb | Consistent with existing service |
| Deployment | Kubernetes + Helm | utm-infrastructure repo |
| Local dev | Docker Compose | Service + PostgreSQL/PostGIS container |
Affected Repos
| Repo | Language | Change |
|---|---|---|
utm-themis-api | Clojure | No changes — stays live until production cutover |
utm-themis-go (new) | Go | New repo — primary deliverable |
utm-infrastructure | HCL | Helm/Terraform updates to deploy new service |
utm-flightops-telemetry-bridge | Go | Verify integration point; may need routing update |
Integration Points
| System | Direction | Protocol | Notes |
|---|---|---|---|
DSS (interuss/dss) | Bidirectional | REST / ASTM F3548-21 | Create/update/delete OIs; manage subscriptions |
| Peer USSs | Bidirectional | REST / InterUSS | Inbound and outbound OI notifications |
| Wings OAuth (JWKS) | Outbound | HTTPS | Validate inbound JWTs; generate service-to-service tokens |
| PostgreSQL + PostGIS | Internal | pgx | flight_plan, operational_intent, dss_subscription |
| Redis | Internal | Redis protocol | Event journal — deferred to v1.1 |
| Honeycomb | Outbound | OTLP | Distributed tracing |
| Slack | Outbound | Webhook | Conflict notifications to #scd-cm-alerts |
Data Model Changes
No destructive schema changes for MVP. The Go service targets the existing PostgreSQL schema (62 migrations as baseline). Additive migrations (indexes only) are permitted if required.
Tables used by MVP: flight_plan, operational_intent, dss_subscription.
Security & Privacy
- JWT validation required on all endpoints; no unauthenticated access
- Authority routing: JWKS endpoint selected by
issclaim + geographic authority config (Wings qual and prod both usekid: 1with different RSA keys — authority config must be ported carefully from the Clojure service) - Service-to-service tokens: generated via Wings OAuth using GCP Service Account credentials from GCP Secret Manager
- External OI data: 45-day retention enforced via background cleanup job
5. Risks & Phased Rollout
Technical Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| ASTM F3548-21 DSS client complexity — spec has subtle edge cases (OVN conflicts, 4D volume queries) | High | High | Use existing Clojure service as behavior spec; run uss_qualifier against dev environment from day one |
| Wings re-qualification timeline unknown | Medium | High | Start re-qual process in parallel with development; keep Clojure service live until complete |
Multi-authority JWT routing — Wings qual and prod share kid: 1 but use different RSA keys | Medium | High | Port authority config and JWKS caching directly from Clojure; add dedicated integration test for auth routing |
| PostgreSQL schema assumptions differ between Clojure and Go | Medium | Medium | Use raw pgx with no ORM; verify Go layer against existing schema with integration tests |
| OVN conflicts under concurrent DSS operations | Low | Medium | Implement retry-with-backoff on OVN mismatch per ASTM spec |
Phased Rollout
Phase 0 — Discovery Spike (2–3 weeks)
Select Go HTTP framework; decide greenfield DSS client vs. adapting interuss/dss client libraries; stand up local uss_qualifier + local DSS + Go skeleton; confirm DB access pattern (pgx vs. sqlc). Output: architecture decision doc, skeleton repo, uss_qualifier running against stub endpoints.
MVP — SDD v2.0.2 Qualification
Flight planning API (create, activate, close); InterUSS USS interface (/uss/v1/*); DSS client (OIs, subscriptions, 4D queries); JWT validation + authority routing; Slack conflict notifications; 45-day OI retention cleanup.
Gate: uss_qualifier SDD v2.0.2 “Basic SCD” — 0 failures.
v1.1 — Full Feature Parity WebSocket streaming; telemetry ingestion; airspace tile endpoints (GeoJSON + MVT); scheduler/pathfinder; Redis event journal; admin UI; report endpoints.
v2.0 — Production Cutover Wings re-qualification approved; traffic migration (strangler fig vs. hard cutover — decided in Phase 0); Clojure service decommissioned.
Rollback Strategy
Database isolation approach: utm-themis-go is provisioned with its own PostgreSQL database, separate from the existing Clojure service database. The two services never share a database instance.
Rationale: A shared database would require the Go service’s schema to remain backward-compatible with the Clojure service at all times — a significant constraint on development. Separate databases eliminate that coupling. This is acceptable because OI data is primarily real-time: the DSS is the authoritative source for currently active and future OIs, and any running service can reconstruct its local state from a DSS query. Historical OIs are valuable for reporting but reporting is out of scope for this PRD.
Rollback procedure:
- Redirect traffic back to
utm-themis-api(Clojure) via the KubernetesServiceselector or ingress weight — no schema changes required. - Both databases are preserved; neither is deleted or truncated on rollback.
- OIs created in the Go DB during the cutover window are not present in the Clojure DB. There is no automated recovery — the DSS stores only OI references (ID, managing USS URL, 4D volumes, state), not the full flight plan content needed to re-populate the Clojure DB.
- The Go DB goes dormant but remains available. If manual reconciliation is required, an operator queries the DSS to enumerate OI IDs registered to our USS URL, then pulls the corresponding records from the dormant Go DB to assess impact.
- In practice the exposure window is the set of flights active at the moment of rollback. Flight durations are short; most OIs will have expired naturally. Operators of any still-active flights would need to re-file.
Data loss boundary: OIs written exclusively to the Go DB during the cutover window will not be visible to the Clojure service after rollback. This could be mitigated with automation to scan the Go DB and reconcile any acitve flights back into the Clojure DB.
Dependencies
- Wings re-qualification must be initiated before production cutover; requires a passing
uss_qualifierreport as evidence - Dev/sandbox DSS endpoints must be accessible for integration testing
utm-infrastructureHelm chart updates required before deployment to any cluster
6. Estimation Input
prd_sizing_input:
feature: "utm-themis-go — Themis API Go Rewrite (MVP: SDD v2.0.2)"
scope_clarity: "B"
key_terms:
- "operational intent"
- "DSS"
- "ASTM F3548"
- "USS"
- "flight plan"
- "strategic conflict detection"
- "JWT JWKS"
risk_flags:
- "new-service"
- "protocol-compliance"
- "multi-authority-auth"
- "regulatory-requalification"
- "production-migration"
affected_repos:
- "utm-themis-api"
- "utm-themis-go"
- "utm-infrastructure"
domains:
- "backend-go"
- "infrastructure-k8s"
regulatory: true
discovery_needed: true