Skip to main content

Edge Node install reference

Audience: PSP infrastructure or platform engineer who installs an Edge Node on the PSP's own servers.

Product term: Edge Node = edge-host/ + edge-engine/ + local Aerospike + the Edge Ledger (PostgreSQL 16). See docs/SYSTEM-GLOSSARY.md.

This is not: the Console or the Control Plane. Those are separate processes (README).

Normative contract: docs/EDGE_DATA_RESIDENCY_CONTRACT.md.

Preferred client path: mint a Console install token and run the public one-liner in 06 — Public Edge install. This document remains the deep reference for ledger, mTLS, classic enrollment codes, and ops.


What you are installing​

The Edge Node is the only component that evaluates the PSP's transactions. It runs inside the PSP estate.

  • PSP API nodes call POST /edge/evaluate over TLS on port 8443. That is the only call they make. See docs/edge-transaction-evaluation.md.
  • Every evaluation is written to the Edge Ledger, a PostgreSQL 16 database on your site. The ledger is the system of record for transaction data. Back it up.
  • Aerospike holds only hourly velocity buckets (26 h TTL) and the rule bundle. It no longer stores transactions.
  • The Edge sends decision records to the Control Plane: IDs, action, score and rule IDs. No PAN, amount or feature values. The Control Plane raises alerts and cases from them.
  • The Edge also sends aggregate metrics and pulls the signed rule bundle.
  • Every connection is opened by the Edge, outbound on TCP 443. You open no inbound port to the internet.

Also built and running on every Edge Node (work package status):

  • Edge config. The Edge pulls the sealed Edge config document (retention, support access, cross-PSP opt-in) from GET /api/v1/edge/config (edge-host/.../residency/EdgeConfigPoller.java).
  • Edge Tunnel. The Edge opens an outbound WebSocket tunnel (WSS on 443) to /api/v1/edge/tunnel, and the Console reads transaction details live from your ledger over it (edge-host/.../tunnel/EdgeTunnelClient.java). While the tunnel is down, the Console shows the Edge as offline and cannot show transaction details.
  • Retention purge. A daily job drops ledger data older than your retention period. Data under an active legal hold is kept (edge-host/.../residency/RetentionPurger.java, LegalHoldService.java). Nothing is purged until the Edge has accepted a config document.
  • mTLS client certificate. At enrolment the Edge sends a CSR, and the Hokeka Edge CA issues its certificate. The Edge renews it before expiry (see Portal approval and pinned keys).

Prerequisites​

