Ctrl K
ComunidadGuías…

ROADMAP.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Roadmap

Where Ojuri is going. This file is intentionally forward-looking — for the
historical record of what changed in each release, see
[`CHANGELOG.md`](CHANGELOG.md).

This is open-source maintained by a small team. We do not commit to dates,
priority order is a working assumption that adopter pull can rearrange, and
external contributors are welcome to claim items via PR. Open an issue first
for anything non-trivial.

## Current state — 1.0

At launch (June 7, 2026) Ojuri ships:

- **Real-time path** — RDA with hot-reloaded rules (PRE / POST stages),
  catalogue-driven feature builder, ONNX inference with per-segment
  thresholds, inline reason codes, decision audit log, HMAC webhooks,
  idempotency keys, circuit-breaker degradation.
- **Async pipelines** — PAA graph + velocity, MLA drift + automated
  retraining + champion/challenger promotion gated on McNemar
  significance, FIA on-demand and Kafka-driven LLM investigations.
- **Model lifecycle** — filesystem registry with `CANDIDATE → SHADOW →
  ACTIVE → RETIRED` transitions, schema-version enforcement on load,
  hot-swap via `onActiveChange`, reviewer-override → `groundTruthFraud`
  retrain loop.
- **Sentinel operator dashboard** — review queue, transaction detail,
  rule editor + builder + templates, model registry, feature catalogue
  browser, audit log, saved reports with CSV/JSON export,
  investigations + conversational thread, users + roles, service health.
- **Adoption tooling** — synthetic-data load generator, decision replay
  CLI, full Docker Compose stack (NGINX + N× RDA (`RDA_REPLICAS`, default 3) + 1× PAA singleton + Postgres +
  Redis + Kafka + Prometheus + Grafana, with FIA gated behind a Compose
  profile so first boot is fast).

## Added since launch

Only the items that change how an adopter starts or integrates. Everything
else is in [`CHANGELOG.md`](CHANGELOG.md).

- **1.7.0** — `npx @ojuri/cli up`, a one-command install needing no clone,
  and [`@ojuri/sdk`](packages/sdk/README.md), a typed Node client for the
  predict call and webhook verification.
- **1.8.0** — that install now starts everything except FIA, including the
  Sentinel dashboard and MLA, with one RDA replica rather than three, and
  prints every URL it started.

See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the technical
reference and [`CHANGELOG.md`](CHANGELOG.md) for the per-release detail.

## Planned

Grouped by what an adopter would be unblocking. Within each group items
are roughly priority-ordered; rearrange via PR or issue discussion if
your deployment needs something else first.

### Scaling beyond the singleton

The current PAA design holds the transaction graph and velocity
windows in process memory. It works comfortably for adopters in the
1–2k TPS sustained range with graphs below ~500k nodes; throughput
drops toward 200–500 TPS once high-degree receiver hubs dominate or
the graph exceeds ~500k nodes, and the memory ceiling sits around
~1M active users in any 30-day window (see `paa-service/README.md`
§ Sizing for the full breakdown). Past that, several things bind in
order: event-loop saturation, Louvain compute time, then memory.
The items below are the realistic path for adopters targeting
Tier-1 mobile-money / large-bank volumes (>5M active users, multi-
thousand-TPS streams).

- **Externalised graph deployment profile.** Move the edge + node
  structure out of `graphology` into a server-side graph store
  (RedisGraph, Memgraph, or Neo4j with GDS). PAA becomes a set of
  stateless writers partitioned by Kafka key; a separate scheduled
  worker runs PageRank / Louvain server-side and writes results
  back into the same Redis hash RDA reads from today. Singleton
  consistency goes away — replaced by the graph store's
  transactional guarantees. Operational cost rises (one more
  stateful service) in exchange for linear ingestion scaling and
  graphs that fit beyond a single host's RAM.
- **Hot / cold split.** Cheaper middle ground for adopters who
  don't want to operate a graph DB. Keep PAA's in-memory graph
  bounded to the last 24–48 h of activity (small, fast,
  triangle-close detection stays real-time), and run a nightly
  batch job over the full transactions history to detect long-
  running rings and write community membership to a slow-changing
  Redis cache. RDA reads both; the model sees short-term + long-
  term signals. Trade: sub-hour ring detection on cross-day rings
  is lost, but those are the ones a nightly batch catches anyway.
- **Streaming / incremental community detection.** Replace
  `graphology-communities-louvain` (full recompute every tick) with
  a Louvain variant that warm-starts from the previous partition
  or a streaming algorithm (DynaMo, Streaming Louvain). At million-
  node scale, warm-starting cuts recompute from minutes to seconds.
  No maintained JavaScript implementation exists today — needs
  either a custom port or a worker-process call into a Python /
  Rust library. The current full-recompute design is the
  conservative choice but explicitly capped: PageRank ~`O(iters ×
  (V + E))`, Louvain empirical `O(V log V)` — both run cold every
  trigger.
