Pular para o conteúdo

ADR-007 · bhawk como compliance engine independiente

Este conteúdo não está disponível em sua língua ainda.

El 2026-05-30 el dueño de producto preguntó si valía la pena fusionar bhawk dentro de bmonkey y eliminar el servicio aparte. La motivación era el acoplamiento percibido: el paso sarlaft del onboarding de bmonkey llama síncronamente a bhawk-api por S2S, y hoy bmonkey es el único consumer real del motor de compliance. Si el cliente es uno solo, ¿por qué tener dos servicios?

La respuesta corta es que el acoplamiento es real pero es síntoma de early-stage, no de mal diseño. bhawk es un motor de reglas de propósito general que evoluciona en su propio bounded context (DSL de nodes+edges, catálogo de listas restrictivas, ciclo draft/published/deprecated, dry-run contra cualquier validation). Su rol natural en el ecosistema —y la oportunidad comercial que abre— es ser un compliance engine vendible standalone, con consumers futuros previstos: bseal (compliance previo a sellado), terceros externos (compliance-as-a-service para fintechs que no compran el wallet) y, eventualmente, una API pública tier 2/3.

ComponenteUbicación
bhawk-apibhawk/backend/go-bhawk-api/
bhawk-workerbhawk/backend/go-bhawk-worker/
Templates de reglasshared-libs/go-bjungle-bhawk-templates/
Evaluator (DSL Go)shared-libs/go-bjungle-bhawk-evaluator/
Screening helpersshared-libs/go-bjungle-bhawk-screening/
Consumer #1bmonkey-api (flow step sarlaft, internal/sarlaft/)

