Files
satellite-provider/_docs/02_task_plans/uav-batch-upload/problem.md
T
Oleksandr Bezdieniezhnykh 8e15e53782 chore: cycle 2 step 9 task plan artifacts + step 10 state
Carries forward new-task research + solution drafts under
_docs/02_task_plans/uav-batch-upload/ that were not included in
the Step 9 task-spec commit (42a3cc7). Also marks the autodev
state as Step 10 in_progress for cycle 2 implementation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 22:54:36 +03:00

9.2 KiB
Raw Blame History

Research Problem — UAV Batch Upload (AZ-485 family)

Context

  • Project: SatelliteProvider — .NET 8.0 ASP.NET Core service that downloads/persists satellite imagery tiles. Currently single-source (Google Maps); AZ-484 just shipped multi-source storage (source + captured_at columns, 5-col unique index, tile-storage v1.0.0 frozen contract).
  • Cycle: 2 (started after cycle 1 closed AZ-484 today, 2026-05-11).
  • Task family (4 tasks under epic AZ-483, accepted by user):
    • AZ-485 — UAV batch upload endpoint + per-tile persistence loop (5 SP)
    • AZ-486 — Quality gate (6 checks + thresholds + reject reasons) (3 SP)
    • AZ-487 — JWT auth middleware (3 SP)
    • AZ-488 — Geofence whitelist (2 SP)
  • Confirmed user requirements (from new-task Step 1):
    1. Batch upload (multipart with metadata + N image files per request)
    2. Minimum metadata only per tile: latitude, longitude, tile_zoom, tile_size_meters, captured_at (UTC), image bytes
    3. Quality gate enforces all of: dimensions, byte size, blank/uniform detection, captured_at age, format=JPEG, geofence containment
    4. JWT auth (HS256, signing key in env, required claims: sub + valid exp)
    5. Sync responses: 200 + per-tile { tileId, status, reason? } array; 4xx for auth/format-of-batch failures
    6. File storage: same ./tiles/{z}/{x}/{y}.jpg layout as Google Maps tiles
  • Project constraints to respect:
    • .NET 8.0 only (no .NET 9 features)
    • Existing libraries: ImageSharp 3.1.11, Npgsql 9.0.2, Dapper 2.1.35, Serilog 8.0.3
    • No auth currently exists in the API (per Security Audit Step 14, 2026-05-11)
    • The "geofence" type in the codebase (SatelliteProvider.Common.DTO.GeofencePolygon) is actually a bounding box (NorthWest + SouthEast corners), not an arbitrary polygon. There is no existing point-in-polygon implementation.
    • The frozen _docs/02_document/contracts/data-access/tile-storage.md v1.0.0 is the producer-side contract this work must implement (source='uav', captured_at per-row, per-source UPSERT)
    • Lessons applied: L-001 (Dapper enum bypass — no new persisted enums planned but the rule applies if any sneak in)

Specific unknowns to investigate

Q1 — JWT auth middleware in ASP.NET Core 8 for an internal service (AZ-487)

  • Recommended pattern for an internal/trusted-network API that needs to validate HS256 tokens issued by an external system?
    • Microsoft.AspNetCore.Authentication.JwtBearer (built-in, recommended? version-pinned for ASP.NET Core 8)
    • vs. third-party (e.g., custom middleware, OpenIddict, etc.)
  • Configuration shape — where does the signing key live? appsettings vs env var vs key vault? (Project constraint: this codebase has no secret manager today.)
  • Required claims minimum: just sub and unexpired exp per user choice. Confirm JwtBearerOptions.TokenValidationParameters setup that validates only signature + exp and tolerates missing aud/iss (or reject — which is the safer ASP.NET Core 8 default?).
  • Swagger integration: how to surface "Bearer " in the Swagger UI for the upload endpoint without changing the existing public endpoints?
  • 401 response shape — does the global GlobalExceptionHandler need to know about it, or does [Authorize] short-circuit before the handler?
  • Test strategy — how do current SatelliteProvider.IntegrationTests (which call the live API via HttpClient per RouteTestHelpers) cleanly inject a valid JWT? Pattern for a "test issuer" using the same signing key.

Q2 — Multipart batch upload in ASP.NET Core 8 minimal API (AZ-485)

  • Idiomatic shape for multipart/form-data with N files + a JSON metadata document in a minimal API endpoint:
    • [FromForm] model binding
    • vs. manual MultipartReader for streaming (relevant if batches are large)
  • Request size limits in ASP.NET Core 8 (Kestrel MaxRequestBodySize and form options) — defaults and how to override for the upload endpoint only.
  • Memory / streaming trade-off: how big can the per-batch IFormFile[] get before we should switch to streaming via MultipartReader? Practical batch-size guidance for satellite tiles (~50500 KB each).
  • Response shape for partial-success batches — common patterns (HTTP 200 with mixed array, HTTP 207 Multi-Status, HTTP 422 with per-item errors).
  • Backpressure / concurrency limit on the per-tile persistence loop — should this be sequential or fanned out? Trade-off vs. Postgres connection pool size (current Npgsql defaults).

