★ New · v1.5 · DATA

Drop a file in the folder. It's on the map.

A push-based hot-folder watcher that auto-registers vector files the moment another app or service drops them into a registered watch folder. A downstream pipeline writes a .geojson / .parquet / .gpkg into C:\data\feeds, and within ~2 seconds it appears on the map as a project layer — no manual Open Vector File, no app restart. The "data landing zone" workflow, fully air-gapped.

FileSystemWatcher1500 ms debounceSSE toast GeoJSON / Parquet / GPKG / FGB / ShapefileSubfolder watchRe-scan on startDrop-oldest fan-out
How it works

A detection layer over the existing ingestion path

One FileSystemWatcher per enabled folder hooks OS-level change events. A 1500 ms per-file debounce collapses the burst of Created/Changed/Renamed events GIS apps emit when write-then-renaming. The debounced file flows through the existing ProjectHub.RegisterLayer + ProjectStore.AddCatalogEntry seam — no new DuckDB code, no new registration route. The watcher is strictly detection.

  • 1500 ms debounce is mandatory — without it a single file drop fires 3–5 events and races CREATE OR REPLACE VIEW. Per-file System.Threading.Timer, configurable via FolderWatch:DebounceMs.
  • Re-scan on start — push-based watchers can drop events on buffer overflow or restart, so StartWatching re-scans each folder once to catch files that arrived while the watcher was down.
  • SSE fan-out — a bounded (256, DropOldest) channel broadcasts each new layer; the browser shows a toast + raises arcgis-map:catalog-changed. A slow browser never stalls ingestion.
  • Never throws on the event thread — every handler is wrapped in try/catch; a corrupt or locked source is logged + skipped. FileSystemWatcher.Error (common on SMB buffer overflow) logs + schedules a SyncWatchers retry.
POST /api/project/watch/folders
// register a hot folder
curl -X POST http://localhost:5059/api/project/watch/folders \
  -H 'Content-Type: application/json' \
  -d '{
    "path": "C:/data/feeds",
    "alias": "Downstream pipeline drop",
    "watchSubdirs": true
  }'

→ FileSystemWatcher armed on C:/data/feeds
→ pipeline writes alerts.geojson
→ 1500 ms debounce → IngestOne → catalog row
→ SSE toast + layer on the map
The contract

Honest, by design

A few deliberate boundaries keep the watcher robust and predictable. None of these are gaps to "fix" — they're the contract.

🚫

KML / KMZ never registered

DuckDB ST_Read doesn't read KML, and the separate KML subsystem targets ground overlays + tours, not vector registration. A dropped .kml is logged + ignored.

🧩

Shapefile-sidecar rejection

Loose sidecar files (.shp without its .dbf/.prj) are rejected — only complete sources register.

📍

No live deletion-marking

ReconcileCatalog on project-open already marks missing sources stale=true. Live Deleted events are out of scope — you asked for new feeds, not removal tracking.

🔁

Project switch re-binds

The watcher lifecycle is owned by the host's SwitchProject (via RebindFolderWatcher), not DI. Old project's watchers stop; the new project's start.

📂

Extension allowlist

FolderScanner.IsSupported(path) is the static allowlist — case-insensitive, covering GeoJSON/Parquet/GPKG/FGB/Shapefile/Arrow/PMTiles.

🛰️

Not observefs

The DuckDB observefs extension is an I/O latency profiler, not a directory watcher. FileSystemWatcher is the correct primitive.

The API surface

Scan, watch, status, events

GroupEndpointNotes
ScanPOST /api/project/scanOne-shot recursive scan + dedupe of a folder
FoldersGET / POST / PUT / DELETE /api/project/watch/folders[/{id}]CRUD over watch-folder connections
ControlPOST /api/project/watch/toggleEnable/disable the whole watcher
StatusGET /api/project/watch/status{ enabled, running, folders, debounceMs }
EventsGET /api/project/watch/eventsSSE stream of FolderWatchEvent (new-layer notifications)

Base URL http://localhost:5059. Config "FolderWatch": { DebounceMs: 1500, RescanOnReconnectMs: 30000, Enabled: true }. Bridge: window.MapApp.{toggleFolderWatchPanel, scanFolder, addWatchFolder, removeWatchFolder, toggleLiveWatch}. Documented in AGENTS.md ("Folder watcher"). Tests: tests/FolderWatchTests/smoke.cs (47 cases).

Your pipeline's last hop, onto the map

A registered watch folder closes the data landing zone — drop a file, see it live, no manual steps.