bhawk se mantiene como servicio independiente, con el rol explícito de “compliance engine vendible standalone”. NO se fusiona dentro de bmonkey y NO se elimina del repo.

  1. Bounded context distinto. bhawk tiene su propio modelo de dominio (validations, rules como DAG de condition+action nodes, position 1..N densa por validation, ciclo draft → published → deprecated), su propio catálogo de listas restrictivas (OFAC, ONU, UE, FATF, PEP, y las colombianas Procuraduría / Contraloría / Policía cuando se integren), su propio versionado y su propio dry-run. Es complejo y evoluciona a un ritmo distinto del wallet SSI.

  2. Reusabilidad. Cuando bseal sume compliance pre-sellado (verificar que el firmante no esté en listas restrictivas antes de emitir el PDF) o cuando se venda compliance-as-a-service a terceros, esos clientes no deberían tener que importar el wallet para usar el motor de reglas. Fusionar en bmonkey deja a esos consumers atados a un módulo que no necesitan.

  3. Escalabilidad independiente. Las evaluaciones de reglas pueden ser CPU-intensive (especialmente cuando entren proveedores externos como OpenSanctions con indexación de ~150k entidades — ver el profile --profile sarlaft en CLAUDE.md gotcha #11). Aislar el servicio permite escalar horizontal sólo bhawk sin tocar el plano del wallet.

  4. Blast radius. Cada app tiene su DB lógica (bjungle_bhawk vs bjungle_bmonkey). Una migración mal hecha en bhawk no puede corromper wallet_passkeys, verifiable_credentials ni la cadena de step-up tokens. Mantenerlos separados es defensa en profundidad.

  5. Compliance audit. SARLAFT en Colombia tiene supervisor financiero (Superintendencia). Que el módulo de reglas sea aislable facilita auditorías regulatorias —el supervisor inspecciona un servicio acotado, no un monolito— y permite certificarlo separado en el futuro.

  • bmonkey es hoy el único consumer real, lo que da la ilusión de relación 1-a-1.
  • El cliente HTTP en bmonkey/internal/sarlaft/client.go es ad-hoc (struct con baseURL/token/http.Client, sin SDK shared-lib). Cualquier segundo consumer hoy tendría que copiar-pegar ese código.
  • bhawk replica patrones de tenancy / RLS / audit que ya viven en bmonkey y en shared-libs/go-bjungle-tenancy. El DRY pendiente refuerza la percepción de duplicación.

Direccionales, sin sprint asignado. Entran cuando el trigger natural lo pida.

  1. Consolidar el SDK Go en shared-libs/go-bjungle-bhawk-client. Hoy bmonkey-api habla con bhawk por HTTP a pelo (POST /v1/internal/screenings con X-Internal-Token). Cuando bseal entre como consumer, antes de copiar-pegar el client.go hay que extraer una interfaz común:

    type ScreeningEngine interface {
    ScreenSubject(ctx context.Context, req ScreenRequest) (*ScreenResult, error)
    DryRun(ctx context.Context, validationID uuid.UUID, subj Subject) (*ScreenResult, error)
    }

    Con wrappers de transporte: HTTP (default, S2S X-Internal-Token), in-process (para tests y para una integración monolítica futura sin pagar el round-trip), y eventualmente gRPC si el throughput lo pide. bmonkey-api, bseal-api y cualquier servicio nuevo importan la interfaz, no el HTTP crudo.

  2. Documentar el caso de uso bseal. Cuando bseal sume el step de compliance pre-sellado, debe consumirlo con el SDK Go consolidado del punto 1, NUNCA con un cliente HTTP custom replicado.

  3. Cache de resultados de screening. Las listas restrictivas no cambian segundo a segundo. Una clave compuesta (subject_ref, rule_set_version, list_version) permite cachear resultados N minutos (Redis o pg_cron materialized) sin sacrificar correctitud — la versión cambia cuando un admin publica una nueva validation o cuando el screener rota listas.

  4. OpenAPI público (Fase 5+). Cuando se decida vender bhawk como compliance-as-a-service, exponer /v1/screenings + /v1/rules con OAuth2 client credentials (Platform como issuer) y publicar un OpenAPI 3.1 generado. Esto cae en el Tier 2/3 del modelo de exposición definido en los ADRs previos del wallet.

  • (+) bhawk sigue evolucionando autónomo en su bounded context, sin presión de “no romper el wallet”.
  • (+) Nuevos consumers (bseal, terceros, API pública) no requieren cambios estructurales — sólo importar el SDK cuando exista.
  • (+) El supervisor financiero ve un servicio acotado y auditable.
  • (−) Latencia extra del HTTP round-trip por cada screening del onboarding (típicamente <100 ms en el VPC; mitigable con cache del punto 3 si se vuelve hot).
  • (−) Costo operacional de mantener servicio aparte (CI, deploy, métricas, alerting). Aceptable mientras bhawk gane consumers.
  • (−) DRY pendiente: patrones tenancy/RLS/audit replicados entre apps. Mitigable extendiendo shared-libs/go-bjungle-tenancy y la lib de audit, no fusionando servicios.
  • NO se refactoriza código en este ADR. Es decisión arquitectónica + marker.
  • NO se planifica sprint dedicado para “consolidar SDK Go”. Eso entra cuando bseal (o cualquier segundo consumer) lo pida.
  • NO se elimina bhawk. NO se fusiona en bmonkey.
  • NO se expone API pública hasta Fase 5+.
DecisiónStatusFecha
ADR-007 (bhawk independiente)Accepted2026-05-30
Refactor SDK Go bhawk-clientPendiente (sin sprint asignado)
Cache de screeningsPendiente
OpenAPI público compliance-as-a-serviceAnti-scope hasta Fase 5+
  • Cliente HTTP actual: bmonkey/backend/go-bmonkey-api/internal/sarlaft/client.go
  • Endpoint S2S: bhawk/backend/go-bhawk-api/internal/http/internal_handler.go
  • Libs reutilizables hoy: shared-libs/go-bjungle-bhawk-{evaluator,screening,templates}/
  • BACKLOG sección DEFERRED · ADR-007 derivados.