Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,31 @@ Open:

For deployed docs or dashboard environments, use the same paths under your deployed host, e.g. `https://<docs-host>/api-refs/api-ref`.

### CockroachDB

CockroachDB is PostgreSQL wire-protocol compatible, so it runs on the existing `postgres` build,
`migrations_pg`, and `pg_database` config — no separate feature or backend. The
`docker-compose.cockroach.yml` overlay swaps the `postgresql` service for a single-node CockroachDB.

Full local dev (the `oneclick.sh` flow), with CockroachDB instead of PostgreSQL:

```bash
./oneclick.sh --cockroach
```

The DB is published on host **26257** (CockroachDB's native port) so it coexists with a local
PostgreSQL on 5432; `--cockroach` points the migrator, seed, and backend there automatically.

Docker-only (app runs in-container against the CockroachDB service):

```bash
docker compose -f docker-compose.yaml -f docker-compose.cockroach.yml --profile postgres-local up -d
```

API stays on `http://localhost:8080`; the CockroachDB DB Console is at `http://localhost:8090`. For a
secure cluster (e.g. CockroachDB Cloud), set `pg_sslmode` (and `pg_ssl_root_cert`) under
`[pg_database]` in your config.

### From Source

Prerequisites: Rust 1.85+, MySQL or PostgreSQL, Redis, [`just`](https://just.systems)
Expand Down
81 changes: 81 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,87 @@ tiers = [
{ transaction_currency = "AED", card_network = "visa", payment_method_type = "credit", card_issuing_country = "eu", interchange_bps = 30.0, scheme_bps = 50.0, markup_bps = 15.0, fixed = 0.15, label = "Cross-border EU Visa credit", example_amount = 2800.0 },
]

[[hypersense.seed_costs]]
psp = "dlocal" # Country-specific blended MDR
# Varies by Local vs Cross-Border (FX)
default = { pct_bps = 450.0, fixed = 0.30 } # fallback = LATAM Local Credit Card
# standard (4.50% + $0.30)
tiers = [
# ====================================================================================
# LATIN AMERICA (LATAM) - Blended Country Card Footprints
# ====================================================================================

# --- BRAZIL (BR) --------------------------------------------------------------------
{ card_issuing_country = "br", card_network = "visa", payment_method_type = "debit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic Visa Debit" },
{ card_issuing_country = "br", card_network = "visa", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic Visa Credit" },
{ card_issuing_country = "br", card_network = "mastercard", payment_method_type = "debit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic MC Debit" },
{ card_issuing_country = "br", card_network = "mastercard", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic MC Credit" },
{ card_issuing_country = "br", card_network = "elo", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic Elo Card" },
{ card_issuing_country = "br", card_network = "hipercard", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "BR Domestic Hipercard" },

# --- ARGENTINA (AR) -----------------------------------------------------------------
{ card_issuing_country = "ar", card_network = "visa", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.00, label = "AR Domestic Visa" },
{ card_issuing_country = "ar", card_network = "mastercard", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.00, label = "AR Domestic MC" },

# --- CHILE (CL) ---------------------------------------------------------------------
{ card_issuing_country = "cl", card_network = "visa", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "CL Domestic Visa" },
{ card_issuing_country = "cl", card_network = "mastercard", payment_method_type = "credit", pct_bps = 490.0, fixed = 0.00, label = "CL Domestic MC" },

# --- COLOMBIA (CO) ------------------------------------------------------------------
{ card_issuing_country = "co", card_network = "visa", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.00, label = "CO Domestic Visa" },
{ card_issuing_country = "co", card_network = "mastercard", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.00, label = "CO Domestic MC" },

# --- ECUADOR (EC) - Highest baseline card ceiling in LATAM --------------------------
{ card_issuing_country = "ec", card_network = "visa", payment_method_type = "credit", pct_bps = 1200.0, fixed = 0.00, label = "EC Domestic Visa" },
{ card_issuing_country = "ec", card_network = "mastercard", payment_method_type = "credit", pct_bps = 1200.0, fixed = 0.00, label = "EC Domestic MC" },



# --- Mexico (MX) Domestic Cards using your exact Adyen parameters ------------------
{ transaction_currency = "MXN", card_network = "visa", payment_method_type = "debit", pct_bps = 420.0, fixed = 0.00, label = "MX Domestic Visa Debit" },
{ transaction_currency = "MXN", card_network = "visa", payment_method_type = "credit", pct_bps = 420.0, fixed = 0.00, label = "MX Domestic Visa Credit" },
{ transaction_currency = "MXN", card_network = "mastercard", payment_method_type = "debit", pct_bps = 420.0, fixed = 0.00, label = "MX Domestic MC Debit" },
{ transaction_currency = "MXN", card_network = "mastercard", payment_method_type = "credit", pct_bps = 420.0, fixed = 0.00, label = "MX Domestic MC Credit" },
{ transaction_currency = "MXN", card_network = "carnet", payment_method_type = "debit", pct_bps = 420.0, fixed = 0.00, label = "MX Carnet Debit" },


# ====================================================================================
# ASIA PACIFIC (APAC) - Highly Competitive Dynamic High-Volume Rails
# ====================================================================================

# --- INDIA (IN) ---------------------------------------------------------------------
{ card_issuing_country = "in", card_network = "rupay", payment_method_type = "debit", pct_bps = 90.0, fixed = 0.00, label = "IN RuPay Local Debit" },
{ card_issuing_country = "in", card_network = "visa", payment_method_type = "debit", pct_bps = 120.0, fixed = 0.00, label = "IN Visa Local Debit" },
{ card_issuing_country = "in", card_network = "visa", payment_method_type = "credit", pct_bps = 250.0, fixed = 0.00, label = "IN Visa Local Credit" },
{ card_issuing_country = "in", card_network = "mastercard", payment_method_type = "credit", pct_bps = 250.0, fixed = 0.00, label = "IN MC Local Credit" },

# --- INDONESIA (ID) & MALAYSIA (MY) -------------------------------------------------
{ card_issuing_country = "id", card_network = "visa", payment_method_type = "credit", pct_bps = 450.0, fixed = 0.00, label = "ID Local Cards" },
{ card_issuing_country = "my", card_network = "visa", payment_method_type = "credit", pct_bps = 450.0, fixed = 0.00, label = "MY Local Cards" },


# ====================================================================================
# MIDDLE EAST & AFRICA (MEA)
# ====================================================================================

# --- NIGERIA (NG) -------------------------------------------------------------------
{ card_issuing_country = "ng", card_network = "verve", payment_method_type = "debit", pct_bps = 250.0, fixed = 0.00, label = "NG Verve Local Card" },
{ card_issuing_country = "ng", card_network = "visa", payment_method_type = "credit", pct_bps = 390.0, fixed = 0.00, label = "NG Domestic Visa" },
{ card_issuing_country = "ng", card_network = "mastercard", payment_method_type = "credit", pct_bps = 390.0, fixed = 0.00, label = "NG Domestic MC" },

# --- SOUTH AFRICA (ZA) --------------------------------------------------------------
{ card_issuing_country = "za", card_network = "visa", payment_method_type = "credit", pct_bps = 350.0, fixed = 0.00, label = "ZA Domestic Visa" },

# ====================================================================================
# CROSS-BORDER INTERNATIONAL FALLBACK
# ====================================================================================
# Triggers if a merchant processes inside an emerging market but the shopper uses a
# foreign international card (e.g., US traveler using an American Visa card inside Peru).
{ card_issuing_country = "intl", card_network = "visa", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.50, label = "Cross-Border Visa Pay-in" },
{ card_issuing_country = "intl", card_network = "mastercard", payment_method_type = "credit", pct_bps = 650.0, fixed = 0.50, label = "Cross-Border MC Pay-in" }
]


[[hypersense.seed_costs]]
psp = "tap"
default = { interchange_bps = 100.0, scheme_bps = 12.0, markup_bps = 18.0, fixed = 0.30 }
Expand Down
10 changes: 10 additions & 0 deletions diesel_pg_cockroach.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Diesel CLI config for running the PostgreSQL migrations against CockroachDB.
#
# Identical to diesel_pg.toml but WITHOUT the [print_schema] section: after applying migrations,
# diesel_cli regenerates the schema by reading information_schema, where CockroachDB returns
# `character_maximum_length` as INT8 while diesel decodes it as i32 — so `diesel migration run`
# fails with "Received more than 4 bytes while decoding an i32" even though the migrations applied.
# The committed src/storage/schema_pg.rs (generated from PostgreSQL) is already correct for
# CockroachDB, so schema regeneration is intentionally skipped here.
[migrations_directory]
dir = "migrations_pg"
95 changes: 95 additions & 0 deletions docker-compose.cockroach.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# CockroachDB overlay for the Decision Engine.
#
# CockroachDB speaks the PostgreSQL wire protocol, so it reuses the existing `postgres` build,
# `migrations_pg`, and the app's `pg_database` config — only the port differs. This CockroachDB build
# refuses to listen on anything but its native port 26257 (it errors on other ports and on a literal
# `0.0.0.0` host), so the DB runs on 26257 end to end. That also lets it coexist with a local
# PostgreSQL on 5432. The in-docker app + migrator are repointed to 26257 here; the `oneclick.sh
# --cockroach` flow does the same for the host-side tooling via env vars.
#
# Usage (mirrors the postgres profiles, e.g. postgres-local / postgres-ghcr / dashboard-*):
# docker compose -f docker-compose.yaml -f docker-compose.cockroach.yml --profile postgres-local up -d
#
# CockroachDB DB Console: http://localhost:8090

services:
# Replace the PostgreSQL server with a single-node CockroachDB. Empty host (`:26257`) binds all
# interfaces; the port must be 26257 for this build.
postgresql:
image: cockroachdb/cockroach:v24.1.5
command: >-
start-single-node --insecure
--listen-addr=:26257
--http-addr=:8080
# 26257 coexists with a local PostgreSQL on 5432; DB Console on host 8090 stays clear of the app
# (8080) and dashboard nginx (8081). `!override` replaces the base service's `5432:5432` mapping
# instead of appending to it (which would keep publishing host 5432 and clash with PostgreSQL).
ports: !override
- "26257:26257"
- "8090:8080"
# `!override` so we don't also inherit the base service's postgres-data mount.
volumes: !override
- cockroach-data:/cockroach/cockroach-data
healthcheck:
test: ["CMD", "cockroach", "sql", "--insecure", "--host=127.0.0.1:26257", "-e", "SELECT 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s

# Create the database + role the app/migrator connect as. In insecure mode CockroachDB accepts any
# password but the role must exist; `public` schema privileges are no longer implicit, so grant them.
# The two role settings make CockroachDB match PostgreSQL's integer typing, which schema_pg.rs (and
# so the compiled backend) assumes: default_int_size=4 makes INTEGER/INT columns INT4 (CockroachDB
# defaults them to INT8), and serial_normalization='sql_sequence' makes SERIAL an INT4 sequence
# (CockroachDB's default `rowid` SERIAL is INT8). Set before migrations so tables are created with
# the right column widths; they also apply to the backend, which connects as the same role.
cockroach-init:
image: cockroachdb/cockroach:v24.1.5
profiles:
- postgres-ghcr
- dashboard-postgres-ghcr
- postgres-local
- dashboard-postgres-local
depends_on:
postgresql:
condition: service_healthy
entrypoint:
- /bin/bash
- -c
- |
set -e
cockroach sql --insecure --host=postgresql:26257 -e "
CREATE DATABASE IF NOT EXISTS decision_engine_db;
CREATE USER IF NOT EXISTS db_user;
GRANT ALL ON DATABASE decision_engine_db TO db_user;
ALTER ROLE db_user SET default_int_size = 4;
ALTER ROLE db_user SET serial_normalization = 'sql_sequence';
"
cockroach sql --insecure --host=postgresql:26257 --database=decision_engine_db -e "
GRANT ALL ON SCHEMA public TO db_user;
"
networks:
- open-router-network

# Wait for the database + role to exist, point the migrations at 26257, and use the CockroachDB
# diesel config (no [print_schema] — see diesel_pg_cockroach.toml).
db-migrator-postgres:
depends_on:
cockroach-init:
condition: service_completed_successfully
environment:
- DATABASE_URL=postgresql://db_user:db_pass@postgresql:26257/decision_engine_db
command: "bash -c 'cargo install diesel_cli --no-default-features --features postgres && diesel migration run --database-url \"$$DATABASE_URL\" --migration-dir /app/migrations_pg --config-file /app/diesel_pg_cockroach.toml'"

# In-container app connects to CockroachDB on 26257 (config default is 5432). Only used by the
# docker-only run; oneclick runs the backend on the host and sets this via env instead.
open-router-pg-ghcr:
environment:
- DECISION_ENGINE__PG_DATABASE__PG_PORT=26257
open-router-pg-local:
environment:
- DECISION_ENGINE__PG_DATABASE__PG_PORT=26257

volumes:
cockroach-data:
Loading
Loading