- **Approximate / local centrality.** For per-event ring scoring,
  personalised PageRank seeded from the dirty edge converges in a
  bounded neighbourhood — `O(local degree)` instead of `O(V + E)`.
  Pairs naturally with the existing triangle-close trigger:
  recompute centrality only for nodes touched by the new edge,
  cheap enough to fire on every event without the
  `TRIANGLE_RECOMPUTE_MIN_INTERVAL_MS` throttle.
- **Configurable velocity history cap.** The
  `MAX_TRANSACTIONS_PER_USER = 1000` constant in
  `paa-service/src/services/velocity.service.ts` is currently
  hard-coded. Surface as `VELOCITY_MAX_RECORDS_PER_USER` env var so
  adopters with very high-throughput super-agents (running orders
  of magnitude over the cap per day) can shrink the per-user
  history to control memory.

### Graph and feature pipeline correctness

Open improvements to the analytics layer that don't strictly need
high TPS to matter. They're called out separately so adopters
running smaller deployments can pull them forward when they hit a
specific gap.

- **Derived community features replace raw `community_id`.**
  Vanilla Louvain integer labels are non-deterministic across
  runs, so the raw ID flowing through the ONNX input vector as an
  ordinal numeric (catalogue index 22, `dtype: uint8`) is at best
  noise and at worst misleading the model. Replace with
  `graph_community_size`, `graph_community_decline_rate`, and
  `graph_community_intra_share` — all stable under relabeling and
  far more predictive. Requires a model retrain when the catalogue
  schema bumps, so bundle with whatever drives the next MLA
  training run.
- **Middle-node snapshot refresh on triangle close.** When a new
  edge closes a 3-cycle and triggers a recompute, the sender and
  receiver of that event get their post-compute snapshots written
  to Postgres in the same handler — but the third "middle" node of
  the cycle does not. Its snapshot in `graphMetadata` lags by one
  outgoing event. Active ring members refresh on their next
  transaction; pure pass-through nodes can stay stale indefinitely.
  Cheap fix: surface the middle nodes from `closesDirectedTriangle`
  and queue them alongside.
- **Edge weight decay.** Edge `weight` and `totalAmount` only
  accumulate, never decay. A relationship that was active a year
  ago but quiet since has the same graph signal as a current one.
  Add age-weighted decay (half-life ~30 d) so the centrality
  scores reflect *current* network structure, not lifetime
  history.
- **Surface `transactionTypes` as features.** The per-edge
  `Set<string>` is captured but never read downstream. Promote to
  features like `pair_has_cashout`, `pair_channel_diversity` so
  ring signatures (e.g., "every leg is CASH_OUT") become learnable.
- **Minimum community size and quality gates for Louvain.** Tiny
  isolated pairs become their own community, indistinguishable
  from genuinely-meaningful clusters in the integer label. Apply a
  minimum-size threshold (e.g., communities <3 nodes get assigned
  community 0 = "no meaningful community"), and surface community
  modularity as a per-prediction confidence signal.
- **Graph-poisoning defences.** No edge-weight cap, no minimum-
  amount filter on graph admission, no edge-age decay (above) —
  an adversary can inflate a target's PageRank by spraying tiny
  transactions toward them from controlled accounts. Cheap to add
  alongside the decay work.

### Operability of high-scale deployments

Items adopters who hit the ceiling need to *notice* before they're
in production trouble.

- **Alert when scheduled prune finds zero eligible victims.**
  Today's failure mode is a silent climb to OOM: the cap fires,
  the prune walks every node looking for `lastSeen < cutoff`,
  finds none (all users active in last 30 d), and PAA keeps
  growing. Add a metric `paa_graph_prune_inelligible_total` and a
  log line at WARN when a prune attempt frees zero slots — that's
  the precursor to OOM.
- **Sentinel sizing panel.** Surface `paa_graph_size`,
  `paa_consumer_lag`, `paa_group_members`, and the velocity-user
  count as a single "PAA capacity" widget so operators see when
  they're approaching either ceiling without learning Prometheus.
- **Manual recompute trigger.** `POST /v1/admin/paa/recompute` for
  ops to force a `computeNetworkMetrics()` on demand — useful for
  ring investigations where the operator wants the freshest
  partition immediately rather than waiting for the next tick.
- **Dirty-triangle backlog metric.** Counter for how often the
  triangle-close trigger fired and how many recomputes it caused.
  Tells operators whether they're getting ring-detection latency
  benefit or just paying for extra recomputes.

### Deployment + packaging

