Self-hosted Tebex Bridge
Run a small open-source proxy on your own server so your Tebex API keys never leave your infrastructure, while keeping every Tebex feature in Joely.
By default, Joely stores your Tebex API keys encrypted on its servers (see Tebex keys & security). If you prefer your keys to never leave your own infrastructure, you can run the Tebex Bridge instead: a small, open-source proxy that holds your keys on your server. Joely then calls your bridge, never the Tebex API directly.
How it works
Without a bridge (default)
With a self-hosted bridge
- You run the bridge (a ~400-line Node.js server) on your own VPS or host. Your Tebex keys live in its
.envfile. - In Joely, you set your bridge URL and a shared secret on the store.
- Every Tebex feature (packages, order lookup, purchase history, coupons, gift cards) now flows through your bridge. The bridge injects your Tebex keys and forwards the request to Tebex.
- The bridge strips buyer PII: name, email, IP address, country, postal code, and the buyer's player profile are removed from payment and purchase-history responses before they ever reach Joely. Only what Joely displays passes through: transaction data and the buyer's webstore username.
The bridge source code is public and intentionally small so you can audit it yourself: every route it exposes mirrors the official Tebex API documentation, and any other path returns 404. It cannot be used as an arbitrary proxy.
What the bridge sends to Joely
Joely only needs transaction data (ids, statuses, prices, currency, dates, and product names) plus the buyer's webstore username to label an order. It never needs the buyer's real-world identity. So before forwarding a Tebex response, the bridge runs a small JSON sanitizer that removes everything else. Two responses carry buyer PII and are cleaned:
Order details (Checkout API). The bridge keeps the transaction itself untouched (id, status, amounts, currency, fees, dates, product names) and reduces the buyer block to the single webstore username. Removed before sending: first and last name, email, IP address, country, postal code, marketing-consent flag, and any gift-recipient username on a line item. The buyer block is an allowlist: only the username can ever pass, so if Tebex adds a new identifying field tomorrow, it is dropped automatically.
Purchase history (Plugin API). The bridge keeps only the list of payments (each with its transaction id, date, price, currency, and status). Removed before sending: the buyer's player profile and their behaviour stats (ban count, chargeback rate, lifetime purchase totals).
Every other request (store information, your package catalog, and the coupons and gift cards Joely creates) carries no buyer PII and is forwarded as-is. The sanitizer only ever drops fields; it cannot add or expose more than Tebex returned.
Setting it up
1. Deploy the bridge
Clone the tebex-bridge repository, copy .env.example to .env, and fill in your Tebex keys:
| Variable | Required | Purpose |
|---|---|---|
TEBEX_PUBLIC_KEY | Yes | Package catalog and store info (Headless API) |
JOELY_SHARED_SECRET | Yes | Authenticates Joely's requests (you generate it in step 2) |
TEBEX_GAME_SERVER_SECRET_KEY | Optional | Purchase history, coupons, gift cards (Plugin API) |
TEBEX_PRIVATE_KEY | Optional | Order detail lookup (Checkout API); the store ID is resolved automatically from your public key |
Run it with Docker or Node 20+, and expose it over HTTPS (a reverse proxy like Caddy or a Cloudflare Tunnel works well); Joely rejects http:// bridge URLs. The optional keys behave exactly like in Joely: leave one out and the features it powers are simply unavailable.
2. Connect it in Joely
You can connect a bridge in two places:
During onboarding: on the Tebex step, switch the toggle from Tebex API keys to Self-hosted, enter your bridge URL and shared secret, then continue. Joely checks that the bridge is reachable, that the shared secret matches, and reads the store identity through the bridge. Your project is created with the bridge already configured, with no Tebex key ever passing through the form.
From settings: in Settings → Tebex, click Add store and switch the toggle from Tebex API keys to Self-hosted:
- Enter your bridge URL (for example
https://bridge.yourdomain.com). - Click Generate to create the shared secret, and copy it into your bridge's
JOELY_SHARED_SECRETenvironment variable. - Restart your bridge, then click Test connection. Joely checks that the bridge is reachable, that the shared secret matches, and shows which optional Tebex keys your bridge holds in the "What you unlock" checklist.
- Click Connect store. The store is created with the bridge already configured.
The settings flow requires the Add Tebex stores permission. Stores using a bridge show a Self-hosted badge in settings.
The connection method is fixed at creation: to switch an existing store between API keys and a bridge, disconnect the store and reconnect it with the other method.
Security model
- Every request from Joely to your bridge is signed with HMAC-SHA256 (timestamp, method, path, and body), with a 5-minute anti-replay window. Requests without a valid signature are rejected.
- The shared secret is stored encrypted in Joely with the same envelope encryption as Tebex keys, and is never returned by the API after saving.
- Buyer PII is stripped by the bridge before responses leave your server; Joely never receives the removed fields. See What the bridge sends to Joely for the exact kept-vs-removed breakdown per response.
What you are responsible for
Self-hosting moves the keys, and the uptime, to you:
- Availability: if your bridge is down, real-time Tebex features (order lookup, purchase history, coupon and gift card creation, package refresh) fail for that store until it is back. Already-synced package data stays available, and stores with other connection methods keep working; only the bridge store is skipped. When this happens, your staff see a persistent "Bridge disconnected" notification on the dashboard naming the affected store, and customers on the portal see a notification asking them to contact you so you can reconnect it (both stay on screen until clicked). Point your monitoring at
GET /v1/healthon your bridge. - HTTPS: the bridge must be served behind TLS; Joely refuses to save an
http://bridge URL. The signature protects authenticity, not confidentiality. - Updates: when Joely ships new Tebex features, your bridge may need an update to expose the new routes. Update with a simple
git pulland restart.
Bridge or default storage?
The default (keys stored encrypted by Joely) is the right choice for most shops: AES-256-GCM envelope encryption on hardened infrastructure is typically stronger than a .env file on a personal VPS. Choose the bridge when key custody matters to you, when you want to be able to say, and verify, that your Tebex keys never touch a third party's servers.