Code before remediation

Application source at the baseline commit, before the agent patched it.

docs-worker/.gitignore

.venv/
__pycache__/
*.pyc
data/*.sqlite

docs-worker/app.py

import os
import threading
import time

from flask import Flask, g, jsonify, request, render_template_string, send_file

from db import get_db, init_db
from middleware import load_session, log_audit, require_role

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STORAGE_ROOT = os.path.join(BASE_DIR, "storage")

app = Flask(__name__)
app.before_request(load_session)
app.after_request(log_audit)


@app.route("/healthz", methods=["GET"])
def healthz():
    return jsonify({"status": "ok", "service": "docs-worker"})


@app.route("/metrics", methods=["GET"])
def metrics():
    db = get_db()
    docs = db.execute("SELECT COUNT(*) AS c FROM documents").fetchone()["c"]
    processed = db.execute(
        "SELECT COUNT(*) AS c FROM documents WHERE status = 'processed'"
    ).fetchone()["c"]
    return jsonify({"documents_total": docs, "documents_processed": processed})


@app.route("/api/documents/search", methods=["GET"])
def search_documents():
    """Lets ops search the document library by type, status and a free
    text filename match, combining whatever filters were passed."""
    org_id = g.org_id
    doc_type = request.args.get("doc_type")
    status = request.args.get("status")
    q = request.args.get("q")

    where = f"org_id = {org_id}"
    if doc_type:
        where += f" AND doc_type = '{doc_type}'"
    if status:
        where += f" AND status = '{status}'"
    if q:
        where += f" AND filename LIKE '%{q}%'"

    query = f"SELECT id, filename, doc_type, status, uploaded_by, created_at FROM documents WHERE {where}"

    db = get_db()
    rows = db.execute(query).fetchall()
    documents = [dict(row) for row in rows]
    return jsonify({"documents": documents, "query": query})


@app.route("/api/documents/<org_id>/upload", methods=["POST"])
@require_role("admin", "analyst")
def upload_document(org_id):
    uploaded = request.files.get("document")
    if uploaded is None:
        return jsonify({"error": "document file is required"}), 400

    doc_type = request.form.get("docType", "other")
    dest_name = request.form.get("filename") or uploaded.filename

    org_dir = os.path.join(STORAGE_ROOT, f"org_{org_id}")
    os.makedirs(org_dir, exist_ok=True)

    dest_path = os.path.join(org_dir, dest_name)
    uploaded.save(dest_path)

    db = get_db()
    cur = db.execute(
        "INSERT INTO documents (org_id, filename, doc_type, status, uploaded_by) VALUES (?, ?, ?, 'uploaded', ?)",
        (org_id, dest_name, doc_type, g.user_email),
    )
    db.commit()

    return jsonify({"id": cur.lastrowid, "filename": dest_name, "orgId": org_id}), 201


@app.route("/api/documents/<org_id>/download", methods=["GET"])
def download_document(org_id):
    file_name = request.args.get("file")
    if not file_name:
        return jsonify({"error": "file query parameter is required"}), 400

    org_dir = os.path.join(STORAGE_ROOT, f"org_{org_id}")
    target = os.path.join(org_dir, file_name)

    if not os.path.exists(target):
        return jsonify({"error": "document not found", "path": target}), 404

    return send_file(target, as_attachment=True, download_name=os.path.basename(file_name))


REPORT_TEMPLATE = """
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>{{ title }}</title></head>
<body>
  <h1>{{ title }}</h1>
  <p>Prepared for: {{ org_name }}</p>
  <div class="analyst-note">{{ note | safe }}</div>
</body>
</html>
"""


@app.route("/api/reports/render", methods=["POST"])
def render_report():
    """Renders an ad-hoc analyst report. The title/org name/note fields are
    supplied by the caller so analysts can annotate the report however they
    like before it goes out to the customer."""
    payload = request.get_json(force=True, silent=True) or {}
    title = payload.get("title", "Document Review Report")
    org_name = payload.get("orgName", "")
    note = payload.get("note", "")

    template_source = payload.get("templateOverride") or REPORT_TEMPLATE
    html = render_template_string(template_source, title=title, org_name=org_name, note=note)
    return html, 200, {"Content-Type": "text/html"}


def _process_document_job(document_id, org_id):
    time.sleep(1)
    db = get_db()
    db.execute("UPDATE documents SET status = 'processed' WHERE id = ?", (document_id,))
    db.execute(
        "INSERT INTO audit_log (org_id, actor, action, detail) VALUES (?, ?, ?, ?)",
        (org_id, "docs-worker", "document.processed", f"document_id={document_id}"),
    )
    db.commit()


@app.route("/api/jobs/process", methods=["POST"])
@require_role("admin", "analyst")
def dispatch_processing_job():
    payload = request.get_json(force=True, silent=True) or {}
    document_id = payload.get("documentId")
    if not document_id:
        return jsonify({"error": "documentId is required"}), 400

    thread = threading.Thread(target=_process_document_job, args=(document_id, g.org_id), daemon=True)
    thread.start()

    return jsonify({"documentId": document_id, "status": "processing"}), 202


if __name__ == "__main__":
    init_db()
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 4002)))

docs-worker/data/.gitkeep

docs-worker/db.py

import os
import sqlite3
import threading

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")
os.makedirs(DATA_DIR, exist_ok=True)
DB_PATH = os.path.join(DATA_DIR, "docs.sqlite")

_local = threading.local()

SCHEMA = """
CREATE TABLE IF NOT EXISTS organizations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS sessions (
    token TEXT PRIMARY KEY,
    org_id INTEGER NOT NULL,
    role TEXT NOT NULL,
    user_email TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS documents (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    org_id INTEGER NOT NULL,
    filename TEXT NOT NULL,
    doc_type TEXT NOT NULL DEFAULT 'other',
    status TEXT NOT NULL DEFAULT 'uploaded',
    uploaded_by TEXT,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS audit_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    org_id INTEGER,
    actor TEXT,
    action TEXT NOT NULL,
    detail TEXT,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
"""


def get_db():
    if not hasattr(_local, "conn"):
        _local.conn = sqlite3.connect(DB_PATH)
        _local.conn.row_factory = sqlite3.Row
    return _local.conn


def init_db():
    conn = get_db()
    conn.executescript(SCHEMA)
    conn.commit()
    _seed(conn)


def _seed(conn):
    row = conn.execute("SELECT COUNT(*) AS c FROM organizations").fetchone()
    if row["c"] > 0:
        return

    conn.execute("INSERT INTO organizations (id, name) VALUES (1, 'Acme Manufacturing')")
    conn.execute("INSERT INTO organizations (id, name) VALUES (2, 'Globex Trading')")

    conn.execute(
        "INSERT INTO sessions (token, org_id, role, user_email) VALUES (?, ?, ?, ?)",
        ("docs_tok_acme_admin_7f3e", 1, "admin", "admin@acme.example"),
    )
    conn.execute(
        "INSERT INTO sessions (token, org_id, role, user_email) VALUES (?, ?, ?, ?)",
        ("docs_tok_globex_admin_1c9a", 2, "admin", "admin@globex.example"),
    )

    conn.execute(
        "INSERT INTO documents (org_id, filename, doc_type, status, uploaded_by) VALUES (?, ?, ?, ?, ?)",
        (1, "invoice-4472.txt", "invoice", "processed", "admin@acme.example"),
    )
    conn.execute(
        "INSERT INTO documents (org_id, filename, doc_type, status, uploaded_by) VALUES (?, ?, ?, ?, ?)",
        (1, "agreement-northwind.txt", "agreement", "uploaded", "admin@acme.example"),
    )
    conn.execute(
        "INSERT INTO documents (org_id, filename, doc_type, status, uploaded_by) VALUES (?, ?, ?, ?, ?)",
        (2, "freight-contract.txt", "agreement", "uploaded", "admin@globex.example"),
    )
    conn.commit()

docs-worker/middleware.py

import functools

from flask import g, jsonify, request

from db import get_db


def load_session():
    """Runs before every request: resolves the bearer token in the
    Authorization header to an org/role and stashes it on flask.g for
    handlers and the audit logger to use."""
    g.org_id = None
    g.role = None
    g.user_email = None

    auth_header = request.headers.get("Authorization", "")
    token = auth_header.replace("Bearer ", "").strip()

    if request.path in ("/healthz", "/metrics"):
        return None

    if not token:
        return jsonify({"error": "missing bearer token"}), 401

    db = get_db()
    row = db.execute(
        "SELECT org_id, role, user_email FROM sessions WHERE token = ?", (token,)
    ).fetchone()

    if row is None:
        return jsonify({"error": "invalid session token"}), 401

    g.org_id = row["org_id"]
    g.role = row["role"]
    g.user_email = row["user_email"]
    return None


def require_role(*allowed):
    def decorator(view):
        @functools.wraps(view)
        def wrapped(*args, **kwargs):
            if g.role not in allowed:
                return jsonify({"error": f"role '{g.role}' is not permitted for this action"}), 403
            return view(*args, **kwargs)

        return wrapped

    return decorator


def log_audit(response):
    """Runs after every request to persist an audit trail entry."""
    if request.path in ("/healthz", "/metrics"):
        return response

    db = get_db()
    db.execute(
        "INSERT INTO audit_log (org_id, actor, action, detail) VALUES (?, ?, ?, ?)",
        (
            getattr(g, "org_id", None),
            getattr(g, "user_email", None),
            f"{request.method} {request.path}",
            request.query_string.decode("utf-8"),
        ),
    )
    db.commit()
    return response

docs-worker/requirements.txt

Flask==3.1.3

docs-worker/storage/org_1/agreement-northwind.txt

Signed vendor agreement - Northwind Supplies.

docs-worker/storage/org_1/invoice-4472.txt

Vendor invoice PDF placeholder for Acme Manufacturing Q3.

docs-worker/storage/org_2/freight-contract.txt

Freight contract - Globex Trading.

payments-api/.gitignore

data/*.sqlite

payments-api/data/.gitkeep

payments-api/db.go

package main

import (
	"database/sql"
	"log"
	"os"
	"path/filepath"

	_ "modernc.org/sqlite"
)

var db *sql.DB

const schema = `
CREATE TABLE IF NOT EXISTS organizations (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	name TEXT NOT NULL,
	plan TEXT NOT NULL DEFAULT 'standard'
);

CREATE TABLE IF NOT EXISTS api_keys (
	api_key TEXT PRIMARY KEY,
	org_id INTEGER NOT NULL,
	role TEXT NOT NULL DEFAULT 'service',
	label TEXT
);

CREATE TABLE IF NOT EXISTS accounts (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	org_id INTEGER NOT NULL,
	name TEXT NOT NULL,
	currency TEXT NOT NULL DEFAULT 'USD',
	balance_cents INTEGER NOT NULL DEFAULT 0,
	status TEXT NOT NULL DEFAULT 'active'
);

CREATE TABLE IF NOT EXISTS beneficiaries (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	org_id INTEGER NOT NULL,
	name TEXT NOT NULL,
	bank_name TEXT,
	iban TEXT,
	status TEXT NOT NULL DEFAULT 'active'
);

CREATE TABLE IF NOT EXISTS transfers (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	org_id INTEGER NOT NULL,
	account_id INTEGER NOT NULL,
	beneficiary_id INTEGER,
	amount_cents INTEGER NOT NULL,
	currency TEXT NOT NULL DEFAULT 'USD',
	status TEXT NOT NULL DEFAULT 'pending',
	memo TEXT,
	created_at TEXT NOT NULL DEFAULT (datetime('now')),
	settled_at TEXT
);

CREATE TABLE IF NOT EXISTS audit_log (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	org_id INTEGER,
	actor TEXT,
	action TEXT NOT NULL,
	detail TEXT,
	ip TEXT,
	created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`

func initDB() {
	dataDir := "data"
	if err := os.MkdirAll(dataDir, 0o755); err != nil {
		log.Fatalf("creating data dir: %v", err)
	}

	dbPath := filepath.Join(dataDir, "payments.sqlite")
	var err error
	db, err = sql.Open("sqlite", dbPath)
	if err != nil {
		log.Fatalf("opening database: %v", err)
	}

	if _, err := db.Exec(schema); err != nil {
		log.Fatalf("applying schema: %v", err)
	}

	seed()
}

func seed() {
	var count int
	if err := db.QueryRow("SELECT COUNT(*) FROM organizations").Scan(&count); err != nil {
		log.Fatalf("counting organizations: %v", err)
	}
	if count > 0 {
		return
	}

	res, err := db.Exec("INSERT INTO organizations (name, plan) VALUES (?, ?)", "Acme Manufacturing", "enterprise")
	if err != nil {
		log.Fatalf("seeding organizations: %v", err)
	}
	acmeID, _ := res.LastInsertId()

	res, err = db.Exec("INSERT INTO organizations (name, plan) VALUES (?, ?)", "Globex Trading", "standard")
	if err != nil {
		log.Fatalf("seeding organizations: %v", err)
	}
	globexID, _ := res.LastInsertId()

	db.Exec("INSERT INTO api_keys (api_key, org_id, role, label) VALUES (?, ?, ?, ?)",
		"sk_live_acme_ops_9f2c1a", acmeID, "admin", "Acme treasury ops key")
	db.Exec("INSERT INTO api_keys (api_key, org_id, role, label) VALUES (?, ?, ?, ?)",
		"sk_live_globex_finance_4b71e0", globexID, "admin", "Globex finance key")

	accRes, err := db.Exec("INSERT INTO accounts (org_id, name, currency, balance_cents, status) VALUES (?, ?, ?, ?, ?)",
		acmeID, "Acme Operating Account", "USD", 500000000, "active")
	if err != nil {
		log.Fatalf("seeding accounts: %v", err)
	}
	acmeAccID, _ := accRes.LastInsertId()

	globexAccRes, err := db.Exec("INSERT INTO accounts (org_id, name, currency, balance_cents, status) VALUES (?, ?, ?, ?, ?)",
		globexID, "Globex Trading Account", "EUR", 250000000, "active")
	if err != nil {
		log.Fatalf("seeding accounts: %v", err)
	}
	globexAccID, _ := globexAccRes.LastInsertId()

	benRes, err := db.Exec("INSERT INTO beneficiaries (org_id, name, bank_name, iban, status) VALUES (?, ?, ?, ?, ?)",
		acmeID, "Northwind Supplies", "First Union Bank", "GB29NWBK60161331926819", "active")
	if err != nil {
		log.Fatalf("seeding beneficiaries: %v", err)
	}
	northwindID, _ := benRes.LastInsertId()

	db.Exec(`INSERT INTO transfers (org_id, account_id, beneficiary_id, amount_cents, currency, status, memo)
		VALUES (?, ?, ?, ?, ?, ?, ?)`,
		acmeID, acmeAccID, northwindID, 340000, "USD", "pending", "Q3 supplies invoice 4472")
	db.Exec(`INSERT INTO transfers (org_id, account_id, beneficiary_id, amount_cents, currency, status, memo)
		VALUES (?, ?, ?, ?, ?, ?, ?)`,
		globexID, globexAccID, nil, 1850000, "EUR", "pending", "Freight contract milestone 2")
}

payments-api/go.mod

module payments-api

go 1.25.0

require (
	github.com/dustin/go-humanize v1.0.1 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/mattn/go-isatty v0.0.24 // indirect
	github.com/ncruces/go-strftime v1.0.0 // indirect
	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
	golang.org/x/sys v0.47.0 // indirect
	modernc.org/libc v1.75.6 // indirect
	modernc.org/mathutil v1.7.1 // indirect
	modernc.org/memory v1.12.1 // indirect
	modernc.org/sqlite v1.58.0 // indirect
)

payments-api/go.sum

github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.58.0 h1:38u40/bwkfM7f0Myhosl+SEMltSDxnGdQf8o6Kjmys0=
modernc.org/sqlite v1.58.0/go.mod h1:rsD2CckafgObKC4DhBlGBf+RiHxkc3hINGt1Xw32tVY=

payments-api/handlers_accounts.go

package main

import (
	"fmt"
	"net/http"
)

type account struct {
	ID           int64  `json:"id"`
	OrgID        int64  `json:"orgId"`
	Name         string `json:"name"`
	Currency     string `json:"currency"`
	BalanceCents int64  `json:"balanceCents"`
	Status       string `json:"status"`
}

// searchAccounts lets treasury ops slice the account list by status,
// currency and a free text name match, all combined ad-hoc from whatever
// query params the caller sends.
func searchAccounts(w http.ResponseWriter, r *http.Request) {
	orgID := orgIDFromContext(r)

	where := fmt.Sprintf("org_id = %d", orgID)

	if status := r.URL.Query().Get("status"); status != "" {
		where += fmt.Sprintf(" AND status = '%s'", status)
	}
	if currency := r.URL.Query().Get("currency"); currency != "" {
		where += fmt.Sprintf(" AND currency = '%s'", currency)
	}
	if q := r.URL.Query().Get("q"); q != "" {
		where += fmt.Sprintf(" AND name LIKE '%%%s%%'", q)
	}

	query := fmt.Sprintf("SELECT id, org_id, name, currency, balance_cents, status FROM accounts WHERE %s", where)

	rows, err := db.Query(query)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	defer rows.Close()

	accounts := []account{}
	for rows.Next() {
		var a account
		if err := rows.Scan(&a.ID, &a.OrgID, &a.Name, &a.Currency, &a.BalanceCents, &a.Status); err != nil {
			writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
			return
		}
		accounts = append(accounts, a)
	}

	writeJSON(w, http.StatusOK, map[string]any{"accounts": accounts, "query": query})
}

func getAccount(w http.ResponseWriter, r *http.Request) {
	orgID := orgIDFromContext(r)
	id := r.PathValue("id")

	var a account
	err := db.QueryRow(
		"SELECT id, org_id, name, currency, balance_cents, status FROM accounts WHERE id = ? AND org_id = ?",
		id, orgID,
	).Scan(&a.ID, &a.OrgID, &a.Name, &a.Currency, &a.BalanceCents, &a.Status)
	if err != nil {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": "account not found"})
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{"account": a})
}

payments-api/handlers_beneficiaries.go

package main

import (
	"encoding/json"
	"net/http"
)

type createBeneficiaryRequest struct {
	Name     string `json:"name"`
	BankName string `json:"bankName"`
	IBAN     string `json:"iban"`
}

func createBeneficiary(w http.ResponseWriter, r *http.Request) {
	orgID := orgIDFromContext(r)

	var req createBeneficiaryRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name is required"})
		return
	}

	res, err := db.Exec(
		"INSERT INTO beneficiaries (org_id, name, bank_name, iban, status) VALUES (?, ?, ?, ?, 'active')",
		orgID, req.Name, req.BankName, req.IBAN,
	)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	id, _ := res.LastInsertId()
	writeJSON(w, http.StatusCreated, map[string]any{"id": id, "name": req.Name})
}

payments-api/handlers_health.go

package main

import "net/http"

func healthz(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, http.StatusOK, map[string]string{"status": "ok", "service": "payments-api"})
}

func metrics(w http.ResponseWriter, r *http.Request) {
	var accounts, transfers, pending int64
	db.QueryRow("SELECT COUNT(*) FROM accounts").Scan(&accounts)
	db.QueryRow("SELECT COUNT(*) FROM transfers").Scan(&transfers)
	db.QueryRow("SELECT COUNT(*) FROM transfers WHERE status = 'pending'").Scan(&pending)

	writeJSON(w, http.StatusOK, map[string]any{
		"accounts_total":    accounts,
		"transfers_total":   transfers,
		"transfers_pending": pending,
	})
}

payments-api/handlers_transfers.go

package main

import (
	"encoding/json"
	"net/http"
)

type transfer struct {
	ID            int64  `json:"id"`
	OrgID         int64  `json:"orgId"`
	AccountID     int64  `json:"accountId"`
	BeneficiaryID *int64 `json:"beneficiaryId,omitempty"`
	AmountCents   int64  `json:"amountCents"`
	Currency      string `json:"currency"`
	Status        string `json:"status"`
	Memo          string `json:"memo"`
}

type createTransferRequest struct {
	AccountID     int64  `json:"accountId"`
	BeneficiaryID *int64 `json:"beneficiaryId"`
	AmountCents   int64  `json:"amountCents"`
	Currency      string `json:"currency"`
	Memo          string `json:"memo"`
}

func getTransfer(w http.ResponseWriter, r *http.Request) {
	orgID := orgIDFromContext(r)
	id := r.PathValue("id")

	var t transfer
	err := db.QueryRow(
		"SELECT id, org_id, account_id, beneficiary_id, amount_cents, currency, status, memo FROM transfers WHERE id = ? AND org_id = ?",
		id, orgID,
	).Scan(&t.ID, &t.OrgID, &t.AccountID, &t.BeneficiaryID, &t.AmountCents, &t.Currency, &t.Status, &t.Memo)
	if err != nil {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": "transfer not found"})
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{"transfer": t})
}

// createTransfer books a new pending transfer for the caller's org and
// kicks off asynchronous settlement processing for it.
func createTransfer(w http.ResponseWriter, r *http.Request) {
	orgID := orgIDFromContext(r)

	var req createTransferRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	if req.AccountID == 0 || req.AmountCents == 0 {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "accountId and amountCents are required"})
		return
	}
	if req.Currency == "" {
		req.Currency = "USD"
	}

	res, err := db.Exec(
		`INSERT INTO transfers (org_id, account_id, beneficiary_id, amount_cents, currency, status, memo)
		 VALUES (?, ?, ?, ?, ?, 'pending', ?)`,
		orgID, req.AccountID, req.BeneficiaryID, req.AmountCents, req.Currency, req.Memo,
	)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	transferID, _ := res.LastInsertId()

	// Dispatch settlement asynchronously so the caller isn't blocked on the
	// downstream clearing check.
	go processSettlement(transferID, orgID, req.AmountCents)

	writeJSON(w, http.StatusCreated, map[string]any{"id": transferID, "status": "pending"})
}

payments-api/main.go

package main

import (
	"log"
	"net/http"
	"os"
)

func main() {
	initDB()
	defer db.Close()

	go settlementSweep()

	mux := http.NewServeMux()

	mux.HandleFunc("GET /healthz", healthz)
	mux.HandleFunc("GET /metrics", metrics)

	mux.HandleFunc("GET /api/accounts/search", withAuth(withAudit("accounts.search")(searchAccounts)))
	mux.HandleFunc("GET /api/accounts/{id}", withAuth(withAudit("accounts.get")(getAccount)))

	mux.HandleFunc("GET /api/transfers/{id}", withAuth(withAudit("transfers.get")(getTransfer)))
	mux.HandleFunc("POST /api/transfers", withAuth(requireRole("admin", "service")(withAudit("transfers.create")(createTransfer))))

	mux.HandleFunc("POST /api/beneficiaries", withAuth(requireRole("admin", "service")(withAudit("beneficiaries.create")(createBeneficiary))))

	port := os.Getenv("PORT")
	if port == "" {
		port = "4001"
	}

	log.Printf("payments-api listening on :%s", port)
	if err := http.ListenAndServe(":"+port, mux); err != nil {
		log.Fatal(err)
	}
}

payments-api/middleware.go

package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
)

type ctxKey string

const (
	ctxOrgID ctxKey = "orgID"
	ctxRole  ctxKey = "role"
)

func writeJSON(w http.ResponseWriter, status int, payload any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	if err := json.NewEncoder(w).Encode(payload); err != nil {
		log.Printf("encoding response: %v", err)
	}
}

// withAuth validates the caller's API key against the api_keys table and
// attaches the resolved org id and role to the request context for
// downstream handlers and the audit middleware to use.
func withAuth(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		apiKey := r.Header.Get("X-Api-Key")
		if apiKey == "" {
			writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "missing X-Api-Key header"})
			return
		}

		var orgID int64
		var role string
		err := db.QueryRow("SELECT org_id, role FROM api_keys WHERE api_key = ?", apiKey).Scan(&orgID, &role)
		if err != nil {
			writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid api key"})
			return
		}

		ctx := context.WithValue(r.Context(), ctxOrgID, orgID)
		ctx = context.WithValue(ctx, ctxRole, role)
		next(w, r.WithContext(ctx))
	}
}

// requireRole rejects the request unless the resolved role is in allowed.
func requireRole(allowed ...string) func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			role, _ := r.Context().Value(ctxRole).(string)
			for _, a := range allowed {
				if a == role {
					next(w, r)
					return
				}
			}
			writeJSON(w, http.StatusForbidden, map[string]string{"error": "role not permitted for this action"})
		}
	}
}

// withAudit records every request that reaches a business handler, after
// the fact, into the audit_log table for compliance review.
func withAudit(action string) func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			next(w, r)

			orgID, _ := r.Context().Value(ctxOrgID).(int64)
			detail := r.URL.RawQuery
			_, err := db.Exec(
				"INSERT INTO audit_log (org_id, actor, action, detail, ip) VALUES (?, ?, ?, ?, ?)",
				orgID, r.Header.Get("X-Api-Key"), action, detail, r.RemoteAddr,
			)
			if err != nil {
				log.Printf("audit log insert failed: %v", err)
			}
		}
	}
}

func orgIDFromContext(r *http.Request) int64 {
	orgID, _ := r.Context().Value(ctxOrgID).(int64)
	return orgID
}

payments-api/settlement.go

package main

import (
	"log"
	"strconv"
	"time"
)

// processSettlement runs the risk/clearing check for a freshly created
// transfer and marks it settled once it clears. Large transfers are held
// a little longer to simulate an additional manual review step.
func processSettlement(transferID, orgID, amountCents int64) {
	delay := 500 * time.Millisecond
	if amountCents > 1000000 {
		delay = 2 * time.Second
	}
	time.Sleep(delay)

	_, err := db.Exec("UPDATE transfers SET status = 'settled', settled_at = datetime('now') WHERE id = ?", transferID)
	if err != nil {
		log.Printf("settlement update failed for transfer %d: %v", transferID, err)
		return
	}

	_, err = db.Exec(
		"INSERT INTO audit_log (org_id, actor, action, detail) VALUES (?, ?, ?, ?)",
		orgID, "settlement-worker", "transfer.settled", "transfer_id="+strconv.FormatInt(transferID, 10),
	)
	if err != nil {
		log.Printf("audit log insert failed for transfer %d: %v", transferID, err)
	}
}

// settlementSweep periodically catches any transfers that were left
// pending (e.g. after a service restart) and runs them through settlement.
func settlementSweep() {
	ticker := time.NewTicker(30 * time.Second)
	for range ticker.C {
		rows, err := db.Query("SELECT id, org_id, amount_cents FROM transfers WHERE status = 'pending'")
		if err != nil {
			log.Printf("settlement sweep query failed: %v", err)
			continue
		}

		var pending []transfer
		for rows.Next() {
			var t transfer
			if err := rows.Scan(&t.ID, &t.OrgID, &t.AmountCents); err != nil {
				continue
			}
			pending = append(pending, t)
		}
		rows.Close()

		for _, t := range pending {
			go processSettlement(t.ID, t.OrgID, t.AmountCents)
		}
	}
}

portal/.gitignore

node_modules/
dist/
data/*.sqlite
storage/documents/_incoming/*
!storage/documents/_incoming/.gitkeep

portal/package-lock.json

{
  "name": "treasury-portal",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "treasury-portal",
      "version": "1.0.0",
      "dependencies": {
        "cookie-parser": "^1.4.6",
        "ejs": "^3.1.9",
        "express": "^4.19.2",
        "multer": "^1.4.5-lts.1"
      },
      "devDependencies": {
        "@types/cookie-parser": "^1.4.6",
        "@types/express": "^4.17.21",
        "@types/multer": "^1.4.11",
        "@types/node": "^22.0.0",
        "typescript": "^5.5.4"
      }
    },
    "node_modules/@types/body-parser": {
      "version": "1.19.6",
      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/connect": "*",
        "@types/node": "*"
      }
    },
    "node_modules/@types/connect": {
      "version": "3.4.38",
      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/@types/cookie-parser": {
      "version": "1.4.10",
      "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz",
      "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==",
      "dev": true,
      "license": "MIT",
      "peerDependencies": {
        "@types/express": "*"
      }
    },
    "node_modules/@types/express": {
      "version": "4.17.25",
      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
      "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/body-parser": "*",
        "@types/express-serve-static-core": "^4.17.33",
        "@types/qs": "*",
        "@types/serve-static": "^1"
      }
    },
    "node_modules/@types/express-serve-static-core": {
      "version": "4.19.9",
      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz",
      "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/node": "*",
        "@types/qs": "*",
        "@types/range-parser": "*",
        "@types/send": "*"
      }
    },
    "node_modules/@types/http-errors": {
      "version": "2.0.5",
      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@types/mime": {
      "version": "1.3.5",
      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@types/multer": {
      "version": "1.4.13",
      "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz",
      "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/express": "*"
      }
    },
    "node_modules/@types/node": {
      "version": "22.20.2",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.2.tgz",
      "integrity": "sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "undici-types": "~6.21.0"
      }
    },
    "node_modules/@types/qs": {
      "version": "6.15.1",
      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
      "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@types/range-parser": {
      "version": "1.2.7",
      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@types/send": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/@types/serve-static": {
      "version": "1.15.10",
      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
      "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/http-errors": "*",
        "@types/node": "*",
        "@types/send": "<1"
      }
    },
    "node_modules/@types/serve-static/node_modules/@types/send": {
      "version": "0.17.6",
      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
      "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/mime": "^1",
        "@types/node": "*"
      }
    },
    "node_modules/accepts": {
      "version": "1.3.8",
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
      "license": "MIT",
      "dependencies": {
        "mime-types": "~2.1.34",
        "negotiator": "0.6.3"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/append-field": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
      "license": "MIT"
    },
    "node_modules/array-flatten": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
      "license": "MIT"
    },
    "node_modules/async": {
      "version": "3.2.6",
      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
      "license": "MIT"
    },
    "node_modules/balanced-match": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
      "license": "MIT"
    },
    "node_modules/body-parser": {
      "version": "1.20.8",
      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz",
      "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==",
      "license": "MIT",
      "dependencies": {
        "bytes": "~3.1.2",
        "content-type": "~1.0.5",
        "debug": "2.6.9",
        "depd": "2.0.0",
        "destroy": "~1.2.0",
        "http-errors": "~2.0.1",
        "iconv-lite": "~0.4.24",
        "on-finished": "~2.4.1",
        "qs": "~6.16.0",
        "raw-body": "~2.5.3",
        "type-is": "~1.6.18",
        "unpipe": "~1.0.0"
      },
      "engines": {
        "node": ">= 0.8",
        "npm": "1.2.8000 || >= 1.4.16"
      }
    },
    "node_modules/body-parser/node_modules/qs": {
      "version": "6.16.0",
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
      "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
      "license": "BSD-3-Clause",
      "dependencies": {
        "es-define-property": "^1.0.1",
        "side-channel": "^1.1.1"
      },
      "engines": {
        "node": ">=0.6"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/brace-expansion": {
      "version": "2.1.4",
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
      "license": "MIT",
      "dependencies": {
        "balanced-match": "^1.0.0"
      }
    },
    "node_modules/buffer-from": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
      "license": "MIT"
    },
    "node_modules/busboy": {
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
      "dependencies": {
        "streamsearch": "^1.1.0"
      },
      "engines": {
        "node": ">=10.16.0"
      }
    },
    "node_modules/bytes": {
      "version": "3.1.2",
      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/call-bind-apply-helpers": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/call-bound": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.2",
        "get-intrinsic": "^1.3.0"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/concat-stream": {
      "version": "1.6.2",
      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
      "engines": [
        "node >= 0.8"
      ],
      "license": "MIT",
      "dependencies": {
        "buffer-from": "^1.0.0",
        "inherits": "^2.0.3",
        "readable-stream": "^2.2.2",
        "typedarray": "^0.0.6"
      }
    },
    "node_modules/content-disposition": {
      "version": "0.5.4",
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
      "license": "MIT",
      "dependencies": {
        "safe-buffer": "5.2.1"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/content-type": {
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/cookie": {
      "version": "0.7.2",
      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/cookie-parser": {
      "version": "1.4.7",
      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
      "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
      "license": "MIT",
      "dependencies": {
        "cookie": "0.7.2",
        "cookie-signature": "1.0.6"
      },
      "engines": {
        "node": ">= 0.8.0"
      }
    },
    "node_modules/cookie-signature": {
      "version": "1.0.6",
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
      "license": "MIT"
    },
    "node_modules/core-util-is": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
      "license": "MIT"
    },
    "node_modules/debug": {
      "version": "2.6.9",
      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
      "license": "MIT",
      "dependencies": {
        "ms": "2.0.0"
      }
    },
    "node_modules/depd": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/destroy": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8",
        "npm": "1.2.8000 || >= 1.4.16"
      }
    },
    "node_modules/dunder-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.1",
        "es-errors": "^1.3.0",
        "gopd": "^1.2.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/ee-first": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
      "license": "MIT"
    },
    "node_modules/ejs": {
      "version": "3.1.10",
      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
      "license": "Apache-2.0",
      "dependencies": {
        "jake": "^10.8.5"
      },
      "bin": {
        "ejs": "bin/cli.js"
      },
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/encodeurl": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/es-define-property": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-errors": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-object-atoms": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/escape-html": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
      "license": "MIT"
    },
    "node_modules/etag": {
      "version": "1.8.1",
      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/express": {
      "version": "4.22.2",
      "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
      "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
      "license": "MIT",
      "dependencies": {
        "accepts": "~1.3.8",
        "array-flatten": "1.1.1",
        "body-parser": "~1.20.5",
        "content-disposition": "~0.5.4",
        "content-type": "~1.0.4",
        "cookie": "~0.7.1",
        "cookie-signature": "~1.0.6",
        "debug": "2.6.9",
        "depd": "2.0.0",
        "encodeurl": "~2.0.0",
        "escape-html": "~1.0.3",
        "etag": "~1.8.1",
        "finalhandler": "~1.3.1",
        "fresh": "~0.5.2",
        "http-errors": "~2.0.0",
        "merge-descriptors": "1.0.3",
        "methods": "~1.1.2",
        "on-finished": "~2.4.1",
        "parseurl": "~1.3.3",
        "path-to-regexp": "~0.1.12",
        "proxy-addr": "~2.0.7",
        "qs": "~6.15.1",
        "range-parser": "~1.2.1",
        "safe-buffer": "5.2.1",
        "send": "~0.19.0",
        "serve-static": "~1.16.2",
        "setprototypeof": "1.2.0",
        "statuses": "~2.0.1",
        "type-is": "~1.6.18",
        "utils-merge": "1.0.1",
        "vary": "~1.1.2"
      },
      "engines": {
        "node": ">= 0.10.0"
      },
      "funding": {
        "type": "opencollective",
        "url": "https://opencollective.com/express"
      }
    },
    "node_modules/filelist": {
      "version": "1.0.6",
      "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
      "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
      "license": "Apache-2.0",
      "dependencies": {
        "minimatch": "^5.0.1"
      }
    },
    "node_modules/finalhandler": {
      "version": "1.3.2",
      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
      "license": "MIT",
      "dependencies": {
        "debug": "2.6.9",
        "encodeurl": "~2.0.0",
        "escape-html": "~1.0.3",
        "on-finished": "~2.4.1",
        "parseurl": "~1.3.3",
        "statuses": "~2.0.2",
        "unpipe": "~1.0.0"
      },
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/forwarded": {
      "version": "0.2.0",
      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/fresh": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/function-bind": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-intrinsic": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.2",
        "es-define-property": "^1.0.1",
        "es-errors": "^1.3.0",
        "es-object-atoms": "^1.1.1",
        "function-bind": "^1.1.2",
        "get-proto": "^1.0.1",
        "gopd": "^1.2.0",
        "has-symbols": "^1.1.0",
        "hasown": "^2.0.2",
        "math-intrinsics": "^1.1.0"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
      "license": "MIT",
      "dependencies": {
        "dunder-proto": "^1.0.1",
        "es-object-atoms": "^1.0.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/gopd": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/has-symbols": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/hasown": {
      "version": "2.0.4",
      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
      "license": "MIT",
      "dependencies": {
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/http-errors": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
      "license": "MIT",
      "dependencies": {
        "depd": "~2.0.0",
        "inherits": "~2.0.4",
        "setprototypeof": "~1.2.0",
        "statuses": "~2.0.2",
        "toidentifier": "~1.0.1"
      },
      "engines": {
        "node": ">= 0.8"
      },
      "funding": {
        "type": "opencollective",
        "url": "https://opencollective.com/express"
      }
    },
    "node_modules/iconv-lite": {
      "version": "0.4.24",
      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
      "license": "MIT",
      "dependencies": {
        "safer-buffer": ">= 2.1.2 < 3"
      },
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/inherits": {
      "version": "2.0.4",
      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
      "license": "ISC"
    },
    "node_modules/ipaddr.js": {
      "version": "1.9.1",
      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.10"
      }
    },
    "node_modules/isarray": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
      "license": "MIT"
    },
    "node_modules/jake": {
      "version": "10.9.4",
      "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
      "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
      "license": "Apache-2.0",
      "dependencies": {
        "async": "^3.2.6",
        "filelist": "^1.0.4",
        "picocolors": "^1.1.1"
      },
      "bin": {
        "jake": "bin/cli.js"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/math-intrinsics": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/media-typer": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/merge-descriptors": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/methods": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/mime": {
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
      "license": "MIT",
      "bin": {
        "mime": "cli.js"
      },
      "engines": {
        "node": ">=4"
      }
    },
    "node_modules/mime-db": {
      "version": "1.52.0",
      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/mime-types": {
      "version": "2.1.35",
      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
      "license": "MIT",
      "dependencies": {
        "mime-db": "1.52.0"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/minimatch": {
      "version": "5.1.9",
      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
      "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
      "license": "ISC",
      "dependencies": {
        "brace-expansion": "^2.0.1"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/minimist": {
      "version": "1.2.8",
      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/mkdirp": {
      "version": "0.5.6",
      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
      "license": "MIT",
      "dependencies": {
        "minimist": "^1.2.6"
      },
      "bin": {
        "mkdirp": "bin/cmd.js"
      }
    },
    "node_modules/ms": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
      "license": "MIT"
    },
    "node_modules/multer": {
      "version": "1.4.5-lts.2",
      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
      "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
      "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.",
      "license": "MIT",
      "dependencies": {
        "append-field": "^1.0.0",
        "busboy": "^1.0.0",
        "concat-stream": "^1.5.2",
        "mkdirp": "^0.5.4",
        "object-assign": "^4.1.1",
        "type-is": "^1.6.4",
        "xtend": "^4.0.0"
      },
      "engines": {
        "node": ">= 6.0.0"
      }
    },
    "node_modules/negotiator": {
      "version": "0.6.3",
      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/object-assign": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
      "license": "MIT",
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/object-inspect": {
      "version": "1.13.4",
      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/on-finished": {
      "version": "2.4.1",
      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
      "license": "MIT",
      "dependencies": {
        "ee-first": "1.1.1"
      },
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/parseurl": {
      "version": "1.3.3",
      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/path-to-regexp": {
      "version": "0.1.13",
      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
      "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
      "license": "MIT"
    },
    "node_modules/picocolors": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
      "license": "ISC"
    },
    "node_modules/process-nextick-args": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
      "license": "MIT"
    },
    "node_modules/proxy-addr": {
      "version": "2.0.7",
      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
      "license": "MIT",
      "dependencies": {
        "forwarded": "0.2.0",
        "ipaddr.js": "1.9.1"
      },
      "engines": {
        "node": ">= 0.10"
      }
    },
    "node_modules/qs": {
      "version": "6.15.3",
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
      "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
      "license": "BSD-3-Clause",
      "dependencies": {
        "es-define-property": "^1.0.1",
        "side-channel": "^1.1.1"
      },
      "engines": {
        "node": ">=0.6"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/range-parser": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/raw-body": {
      "version": "2.5.3",
      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
      "license": "MIT",
      "dependencies": {
        "bytes": "~3.1.2",
        "http-errors": "~2.0.1",
        "iconv-lite": "~0.4.24",
        "unpipe": "~1.0.0"
      },
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/readable-stream": {
      "version": "2.3.8",
      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
      "license": "MIT",
      "dependencies": {
        "core-util-is": "~1.0.0",
        "inherits": "~2.0.3",
        "isarray": "~1.0.0",
        "process-nextick-args": "~2.0.0",
        "safe-buffer": "~5.1.1",
        "string_decoder": "~1.1.1",
        "util-deprecate": "~1.0.1"
      }
    },
    "node_modules/readable-stream/node_modules/safe-buffer": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
      "license": "MIT"
    },
    "node_modules/safe-buffer": {
      "version": "5.2.1",
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/feross"
        },
        {
          "type": "patreon",
          "url": "https://www.patreon.com/feross"
        },
        {
          "type": "consulting",
          "url": "https://feross.org/support"
        }
      ],
      "license": "MIT"
    },
    "node_modules/safer-buffer": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
      "license": "MIT"
    },
    "node_modules/send": {
      "version": "0.19.2",
      "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
      "license": "MIT",
      "dependencies": {
        "debug": "2.6.9",
        "depd": "2.0.0",
        "destroy": "1.2.0",
        "encodeurl": "~2.0.0",
        "escape-html": "~1.0.3",
        "etag": "~1.8.1",
        "fresh": "~0.5.2",
        "http-errors": "~2.0.1",
        "mime": "1.6.0",
        "ms": "2.1.3",
        "on-finished": "~2.4.1",
        "range-parser": "~1.2.1",
        "statuses": "~2.0.2"
      },
      "engines": {
        "node": ">= 0.8.0"
      }
    },
    "node_modules/send/node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
    "node_modules/serve-static": {
      "version": "1.16.3",
      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
      "license": "MIT",
      "dependencies": {
        "encodeurl": "~2.0.0",
        "escape-html": "~1.0.3",
        "parseurl": "~1.3.3",
        "send": "~0.19.1"
      },
      "engines": {
        "node": ">= 0.8.0"
      }
    },
    "node_modules/setprototypeof": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
      "license": "ISC"
    },
    "node_modules/side-channel": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "object-inspect": "^1.13.4",
        "side-channel-list": "^1.0.1",
        "side-channel-map": "^1.0.1",
        "side-channel-weakmap": "^1.0.2"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/side-channel-list": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "object-inspect": "^1.13.4"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/side-channel-map": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
      "license": "MIT",
      "dependencies": {
        "call-bound": "^1.0.2",
        "es-errors": "^1.3.0",
        "get-intrinsic": "^1.2.5",
        "object-inspect": "^1.13.3"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/side-channel-weakmap": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
      "license": "MIT",
      "dependencies": {
        "call-bound": "^1.0.2",
        "es-errors": "^1.3.0",
        "get-intrinsic": "^1.2.5",
        "object-inspect": "^1.13.3",
        "side-channel-map": "^1.0.1"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/statuses": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/streamsearch": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/string_decoder": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
      "license": "MIT",
      "dependencies": {
        "safe-buffer": "~5.1.0"
      }
    },
    "node_modules/string_decoder/node_modules/safe-buffer": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
      "license": "MIT"
    },
    "node_modules/toidentifier": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
      "license": "MIT",
      "engines": {
        "node": ">=0.6"
      }
    },
    "node_modules/type-is": {
      "version": "1.6.18",
      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
      "license": "MIT",
      "dependencies": {
        "media-typer": "0.3.0",
        "mime-types": "~2.1.24"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/typedarray": {
      "version": "0.0.6",
      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
      "license": "MIT"
    },
    "node_modules/typescript": {
      "version": "5.9.3",
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
      "dev": true,
      "license": "Apache-2.0",
      "bin": {
        "tsc": "bin/tsc",
        "tsserver": "bin/tsserver"
      },
      "engines": {
        "node": ">=14.17"
      }
    },
    "node_modules/undici-types": {
      "version": "6.21.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
      "dev": true,
      "license": "MIT"
    },
    "node_modules/unpipe": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/util-deprecate": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
      "license": "MIT"
    },
    "node_modules/utils-merge": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.4.0"
      }
    },
    "node_modules/vary": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
      "license": "MIT",
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/xtend": {
      "version": "4.0.2",
      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
      "license": "MIT",
      "engines": {
        "node": ">=0.4"
      }
    }
  }
}

portal/package.json

{
  "name": "treasury-portal",
  "version": "1.0.0",
  "description": "Admin portal for the treasury operations platform",
  "main": "dist/server.js",
  "scripts": {
    "build": "tsc -p .",
    "start": "node --experimental-sqlite dist/server.js",
    "dev": "tsc -p . && node --experimental-sqlite dist/server.js"
  },
  "dependencies": {
    "cookie-parser": "^1.4.6",
    "ejs": "^3.1.9",
    "express": "^4.19.2",
    "multer": "^1.4.5-lts.1"
  },
  "devDependencies": {
    "@types/cookie-parser": "^1.4.6",
    "@types/express": "^4.17.21",
    "@types/multer": "^1.4.11",
    "@types/node": "^22.0.0",
    "typescript": "^5.5.4"
  }
}

portal/src/db/index.ts

import path from "path";
import fs from "fs";
import { DatabaseSync } from "node:sqlite";

const DATA_DIR = path.join(__dirname, "..", "..", "data");
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });

const DB_PATH = path.join(DATA_DIR, "portal.sqlite");
export const db = new DatabaseSync(DB_PATH);

db.exec(`
CREATE TABLE IF NOT EXISTS organizations (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  plan TEXT NOT NULL DEFAULT 'standard',
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS teams (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  org_id INTEGER NOT NULL,
  name TEXT NOT NULL,
  FOREIGN KEY (org_id) REFERENCES organizations(id)
);

CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  org_id INTEGER NOT NULL,
  team_id INTEGER,
  email TEXT NOT NULL UNIQUE,
  password_hash TEXT NOT NULL,
  role TEXT NOT NULL DEFAULT 'member',
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  FOREIGN KEY (org_id) REFERENCES organizations(id)
);

CREATE TABLE IF NOT EXISTS sessions (
  token TEXT PRIMARY KEY,
  user_id INTEGER NOT NULL,
  org_id INTEGER NOT NULL,
  role TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE IF NOT EXISTS beneficiaries (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  org_id INTEGER NOT NULL,
  name TEXT NOT NULL,
  bank_name TEXT,
  iban TEXT,
  status TEXT NOT NULL DEFAULT 'active',
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  FOREIGN KEY (org_id) REFERENCES organizations(id)
);

CREATE TABLE IF NOT EXISTS transfers (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  org_id INTEGER NOT NULL,
  beneficiary_id INTEGER,
  account_ref TEXT,
  amount_cents INTEGER NOT NULL,
  currency TEXT NOT NULL DEFAULT 'USD',
  status TEXT NOT NULL DEFAULT 'pending',
  memo TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS audit_log (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  org_id INTEGER,
  user_id INTEGER,
  action TEXT NOT NULL,
  detail TEXT,
  ip TEXT,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);

function seed() {
  const orgCount = db.prepare("SELECT COUNT(*) AS c FROM organizations").get() as any;
  if (orgCount.c > 0) return;

  const insertOrg = db.prepare("INSERT INTO organizations (name, plan) VALUES (?, ?)");
  const acme = insertOrg.run("Acme Manufacturing", "enterprise");
  const globex = insertOrg.run("Globex Trading", "standard");

  const insertTeam = db.prepare("INSERT INTO teams (org_id, name) VALUES (?, ?)");
  insertTeam.run(Number(acme.lastInsertRowid), "Treasury Ops");
  insertTeam.run(Number(globex.lastInsertRowid), "Finance");

  const insertUser = db.prepare(
    "INSERT INTO users (org_id, team_id, email, password_hash, role) VALUES (?, ?, ?, ?, ?)"
  );
  // password for every seed user is "password123" (sha256 hex, see auth.ts)
  const pw = "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f";
  insertUser.run(Number(acme.lastInsertRowid), 1, "admin@acme.example", pw, "admin");
  insertUser.run(Number(acme.lastInsertRowid), 1, "analyst@acme.example", pw, "analyst");
  insertUser.run(Number(globex.lastInsertRowid), 2, "admin@globex.example", pw, "admin");

  const insertBen = db.prepare(
    "INSERT INTO beneficiaries (org_id, name, bank_name, iban, status) VALUES (?, ?, ?, ?, ?)"
  );
  insertBen.run(Number(acme.lastInsertRowid), "Northwind Supplies", "First Union Bank", "GB29NWBK60161331926819", "active");
  insertBen.run(Number(acme.lastInsertRowid), "Initech Logistics", "Metro Bank", "GB29METR60161334455667", "active");
  insertBen.run(Number(globex.lastInsertRowid), "Umbrella Freight", "Globex Bank", "GB29GLBX60161339988776", "active");

  const insertTransfer = db.prepare(
    "INSERT INTO transfers (org_id, beneficiary_id, account_ref, amount_cents, currency, status, memo) VALUES (?, ?, ?, ?, ?, ?, ?)"
  );
  insertTransfer.run(Number(acme.lastInsertRowid), 1, "ACC-1001", 250000, "USD", "settled", "Q3 supplies invoice 4471");
  insertTransfer.run(Number(acme.lastInsertRowid), 2, "ACC-1001", 75000, "USD", "pending", "Logistics retainer");
  insertTransfer.run(Number(globex.lastInsertRowid), 3, "ACC-2002", 1200000, "EUR", "pending", "Freight contract deposit");
}

seed();

portal/src/middleware/audit.ts

import { Request, Response, NextFunction } from "express";
import { db } from "../db";

const insertAudit = db.prepare(
  "INSERT INTO audit_log (org_id, user_id, action, detail, ip) VALUES (?, ?, ?, ?, ?)"
);

export function auditMiddleware(req: Request, res: Response, next: NextFunction) {
  res.on("finish", () => {
    const action = `${req.method} ${req.path}`;
    const detail = JSON.stringify({
      status: res.statusCode,
      query: req.query,
    });
    insertAudit.run(
      req.user?.orgId ?? null,
      req.user?.id ?? null,
      action,
      detail,
      req.ip ?? null
    );
  });
  next();
}

portal/src/middleware/auth.ts

import { Request, Response, NextFunction } from "express";
import { db } from "../db";

export function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const header = req.headers["authorization"] || "";
  const token = Array.isArray(header) ? header[0] : header.replace(/^Bearer\s+/i, "");

  if (!token) {
    res.status(401).json({ error: "missing bearer token" });
    return;
  }

  const session = db
    .prepare("SELECT token, user_id, org_id, role FROM sessions WHERE token = ?")
    .get(token) as any;

  if (!session) {
    res.status(401).json({ error: "invalid or expired session" });
    return;
  }

  const user = db.prepare("SELECT id, org_id, email, role FROM users WHERE id = ?").get(
    session.user_id
  ) as any;

  if (!user) {
    res.status(401).json({ error: "session user not found" });
    return;
  }

  req.user = {
    id: user.id,
    orgId: user.org_id,
    role: user.role,
    email: user.email,
  };

  next();
}

portal/src/middleware/rbac.ts

import { Request, Response, NextFunction } from "express";

export function requireRole(...allowed: string[]) {
  return function (req: Request, res: Response, next: NextFunction) {
    if (!req.user) {
      res.status(401).json({ error: "not authenticated" });
      return;
    }
    if (!allowed.includes(req.user.role)) {
      res.status(403).json({ error: `role '${req.user.role}' is not permitted for this action` });
      return;
    }
    next();
  };
}

portal/src/routes/auth.ts

import { Router } from "express";
import crypto from "crypto";
import { db } from "../db";

export const authRouter = Router();

function sha256(value: string): string {
  return crypto.createHash("sha256").update(value).digest("hex");
}

authRouter.post("/login", (req, res) => {
  const { email, password } = req.body || {};

  if (!email || !password) {
    res.status(400).json({ error: "email and password are required" });
    return;
  }

  const user = db
    .prepare("SELECT id, org_id, role, password_hash FROM users WHERE email = ?")
    .get(email) as any;

  if (!user || user.password_hash !== sha256(password)) {
    res.status(401).json({ error: "invalid credentials" });
    return;
  }

  const token = crypto.randomBytes(24).toString("hex");
  db.prepare(
    "INSERT INTO sessions (token, user_id, org_id, role) VALUES (?, ?, ?, ?)"
  ).run(token, user.id, user.org_id, user.role);

  res.json({ token, orgId: user.org_id, role: user.role });
});

authRouter.post("/logout", (req, res) => {
  const header = req.headers["authorization"] || "";
  const token = Array.isArray(header) ? header[0] : header.replace(/^Bearer\s+/i, "");
  db.prepare("DELETE FROM sessions WHERE token = ?").run(token);
  res.json({ ok: true });
});

portal/src/routes/beneficiaries.ts

import { Router } from "express";
import { db } from "../db";
import { requireRole } from "../middleware/rbac";

export const beneficiariesRouter = Router();

beneficiariesRouter.post("/", requireRole("admin", "analyst"), (req, res) => {
  const { name, bankName, iban } = req.body || {};

  if (!name) {
    res.status(400).json({ error: "beneficiary name is required" });
    return;
  }

  const result = db
    .prepare(
      "INSERT INTO beneficiaries (org_id, name, bank_name, iban, status) VALUES (?, ?, ?, ?, 'active')"
    )
    .run(req.user!.orgId, name, bankName ?? null, iban ?? null);

  res.status(201).json({ id: Number(result.lastInsertRowid), name, bankName, iban });
});

beneficiariesRouter.get("/", (req, res) => {
  const rows = db
    .prepare("SELECT id, name, bank_name, iban, status FROM beneficiaries WHERE org_id = ?")
    .all(req.user!.orgId);
  res.json({ beneficiaries: rows });
});

portal/src/routes/documents.js

const express = require("express");
const fs = require("fs");
const path = require("path");
const multer = require("multer");

const router = express.Router();
const STORAGE_ROOT = path.join(__dirname, "..", "..", "storage", "documents");

const upload = multer({ dest: path.join(STORAGE_ROOT, "_incoming") });

// Old-school download endpoint kept around from the original PHP-to-Node
// port. Caller tells us which org folder and which file inside it to stream.
router.get("/:orgId/download", function (req, res) {
  const orgId = req.params.orgId;
  const file = req.query.file;

  if (!file) {
    return res.status(400).json({ error: "file query parameter is required" });
  }

  const orgDir = path.join(STORAGE_ROOT, "org-" + orgId);
  const target = path.join(orgDir, file);

  fs.readFile(target, function (err, data) {
    if (err) {
      return res.status(404).json({ error: "document not found", path: target });
    }
    res.setHeader("Content-Disposition", "attachment; filename=" + file);
    res.send(data);
  });
});

router.get("/:orgId/list", function (req, res) {
  const orgDir = path.join(STORAGE_ROOT, "org-" + req.params.orgId);
  fs.readdir(orgDir, function (err, files) {
    if (err) {
      return res.json({ files: [] });
    }
    res.json({ files: files });
  });
});

router.post("/:orgId/upload", upload.single("document"), function (req, res) {
  const orgId = req.params.orgId;
  const orgDir = path.join(STORAGE_ROOT, "org-" + orgId);

  if (!fs.existsSync(orgDir)) {
    fs.mkdirSync(orgDir, { recursive: true });
  }

  const destName = req.body.filename || req.file.originalname;
  const destPath = path.join(orgDir, destName);

  fs.renameSync(req.file.path, destPath);

  res.status(201).json({ stored: destName, orgId: orgId });
});

module.exports = router;

portal/src/routes/health.ts

import { Router } from "express";
import { db } from "../db";

export const healthRouter = Router();

healthRouter.get("/healthz", (req, res) => {
  res.json({ status: "ok", service: "treasury-portal" });
});

healthRouter.get("/metrics", (req, res) => {
  const orgCount = db.prepare("SELECT COUNT(*) AS c FROM organizations").get() as any;
  const userCount = db.prepare("SELECT COUNT(*) AS c FROM users").get() as any;
  const transferCount = db.prepare("SELECT COUNT(*) AS c FROM transfers").get() as any;
  const pendingCount = db
    .prepare("SELECT COUNT(*) AS c FROM transfers WHERE status = 'pending'")
    .get() as any;

  res.json({
    organizations: orgCount.c,
    users: userCount.c,
    transfers_total: transferCount.c,
    transfers_pending: pendingCount.c,
    uptime_seconds: process.uptime(),
  });
});

portal/src/routes/orgs.ts

import { Router } from "express";
import { db } from "../db";
import { requireRole } from "../middleware/rbac";

export const orgsRouter = Router();

orgsRouter.get("/", (req, res) => {
  const orgs = db.prepare("SELECT id, name, plan, created_at FROM organizations").all();
  res.json({ organizations: orgs });
});

orgsRouter.get("/:id", (req, res) => {
  const org = db
    .prepare("SELECT id, name, plan, created_at FROM organizations WHERE id = ?")
    .get(req.params.id);

  if (!org) {
    res.status(404).json({ error: "organization not found" });
    return;
  }

  const teams = db.prepare("SELECT id, name FROM teams WHERE org_id = ?").all(req.params.id);
  const users = db
    .prepare("SELECT id, email, role, team_id FROM users WHERE org_id = ?")
    .all(req.params.id);

  res.json({ organization: org, teams, users });
});

orgsRouter.post("/:id/teams", requireRole("admin"), (req, res) => {
  const { name } = req.body || {};
  if (!name) {
    res.status(400).json({ error: "team name is required" });
    return;
  }
  const result = db
    .prepare("INSERT INTO teams (org_id, name) VALUES (?, ?)")
    .run(req.params.id, name);
  res.status(201).json({ id: Number(result.lastInsertRowid), orgId: Number(req.params.id), name });
});

portal/src/routes/reports.ts

import { Router } from "express";
import path from "path";
import ejs from "ejs";
import { db } from "../db";

export const reportsRouter = Router();

reportsRouter.post("/statement", (req, res) => {
  const {
    accountName,
    periodStart,
    periodEnd,
    customerNote,
    footerHtml,
    lines,
  } = req.body || {};

  const transfers = db
    .prepare(
      "SELECT created_at as date, memo, amount_cents as amount FROM transfers WHERE org_id = ? ORDER BY created_at DESC LIMIT 25"
    )
    .all(req.user!.orgId);

  const templatePath = path.join(__dirname, "..", "..", "views", "statement.ejs");

  const html = ejs.renderFile(
    templatePath,
    {
      accountName: accountName || "Untitled Account",
      periodStart: periodStart || "",
      periodEnd: periodEnd || "",
      customerNote: customerNote || "",
      footerHtml: footerHtml || "",
      lines: Array.isArray(lines) && lines.length ? lines : transfers,
    },
    { async: false }
  );

  Promise.resolve(html).then((rendered) => {
    res.set("Content-Type", "text/html");
    res.send(rendered);
  });
});

portal/src/routes/transfers.ts

import { Router } from "express";
import { db } from "../db";

export const transfersRouter = Router();

// Search/filter transfers for the treasury ops dashboard. Supports ad-hoc
// filtering by any combination of status, currency, beneficiary and a free
// text memo search so analysts can slice the ledger however they need.
transfersRouter.get("/search", (req, res) => {
  const { status, currency, beneficiary, minAmount, maxAmount, memo, sort } = req.query;

  let where = `org_id = ${req.user!.orgId}`;

  if (status) {
    where += ` AND status = '${status}'`;
  }
  if (currency) {
    where += ` AND currency = '${currency}'`;
  }
  if (beneficiary) {
    where += ` AND beneficiary_id = ${beneficiary}`;
  }
  if (minAmount) {
    where += ` AND amount_cents >= ${minAmount}`;
  }
  if (maxAmount) {
    where += ` AND amount_cents <= ${maxAmount}`;
  }
  if (memo) {
    where += ` AND memo LIKE '%${memo}%'`;
  }

  const orderBy = sort ? `${sort}` : "created_at DESC";
  const sql = `SELECT id, beneficiary_id, account_ref, amount_cents, currency, status, memo, created_at
               FROM transfers WHERE ${where} ORDER BY ${orderBy}`;

  const rows = db.prepare(sql).all();
  res.json({ transfers: rows, sql });
});

transfersRouter.get("/:id", (req, res) => {
  const row = db
    .prepare("SELECT * FROM transfers WHERE id = ? AND org_id = ?")
    .get(req.params.id, req.user!.orgId);

  if (!row) {
    res.status(404).json({ error: "transfer not found" });
    return;
  }
  res.json({ transfer: row });
});

transfersRouter.post("/", (req, res) => {
  const { beneficiaryId, accountRef, amountCents, currency, memo } = req.body || {};

  if (!amountCents || !accountRef) {
    res.status(400).json({ error: "accountRef and amountCents are required" });
    return;
  }

  const result = db
    .prepare(
      `INSERT INTO transfers (org_id, beneficiary_id, account_ref, amount_cents, currency, status, memo)
       VALUES (?, ?, ?, ?, ?, 'pending', ?)`
    )
    .run(req.user!.orgId, beneficiaryId ?? null, accountRef, amountCents, currency ?? "USD", memo ?? null);

  res.status(201).json({ id: Number(result.lastInsertRowid), status: "pending" });
});

portal/src/server.ts

import express from "express";
import cookieParser from "cookie-parser";
import "./db";
import { authMiddleware } from "./middleware/auth";
import { auditMiddleware } from "./middleware/audit";
import { authRouter } from "./routes/auth";
import { orgsRouter } from "./routes/orgs";
import { beneficiariesRouter } from "./routes/beneficiaries";
import { transfersRouter } from "./routes/transfers";
import { reportsRouter } from "./routes/reports";
import { healthRouter } from "./routes/health";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const documentsRouter = require("./routes/documents");

const app = express();
const PORT = process.env.PORT ? Number(process.env.PORT) : 4000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());

app.use(healthRouter);
app.use("/api/auth", authRouter);

// Everything below this line requires an authenticated session and gets
// its activity recorded in the audit trail.
app.use(authMiddleware);
app.use(auditMiddleware);

app.use("/api/orgs", orgsRouter);
app.use("/api/beneficiaries", beneficiariesRouter);
app.use("/api/transfers", transfersRouter);
app.use("/api/reports", reportsRouter);
app.use("/api/documents", documentsRouter);

app.listen(PORT, () => {
  console.log(`treasury-portal listening on :${PORT}`);
});

portal/src/types.ts

export interface AuthedUser {
  id: number;
  orgId: number;
  role: string;
  email: string;
}

declare global {
  namespace Express {
    interface Request {
      user?: AuthedUser;
    }
  }
}

export {};

portal/storage/documents/_incoming/.gitkeep

portal/storage/documents/org-1/statement-q3.txt

Q3 2026 Statement
Acme Manufacturing
All transactions reconciled.

portal/storage/documents/org-1/vendor-agreement.txt

Vendor Agreement - Northwind Supplies
Confidential - internal use only.

portal/storage/documents/org-2/statement-q3.txt

Q3 2026 Statement
Globex Trading

portal/tsconfig.json

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "CommonJS",
    "moduleResolution": "node",
    "outDir": "dist",
    "rootDir": "src",
    "allowJs": true,
    "checkJs": false,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "strict": false,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

portal/views/statement.ejs

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Statement - <%= accountName %></title>
</head>
<body>
  <h1>Treasury Statement</h1>
  <h2>Account: <%= accountName %></h2>
  <p>Period: <%= periodStart %> - <%= periodEnd %></p>

  <div class="customer-note">
    <%- customerNote %>
  </div>

  <table>
    <thead>
      <tr><th>Date</th><th>Memo</th><th>Amount</th></tr>
    </thead>
    <tbody>
      <% lines.forEach(function(line) { %>
      <tr>
        <td><%= line.date %></td>
        <td><%- line.memo %></td>
        <td><%= line.amount %></td>
      </tr>
      <% }) %>
    </tbody>
  </table>

  <footer><%- footerHtml %></footer>
</body>
</html>

risk-api/.gitignore

target/
data/*.sqlite
dependency-reduced-pom.xml

risk-api/data/.gitkeep

risk-api/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.reachco</groupId>
  <artifactId>risk-api</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.xerial</groupId>
      <artifactId>sqlite-jdbc</artifactId>
      <version>3.46.1.3</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>risk-api</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.6.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>com.reachco.risk.Main</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

risk-api/src/main/java/com/reachco/risk/AuditFilter.java

package com.reachco.risk;

import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class AuditFilter extends Filter {

    @Override
    public String description() {
        return "Records every request that reaches a business handler into audit_log";
    }

    @Override
    public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
        chain.doFilter(exchange);

        Object orgIdAttr = exchange.getAttribute("orgId");
        Object apiKeyAttr = exchange.getAttribute("apiKey");

        try {
            Connection conn = Database.get();
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO audit_log (org_id, actor, action, detail) VALUES (?, ?, ?, ?)")) {
                if (orgIdAttr != null) {
                    ps.setInt(1, (Integer) orgIdAttr);
                } else {
                    ps.setNull(1, java.sql.Types.INTEGER);
                }
                ps.setString(2, apiKeyAttr == null ? null : apiKeyAttr.toString());
                ps.setString(3, exchange.getRequestMethod() + " " + exchange.getRequestURI().getPath());
                ps.setString(4, exchange.getRequestURI().getRawQuery());
                ps.executeUpdate();
            }
        } catch (Exception e) {
            System.err.println("audit log insert failed: " + e.getMessage());
        }
    }
}

risk-api/src/main/java/com/reachco/risk/AuthFilter.java

package com.reachco.risk;

import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;

public class AuthFilter extends Filter {

    @Override
    public String description() {
        return "Resolves the caller's API key to an org id and role";
    }

    @Override
    public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
        List<String> headerValues = exchange.getRequestHeaders().get("X-Api-Key");
        String apiKey = (headerValues == null || headerValues.isEmpty()) ? null : headerValues.get(0);

        if (apiKey == null || apiKey.isBlank()) {
            Http.sendJson(exchange, 401, "{\"error\":\"missing X-Api-Key header\"}");
            return;
        }

        Connection conn = Database.get();
        try (PreparedStatement ps = conn.prepareStatement(
                "SELECT org_id, role FROM api_keys WHERE api_key = ?")) {
            ps.setString(1, apiKey);
            try (ResultSet rs = ps.executeQuery()) {
                if (!rs.next()) {
                    Http.sendJson(exchange, 401, "{\"error\":\"invalid api key\"}");
                    return;
                }
                exchange.setAttribute("orgId", rs.getInt("org_id"));
                exchange.setAttribute("role", rs.getString("role"));
                exchange.setAttribute("apiKey", apiKey);
            }
        } catch (Exception e) {
            Http.sendJson(exchange, 500, "{\"error\":\"auth lookup failed\"}");
            return;
        }

        chain.doFilter(exchange);
    }
}

risk-api/src/main/java/com/reachco/risk/Database.java

package com.reachco.risk;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public final class Database {

    private static Connection connection;

    private Database() {
    }

    public static synchronized Connection get() {
        if (connection != null) {
            return connection;
        }
        try {
            File dataDir = new File("data");
            if (!dataDir.exists()) {
                dataDir.mkdirs();
            }
            connection = DriverManager.getConnection("jdbc:sqlite:data/risk.sqlite");
            init(connection);
            return connection;
        } catch (SQLException e) {
            throw new RuntimeException("failed to open risk database", e);
        }
    }

    private static void init(Connection conn) throws SQLException {
        try (Statement st = conn.createStatement()) {
            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS organizations (" +
                "  id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "  name TEXT NOT NULL" +
                ")");

            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS api_keys (" +
                "  api_key TEXT PRIMARY KEY," +
                "  org_id INTEGER NOT NULL," +
                "  role TEXT NOT NULL DEFAULT 'analyst'" +
                ")");

            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS risk_reviews (" +
                "  id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "  org_id INTEGER NOT NULL," +
                "  transfer_ref TEXT," +
                "  beneficiary_name TEXT," +
                "  score INTEGER NOT NULL," +
                "  level TEXT NOT NULL," +
                "  notes TEXT," +
                "  reviewer TEXT," +
                "  created_at TEXT NOT NULL DEFAULT (datetime('now'))" +
                ")");

            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS audit_log (" +
                "  id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "  org_id INTEGER," +
                "  actor TEXT," +
                "  action TEXT NOT NULL," +
                "  detail TEXT," +
                "  created_at TEXT NOT NULL DEFAULT (datetime('now'))" +
                ")");
        }

        seed(conn);
    }

    private static void seed(Connection conn) throws SQLException {
        try (Statement st = conn.createStatement();
             ResultSet rs = st.executeQuery("SELECT COUNT(*) AS c FROM organizations")) {
            rs.next();
            if (rs.getInt("c") > 0) {
                return;
            }
        }

        try (Statement st = conn.createStatement()) {
            st.executeUpdate("INSERT INTO organizations (id, name) VALUES (1, 'Acme Manufacturing')");
            st.executeUpdate("INSERT INTO organizations (id, name) VALUES (2, 'Globex Trading')");

            st.executeUpdate("INSERT INTO api_keys (api_key, org_id, role) VALUES ('risk_key_acme_5e91', 1, 'admin')");
            st.executeUpdate("INSERT INTO api_keys (api_key, org_id, role) VALUES ('risk_key_globex_2b70', 2, 'admin')");
        }

        try (PreparedStatement ps = conn.prepareStatement(
                "INSERT INTO risk_reviews (org_id, transfer_ref, beneficiary_name, score, level, notes, reviewer) " +
                "VALUES (?, ?, ?, ?, ?, ?, ?)")) {
            ps.setInt(1, 1);
            ps.setString(2, "TRF-1001");
            ps.setString(3, "Northwind Supplies");
            ps.setInt(4, 22);
            ps.setString(5, "low");
            ps.setString(6, "Recurring vendor, no watchlist hits.");
            ps.setString(7, "system");
            ps.executeUpdate();

            ps.setInt(1, 1);
            ps.setString(2, "TRF-1002");
            ps.setString(3, "Initech Logistics");
            ps.setInt(4, 61);
            ps.setString(5, "medium");
            ps.setString(6, "New beneficiary, first transfer above threshold.");
            ps.setString(7, "system");
            ps.executeUpdate();

            ps.setInt(1, 2);
            ps.setString(2, "TRF-2001");
            ps.setString(3, "Umbrella Freight");
            ps.setInt(4, 85);
            ps.setString(5, "high");
            ps.setString(6, "Beneficiary bank in high-risk jurisdiction.");
            ps.setString(7, "system");
            ps.executeUpdate();
        }
    }
}

risk-api/src/main/java/com/reachco/risk/Http.java

package com.reachco.risk;

import com.sun.net.httpserver.HttpExchange;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

public final class Http {

    private Http() {
    }

    public static void sendJson(HttpExchange exchange, int status, String json) throws IOException {
        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
        exchange.getResponseHeaders().set("Content-Type", "application/json");
        exchange.sendResponseHeaders(status, bytes.length);
        try (OutputStream os = exchange.getResponseBody()) {
            os.write(bytes);
        }
    }

    public static void sendHtml(HttpExchange exchange, int status, String html) throws IOException {
        byte[] bytes = html.getBytes(StandardCharsets.UTF_8);
        exchange.getResponseHeaders().set("Content-Type", "text/html");
        exchange.sendResponseHeaders(status, bytes.length);
        try (OutputStream os = exchange.getResponseBody()) {
            os.write(bytes);
        }
    }

    public static String readBody(HttpExchange exchange) throws IOException {
        try (InputStream is = exchange.getRequestBody();
             ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
            is.transferTo(buffer);
            return buffer.toString(StandardCharsets.UTF_8);
        }
    }

    public static Map<String, String> queryParams(HttpExchange exchange) {
        Map<String, String> params = new LinkedHashMap<>();
        String query = exchange.getRequestURI().getRawQuery();
        if (query == null || query.isBlank()) {
            return params;
        }
        for (String pair : query.split("&")) {
            int idx = pair.indexOf('=');
            if (idx < 0) {
                params.put(urlDecode(pair), "");
            } else {
                params.put(urlDecode(pair.substring(0, idx)), urlDecode(pair.substring(idx + 1)));
            }
        }
        return params;
    }

    private static String urlDecode(String value) {
        return URLDecoder.decode(value, StandardCharsets.UTF_8);
    }

    // Extremely small JSON string-field reader, good enough for the flat
    // request bodies this service accepts without pulling in a full JSON
    // dependency.
    public static String jsonField(String json, String field) {
        String needle = "\"" + field + "\"";
        int keyIdx = json.indexOf(needle);
        if (keyIdx < 0) {
            return null;
        }
        int colon = json.indexOf(':', keyIdx + needle.length());
        if (colon < 0) {
            return null;
        }
        int i = colon + 1;
        while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
            i++;
        }
        if (i >= json.length()) {
            return null;
        }
        if (json.charAt(i) == '"') {
            int end = i + 1;
            StringBuilder sb = new StringBuilder();
            while (end < json.length() && json.charAt(end) != '"') {
                char c = json.charAt(end);
                if (c == '\\' && end + 1 < json.length()) {
                    end++;
                    c = json.charAt(end);
                }
                sb.append(c);
                end++;
            }
            return sb.toString();
        }
        int end = i;
        while (end < json.length() && ",}".indexOf(json.charAt(end)) < 0) {
            end++;
        }
        return json.substring(i, end).trim();
    }
}

risk-api/src/main/java/com/reachco/risk/Main.java

package com.reachco.risk;

import com.reachco.risk.handlers.HealthHandler;
import com.reachco.risk.handlers.RiskExposureHandler;
import com.reachco.risk.handlers.RiskReviewsHandler;
import com.sun.net.httpserver.HttpServer;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executors;

public class Main {

    public static void main(String[] args) throws Exception {
        Database.get();

        int port = 4003;
        String portEnv = System.getenv("PORT");
        if (portEnv != null && !portEnv.isBlank()) {
            port = Integer.parseInt(portEnv);
        }

        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.setExecutor(Executors.newFixedThreadPool(8));

        server.createContext("/healthz", new HealthHandler(false));
        server.createContext("/metrics", new HealthHandler(true));

        server.createContext("/api/risk/reviews", new RiskReviewsHandler())
                .getFilters().addAll(List.of(new AuthFilter(), new AuditFilter()));

        server.createContext("/api/risk/exposure", new RiskExposureHandler())
                .getFilters().addAll(List.of(new AuthFilter(), new AuditFilter()));

        server.start();
        System.out.println("risk-api listening on :" + port);
    }
}

risk-api/src/main/java/com/reachco/risk/handlers/HealthHandler.java

package com.reachco.risk.handlers;

import com.reachco.risk.Database;
import com.reachco.risk.Http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class HealthHandler implements HttpHandler {

    private final boolean metrics;

    public HealthHandler(boolean metrics) {
        this.metrics = metrics;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
        if (!metrics) {
            Http.sendJson(exchange, 200, "{\"status\":\"ok\",\"service\":\"risk-api\"}");
            return;
        }

        Connection conn = Database.get();
        int reviewCount = 0;
        int highRiskCount = 0;
        try (Statement st = conn.createStatement();
             ResultSet rs = st.executeQuery(
                     "SELECT COUNT(*) AS total, SUM(CASE WHEN level = 'high' THEN 1 ELSE 0 END) AS high_risk " +
                     "FROM risk_reviews")) {
            if (rs.next()) {
                reviewCount = rs.getInt("total");
                highRiskCount = rs.getInt("high_risk");
            }
        } catch (Exception e) {
            Http.sendJson(exchange, 500, "{\"error\":\"metrics query failed\"}");
            return;
        }

        Http.sendJson(exchange, 200,
                "{\"reviews_total\":" + reviewCount + ",\"reviews_high_risk\":" + highRiskCount + "}");
    }
}

risk-api/src/main/java/com/reachco/risk/handlers/RiskExposureHandler.java

package com.reachco.risk.handlers;

import com.reachco.risk.Database;
import com.reachco.risk.Http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.Duration;
import java.util.Map;

public class RiskExposureHandler implements HttpHandler {

    private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(3))
            .build();

    @Override
    public void handle(HttpExchange exchange) throws IOException {
        if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
            Http.sendJson(exchange, 405, "{\"error\":\"method not allowed\"}");
            return;
        }

        int orgId = (Integer) exchange.getAttribute("orgId");
        Map<String, String> params = Http.queryParams(exchange);
        String beneficiaryName = params.getOrDefault("beneficiaryName", "");
        String country = params.getOrDefault("country", "US");

        int totalScore = 0;
        int reviewCount = 0;
        int highRiskCount = 0;

        Connection conn = Database.get();
        try (PreparedStatement ps = conn.prepareStatement(
                "SELECT score, level FROM risk_reviews WHERE org_id = ?")) {
            ps.setInt(1, orgId);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    totalScore += rs.getInt("score");
                    reviewCount++;
                    if ("high".equals(rs.getString("level"))) {
                        highRiskCount++;
                    }
                }
            }
        } catch (Exception e) {
            Http.sendJson(exchange, 500, "{\"error\":\"exposure query failed\"}");
            return;
        }

        double avgScore = reviewCount == 0 ? 0.0 : (double) totalScore / reviewCount;

        String screeningResult = "unavailable";
        if (!beneficiaryName.isBlank()) {
            screeningResult = screenBeneficiary(beneficiaryName, country);
        }

        String json = "{" +
                "\"orgId\":" + orgId + "," +
                "\"reviewCount\":" + reviewCount + "," +
                "\"averageScore\":" + avgScore + "," +
                "\"highRiskCount\":" + highRiskCount + "," +
                "\"screening\":" + jsonQuote(screeningResult) +
                "}";

        Http.sendJson(exchange, 200, json);
    }

    // Calls out to the partner sanctions-screening service for the named
    // beneficiary so the exposure report can include an up to date hit
    // status alongside our own historical review data.
    private String screenBeneficiary(String beneficiaryName, String country) {
        try {
            String encodedName = URLEncoder.encode(beneficiaryName, StandardCharsets.UTF_8);
            String encodedCountry = URLEncoder.encode(country, StandardCharsets.UTF_8);
            URI uri = URI.create(
                    "http://risk-partner.internal.example/api/screen?name=" + encodedName +
                    "&country=" + encodedCountry);

            HttpRequest request = HttpRequest.newBuilder(uri)
                    .timeout(Duration.ofSeconds(3))
                    .GET()
                    .build();

            HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
            return "status=" + response.statusCode();
        } catch (Exception e) {
            return "error=" + e.getClass().getSimpleName();
        }
    }

    private static String jsonQuote(String value) {
        return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }
}

risk-api/src/main/java/com/reachco/risk/handlers/RiskReviewsHandler.java

package com.reachco.risk.handlers;

import com.reachco.risk.Database;
import com.reachco.risk.Http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;

public class RiskReviewsHandler implements HttpHandler {

    @Override
    public void handle(HttpExchange exchange) throws IOException {
        String method = exchange.getRequestMethod();
        if ("GET".equalsIgnoreCase(method)) {
            search(exchange);
        } else if ("POST".equalsIgnoreCase(method)) {
            create(exchange);
        } else {
            Http.sendJson(exchange, 405, "{\"error\":\"method not allowed\"}");
        }
    }

    // Lets risk analysts slice the review queue by level and free-text
    // search across the beneficiary name and notes fields.
    private void search(HttpExchange exchange) throws IOException {
        int orgId = (Integer) exchange.getAttribute("orgId");
        Map<String, String> params = Http.queryParams(exchange);

        StringBuilder where = new StringBuilder("org_id = ").append(orgId);

        String level = params.get("level");
        if (level != null && !level.isBlank()) {
            where.append(" AND level = '").append(level).append("'");
        }
        String minScore = params.get("minScore");
        if (minScore != null && !minScore.isBlank()) {
            where.append(" AND score >= ").append(minScore);
        }
        String q = params.get("q");
        if (q != null && !q.isBlank()) {
            where.append(" AND (beneficiary_name LIKE '%").append(q).append("%'")
                 .append(" OR notes LIKE '%").append(q).append("%')");
        }

        String sql = "SELECT id, transfer_ref, beneficiary_name, score, level, notes, reviewer, created_at " +
                "FROM risk_reviews WHERE " + where;

        Connection conn = Database.get();
        StringBuilder json = new StringBuilder("{\"reviews\":[");
        try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql)) {
            boolean first = true;
            while (rs.next()) {
                if (!first) {
                    json.append(",");
                }
                first = false;
                json.append("{")
                    .append("\"id\":").append(rs.getInt("id")).append(",")
                    .append("\"transferRef\":").append(quote(rs.getString("transfer_ref"))).append(",")
                    .append("\"beneficiaryName\":").append(quote(rs.getString("beneficiary_name"))).append(",")
                    .append("\"score\":").append(rs.getInt("score")).append(",")
                    .append("\"level\":").append(quote(rs.getString("level"))).append(",")
                    .append("\"notes\":").append(quote(rs.getString("notes")))
                    .append("}");
            }
        } catch (Exception e) {
            Http.sendJson(exchange, 500, "{\"error\":" + quote(e.getMessage()) + "}");
            return;
        }
        json.append("],\"query\":").append(quote(sql)).append("}");

        Http.sendJson(exchange, 200, json.toString());
    }

    private void create(HttpExchange exchange) throws IOException {
        int orgId = (Integer) exchange.getAttribute("orgId");
        String body = Http.readBody(exchange);

        String transferRef = Http.jsonField(body, "transferRef");
        String beneficiaryName = Http.jsonField(body, "beneficiaryName");
        String scoreStr = Http.jsonField(body, "score");
        String level = Http.jsonField(body, "level");
        String notes = Http.jsonField(body, "notes");
        String reviewer = Http.jsonField(body, "reviewer");

        if (transferRef == null || scoreStr == null || level == null) {
            Http.sendJson(exchange, 400, "{\"error\":\"transferRef, score and level are required\"}");
            return;
        }

        int score = Integer.parseInt(scoreStr.trim());

        Connection conn = Database.get();
        try (PreparedStatement ps = conn.prepareStatement(
                "INSERT INTO risk_reviews (org_id, transfer_ref, beneficiary_name, score, level, notes, reviewer) " +
                "VALUES (?, ?, ?, ?, ?, ?, ?)",
                Statement.RETURN_GENERATED_KEYS)) {
            ps.setInt(1, orgId);
            ps.setString(2, transferRef);
            ps.setString(3, beneficiaryName);
            ps.setInt(4, score);
            ps.setString(5, level);
            ps.setString(6, notes);
            ps.setString(7, reviewer == null ? "analyst" : reviewer);
            ps.executeUpdate();

            try (ResultSet keys = ps.getGeneratedKeys()) {
                long id = keys.next() ? keys.getLong(1) : -1;
                Http.sendJson(exchange, 201, "{\"id\":" + id + ",\"level\":" + quote(level) + "}");
            }
        } catch (Exception e) {
            Http.sendJson(exchange, 500, "{\"error\":" + quote(e.getMessage()) + "}");
        }
    }

    private static String quote(String value) {
        if (value == null) {
            return "null";
        }
        return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }
}