ADR-007 · bhawk como compliance engine independiente
Este conteúdo não está disponível em sua língua ainda.
Contexto
Sección titulada «Contexto»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.
Estado actual
Sección titulada «Estado actual»| Componente | Ubicación |
|---|---|
bhawk-api | bhawk/backend/go-bhawk-api/ |
bhawk-worker | bhawk/backend/go-bhawk-worker/ |
| Templates de reglas | shared-libs/go-bjungle-bhawk-templates/ |
| Evaluator (DSL Go) | shared-libs/go-bjungle-bhawk-evaluator/ |
| Screening helpers | shared-libs/go-bjungle-bhawk-screening/ |
| Consumer #1 | bmonkey-api (flow step sarlaft, internal/sarlaft/) |
Decisión
Sección titulada «Decisión»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.
Por qué no fusionar
Sección titulada «Por qué no fusionar»-
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. -
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.
-
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 sarlaften CLAUDE.md gotcha #11). Aislar el servicio permite escalar horizontal sólo bhawk sin tocar el plano del wallet. -
Blast radius. Cada app tiene su DB lógica (
bjungle_bhawkvsbjungle_bmonkey). Una migración mal hecha en bhawk no puede corromperwallet_passkeys,verifiable_credentialsni la cadena de step-up tokens. Mantenerlos separados es defensa en profundidad. -
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.
Por qué se sentía acoplado
Sección titulada «Por qué se sentía acoplado»- 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.goes ad-hoc (struct conbaseURL/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.
Acciones derivadas
Sección titulada «Acciones derivadas»Direccionales, sin sprint asignado. Entran cuando el trigger natural lo pida.
-
Consolidar el SDK Go en
shared-libs/go-bjungle-bhawk-client. Hoybmonkey-apihabla con bhawk por HTTP a pelo (POST /v1/internal/screeningsconX-Internal-Token). Cuando bseal entre como consumer, antes de copiar-pegar elclient.gohay 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-apiy cualquier servicio nuevo importan la interfaz, no el HTTP crudo. -
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.
-
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 opg_cronmaterialized) sin sacrificar correctitud — la versión cambia cuando un admin publica una nueva validation o cuando el screener rota listas. -
OpenAPI público (Fase 5+). Cuando se decida vender bhawk como compliance-as-a-service, exponer
/v1/screenings+/v1/rulescon 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.
Consecuencias
Sección titulada «Consecuencias»- (+) 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-tenancyy la lib de audit, no fusionando servicios.
Anti-scope
Sección titulada «Anti-scope»- 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ón | Status | Fecha |
|---|---|---|
| ADR-007 (bhawk independiente) | Accepted | 2026-05-30 |
Refactor SDK Go bhawk-client | Pendiente (sin sprint asignado) | — |
| Cache de screenings | Pendiente | — |
| OpenAPI público compliance-as-a-service | Anti-scope hasta Fase 5+ | — |
Referencias
Sección titulada «Referencias»- 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.