On-Premise Data Gateway
The on-premise gateway lets the SQL Database Reader query your database without exposing a database port to the internet, and without Qlar storing your database credentials.
You run a small open-source program next to your database. It makes outbound HTTPS calls to Qlar asking whether there is a query to run, runs it read-only, and sends the rows back. Nothing connects in.
Direct connection vs. on-premise gateway
You choose between the two on the first step of the plugin setup. Both answer the same questions with the same data; they differ in what your network has to allow and in what Qlar ends up holding.
| Direct connection | On-premise gateway | |
|---|---|---|
| Database port reachable from Qlar | Required | Not required |
| Inbound firewall rule | Required | None |
| Public DNS name or TLS certificate on your side | Sometimes | None |
| Database credentials held by Qlar | Yes | No โ they stay on your machine |
| Record of every query in your own systems | No | Yes โ local audit log |
| Table allow-list under your control | In the Qlar UI | Also in your own config file |
| Setup effort | Lower | One program to install and keep running |
Choose the gateway when your security team will not open a database port, or when credentials leaving your environment is the objection. Choose direct when neither is a concern โ it is fewer moving parts.
What the gateway protects, and what it does not
Worth being precise about, because an overstated control is worse than none.
It does protect:
- Network exposure. No inbound rule, no listening socket, nothing of yours reachable from the internet.
- Your credentials. They live only in the gateway's own configuration file. Qlar never receives them and has no way to read them.
- What may be executed. The gateway parses every statement and accepts exactly one read, refuses the file, shell and network escape hatches its dialect offers, and runs everything in a read-only transaction enforced by your database.
- Visibility. Every statement is written to a local audit log with the agent, user and conversation that caused it. The file is yours and nothing in the protocol can switch it off.
It does not protect:
- Query results still reach Qlar and the AI model. That is what the feature is for. Columns that must never leave your environment should not be in the allow-list.
- Anything an approved gateway is allowed to read, if Qlar itself were compromised. Your allow-list bounds that; your audit log reveals it.
- Access to the machine. Anyone who can read the gateway's
.envhas your database credentials, exactly as with any application config.
Before you start
- A machine on your network that can reach the database and make outbound HTTPS calls.
- Docker, or Python 3.11+.
- A database account with
SELECTon the tables the agent needs, and nothing more. The gateway always opens read-only transactions, but a read-only account is the guarantee that does not depend on software being correct.
Setting it up
1. Choose the gateway
In the plugin setup, on How should Qlar reach your database?, pick On-premise gateway. The Connection step is replaced by a Gateway step, which never asks for a host, username or password.
2. Get a one-time enrolment code
Click Connect a new gateway. Qlar shows a code such as K7P4-9WQX-2MTD, together with the commands to run โ including the two lines that tell the gateway where to send that code.
The code is single use and expires after 15 minutes. Qlar stores only a hash of it, so it cannot be shown again โ copy it before you leave the page. If you lose it, create a new one.
3. Install and configure
Download the gateway from github.com/pusakaai/data-gateway and create a .env next to it:
# Both of these come from the panel that showed you the code. QLAR_BASE_URL is the API
# endpoint, which is not the address of the Qlar web interface: that one serves a website,
# and a gateway pointed at it fails to enrol.
QLAR_BASE_URL=https://your-qlar-endpoint/api/data-gateway
QLAR_ENROLLMENT_CODE=K7P4-9WQX-2MTD
DB_PROVIDER=postgresql
DB_HOST=db.internal
DB_PORT=5432
DB_NAME=warehouse
DB_USER=qlar_readonly
DB_PASSWORD=...
Check the database before involving Qlar at all:
docker run --rm --env-file .env -v "$PWD/state:/state" \
ghcr.io/pusakaai/data-gateway:0.2.0 test-db
4. Start it
One command. enroll registers this machine, waits to be approved, and then starts serving โ there is nothing to come back and run afterwards.
docker run -d --name qlar-gateway --restart unless-stopped \
--env-file .env -v "$PWD/state:/state" \
ghcr.io/pusakaai/data-gateway:0.2.0 enroll
docker logs -f qlar-gateway
The gateway generates its own key pair โ the private half is written to gateway-key.pem and never leaves your machine โ registers only the public half, and prints a fingerprint to the log:
3A:7F:19:C4:...:C2
enrolled, waiting for approval in Qlar. Compare the key fingerprint above with the one
the CMS shows and click Approve; this starts working on its own, nothing else to run here.
Stopping docker logs does not stop the gateway, and running that same docker run line again after a restart re-uses the enrolment it already has rather than trying to redeem a code that was spent the first time.
5. Approve the fingerprint
Qlar now shows a card with the hostname that registered and the same fingerprint. Compare the two strings character for character, and approve only if they match.
This is the step that matters. Enrolling grants nothing on its own: until a person approves the fingerprint, the gateway receives no queries at all. It is what makes a leaked enrolment code useless โ whoever stole it would also have to get you to approve a fingerprint you have never seen.
If the fingerprint does not match what your machine printed, do not approve it. Cancel and issue a new code.
There is no step 6. Within a few seconds of the click the log says approved. Serving queries for ..., the gateway shows as online in Qlar, and you continue to configuring tables and columns exactly as with a direct connection.
Notice that no command on this page publishes a port. That is the point.
Hardening (recommended)
Set TABLE_ALLOWLIST in the gateway's .env to the tables the agent may read:
TABLE_ALLOWLIST=public.shipment_order,public.hub,public.customer
This list lives on your premises, so no change on Qlar's side can widen it. A query touching anything else is refused before the database sees it, and the refusal is recorded in your audit log.
Running it
| Task | What to do |
|---|---|
| Check it is alive | The gateway shows Connected / Online in the plugin's Gateway step. |
| See what has run | tail -f audit/queries.jsonl on the gateway machine. |
| Stop access immediately | Revoke in Qlar. The gateway stops itself on its next poll โ nothing to redeploy. |
| Upgrade | Pull a newer image and restart. Upgrades are on your schedule; Qlar keeps accepting the published protocol version. |
| Move it to another machine | Copy gateway-key.pem and .env. Without the key the fingerprint changes and you must enrol and approve again. |
Back up gateway-key.pem. It is the gateway's identity.
Restarting Qlar does not disconnect anything: there is no session to lose. A poll that fails is simply retried, and enrolment and approval are stored server-side.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Qlar rejected the enrolment code | Codes are single use and expire after 15 minutes | Generate a fresh one |
| Stays Waiting for your approval | Nobody approved the fingerprint yet | Click the card, or Review fingerprint on it, compare, and click Approve. This works even if the page that issued the code was closed. |
Every request fails with 401 | Clock skew beyond ยฑ2 minutes โ by far the most common cause | Fix NTP on the gateway machine |
this gateway has been revoked, then it exits | Revoked in Qlar | Delete gateway-state.json and enrol again |
| Questions fail with the data source is offline | The gateway is not running, or cannot reach Qlar | Start it; allow outbound 443 to your Qlar endpoint |
| A query is refused as not allowed | Working as designed | Add the table to TABLE_ALLOWLIST, or clear the list |
| Fingerprint changed after a redeploy | The key file was not persisted | Mount the state volume, then enrol and approve again |
The gateway's own install guide covers systemd and Kubernetes, and the protocol specification documents the wire contract in full โ it is published so you can audit it, or write your own gateway in another language.