Skip to content

Themis API Go Rewrite (UTM)

Andi Lamprecht Andi Lamprecht ·· 10 min read· Draft
FieldValue
StatusDraft
OwnerEric Gesell
ContributorsTBD
Date2026-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:

  1. The Go service passes 100% of the uss_qualifier SDD v2.0.2 “Basic SCD” suite (27 ASTM F3548-21 requirements) with zero failures.
  2. A DroneUp Go engineer can build, run, and debug the service without Clojure knowledge.
  3. The service deploys to Kubernetes via the existing utm-infrastructure Helm chart with only configuration changes.
  4. DSS round-trip latency (create OI → receive subscription notification → notify peer USSs) is ≤ 5 seconds p95 in the dev environment.
  5. 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_intents notifications; expects GET /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/v2 accepts 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; returns 201 Created with the flight plan and its OI ID.
  • Conflict found: returns 409 Conflict with details of the conflicting OI.
  • OI is written to the operational_intent PostgreSQL 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/:id transitions an Accepted OI to Activated in the DSS.
  • Peer USSs with overlapping subscriptions receive a notification within 5 seconds p95.
  • Returns 200 OK with the updated flight plan.
  • Returns 409 Conflict if 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/:id deletes the OI from the DSS and notifies subscribers.
  • Returns 200 OK.
  • Returns 404 Not Found if 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/:uuid returns the OI in ASTM F3548-21 format (volumes, state, priority).
  • Returns 404 Not Found if 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_intents accepts 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 OK within 10 seconds of receipt.
  • Returns 400 Bad Request for malformed payloads.
  • JWT in Authorization: Bearer is 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/v1 returns 200 OK with the correct api_version per ASTM F3548-21.

Non-Goals (MVP)

Out of ScopeRationale
Scheduler / pathfinder APINot tested by SDD v2.0.2
Admin UI (/admin/*)Not part of SDD test surface
WebSocket streaming endpointsNot tested by SDD v2.0.2; deferred to v1.1
Telemetry ingestionNot tested by SDD v2.0.2
Airspace tile endpointsNot tested by SDD v2.0.2
Charged airspace integrationNot tested by SDD v2.0.2
DAA simulatorDev tooling only
Production traffic cutoverMVP is a qualification-passing service running alongside the Clojure service
Replacing utm-themis-altitudeSeparate 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:

GroupRequirementsDescription
GENGEN0100, GEN0105, GEN0300, GEN0310, GEN0500HTTP conformance, timestamping, versioning
OPINOPIN0015–OPIN0040OI format and retrieval from the USS
USSUSS0005USS-to-USS notification protocol
SCDSCD0005–SCD0095Deconfliction, 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

ComponentChoiceNotes
LanguageGoConsistent with rest of DroneUp platform
HTTP frameworkTBD (discovery spike)net/http + chi, or DroneUp standard
DB driverpgx + raw SQLNo ORM — preserve existing schema exactly
AuthJWKS-validated JWTMultiple authorities; authority selected by iss claim + geographic bounds
ObservabilityOpenTelemetry → HoneycombConsistent with existing service
DeploymentKubernetes + Helmutm-infrastructure repo
Local devDocker ComposeService + PostgreSQL/PostGIS container

Affected Repos

RepoLanguageChange
utm-themis-apiClojureNo changes — stays live until production cutover
utm-themis-go (new)GoNew repo — primary deliverable
utm-infrastructureHCLHelm/Terraform updates to deploy new service
utm-flightops-telemetry-bridgeGoVerify integration point; may need routing update

Integration Points

SystemDirectionProtocolNotes
DSS (interuss/dss)BidirectionalREST / ASTM F3548-21Create/update/delete OIs; manage subscriptions
Peer USSsBidirectionalREST / InterUSSInbound and outbound OI notifications
Wings OAuth (JWKS)OutboundHTTPSValidate inbound JWTs; generate service-to-service tokens
PostgreSQL + PostGISInternalpgxflight_plan, operational_intent, dss_subscription
RedisInternalRedis protocolEvent journal — deferred to v1.1
HoneycombOutboundOTLPDistributed tracing
SlackOutboundWebhookConflict 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 iss claim + geographic authority config (Wings qual and prod both use kid: 1 with 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

RiskLikelihoodImpactMitigation
ASTM F3548-21 DSS client complexity — spec has subtle edge cases (OVN conflicts, 4D volume queries)HighHighUse existing Clojure service as behavior spec; run uss_qualifier against dev environment from day one
Wings re-qualification timeline unknownMediumHighStart 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 keysMediumHighPort authority config and JWKS caching directly from Clojure; add dedicated integration test for auth routing
PostgreSQL schema assumptions differ between Clojure and GoMediumMediumUse raw pgx with no ORM; verify Go layer against existing schema with integration tests
OVN conflicts under concurrent DSS operationsLowMediumImplement 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:

  1. Redirect traffic back to utm-themis-api (Clojure) via the Kubernetes Service selector or ingress weight — no schema changes required.
  2. Both databases are preserved; neither is deleted or truncated on rollback.
  3. 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.
  4. 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.
  5. 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_qualifier report as evidence
  • Dev/sandbox DSS endpoints must be accessible for integration testing
  • utm-infrastructure Helm 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
Last updated on