- **One-command install** — shipped in 1.7.0. `npx @ojuri/cli up` brings the
  stack up in an empty directory with no clone: the compose file and the
  assets it mounts travel in the package, and an `ojuri.yaml` manifest layers
  over `.env` without replacing it. Both earlier install paths are unchanged.
  What is still open is a `doctor` that works before a manifest exists, and
  richer preflight on the host. 1.8.0 turned the dashboard and MLA on by
  default, leaving FIA the one opt-in service.
- **Helm chart** — Kubernetes-first adopters currently template their own
  manifests off `docker-compose.yml`. A first-party chart with sane
  defaults for the RDA replica count, the PAA singleton, the FIA opt-in,
  and the Postgres / Redis / Kafka externals is the highest-leverage
  follow-up on this list.
- **Terraform module** — companion to the Helm chart for cloud-resource
  provisioning (managed Postgres / Redis / MSK). Will likely live in its
  own repo to keep the core repo language-pure.

### Integration surface

- **Python client.** The TypeScript half shipped in 1.7.0 as
  [`@ojuri/sdk`](packages/sdk/README.md) — a typed `predict` with retries, a
  wall-clock deadline, idempotency handling and webhook signature
  verification, at `packages/sdk` rather than the `clients/typescript/`
  originally sketched here. The Python equivalent is still open. It should
  match the same surface and be generated from, or checked against, the same
  contract; the PyPI name is undecided, `ojuri` reading better than
  `ojuri-client` but foreclosing the namespace for anything else.

  Deliberately out of scope for both: the audit reads, the review queue, the
  override endpoint and the FIA report API. Those are the dashboard's surface
  and it calls them directly. Wrapping them once cost ten wrong field names
  on types nothing exercised, which is why the client is narrow.
- **Pre-built connectors (Stripe / Adyen / Plaid).** Each is a small
  adapter that maps the provider's webhook or callback into a
  `POST /v1/predict` call and feeds the decision back into the
  provider's flow. Stripe is the highest-leverage to ship first.

### Security and access control

- **mTLS for service-to-service callers.** RDA→PAA, RDA→FIA, and any
  external machine client benefit from mutual auth in deployments where
  network segmentation alone isn't enough. Implemented as an NGINX
  client-cert config + an env flag on RDA's API-key middleware to
  accept verified certs in lieu of `X-Api-Key`.
- **OAuth 2.0 client-credentials grant for admin APIs.** Today admin
  surface is JWT-from-login or seeded service tokens. CC grant unlocks
  machine clients (CI, CLIs, partner integrations) authenticating
  against your IdP instead of holding a long-lived JWT.

### Compliance and regulated deployments

- **PII tokenisation hook before persistence.** A pluggable
  `tokeniseRequest(req): req` boundary applied before any write to
  `decisionAuditLog`, `transactions`, or Kafka, so the platform stores
  only tokens for `sender_id` / `receiver_id` / `customer_id_number`
  while the model still sees the raw values in-memory. Adopters in
  financial or healthcare deployments can plug in their tokenisation
  vendor without forking.
- **Region-pinned storage.** Operator config to constrain which
  Postgres / Kafka / model-registry paths a given tenant's data is
  written to — useful for adopters with explicit data-residency
  contracts.

### Model lifecycle

- **Canary traffic split by API-key cohort.** Today `SHADOW` runs the
  candidate on every request and writes its score alongside the
  champion's for offline comparison. Canary would route 1–10% of live
  traffic from a chosen API-key cohort to the candidate decision —
  graduated rollout instead of cliff-edge promotion.

### Adoption

- **Demo dataset under `data/demo/`.** A trimmed, anonymised dataset
  adopters can seed the stack with — a few thousand realistic
  transactions, labelled fraud, and pre-built rules to play with.
  Removes the cold-start "what do I evaluate against" problem.
- **Hosted sandbox.** A short-lived public deployment that adopters can
  point a curl at to try `/v1/predict` without standing up Postgres /
  Redis / Kafka locally. Infra-only; everything else on this list is
  shipped in the codebase.

## Out of scope

Things that are explicitly *not* planned, so contributors don't spend
cycles proposing them:

- **A managed / hosted SaaS offering.** Ojuri is and will remain a
  self-hosted platform. A hosted sandbox for evaluation (above) is
  different from a managed production tier.
- **Vendor-specific feature engineering.** The catalogue + adopter
  overlay is the extension point. We won't ship a Stripe-Radar-shaped
  feature set; adopters who want one extend the overlay.
- **Anything that puts FIA on the authorization hot path.** The LLM
  is async by design. Reports must continue to be generated off the
  predict path, no matter how fast LLMs get.

## Contributing to a roadmap item

If you want to work on any of the above, open an issue with the
roadmap section + item name in the title — that way the maintainer
can give scope guidance before you sink time into a PR. See
[`CONTRIBUTING.md`](CONTRIBUTING.md).