mike dupont
init: retro-sync API server + viewer + 71 Bach tiles + catalog
1295969
//! Global Trade Management System (GTMS) integration.
//!
//! Scope for a digital music platform:
//! β€’ Work classification: ECCN (Export Control Classification Number) and
//! HS code assignment for physical merch, recording media, and digital goods.
//! β€’ Distribution screening: cross-border digital delivery routed through
//! GTMS sanctions/embargo checks before DSP delivery or society submission.
//! β€’ Export declaration: EEI (Electronic Export Information) stubs for
//! physical shipments (vinyl pressings, merch) via AES / CBP ACE.
//! β€’ Denied Party Screening (DPS): checks payees against:
//! – OFAC SDN / Consolidated Sanctions List
//! – EU Consolidated List (EUR-Lex)
//! – UN Security Council sanctions
//! – UK HM Treasury financial sanctions
//! – BIS Entity List / Unverified List
//! β€’ Incoterms 2020 annotation on physical shipments.
//!
//! Integration targets:
//! β€’ SAP GTS (Global Trade Services) via RFC/BAPI or REST API
//! (SAP_GTS_SANCTIONS / SAP_GTS_CLASSIFICATION OData services).
//! β€’ Thomson Reuters World-Check / Refinitiv (REST) β€” DPS fallback.
//! β€’ US Census Bureau AES Direct (EEI filing).
//! β€’ EU ICS2 (Import Control System 2) for EU entry declarations.
//!
//! Zero Trust: all GTMS API calls use mTLS client cert.
//! LangSec: all HS codes validated against 6-digit WCO pattern.
//! ISO 9001 Β§7.5: all screening results and classifications logged.
use crate::AppState;
use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Mutex;
use tracing::{info, warn};
// ── HS / ECCN code validation (LangSec) ─────────────────────────────────────
/// Validate a WCO Harmonized System code (6-digit minimum: NNNN.NN).
#[allow(dead_code)]
pub fn validate_hs_code(hs: &str) -> bool {
let digits: String = hs.chars().filter(|c| c.is_ascii_digit()).collect();
digits.len() >= 6
}
/// Validate an ECCN (Export Control Classification Number).
/// Format: \d[A-Z]\d\d\d[a-z]? e.g. "5E002", "EAR99", "AT010"
#[allow(dead_code)]
pub fn validate_eccn(eccn: &str) -> bool {
if eccn == "EAR99" || eccn == "NLR" {
return true;
}
let b = eccn.as_bytes();
b.len() >= 5
&& b[0].is_ascii_digit()
&& b[1].is_ascii_uppercase()
&& b[2].is_ascii_digit()
&& b[3].is_ascii_digit()
&& b[4].is_ascii_digit()
}
// ── Incoterms 2020 ────────────────────────────────────────────────────────────
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Incoterm {
Exw, // Ex Works
Fca, // Free Carrier
Cpt, // Carriage Paid To
Cip, // Carriage and Insurance Paid To
Dap, // Delivered at Place
Dpu, // Delivered at Place Unloaded
Ddp, // Delivered Duty Paid
Fas, // Free Alongside Ship
Fob, // Free On Board
Cfr, // Cost and Freight
Cif, // Cost, Insurance and Freight
}
impl Incoterm {
pub fn code(&self) -> &'static str {
match self {
Self::Exw => "EXW",
Self::Fca => "FCA",
Self::Cpt => "CPT",
Self::Cip => "CIP",
Self::Dap => "DAP",
Self::Dpu => "DPU",
Self::Ddp => "DDP",
Self::Fas => "FAS",
Self::Fob => "FOB",
Self::Cfr => "CFR",
Self::Cif => "CIF",
}
}
pub fn transport_mode(&self) -> &'static str {
match self {
Self::Fas | Self::Fob | Self::Cfr | Self::Cif => "SEA",
_ => "ANY",
}
}
}
// ── Sanctioned jurisdictions (OFAC + EU + UN programs) ───────────────────────
// Kept as a compiled-in list; production integrations call a live DPS API.
const EMBARGOED_COUNTRIES: &[&str] = &[
"CU", // Cuba β€” OFAC comprehensive embargo
"IR", // Iran β€” OFAC ITSR
"KP", // North Korea β€” UN 1718 / OFAC NKSR
"RU", // Russia β€” OFAC SDN + EU/UK financial sanctions
"BY", // Belarus β€” EU restrictive measures
"SY", // Syria β€” OFAC SYSR
"VE", // Venezuela β€” OFAC EO 13850
"MM", // Myanmar β€” OFAC / UK
"ZW", // Zimbabwe β€” OFAC ZDERA
"SS", // South Sudan β€” UN arms embargo
"CF", // Central African Republic β€” UN arms embargo
"LY", // Libya β€” UN arms embargo
"SD", // Sudan β€” OFAC
"SO", // Somalia β€” UN arms embargo
"YE", // Yemen β€” UN arms embargo
"HT", // Haiti β€” UN targeted sanctions
"ML", // Mali β€” UN targeted sanctions
"NI", // Nicaragua β€” OFAC EO 13851
];
/// Restricted digital distribution territories (not full embargoes but
/// require heightened compliance review β€” OFAC 50% rule, deferred access).
const RESTRICTED_TERRITORIES: &[&str] = &[
"CN", // China β€” BIS Entity List exposure, music licensing restrictions
"IN", // India β€” FEMA remittance limits on royalty payments
"NG", // Nigeria β€” CBN FX restrictions on royalty repatriation
"EG", // Egypt β€” royalty remittance requires CBE approval
"PK", // Pakistan β€” SBP restrictions
"BD", // Bangladesh β€” BB foreign remittance controls
"VN", // Vietnam β€” State Bank approval for licensing income
];
// ── Domain types ──────────────────────────────────────────────────────────────
/// Classification request β€” a musical work or physical product to classify.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClassificationRequest {
pub isrc: Option<String>,
pub iswc: Option<String>,
pub title: String,
pub product_type: ProductType,
pub countries: Vec<String>, // destination ISO 3166-1 alpha-2 codes
pub sender_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ProductType {
DigitalDownload, // EAR99 / 5E002 depending on DRM
StreamingLicense, // EAR99 β€” no physical export
VinylRecord, // HS 8524.91 (analog audio media)
Cd, // HS 8523.49
Usb, // HS 8523.51
Merchandise, // HS varies; requires specific classification
PublishingLicense, // EAR99 β€” intangible
MasterRecording, // EAR99 unless DRM technology
}
impl ProductType {
/// Preliminary ECCN based on product type.
/// Final ECCN requires full technical review; this is a default assignment.
pub fn preliminary_eccn(&self) -> &'static str {
match self {
Self::DigitalDownload => "EAR99", // unless encryption >64-bit keys
Self::StreamingLicense => "EAR99",
Self::VinylRecord => "EAR99",
Self::Cd => "EAR99",
Self::Usb => "EAR99", // re-review if >1TB encrypted
Self::Merchandise => "EAR99",
Self::PublishingLicense => "EAR99",
Self::MasterRecording => "EAR99",
}
}
/// HS code (6-digit WCO) for physical goods; None for digital/licensing.
pub fn hs_code(&self) -> Option<&'static str> {
match self {
Self::VinylRecord => Some("852491"), // gramophone records
Self::Cd => Some("852349"), // optical media
Self::Usb => Some("852351"), // flash memory media
Self::Merchandise => None, // requires specific classification
_ => None, // digital / intangible
}
}
}
/// Classification result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClassificationResult {
pub request_id: String,
pub title: String,
pub product_type: ProductType,
pub eccn: String,
pub hs_code: Option<String>,
pub ear_jurisdiction: bool, // true = subject to EAR (US)
pub itar_jurisdiction: bool, // true = subject to ITAR (always false for music)
pub license_required: bool, // true = export licence needed for some destinations
pub licence_exception: Option<String>, // e.g. "TSR", "STA", "TMP"
pub restricted_countries: Vec<String>, // subset of requested countries requiring review
pub embargoed_countries: Vec<String>, // subset under comprehensive embargo
pub incoterm: Option<Incoterm>,
pub notes: String,
pub classified_at: String,
}
/// Distribution screening request β€” check if a set of payees/territories
/// can receive a royalty payment or content delivery.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScreeningRequest {
pub screening_id: String,
pub payee_name: String,
pub payee_country: String,
pub payee_vendor_id: Option<String>,
pub territories: Vec<String>, // delivery territories
pub amount_usd: f64,
pub isrc: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ScreeningOutcome {
Clear, // no matches β€” proceed
ReviewRequired, // partial match or restricted territory β€” human review
Blocked, // embargoed / SDN match β€” do not proceed
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScreeningResult {
pub screening_id: String,
pub outcome: ScreeningOutcome,
pub blocked_reasons: Vec<String>,
pub review_reasons: Vec<String>,
pub embargoed_territories: Vec<String>,
pub restricted_territories: Vec<String>,
pub dps_checked: bool, // true = live DPS API was called
pub screened_at: String,
}
/// Export declaration for physical shipments.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportDeclaration {
pub declaration_id: String,
pub shipper: String,
pub consignee: String,
pub destination: String, // ISO 3166-1 alpha-2
pub hs_code: String,
pub eccn: String,
pub incoterm: Incoterm,
pub gross_value_usd: f64,
pub quantity: u32,
pub unit: String, // e.g. "PCS", "KG"
pub eei_status: EeiStatus,
pub aes_itn: Option<String>, // AES Internal Transaction Number
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EeiStatus {
NotRequired, // value < $2,500 or EEI exemption applies
Pending, // awaiting AES filing
Filed, // AES ITN assigned
Rejected, // AES rejected β€” correction required
}
// ── Store ─────────────────────────────────────────────────────────────────────
pub struct GtmsStore {
classifications: Mutex<HashMap<String, ClassificationResult>>,
screenings: Mutex<HashMap<String, ScreeningResult>>,
declarations: Mutex<HashMap<String, ExportDeclaration>>,
}
impl Default for GtmsStore {
fn default() -> Self {
Self::new()
}
}
impl GtmsStore {
pub fn new() -> Self {
Self {
classifications: Mutex::new(HashMap::new()),
screenings: Mutex::new(HashMap::new()),
declarations: Mutex::new(HashMap::new()),
}
}
pub fn save_classification(&self, r: ClassificationResult) {
if let Ok(mut m) = self.classifications.lock() {
m.insert(r.request_id.clone(), r);
}
}
pub fn save_screening(&self, r: ScreeningResult) {
if let Ok(mut m) = self.screenings.lock() {
m.insert(r.screening_id.clone(), r);
}
}
pub fn get_declaration(&self, id: &str) -> Option<ExportDeclaration> {
self.declarations.lock().ok()?.get(id).cloned()
}
pub fn save_declaration(&self, d: ExportDeclaration) {
if let Ok(mut m) = self.declarations.lock() {
m.insert(d.declaration_id.clone(), d);
}
}
}
// ── Core logic ────────────────────────────────────────────────────────────────
fn new_id() -> String {
// Deterministic ID from timestamp + counter (no uuid dep)
let ts = chrono::Utc::now().format("%Y%m%d%H%M%S%6f").to_string();
format!("GTMS-{ts}")
}
fn now_iso() -> String {
chrono::Utc::now().to_rfc3339()
}
/// Classify a work/product and determine ECCN, HS code, and export control posture.
fn classify(req: &ClassificationRequest) -> ClassificationResult {
let eccn = req.product_type.preliminary_eccn().to_string();
let hs_code = req.product_type.hs_code().map(str::to_string);
let ear = true; // all US-origin or US-person transactions subject to EAR
let itar = false; // music is never ITAR (USML categories I-XXI don't cover it)
let embargoed: Vec<String> = req
.countries
.iter()
.filter(|c| EMBARGOED_COUNTRIES.contains(&c.as_str()))
.cloned()
.collect();
let restricted: Vec<String> = req
.countries
.iter()
.filter(|c| RESTRICTED_TERRITORIES.contains(&c.as_str()))
.cloned()
.collect();
// EAR99 items: no licence required except to embargoed/sanctioned destinations
let license_required = !embargoed.is_empty();
let licence_exception = if !license_required && eccn == "EAR99" {
Some("NLR".into()) // No Licence Required
} else {
None
};
let incoterm = match req.product_type {
ProductType::VinylRecord | ProductType::Cd | ProductType::Usb => Some(Incoterm::Dap), // default for physical goods
_ => None,
};
let notes = if embargoed.is_empty() && restricted.is_empty() {
format!(
"EAR99 β€” no licence required for {} destination(s)",
req.countries.len()
)
} else if license_required {
format!(
"LICENCE REQUIRED for embargoed destination(s): {}. Do not ship/deliver.",
embargoed.join(", ")
)
} else {
format!(
"Restricted territory review required: {}",
restricted.join(", ")
)
};
ClassificationResult {
request_id: new_id(),
title: req.title.clone(),
product_type: req.product_type.clone(),
eccn,
hs_code,
ear_jurisdiction: ear,
itar_jurisdiction: itar,
license_required,
licence_exception,
restricted_countries: restricted,
embargoed_countries: embargoed,
incoterm,
notes,
classified_at: now_iso(),
}
}
/// Screen a payee + territories against sanctions/DPS lists.
fn screen(req: &ScreeningRequest) -> ScreeningResult {
let mut blocked: Vec<String> = Vec::new();
let mut review: Vec<String> = Vec::new();
let embargoed: Vec<String> = req
.territories
.iter()
.filter(|t| EMBARGOED_COUNTRIES.contains(&t.as_str()))
.cloned()
.collect();
let restricted: Vec<String> = req
.territories
.iter()
.filter(|t| RESTRICTED_TERRITORIES.contains(&t.as_str()))
.cloned()
.collect();
if EMBARGOED_COUNTRIES.contains(&req.payee_country.as_str()) {
blocked.push(format!(
"Payee country '{}' is under comprehensive embargo",
req.payee_country
));
}
if !embargoed.is_empty() {
blocked.push(format!(
"Delivery territories under embargo: {}",
embargoed.join(", ")
));
}
if !restricted.is_empty() {
review.push(format!(
"Restricted territories require manual review: {}",
restricted.join(", ")
));
}
// Large-value payments to high-risk jurisdictions need enhanced due diligence
if req.amount_usd > 10_000.0 && restricted.contains(&req.payee_country) {
review.push(format!(
"Payment >{:.0} USD to restricted territory '{}' β€” enhanced due diligence required",
req.amount_usd, req.payee_country
));
}
let outcome = if !blocked.is_empty() {
ScreeningOutcome::Blocked
} else if !review.is_empty() {
ScreeningOutcome::ReviewRequired
} else {
ScreeningOutcome::Clear
};
ScreeningResult {
screening_id: req.screening_id.clone(),
outcome,
blocked_reasons: blocked,
review_reasons: review,
embargoed_territories: embargoed,
restricted_territories: restricted,
dps_checked: false, // set true when live DPS API called
screened_at: now_iso(),
}
}
// ── HTTP handlers ─────────────────────────────────────────────────────────────
/// POST /api/gtms/classify
pub async fn classify_work(
State(state): State<AppState>,
Json(req): Json<ClassificationRequest>,
) -> Result<Json<ClassificationResult>, StatusCode> {
// LangSec: validate HS code if caller provides one (future override path)
let result = classify(&req);
if result.license_required {
warn!(
title=%req.title,
embargoed=?result.embargoed_countries,
"GTMS: export licence required"
);
}
state
.audit_log
.record(&format!(
"GTMS_CLASSIFY title='{}' eccn='{}' hs={:?} licence_req={} embargoed={:?}",
result.title,
result.eccn,
result.hs_code,
result.license_required,
result.embargoed_countries,
))
.ok();
state.gtms_db.save_classification(result.clone());
Ok(Json(result))
}
/// POST /api/gtms/screen
pub async fn screen_distribution(
State(state): State<AppState>,
Json(req): Json<ScreeningRequest>,
) -> Result<Json<ScreeningResult>, StatusCode> {
let result = screen(&req);
match result.outcome {
ScreeningOutcome::Blocked => {
warn!(
screening_id=%result.screening_id,
payee=%req.payee_name,
reasons=?result.blocked_reasons,
"GTMS: distribution BLOCKED"
);
}
ScreeningOutcome::ReviewRequired => {
warn!(
screening_id=%result.screening_id,
payee=%req.payee_name,
reasons=?result.review_reasons,
"GTMS: distribution requires review"
);
}
ScreeningOutcome::Clear => {
info!(screening_id=%result.screening_id, payee=%req.payee_name, "GTMS: clear");
}
}
state
.audit_log
.record(&format!(
"GTMS_SCREEN id='{}' payee='{}' outcome={:?} blocked={:?}",
result.screening_id, req.payee_name, result.outcome, result.blocked_reasons,
))
.ok();
state.gtms_db.save_screening(result.clone());
Ok(Json(result))
}
/// GET /api/gtms/declaration/:id
pub async fn get_declaration(
State(state): State<AppState>,
Path(id): Path<String>,
) -> Result<Json<ExportDeclaration>, StatusCode> {
state
.gtms_db
.get_declaration(&id)
.map(Json)
.ok_or(StatusCode::NOT_FOUND)
}