# 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 <token>" 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 (~50–500 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.
