RFX docs

Running RFX

Deployment

RFX runs on a server behind a Cloudflare tunnel: no port is opened on the host, Cloudflare provides HTTPS. docker-compose.prod.yml runs RFX, Postgres, the backup job, the tunnel and the demo's mock systems.

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

Database

Postgres is the system of record, and the only database. RFX keeps a working copy in memory, loaded at start, and writes back only the rows that changed: soon after each change, and on shutdown. One RFX process writes to a database.

Row-level security is on for every table and binds RFX's own role too: each query says on whose behalf it runs, and one that does not gets nothing and can write nothing. When a company reads its activity log, Postgres itself returns that company's rows only. To query as rfx_app by hand, open a transaction and run SELECT set_config('rfx.scope', 'system', true) first; the superuser (backups, restores) is not limited.

Encryption at rest

With RFX_DATA_KEY set (32 random bytes, base64; required on an https address), RFX encrypts with AES-256-GCM what it writes, before it leaves the process: the content of every database row, the activity log's company, agent, identity, address and arguments, the signed documents in R2 and vendors' files on disk. Postgres keeps only structure (collection names, sequence numbers, kinds, states, counts), so the database, its backups and the storage bucket hold nothing readable without the key. Each value is bound to its table and column, each document to its path: moved elsewhere, it does not open. Only the RFX container gets the key.

Backups and point-in-time recovery

deploy/pitr-restore.sh list                    # what can be restored
deploy/pitr-restore.sh 2026-11-14T10:30:00Z    # the database as it was then (UTC)
deploy/pitr-restore.sh latest                  # replay everything archived

The current data is kept beside the restored one, never deleted. If the server itself is lost, start the stack on a new one with the same .env (it must hold the same BACKUP_ENCRYPTION_KEY) and bring the backups back from R2 before restoring:

docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backup rfx-offsite list
docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backup rfx-offsite fetch

The monitor

monitor.rfx.market (or /monitor), for the operator with the admin key: uptime and memory; requests, refusals, server errors and response time for the last 24 hours, by hour and by route; the latest unexpected errors; the database (size, last write, write-ahead-log archiving); the nightly backup and the off-site copy, as the backup job reports them; mail, e-signature, the listings feed, payments; language-model calls and tokens by day and purpose; and the marketplace (listings, companies, introductions, NDAs, deals, fees due and paid). It refreshes every 15 seconds. Counts restart with the process; the activity log keeps the record.

The graph

/graph: the marketplace as a graph. The operator, with the admin key, sees every company, agent, person, product, need and step and how they connect; a person sees their own company's part, with other companies by name only. A flat view to explore (find, families, a list of every node) and a 3D view. The buying side finds vendors from it, by name, product, category or capability, and adds them to its graph: the public listing for every vendor, and which vendors claim a capability by name; whether they support it once the NDA with that vendor is signed (a dashed line until then). Finding starts no introduction and no fee. Colour shows the family and shape the kind, in one of three palettes checked for colour blindness. Its scripts are served from /js (public/js, licences in LICENSES.txt), never from another site.

Error reporting and alerts

With RFX_ALERT_EMAIL set, unexpected errors and problems with the database or the backups are mailed there through the mail service, at no cost: the same problem at most once an hour, at most 12 alerts an hour in all. RFX checks the backups itself every 10 minutes (a late or failed off-site copy, a nightly backup older than a day, failing archiving or database writes).

With SENTRY_DSN set, unexpected errors go to Sentry with their stack, and nothing about the request (no body, cookies, headers or addresses). With SENTRY_CRON_BACKUP and SENTRY_CRON_OFFSITE set to Sentry Crons check-in URLs, Sentry raises an alert when a nightly backup or an off-site copy fails or does not happen. A Sentry uptime monitor on the public address covers the site being down.

Logs

Each request is one JSON line on standard output (time, method, path, status, duration, caller); errors carry their code. docker compose logs rfx-core shows them.