From Console (before touching the server)​

  1. A Hokeka operator creates the Edge Node in Console → Edge Nodes (Setup Wizard).
  2. Collect from the wizard:
    • Numeric PSP id (for example 42). Not a slug. Activation returns 202 (PENDING_APPROVAL) while the node awaits approval, and 403 on a --pspid mismatch, an unknown, used or expired code, or a PSP that is not ACTIVE.
    • Edge id (for example acme-eu-1).
    • Enrollment code (single use, time limited).
    • Control Plane edge URL (normally https://edge.hokeka.com).
  3. Copy the install command from the wizard. Do not compose --pspid by hand.

Console installation is documented separately: 02 — Console. You only need Console access for enrollment.

Retention​

A PSP admin sets retention in Console → Settings → Data Retention.

  • Minimum and default: 5 years (FATF R.11). Maximum: 25 years.
  • Changes go through maker-checker. A reduction takes effect 30 days after approval.
  • The Control Plane publishes the setting in the sealed Edge config document. The Edge reads it and purges to it daily.
  • It is not an installer setting. --retention-days is accepted and ignored with a warning.

Ledger disk sizing (5 years)​

Size the ledger disk for at least 5 years. install.sh --peak-tps N prints this estimate at the start of the install:

records = N × 86 400 × 365 × 5
disk = records × 2 048 bytes ÷ 2^30 × 1.3 (GiB, 30% headroom)
Peak TPSEvaluations in 5 yearsLedger disk (approx.)
1157 M390 GiB
5788 M1 953 GiB
101 576 M3 909 GiB
507 884 M19 548 GiB
10015 768 M39 097 GiB

This is an upper bound. It assumes peak TPS for 24 hours a day, every day. 2 048 bytes per evaluation is an estimate (row, indexes, outbox row, Postgres overhead). Measure real volumes in a pilot before you buy storage. If you keep data longer than 5 years, scale the numbers by years ÷ 5. install.ps1 has no -PeakTps option; use the formula above on Windows.

If the ledger disk fills, the node cannot record evaluations and answers HOLD (fail-closed).

Server requirements (summary)​

ConcernGuidance
Ledger diskSee the sizing table above. Put the Postgres data volume on durable, backed-up storage.
AerospikeVelocity buckets only (26 h TTL). The shipped data file size is enough; --data-file-size overrides it.
OSLinux (container or native) or Windows Server (container only). Full matrix: edge-client-install-guide §2.1.
Network inboundNone from the internet. 8443/tcp only from your own PSP API nodes. The API binds to 127.0.0.1 by default; use --bind and --allow-subnet to open it to your subnet.
Network outboundTCP 443 to edge.hokeka.com (ongoing, mTLS). TCP 443 to packages.hokeka.com (install and upgrade only). No other egress. No TLS interception on the Control Plane path.
AccessRoot (Linux) or Administrator (Windows).
BackupBack up the Edge Ledger (the PSP's multi-year system of record) and /opt/hokeka/data/ (node identity keys). If they are lost the node must re-enrol as a new node with a new edge id; the old edge id returns 409.

More sizing, disk and network detail: docs/edge-client-install-guide.md §2–3.


Choose deployment mode​

ModeCommand flagLedgerBest for
Container (default)(none)ledger service (postgres:16-alpine) on the private network, no published portAlmost all estates
Native--nativePostgreSQL 16 installed from the distro or PGDG, bound to 127.0.0.1, port 5432 firewalledLinux estates that prohibit containers
Windowsinstall.ps1ledger container, as in container modeWindows Server (container path only)

Multi-node Edge Site (shared ledger)​

Several nodes of one PSP can share one ledger and one Aerospike cluster. That group is an Edge Site.

  • Linux: --ledger-url jdbc:postgresql://site-db:5432/hokeka_ledger --ledger-user <user> --site-id <site>
  • Windows: -LedgerUrl jdbc:postgresql://site-db:5432/hokeka_ledger -LedgerUser <user> -SiteId <site>
  • Put the database password in the HOKEKA_LEDGER_PASSWORD environment variable. The installers never take it on the command line.
  • With a shared ledger the installer does not start a local ledger service.
  • Without --site-id the node is a single-node site whose site id is its edge id.

On a fresh Linux or Windows host, install prerequisites first with the unified bootstrap (07 — First-run bootstrap):

sudo ./scripts/install/hokeka-install.sh --role edge --yes -- \
--pspid <PSP_ID> --edgeid <EDGE_ID> --controlplane <URL> --enrollment-code <CODE>

Installation steps​

Step 1 — Download and review the installer​

curl -fsSL https://packages.hokeka.com/edge/install.sh -o install.sh
less install.sh # read before running as root

Windows: get install.ps1 from the same origin or from Hokeka support.

Step 2 — Run install (use wizard-generated values)​

Linux — container (recommended):

sudo bash install.sh \
--pspid 42 \
--edgeid acme-eu-1 \
--controlplane https://edge.hokeka.com \
--enrollment-code ABCD-1234 \
--bind 10.0.0.5 \
--allow-subnet 10.0.0.0/24 \
--peak-tps 20

Linux — native:

sudo bash install.sh --native \
--pspid 42 --edgeid acme-eu-1 \
--controlplane https://edge.hokeka.com \
--enrollment-code ABCD-1234

Windows (Administrator PowerShell):

.\install.ps1 -PspId 42 -EdgeId acme-eu-1 `
-ControlPlane https://edge.hokeka.com -EnrollmentCode ABCD-1234

Production change control — pin the version:

sudo bash install.sh --version 1.2.3 ...

Other flags: --channel beta, --repo-url URL, --offline, --peak-tps N, --tls-client-auth need with --tls-truststore FILE (see Mutual TLS from your API nodes), --data-file-size 16G, --ledger-url, --ledger-user, --site-id.

--pspid (-PspId on Windows) is required and must be the numeric PSP id (^[1-9][0-9]*$, for example 42). The installers stop with a clear error on anything else, the edge compose file refuses to start without EDGE_PSPID, and the Edge itself refuses to boot when EDGE_PSPID is not a positive integer.

Step 3 — What the installer does​

  1. Detects distro, package manager, CPU architecture and libc.
  2. Downloads artifacts and verifies the GPG-signed SHA256SUMS against a key pinned in the script.
  3. Installs prerequisites: a container runtime, or Temurin JRE 21 + Aerospike CE + PostgreSQL 16 in native mode.
  4. Isolates the databases. Container mode: Aerospike and the ledger have no published port. Native mode: both bind to 127.0.0.1, and ports 3000 and 5432 are firewalled.
  5. Generates a random ledger password into secrets/edge-ledger.pass (mode 600) and reuses it on re-runs. It does the same for the mTLS client keystore password (secrets/edge-mtls.pass, EDGE_MTLS_KEYSTORE_PASSWORD).
  6. Generates a self-signed TLS keystore (mode 600). The engine refuses to start without TLS.
  7. Writes .env (container) or /opt/hokeka/config/edge.env (native), mode 600, including EDGE_LEDGER_URL, EDGE_LEDGER_USER, EDGE_LEDGER_PASSWORD, EDGE_LEDGER_FAIL_CLOSED=true and EDGE_SITE_ID.
  8. Starts the service and checks https://127.0.0.1:8443/actuator/health.

The edge runs its own ledger schema migrations at start-up. If the ledger is down, the node still starts, retries the connection, and answers HOLD with failClosedCode = LEDGER_UNAVAILABLE until the ledger is writable.

On a signature failure, stop. Do not use --insecure-skip-signature. Contact Hokeka.

Installer internals: docs/INSTALL.md §2.2, edge-host/deploy/README.md.


Post-installation​

1. Replace the TLS certificate (required for production)​

The installer creates a self-signed certificate. Replace it with a PKCS#12 from your CA:

keytool -importkeystore \
-srckeystore your-ca-issued.p12 -srcstoretype PKCS12 \
-destkeystore /opt/hokeka/secrets/edge-tls.p12 -deststoretype PKCS12 -alias edge
# Update EDGE_TLS_KEYSTORE_PASSWORD in .env, then restart the service / docker compose

Mutual TLS from your API nodes​

POST /edge/evaluate can require a client certificate from each of your API nodes (Spring Boot server.ssl.client-auth=need). You need a PKCS#12 truststore holding the CA that issued your API nodes' client certificates, as a trusted-certificate entry (a store that only holds a key entry gives the server no trust anchors and the edge fails to start):

keytool -importcert -noprompt -alias psp-client-ca -file psp-client-ca.pem \
-storetype PKCS12 -keystore psp-client-ca.p12 -storepass '<truststore password>'
# Linux: the truststore password is read from the environment, never from the command line
sudo HOKEKA_TLS_TRUSTSTORE_PASSWORD='<truststore password>' bash install.sh \
--pspid 42 --edgeid acme-eu-1 --controlplane https://edge.hokeka.com \
--tls-client-auth need --tls-truststore /path/to/psp-client-ca.p12
# Windows
$env:HOKEKA_TLS_TRUSTSTORE_PASSWORD = '<truststore password>'
.\install.ps1 -PspId 42 -EdgeId acme-eu-1 -ControlPlane https://edge.hokeka.com `
-TlsClientAuth need -TlsTruststore C:\path\psp-client-ca.p12

The installer copies the file to secrets/psp-client-ca.p12 (mode 600; the container sees it as /opt/hokeka/secrets/psp-client-ca.p12), keeps the password in secrets/psp-client-ca.pass, and writes:

VariableValue
EDGE_TLS_CLIENT_AUTHneed (or want to accept but not require a certificate)
SERVER_SSL_TRUST_STORE/opt/hokeka/secrets/psp-client-ca.p12 (native: <data-dir>/secrets/psp-client-ca.p12, default data dir /opt/hokeka)
SERVER_SSL_TRUST_STORE_PASSWORDthe truststore password
SERVER_SSL_TRUST_STORE_TYPEPKCS12

edge-host/deploy/docker-compose.yml passes all four into the edge container; blank values mean no truststore. With --tls-client-auth want|need and no truststore the installer stops, because every client certificate would otherwise be rejected. To set it up by hand, place the file in secrets/, set the four variables in .env, and run docker compose up -d. Check it with curl --cert api-node.pem --key api-node.key --cacert edge-ca.pem https://<edge>:8443/actuator/health; the same call without --cert must fail the TLS handshake.

2. Portal approval and pinned keys​

Until a Hokeka platform admin approves the node in the Console:

  • GET /edge/status returns "authorization": "unauthorized"
  • POST /edge/evaluate returns HOLD (fail-closed)
  • No rule bundle is served

The node can only activate after approval: POST /api/v1/edge/enroll for a node that is not APPROVED returns 403. Approve it within the code's 24 h life, or reissue the code with POST /edge/nodes/{id}/enrollment-code.

The Control Plane public keys (EDGE_BUNDLE_SIGNING_PUBLIC_KEY, EDGE_METRICS_INGEST_PUBLIC_KEY) are pinned at enrolment: when .env (native: edge.env) leaves CONTROLPLANE_ED25519_PUBLIC_KEY and CONTROLPLANE_X25519_PUBLIC_KEY blank, the first successful POST /api/v1/edge/enroll (after approval, over the authenticated HTTPS call) stores both in controlplane-keys.json beside the identity in /opt/hokeka/data/ and every later start uses them (edge-host/.../activation/ActivationService.java). Keys set in .env always win, a pinned key is never replaced, and a different key in a later response is logged as an error, not adopted. For a stricter install, ask the Hokeka platform operator for the two values (KEY_MANAGEMENT.md) and set them before the first start:

CONTROLPLANE_ED25519_PUBLIC_KEY=<EDGE_BUNDLE_SIGNING_PUBLIC_KEY from the platform operator>
CONTROLPLANE_X25519_PUBLIC_KEY=<EDGE_METRICS_INGEST_PUBLIC_KEY from the platform operator>

The Control Plane must run with persistent values of those keys: a production profile refuses to start without them, and a development Control Plane that generated ephemeral keys breaks every enrolled Edge on its next restart (bundle signatures and metrics seals stop verifying; the tunnel closes with 4401).

Without the Ed25519 key every bundle poll fails before it starts, no bundle loads, and every evaluation answers HOLD NO_BUNDLE.

You do not install an mTLS client certificate by hand. Certificate issuance is automatic:

  • Enrolment. The Edge generates its own EC P-256 key pair and a PKCS#10 CSR (CN=<edgeId>, OU=psp-<pspId>) and sends it with POST /api/v1/edge/enroll. The Control Plane signs it with the Hokeka Edge CA (BACKEND/.../service/edge/pki/EdgeCertificateAuthority.java, recorded in edge_client_certificates, V274).
  • Renewal. A scheduled job (edge-host/.../activation/ClientCertificateManager.java) sends a fresh CSR to POST /api/v1/edge/certificate/renew over the current mTLS identity once the certificate is inside its renewal window. The defaults are 90-day certificates renewed 30 days before expiry (EDGE_CA_CERT_VALIDITY, EDGE_CA_RENEW_BEFORE on the Control Plane).
  • Revocation. Revoking the node in the Console revokes its certificates. They appear in the CRL (GET /api/v1/edge/ca/crl), which nginx enforces with ssl_crl.

The private key never leaves the Edge. The Edge stores the key and certificate in EDGE_MTLS_KEYSTORE (default /opt/hokeka/data/mtls/edge-client.p12, on the writable data volume), protected by the installer-generated EDGE_MTLS_KEYSTORE_PASSWORD. The Edge renews the certificate automatically before it expires. Keep secrets/edge-mtls.pass: without it the stored certificate cannot be opened and the node must re-enrol.

3. Verify status​

curl -sk https://127.0.0.1:8443/edge/status | jq
FieldRequired
authorizationNot unauthorized
evaluatornative (not fallback-java-interpreter)
featureStoreNot unavailable (velocity rules need Aerospike)
ledger.stateOK (DOWN or NOT_CONFIGURED means every evaluation answers HOLD)
ledger.outboxBacklogSmall and falling (decision records waiting to ship)
ledger.outboxQuarantined0 (records the Control Plane kept refusing; kept in the ledger, report to support)
healthUP

4. Test evaluate​

curl -sk https://127.0.0.1:8443/edge/evaluate \
-H 'Content-Type: application/json' \
-d '{"txn_id":"test-1","pan_hash":"<hash>","amount_cents":1000,
"currency":"KES","country_code":"KE","mcc":"5411","merchant_id":"m-1"}' | jq

Field names are snake_case. See docs/edge-transaction-evaluation.md.

txn_id must not look like a card number. A 13–19-digit value that passes the Luhn check gets 422 TXN_REF_LOOKS_LIKE_PAN, and nothing is evaluated or stored.

5. Wire your API nodes​

Your API nodes call /edge/evaluate before authorisation and act on the decision. Nothing else is needed for cloud alerts and cases: the Edge ships a decision record for each evaluation, and the Control Plane raises alerts and cases from it.

The older cloud ingest endpoint (POST /api/v1/transactions/ingest) still works for existing integrations. It stores transaction data in the cloud, which the residency model forbids, and R12 deletes it; that work is still pending (work package status). Do not build new integrations on it. The superseded contract is archived at docs/archive/EDGE_AND_CLOUD_DUAL_POST_CONTRACT.md.

Hokeka AI recommendation​

The installer does not install a local AI service. Every Hokeka AI recommendation is produced on the Control Plane. Edge Nodes never hold AI credentials.

The Edge does not request a Hokeka AI recommendation. The former request for borderline decisions (POST /api/v1/edge/decision) carried the evaluation's feature values and was removed in R10c, so no transaction features leave the Edge. A future recommendation will use decision records only, and will stay advisory: it will never change the action or score the Edge returned.


Operations​

Day-to-day​

systemctl status hokeka-edge # native
journalctl -u hokeka-edge -f
docker compose ps && docker compose logs -f edge # container
docker compose logs -f ledger # container, local ledger

Monitor​

From the Control Plane (Hokeka operators, PSP admins). Console > Edge Nodes shows each node's health (HEALTHY, STALE after 5 minutes without a poll, NEVER_SEEN, AWAITING_APPROVAL, …), last seen time, bundle version and agent version. The Control Plane's liveness monitor (EdgeLivenessMonitor) checks every minute: an ACTIVE node of an active PSP that has not polled for EDGE_MONITOR_SILENT_AFTER (10 minutes default) is audited EDGE_NODE_SILENT and emailed once per outage to the PSP's billing/contact address with EDGE_MONITOR_ALERT_EMAIL (Hokeka operators) copied; the first poll after that sends one "reachable again" notice (EDGE_NODE_RECOVERED). The gauge hokeka_edge_nodes_silent on the Control Plane's Prometheus endpoint carries the count for paging. Decision records and alerts from the node appear in Alerts and Cases as usual; ledger.outboxBacklog growing on the node means records are queued.

On the node (PSP operations).

SignalAction
evaluator ≠ nativeNative core failed to load. Check /opt/hokeka/lib/.
featureStore: unavailableAerospike is down. Velocity rules are disabled.
Evaluations answer HOLD with LEDGER_UNAVAILABLEThe ledger is down or full. Check the ledger service (or PostgreSQL 16) and its disk.
Evaluations answer HOLD with NO_BUNDLENo verified rule bundle: check outbound HTTPS to the Control Plane and the pinned keys (step 2).
Evaluations answer HOLD with LICENCE_SUSPENDEDAn invoice is unpaid past the suspension day (BILLING_LICENCE.md).
ledger.outboxQuarantined > 0The Control Plane kept refusing some decision records; they stay in the ledger. Report to support with /edge/status.
Ledger disk usageCompare with the sizing table. The daily retention purge keeps usage near the retention-period size; legal holds add to it.
authorization changeThe node was de-authorised. All traffic answers HOLD.
Certificate expiryServer certificate (yours to renew) and mTLS client certificate (renewed automatically; a renewal failure is logged as a warning by ClientCertificateManager).
Evaluate latencyEvery evaluation is a durable ledger commit, so commit-flush time adds to p99. Put the ledger on local SSD/NVMe; network block storage with a 2–3 ms flush adds that much to every answer.

Upgrades​

curl -fsSL https://packages.hokeka.com/edge/install.sh | sudo bash -s -- --version 1.3.0 ...

Re-running is idempotent. Identity in /opt/hokeka/data/, the ledger password and the ledger data are kept. Roll back by re-installing a prior --version. There is no automatic canary: upgrade one node, verify step 3, then the rest.

Artifacts come from 04 — Packages CDN.


Uninstall​

# native
sudo systemctl disable --now hokeka-edge
sudo rm /etc/systemd/system/hokeka-edge.service && sudo systemctl daemon-reload

# container (keeps volumes, including the ledger)
cd /opt/hokeka && docker compose down

docker compose down -v also deletes the ledger-data volume, which holds the PSP's transaction records. Back up the ledger and /opt/hokeka/data/ first, and only delete them when your retention obligations allow it.


Troubleshooting​

SymptomDoc
Install, signature, ledger or activation failuresdocs/edge-install-troubleshooting.md
Quick symptom tabledocs/edge-client-install-guide.md §8

Collect for support: /edge/status JSON, the last hour of service logs, the installed version, uname -a, the edge id.