Q3 — Image quality heuristics for satellite tiles using ImageSharp 3.1.11 (AZ-486)

  • API surface in ImageSharp 3.1.11 specifically for:
    • Reading width/height without full decode (Image.IdentifyAsync returns ImageInfo)
    • Detecting actual format from magic bytes vs. trusting Content-Type
    • Computing a "uniformity / blankness" metric efficiently — luminance stddev across the image, or histogram-based, or a perceptual hash
  • Performance budget: per-tile quality check ideally under ~10ms on a 256×256 JPEG. Confirm which ImageSharp APIs are streaming vs. full-decode.
  • Robustness against adversarial inputs — e.g., a 1-pixel image with a JPEG header. What does ImageSharp 3.1.11 throw, and is there a safe-decode pattern?
  • Threshold-tuning approach — recommended way to tune the blank-detection threshold against a small fixture set of real satellite tiles vs. uniform tiles, without having that fixture set today.

Q4 — Geofence whitelist: bbox vs. polygon (AZ-488)

  • The codebase's existing geofence type is a bounding box (NorthWest+SouthEast). Two options for the AZ-488 whitelist:
    • Option A: reuse the existing bbox shape (simple, consistent with current code, but coarser — a UAV tile inside a non-rectangular real-world allowed area can't be expressed)
    • Option B: introduce a true polygon (lat/lon list) + ray-casting point-in-polygon, store as JSON in config. Requires writing the ray-cast utility (or pulling in NetTopologySuite).
  • Recommendation: which one is the right fit given that the user only said "geofence" without specifying shape and the existing code is bbox? Compare with NetTopologySuite as a third option.
  • Storage: where does the whitelist live? appsettings.json array, dedicated config file, dedicated DB table?
  • Performance: typical UAV batch is N tiles vs. M whitelist polygons — is M small enough (e.g., < 10 polygons, < 1000 vertices) that O(N×M) ray-cast per batch is fine?

Q5 — Per-tile persistence loop: file-system + DB consistency (AZ-485)

  • Pattern for "write JPEG to disk, then write DB row" with rollback on either failure:
    • Two-phase: write file with a .tmp suffix, INSERT, then rename. On INSERT failure, delete .tmp.
    • vs. write DB row first (ON CONFLICT does UPSERT), then write file. On file failure, what happens to the row? (Could leave a row pointing at a missing file path — bad.)
    • Idempotency: if the same (lat, lon, zoom, size, source='uav') tile is uploaded twice, the per-source UPSERT already handles the DB side — but does the file get overwritten cleanly? Atomic file replace pattern.
  • Concurrency: two simultaneous batch uploads for adjacent tiles — any shared resource that needs locking?

Out of scope for this research

  • Distributed UAV fleet management, queueing, or real-time streaming (these are upstream concerns the user has not raised)
  • Async/202 + status polling response model (user explicitly said sync 200)
  • Per-tile encryption, compression beyond JPEG, or alternate image formats
  • External IdP integration (HS256 with shared secret only)
  • UAV mission/operator/sensor metadata (user explicitly said "not necessary for now")

Acceptance criteria for the research output

The research deliverable (solution_draft01.md) must:

  1. Recommend a concrete library + config approach for Q1 (JWT). Include: package name + version, code shape for builder.Services.AddAuthentication().AddJwtBearer(...), key storage recommendation, integration-test pattern.
  2. Recommend a concrete request shape + size-limit approach for Q2 (multipart). Include: [FromForm] vs streaming decision criteria, response shape choice with rationale, concurrency-limit recommendation.
  3. Recommend a concrete API call sequence in ImageSharp 3.1.11 for Q3 (quality heuristics). Include: blank-detection threshold starting point + tuning plan, performance budget validation, error-handling pattern.
  4. Recommend bbox-vs-polygon-vs-NTS for Q4 (geofence). Include: exact-fit verdict per the project's actual operating context (M < 10 polygons, simple ray-cast acceptable, or pull in NTS).
  5. Recommend a file+DB consistency pattern for Q5. Include: failure modes covered, rollback shape, idempotency story.
  6. Include a "do not use" / rejected list for each question — alternatives the research considered and rejected with one-line evidence.

Execution mode

Mode A — Initial Research. Output class: Technical-component selection (recommends specific libraries, ASP.NET Core 8 modes, ImageSharp APIs, optional NTS).

Per-mode API capability verification applies to every recommended library. Saved Minimum Viable Examples required for: Microsoft.AspNetCore.Authentication.JwtBearer JWT validation, ASP.NET Core 8 minimal-API multipart, ImageSharp 3.1.11 luminance stddev.