* feat(sanctions): entity lookup index + OpenSanctions search (#2042)
* fix: guard tokens[0] access in sanctions lookup
* fix: use createIpRateLimiter pattern in sanctions-entity-search
* fix: add sanctions-entity-search to allowlist and cache tier
* fix: add LookupSanctionEntity RPC to service.proto, regenerate
* fix(sanctions): strip _entityIndex/_state from main key publish, guard limit NaN
P0: seed-sanctions-pressure was writing the full _entityIndex array and _state
snapshot into sanctions:pressure:v1 because afterPublish runs after atomicPublish.
Add publishTransform to strip both fields before the main key write so the
pressure payload stays compact; afterPublish and extraKeys still receive the full
data object and write the correct separate keys.
P1: limit param in sanctions-entity-search edge function passed NaN to OpenSanctions
when a non-numeric value was supplied. Fix with Number.isFinite guard.
P2: add 200-char max length on q param to prevent oversized upstream requests.
* fix(sanctions): maxStaleMin 2x interval, no-store on entity search
health.js: 720min (1x) → 1440min (2x) for both sanctionsPressure and
sanctionsEntities. A single missed 12h cron was immediately flagging stale.
sanctions-entity-search.js: Cache-Control public → no-store. Sanctions
lookups include compliance-sensitive names in the query string; public
caching would have logged/stored these at CDN/proxy layer.
* feat(sanctions): add OFAC sanctions pressure intelligence
* fix(sanctions): strip _state from API response, fix code/name alignment, cap seed limit
- trimResponse now destructures _state before spreading to prevent seed
internals leaking to API clients during the atomicPublish→afterPublish window
- buildLocationMap and extractPartyCountries now sort (code, name) as aligned
pairs instead of calling uniqueSorted independently on each array; fixes
code↔name mispairing for OFAC-specific codes like XC (Crimea) where
alphabetic order of codes and names diverges
- DEFAULT_RECENT_LIMIT reduced from 120 to 60 to match MAX_ITEMS_LIMIT so
seeded entries beyond the handler cap are not written unnecessarily
- Add tests/sanctions-pressure.test.mjs covering all three invariants
* fix(sanctions): register sanctions:pressure:v1 in health.js BOOTSTRAP_KEYS and SEED_META
Adds sanctionsPressure to health.js so the health endpoint monitors the
seeded key for emptiness (CRIT) and freshness via seed-meta:sanctions:pressure
(maxStaleMin: 720 matches 12h seed TTL). Without this, health was blind to
stale or missing sanctions data.