* feat(supply-chain): Sprint D — GetSectorDependency RPC + vendor route-intelligence API + webhooks
* fix(supply-chain): move bypass-corridors + chokepoint-registry to server/_shared to fix api/ boundary violations
* fix(supply-chain): webhooks — persist secret, fix sub-resource routing, add ownership check
* fix(supply-chain): address PR #2905 review findings
- Use SHA-256(apiKey) for ownerTag instead of last-12-chars (unambiguous ownership)
- Implement GET /api/v2/shipping/webhooks list route via per-owner Redis Set index
- Tighten SSRF: https-only, expanded metadata hostname blocklist, document DNS rebinding edge-runtime limitation
- Fix get-sector-dependency.ts stale src/config/ imports → server/_shared/ (Greptile P1)
* fix(supply-chain): getSectorDependency returns blank primaryChokepointId for landlocked countries
computeExposures() previously mapped over all of CHOKEPOINT_REGISTRY even
when nearestRouteIds was empty, producing a full array of score-0 entries
in registry insertion order. The caller's exposures[0] then picked the
first registry entry (Suez) as the "primary" chokepoint despite
primaryChokepointExposure = 0. LI, AD, SM, BT and other landlocked
countries were all silently assigned a fake chokepoint.
Fix: guard at the top of computeExposures() -- return [] when input is
empty so primaryChokepointId stays '' and primaryChokepointExposure stays 0.