ROADMAP.md
Rama main · réplica de lectura del repositorio original.
# 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).