Administrator Guide · Version 1.1
SQL Studio Gateway
This guide is for the IT administrators and DBAs who install and run SQL Studio Gateway. It covers planning, licensing, installation on Windows Server, Linux and Docker, the TLS certificate, gateway users, the list of allowed servers, every configuration setting, connecting the SQL Studio apps, and day-to-day operations.
gateway.contoso.local is the gateway's DNS name and sql01.contoso.local is a SQL Server instance. Replace them with your own names. SQLS-XXXXX-XXXXX-XXXXX-XXXXX stands for the license key you received by email.1. Overview
How it works
SQL Studio for Mac, iPhone and iPad never connects to SQL Server directly. The app talks only HTTPS/JSON to SQL Studio Gateway, a .NET 10 service that you install on your own server. The gateway does all the work against SQL Server (TDS connections, SMO, DacFx, ScriptDom) on behalf of the app.
Mac · iPhone · iPad Your SQL estate
┌────────────────────┐ HTTPS (TLS 1.2/1.3) ┌──────────────────────────┐ TDS (Encrypt) ┌────────────────┐
│ SQL Studio app │ ──────────────────────▶ │ SQL Studio Gateway │ ─────────────▶ │ SQL Server │
│ (App Store) │ port 8443, certificate │ .NET 10 service │ port 1433 │ Azure SQL │
│ Keychain, sandbox │ pinning, signed tokens │ users · allow-list · │ allow-list └────────────────┘
└────────────────────┘ │ audit · background tasks │
└────────────┬─────────────┘
│ HTTPS 443 (license check)
▼
sqlstudio.geolox.itThe gateway:
- Authenticates people. Each colleague gets a personal gateway account with a strong password and optional two-step verification (TOTP). Gateway accounts are separate from SQL Server logins.
- Decides which servers can be reached. Every connection is checked against the list of allowed servers that you configure, before any network socket is opened.
- Keeps SQL credentials out of storage. SQL Server passwords and Microsoft Entra tokens stay in the gateway's memory only for the lifetime of the connection and are never written to disk or to logs.
- Runs long operations on the server. Backups, restores, data-tier (DAC) operations, script generation and imports run as gateway tasks and continue even if the user closes the app.
- Records who did what. Sign-ins, connections, uploads and tasks are written to a daily audit log.
- Hides your SQL Servers. Users reach only the gateway's HTTPS port; the SQL Servers do not need to be reachable from user devices.
The gateway listens only on HTTPS (TLS 1.2 or 1.3) and never on plain HTTP. Rights inside SQL Server are always those of the SQL login or Entra identity used to connect.
What's in the package
You receive SQLStudio-Gateway-<version>.zip, a framework-dependent .NET 10 build. The same files run on Windows and Linux.
| File | Purpose |
|---|---|
SqlStudio.Gateway.exe | Windows launcher. |
SqlStudio.Gateway.dll and the other *.dll files | The gateway. On Linux run it with dotnet SqlStudio.Gateway.dll. |
appsettings.json | Built-in defaults. Do not edit: it is replaced on every update. |
appsettings.Production.example.json | Template. Copy it to appsettings.Production.json and put your settings there. |
GUIDE.html | Offline copy of this guide. |
extras/install-windows-service.ps1 | Installs the gateway as a Windows service (see section 5). |
extras/sqlstudio-gateway.service | systemd unit for Linux (see section 6). |
extras/Dockerfile | Builds a container image from the extracted package (see section 7). |
The data directory
All persistent state lives in one folder, set by Gateway:DataDirectory. This guide uses C:\ProgramData\SqlStudioGateway on Windows, /var/lib/sqlstudio-gateway on Linux and the volume mounted at /data in Docker.
| Item | Contents |
|---|---|
license.json | License key, installation id and the last signed answer of the license server. |
users.json | Gateway users (Argon2id password hashes, encrypted TOTP secrets, lockout state). |
refresh-tokens.json | SHA-256 hashes of active and used refresh tokens (never the tokens themselves). |
jwt-signing.key | 64-byte token signing key, encrypted with ASP.NET Core Data Protection. |
keys/ | Data Protection key ring (on Windows also encrypted with DPAPI for the service account). |
gateway.pfx + gateway.pfx.pwd | Certificate created with cert create and its random, encrypted password. |
audit/audit-YYYYMMDD.jsonl | Audit log, one JSON line per event. |
uploads/, tasks/, temp/ | Temporary files, deleted automatically. |
The gateway creates the folder with permissions reserved to the account that runs it (explicit ACL on Windows, 700/600 on Linux). It contains keys: treat the folder and its backups as secrets.
license, users, cert) as the same account that runs the service. On Windows the keys are protected with DPAPI for that account: files created by another account cannot be decrypted by the service.2. Before you start
Requirements
| Component | Requirement |
|---|---|
| Operating system | Windows Server 2019 or later, Windows 10 or later, Linux x64 or arm64 (glibc), or a Docker host. |
| Runtime | ASP.NET Core Runtime 10.0 from dotnet.microsoft.com/download/dotnet/10.0, on Windows and Linux. On Windows either the Hosting Bundle or the ASP.NET Core Runtime x64 installer works. Not needed for Docker (the image contains it). |
| Network | Devices reach the gateway on TCP 8443 (configurable). The gateway reaches SQL Server on TCP 1433 or the configured port. The gateway reaches the license server on TCP 443. |
| License | A license key (SQLS-XXXXX-XXXXX-XXXXX-XXXXX) for each gateway installation. See section 4. |
| Apps | SQL Studio from the App Store: Mac with macOS 26 or later; iPhone and iPad with iOS/iPadOS 26 or later (the iPad app also runs on Mac with Apple silicon). |
Planning checklist
Decide the following before you install. Each item links to the section that explains it.
| Item | Decide or prepare | Example |
|---|---|---|
| Server | A dedicated, patched host (or container host) that can reach your SQL Servers. Run only one gateway instance per data directory: users and tokens are not replicated between nodes. | Windows Server 2022 VM |
| DNS name | A stable name for the gateway, with an A/AAAA record in your internal DNS. Users type it in the app; it must be in the certificate. | gateway.contoso.local |
| Certificate | Self-signed certificate created by the gateway, verified in the app by its SHA-256 fingerprint (recommended), or a certificate from your corporate CA with the DNS name in the Subject Alternative Name. See section 8. | cert create --host gateway.contoso.local |
| Allowed servers | The SQL Server instances users may connect to, written in the form users will type them. See section 10. | sql01.contoso.local, *.database.windows.net |
| Service account | The low-privilege account that runs the gateway. Windows: local account SqlStudioGateway (created by the install script) or a gMSA in a domain. Linux: system user sqlstudio. Docker: non-root user app. | .\SqlStudioGateway |
| Authentication types | Which SQL authentication methods users need. Methods that use the gateway's own identity (Windows Authentication, Entra Integrated, Managed Identity, Default) are off by default. See Authentication types. | SQL Server Authentication, Entra MFA |
| Gateway users | One account per person, who gets the admin role, and whether you require two-step verification (recommended for everyone, mandatory in practice for administrators). See section 9. | mario.rossi |
| Firewall | Inbound TCP 8443 to the gateway from user devices (ideally only from VPN or the management network). Outbound from the gateway: TCP 1433 (or your ports) to SQL Server, UDP 1434 to SQL Server Browser if users connect to named instances without a port, TCP 443 to sqlstudio.geolox.it. | VPN subnet → 8443 |
| Proxy | If outbound HTTPS goes through a proxy, the gateway must use it for the license check. Windows: system proxy settings. Linux/Docker: HTTPS_PROXY and NO_PROXY. | http://proxy.contoso.local:8080 |
| Backup | Include the data directory and appsettings.Production.json in your backups, protected like secrets. | Nightly file backup |
3. Quick start
The shortest path to a working gateway. Each step links to the detailed instructions.
Windows Server
- Install the runtime. Install ASP.NET Core Runtime 10.0 (Hosting Bundle or ASP.NET Core Runtime x64). Details
- Extract the package to
C:\Program Files\SQLStudio Gateway. Details - Run the install script in an elevated PowerShell:
.\extras\install-windows-service.ps1. Details - Edit
appsettings.Production.jsonand list the allowed servers. Details - Open a console as the service account with
runas. Details - Activate the license:
.\SqlStudio.Gateway.exe license activate SQLS-XXXXX-XXXXX-XXXXX-XXXXX. Details - Create the certificate:
.\SqlStudio.Gateway.exe cert create --host gateway.contoso.localand note the fingerprint. Details - Create the first user:
.\SqlStudio.Gateway.exe users add admin --admin, then enable TOTP. Details - Start the service:
Start-Service SqlStudioGateway. Details - Verify
https://gateway.contoso.local:8443/healthzand connect the app. Details
Linux (systemd)
- Install the runtime (ASP.NET Core Runtime 10.0). Details
- Create the user and folders
sqlstudio,/opt/sqlstudio-gateway,/var/lib/sqlstudio-gateway. Details - Extract the package to
/opt/sqlstudio-gateway. Details - Create
appsettings.Production.jsonfrom the example and list the allowed servers. Details - Activate the license, create the certificate and the first user as
sqlstudio. Details - Install the systemd unit and start it. Details
- Open TCP 8443 in the host firewall. Details
- Verify with
health,curlandjournalctl. Details
Docker
- Build the image from the extracted package:
docker build -t sqlstudio-gateway -f extras/Dockerfile .Details - Create the volume
sqlstudio-data. Details - Activate the license, create the certificate and the first user against the volume. Details
- Run the container with the allowed servers as environment variables. Details
- Verify the health status and print the fingerprint. Details
Then connect the apps: section 12.
4. License
Every gateway needs a valid license key to start.
Your key and download link
- The key has the format
SQLS-XXXXX-XXXXX-XXXXX-XXXXX. It is delivered by email right after the purchase, together with a one-time download link for the package. - The download link is valid for 30 days until it is used. After you click Download, the browser can resume an interrupted download for 30 minutes. If the link has expired or was already used, request a new one with the support form.
- Keep the email: you need the key again if you reinstall or move the gateway.
Seats
One license covers one server, that is one gateway installation; every other server needs its own license. An installation is identified by an installation id kept in the data directory, so the seat follows the data directory, not the machine name.
- An installation not seen by the license server for 30 days frees its seat automatically.
- To move the gateway to another server, run
license deactivateon the old server, thenlicense activateon the new one. If the old server is no longer available, contact support. - A new, empty data directory (for example a Docker container started without its volume) is a new installation and takes another seat.
Users
Each license includes 5 gateway users: the accounts created with users add (section 9). Accounts are personal: do not share one account among several people. You can buy extra users at €9.99 per user per year, VAT included, when you buy the license or later on the Manage license page (license key and purchase email). Extra users are a yearly subscription renewed automatically; on the same page you can add more, reduce them, update the card or cancel.
- The license server tells the gateway how many users the license allows; the gateway enforces the limit locally and never sends user names or the number of accounts.
users addrefuses to create an account beyond the limit. It checks the license online first, so extra users bought a moment ago count right away.- Extra users apply without a restart: at the first sign-in beyond the previous limit the gateway checks the license again (at most every 2 minutes), and in any case at the regular check every 6 hours.
- When there are more accounts than allowed, for example after cancelling extra users at the end of the paid year, accounts count in creation order: the oldest ones keep working, the most recent ones cannot sign in and their open sessions end. They see the message "The gateway license allows N users and this account is beyond the limit".
users listmarks them over the limit; remove the accounts no longer used withusers remove, or buy extra users. - Licenses issued before the user limit was introduced (October 2026) keep unlimited users.
License commands
Run them with the management command syntax for your platform (see below), as the service account.
| Command | What it does |
|---|---|
license activate SQLS-XXXXX-XXXXX-XXXXX-XXXXX | Checks the key online, binds this installation to it, and stores the key and the last signed answer in <DataDirectory>/license.json. |
license status | Shows the licensee, the license state, the installation id, the users allowed, the last successful check and the end of the offline grace period. |
license deactivate | Releases this server's seat on the license server and removes the key. Use it before moving the gateway to another server. Stop the service first. |
As an alternative to license activate, set the key in the configuration: Gateway:License:Key (environment variable Gateway__License__Key). This is handy for Docker. Gateway:License:ServerUrl defaults to https://sqlstudio.geolox.it/store; do not change it unless support asks you to.
How the license is checked
- At startup the gateway contacts the license server.
- Valid license: the gateway starts.
- Disabled or unknown key, or no free seat: the gateway refuses to start and logs the reason with the support link.
- License server unreachable: the gateway starts on its last valid answer if that answer is less than 14 days old, and logs a warning. Otherwise it refuses to start.
- While running the gateway re-checks every 6 hours. If the key has been disabled, the API answers HTTP 503 with the message "The gateway license is not valid…" and the support link; the apps show this message to users.
- The license server's answers are signed (ECDSA), so they cannot be forged or edited in
license.json. - Each check also reports the server's description: a non-reversible fingerprint of the operating system's machine id (Windows
MachineGuid, Linux/etc/machine-id), the operating system, CPU, memory, versions and the number of gateway accounts (never their names). The installation is bound to that machine: the same data directory started on another machine is refused. Reinstalling Windows changes the machine id: runlicense deactivatefirst, or ask support to reset the installation.
Network requirements
- Outbound HTTPS (TCP 443) from the gateway to
sqlstudio.geolox.it. - Proxies: on Windows the gateway uses the system proxy settings. On Linux and Docker set the
HTTPS_PROXYandNO_PROXYenvironment variables (examples in sections 6 and 7).
Help with keys
To add or remove extra users use the Manage license page. For lost keys, seats, transfers or download links, use the support form at sqlstudio.geolox.it/store/support.html (it sends your request to support@geolox.it) or email support@geolox.it.
Management command syntax
All management commands (license, users, cert, health) are run with the gateway program itself. In the rest of this guide <command> stands for one of them.
- Windows
- From the install folder, in a console running as the service account (keys are protected with DPAPI for that account). Open it from a Command Prompt with:
then run:runas /user:.\SqlStudioGateway "cmd /k cd /d \"C:\Program Files\SQLStudio Gateway\"".\SqlStudio.Gateway.exe <command> - Linux
cd /opt/sqlstudio-gateway && sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll <command>- Docker
docker run --rm -it -v sqlstudio-data:/data sqlstudio-gateway <command>
The help command prints the list of commands. Run without a command, the program starts the gateway itself in the foreground, which is useful only for testing (stop it with Ctrl+C).
5. Install on Windows Server
This procedure installs the gateway in C:\Program Files\SQLStudio Gateway, keeps its data in C:\ProgramData\SqlStudioGateway and runs it as the Windows service SqlStudioGateway under the local account SqlStudioGateway.
Procedure
- Install ASP.NET Core Runtime 10.0.
Download the Hosting Bundle or the ASP.NET Core Runtime x64 installer from dotnet.microsoft.com/download/dotnet/10.0 and run it. Then check, in a new PowerShell window:
dotnet --list-runtimesThe output must contain a line starting with
Microsoft.AspNetCore.App 10.0. - Extract the package.
Open PowerShell as Administrator, go to the folder that contains the downloaded zip and run:
Unblock-File -Path .\SQLStudio-Gateway-*.zip Expand-Archive -Path .\SQLStudio-Gateway-*.zip -DestinationPath "C:\Program Files\SQLStudio Gateway"Unblock-Fileremoves the "downloaded from the Internet" mark so Windows does not block the extracted files. Check thatC:\Program Files\SQLStudio Gateway\SqlStudio.Gateway.exeexists. - Run the install script.
In the same elevated PowerShell:
Set-Location "C:\Program Files\SQLStudio Gateway" Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass .\extras\install-windows-service.ps1With no parameters the script uses the defaults below. Pass a parameter only to change it, for example
.\extras\install-windows-service.ps1 -Port 9443.Parameter Default Meaning -InstallDirThe extracted package folder Where the gateway files are. -DataDirectoryC:\ProgramData\SqlStudioGatewayThe gateway's data directory. -AccountSqlStudioGatewayLocal account that runs the service. The script creates it if missing and asks you for its password. -Port8443HTTPS port. The script:
- creates the local account (if missing) and grants it the Log on as a service right;
- sets the ACLs: read access to the install folder; full control of the data directory only for that account and Administrators;
- writes
appsettings.Production.jsonfrom the example, with the data directory and port (only if the file does not exist yet); - registers the Windows service SqlStudioGateway with startup type Automatic (Delayed Start), without starting it;
- adds the inbound Windows Firewall rule for the port;
- prints the next steps.
Note. Store the account's password in your password vault. If you change it later, update it in the service properties too (services.msc › SQL Studio Gateway › Log On), otherwise the service will not start. - Edit the configuration.
Open
C:\Program Files\SQLStudio Gateway\appsettings.Production.jsonin an elevated editor and add at least the allowed servers:{ "Gateway": { "DataDirectory": "C:\\ProgramData\\SqlStudioGateway", "AllowedServers": [ "sql01.contoso.local", "sql02.contoso.local,1433", "*.database.windows.net" ], "Https": { "Port": 8443 } } }Backslashes must be doubled in JSON. All settings are described in section 11; the pattern syntax in section 10.
- Open a console as the service account.
From a Command Prompt (
cmd.exe, it does not need to be elevated):runas /user:.\SqlStudioGateway "cmd /k cd /d \"C:\Program Files\SQLStudio Gateway\""Type the account's password. A new console opens in the install folder, running as
SqlStudioGateway. Run steps 6 to 8 in this console. - Activate the license.
.\SqlStudio.Gateway.exe license activate SQLS-XXXXX-XXXXX-XXXXX-XXXXX .\SqlStudio.Gateway.exe license statusIf activation fails because the server cannot reach
sqlstudio.geolox.it, check the firewall and the Windows proxy settings (section 4). - Create the TLS certificate.
List every name users will type in the app, and optionally the IP address:
.\SqlStudio.Gateway.exe cert create --host gateway.contoso.local --host 10.0.0.15The command prints the SHA-256 fingerprint. Keep it: users compare it in the app. To use a certificate from your corporate CA instead, see section 8.
- Create the first administrator and enable two-step verification.
.\SqlStudio.Gateway.exe users add admin --admin .\SqlStudio.Gateway.exe users totp admin enableThe first command asks for the password twice (at least 12 characters). The second prints an
otpauth://URI and the secret key to add to an authenticator app; they are shown only once. Create the other users the same way (section 9). - Start the service.
Back in the elevated PowerShell:
Start-Service SqlStudioGateway Get-Service SqlStudioGatewayThe status must be
Running. If the service stops immediately, look at Event Viewer › Windows Logs › Application: the gateway logs the reason (missing certificate, license problem, invalid setting). - Verify.
On the server, in the elevated PowerShell:
Set-Location "C:\Program Files\SQLStudio Gateway" .\SqlStudio.Gateway.exe health; $LASTEXITCODE0means the running gateway answered;1means it did not. Then, from a user's network:curl.exe -k https://gateway.contoso.local:8443/healthzThe answer is
Healthy. You can also openhttps://gateway.contoso.local:8443/healthzin a browser; with a self-signed certificate the browser warns that the certificate is not trusted, which is expected (the app verifies it by fingerprint). Finally print the fingerprint again for your users:.\SqlStudio.Gateway.exe cert fingerprint - Publish the DNS name and connect the apps.
Make sure
gateway.contoso.localresolves to the server for your users, then follow section 12.
Checking the installation
Get-CimInstance Win32_Service -Filter "Name='SqlStudioGateway'" | Select-Object Name, StartName, StartMode, State
Get-NetFirewallPortFilter | Where-Object LocalPort -eq 8443 | Get-NetFirewallRule | Select-Object DisplayName, Enabled, Direction, Action
Get-Acl C:\ProgramData\SqlStudioGateway | Format-List
Whenever you change appsettings.Production.json or the certificate, restart the service: Restart-Service SqlStudioGateway. User commands (users …) take effect without a restart.
Alternative: a group Managed Service Account (gMSA)
In an Active Directory domain you can run the service as a gMSA instead of a local account. The password is then managed by the domain. Keep these points in mind:
- Grant the gMSA the same rights the script grants the local account: Log on as a service, read access to the install folder and full control of the data directory.
- Decide the account before activating the license and creating the certificate and users: keys created by one account cannot be decrypted by another. Management commands must also run as the gMSA; because a gMSA has no password you can type,
runascannot be used (use a tool that can start a process as the service account, such as PsExec). - Windows Authentication to SQL Server uses the identity of the gateway's service account. A local account cannot authenticate to SQL Servers on other machines; if you plan to enable Windows Authentication (section 12), run the gateway as a gMSA or domain account with the least privileges needed.
6. Install on Linux (systemd)
This procedure installs the gateway in /opt/sqlstudio-gateway, keeps its data in /var/lib/sqlstudio-gateway and runs it as the systemd service sqlstudio-gateway under the system user sqlstudio.
- Install ASP.NET Core Runtime 10.0.
Follow the instructions for your distribution at dotnet.microsoft.com/download/dotnet/10.0 (package
aspnetcore-runtime-10.0). Then check:dotnet --list-runtimesThe output must contain
Microsoft.AspNetCore.App 10.0.anddotnetmust be at/usr/bin/dotnet, which the systemd unit uses. If it is elsewhere (command -v dotnet), adjustExecStartin step 6. - Create the service user and the folders.
sudo useradd --system --home /var/lib/sqlstudio-gateway --shell /usr/sbin/nologin sqlstudio sudo install -d -o sqlstudio -g sqlstudio -m 700 /var/lib/sqlstudio-gateway sudo install -d -o root -g root -m 755 /opt/sqlstudio-gateway - Extract the package.
From the folder that contains the downloaded zip:
sudo unzip SQLStudio-Gateway-*.zip -d /opt/sqlstudio-gateway sudo chmod -R a+rX /opt/sqlstudio-gatewayThe program files are owned by root and only readable by
sqlstudio. - Create the configuration.
cd /opt/sqlstudio-gateway sudo cp appsettings.Production.example.json appsettings.Production.json sudo chown root:sqlstudio appsettings.Production.json sudo chmod 640 appsettings.Production.json sudo nano appsettings.Production.jsonSet at least the allowed servers:
{ "Gateway": { "DataDirectory": "/var/lib/sqlstudio-gateway", "AllowedServers": [ "sql01.contoso.local", "*.database.windows.net" ], "Https": { "Port": 8443 } } } - Activate the license, create the certificate and the first user.
cd /opt/sqlstudio-gateway sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll license activate SQLS-XXXXX-XXXXX-XXXXX-XXXXX sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll cert create --host gateway.contoso.local --host 10.0.0.15 sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll users add admin --admin sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll users totp admin enableNote the SHA-256 fingerprint printed by
cert createand the TOTP secret printed byusers totp(shown only once). - Install and start the systemd unit.
sudo cp /opt/sqlstudio-gateway/extras/sqlstudio-gateway.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now sqlstudio-gatewayThe unit shipped in the package:
# /etc/systemd/system/sqlstudio-gateway.service [Unit] Description=SQL Studio Gateway After=network-online.target Wants=network-online.target [Service] Type=notify User=sqlstudio Group=sqlstudio WorkingDirectory=/opt/sqlstudio-gateway ExecStart=/usr/bin/dotnet /opt/sqlstudio-gateway/SqlStudio.Gateway.dll Environment=ASPNETCORE_ENVIRONMENT=Production Environment=Gateway__DataDirectory=/var/lib/sqlstudio-gateway Restart=on-failure NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true ReadWritePaths=/var/lib/sqlstudio-gateway CapabilityBoundingSet= [Install] WantedBy=multi-user.targetThe gateway supports
Type=notify, so systemd knows when it is ready. The unit only allows writes to the data directory and grants no capabilities (port 8443 does not need any). - Open the port in the host firewall.
With firewalld:
sudo firewall-cmd --permanent --add-port=8443/tcp sudo firewall-cmd --reloadWith ufw:
sudo ufw allow 8443/tcp - Verify.
systemctl status sqlstudio-gateway cd /opt/sqlstudio-gateway && sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll health; echo $? curl -k https://localhost:8443/healthz journalctl -u sqlstudio-gateway -n 50healthreturns0andcurlprintsHealthy. Print the fingerprint for your users withcert fingerprint.
Proxy and other environment variables
Add environment variables to the service with a drop-in file instead of editing the unit:
sudo systemctl edit sqlstudio-gateway
[Service]
Environment=HTTPS_PROXY=http://proxy.contoso.local:8080
Environment=NO_PROXY=localhost,127.0.0.1,.contoso.local
sudo systemctl restart sqlstudio-gateway
Management commands that contact the license server (license activate, license deactivate) need the proxy too: add HTTPS_PROXY=http://proxy.contoso.local:8080 after env in the command.
Logs go to the systemd journal: journalctl -u sqlstudio-gateway (add -f to follow).
7. Install with Docker
The image runs as the non-root user app, writes only to the /data volume and /tmp, listens on 8443 and has a built-in HEALTHCHECK (the health command, which queries https://127.0.0.1:8443/healthz). The ASP.NET Core runtime is included in the image.
/data volume. A container started without it is a new installation: it has no users, no certificate and no keys, and it takes another license seat.- Build the image.
Extract the package, then from the extracted folder:
docker build -t sqlstudio-gateway -f extras/Dockerfile . - Create the data volume.
docker volume create sqlstudio-data - Activate the license, create the certificate and the first user (same volume, same non-root user as the service).
docker run --rm -it -v sqlstudio-data:/data sqlstudio-gateway license activate SQLS-XXXXX-XXXXX-XXXXX-XXXXX docker run --rm -it -v sqlstudio-data:/data sqlstudio-gateway cert create --host gateway.contoso.local docker run --rm -it -v sqlstudio-data:/data sqlstudio-gateway users add admin --admin docker run --rm -it -v sqlstudio-data:/data sqlstudio-gateway users totp admin enableBehind a proxy add
-e HTTPS_PROXY=http://proxy.contoso.local:8080to thelicensecommands. Instead oflicense activateyou can pass the key to the container with-e Gateway__License__Key=SQLS-XXXXX-XXXXX-XXXXX-XXXXXin the next step. - Run the container.
docker run -d --name sqlstudio-gateway --restart unless-stopped \ --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges \ -p 8443:8443 -v sqlstudio-data:/data -v /etc/machine-id:/etc/machine-id:ro \ -e Gateway__AllowedServers__0=sql01.contoso.local \ -e Gateway__AllowedServers__1=*.database.windows.net \ sqlstudio-gatewayThe read-only
/etc/machine-idmount binds the license to the Docker host, so updating or recreating the container keeps the same installation. Without it the container has no machine id and the installation is bound only to the/datavolume.Configure the gateway with environment variables (syntax in section 11). For many settings, or for secrets such as a certificate password, put them in a file readable only by root and pass it with
--env-file:# /etc/sqlstudio-gateway.env (chmod 600) Gateway__AllowedServers__0=sql01.contoso.local Gateway__AllowedServers__1=*.database.windows.net Gateway__License__Key=SQLS-XXXXX-XXXXX-XXXXX-XXXXX HTTPS_PROXY=http://proxy.contoso.local:8080 NO_PROXY=localhost,127.0.0.1,.contoso.localdocker run -d --name sqlstudio-gateway --restart unless-stopped \ --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges \ -p 8443:8443 -v sqlstudio-data:/data -v /etc/machine-id:/etc/machine-id:ro --env-file /etc/sqlstudio-gateway.env \ sqlstudio-gateway - Verify.
docker inspect --format '{{.State.Health.Status}}' sqlstudio-gateway docker logs sqlstudio-gateway docker run --rm -v sqlstudio-data:/data sqlstudio-gateway cert fingerprint docker run --rm -v sqlstudio-data:/data sqlstudio-gateway license statusThe health status becomes
healthya few seconds after start. Open TCP 8443 on the host firewall for your users.
8. TLS certificate
The gateway accepts only HTTPS (TLS 1.2 or 1.3) and needs a certificate to start. It looks for one in this order:
Gateway:Https:Certificate:Path: a PFX/PKCS#12 file;Gateway:Https:Certificate:Thumbprint: a certificate in the Windows certificate store;<DataDirectory>/gateway.pfx: the certificate created bycert create.
Which certificate to use
| Self-signed with pinning (recommended) | Corporate CA | |
|---|---|---|
| How | cert create on the gateway | Certificate issued by your internal CA, with the gateway's DNS name in the Subject Alternative Name |
| In the app | The app shows the SHA-256 fingerprint at first connection; the user compares it with yours and trusts it. From then on the app accepts only that certificate. | Trusted automatically if the CA root is installed on the devices (for example via MDM); otherwise verified by fingerprint like a self-signed one. Users can still pin it. |
| Renewal | Every 825 days by default (or the --days you choose). Users must confirm the new fingerprint. | Per your CA policy. Users who pinned the certificate must confirm the new fingerprint. |
Create a self-signed certificate
cert create --host gateway.contoso.local --host 10.0.0.15
Creates an ECDSA P-256 certificate valid 825 days with the given names in the Subject Alternative Name, saves it as <DataDirectory>/gateway.pfx with a random password protected in the data directory (the gateway loads it automatically), and prints the validity, the names and the SHA-256 fingerprint. Repeat --host for every DNS name or IP address users will type.
| Option | Meaning |
|---|---|
--host <name> | DNS name or IP address to include (required, repeatable). Letters, digits, ., - and *, or an IP address. |
--days N | Validity in days, 1 to 3650 (default 825). |
--out file.pfx | Writes a PFX file instead of the default location. It asks for a password of at least 12 characters (twice). Then set Gateway:Https:Certificate:Path and the environment variable Gateway__Https__Certificate__Password. |
--password-stdin | With --out, reads the PFX password from standard input (for scripts). |
Use a corporate certificate
From a PFX file (any platform): set the path in appsettings.Production.json and the password only as an environment variable or secret store, never in the JSON file.
"Https": { "Port": 8443, "Certificate": { "Path": "C:\\ProgramData\\SqlStudioGateway\\certs\\gateway.pfx" } }
On Windows, set the password as a machine environment variable (elevated PowerShell), then restart the service:
[Environment]::SetEnvironmentVariable("Gateway__Https__Certificate__Password", "<pfx-password>", "Machine")
Restart-Service SqlStudioGateway
On Linux use Environment=Gateway__Https__Certificate__Password=… in a systemctl edit drop-in; in Docker mount the file read-only and use an --env-file:
-v /etc/sqlstudio/gateway.pfx:/certs/gateway.pfx:ro \
-e Gateway__Https__Certificate__Path=/certs/gateway.pfx \
--env-file /etc/sqlstudio-gateway.env
From the Windows certificate store: import the certificate with its private key into Local Computer › Personal, and set its thumbprint:
"Https": { "Port": 8443, "Certificate": { "Thumbprint": "8F3C…", "StoreName": "My", "StoreLocation": "LocalMachine" } }
The service account must be able to read the private key: in certlm.msc right-click the certificate › All Tasks › Manage Private Keys… and give SqlStudioGateway Read permission. Restart the service.
Fingerprint
cert fingerprint # the configured certificate
cert fingerprint gateway.cer # any .cer/.crt/.pem/.pfx file
Prints the subject, the expiry date and the SHA-256 fingerprint of the DER certificate in two forms: hexadecimal (0e9c8e…) and with colons (0E:9C:8E:…). The app shows the colon form. For a PFX file the command asks for its password (empty if none), or reads it with --password-stdin.
Send the fingerprint to your users over a channel other than the gateway connection itself (for example your intranet or a signed email), so they can verify it independently.
Renewal plan
- Track the expiry date.
cert fingerprintshows it; add a reminder at least a month before. - Announce the change. Publish the new fingerprint to users before the switch.
- Replace the certificate. Self-signed: run
cert createagain with the same--hostnames (it replacesgateway.pfx). Corporate: import the new certificate and updateThumbprintorPath. - Restart the gateway so it loads the new certificate, and run
cert fingerprint. - Users confirm the new fingerprint in the app with Verify Certificate... in the sign-in window, or Options › General › Edit.... Until then the app refuses the new certificate.
9. Gateway users and two-step verification
Gateway users are separate from SQL Server logins: they only grant access to the gateway. The license sets how many accounts can sign in (5 included, more as extra users: see Users in section 4). After signing in, users still connect to each SQL Server with their own SQL or Entra credentials. Users are managed only from the command line on the server; there is no self-registration and no remote password reset.
Commands
| Command | What it does |
|---|---|
users add <name> | Creates a user. Asks for the password twice, without echo. Refused when the license's user limit has been reached. |
users add <name> --admin | Creates a user with the admin role. |
users add <name> --password-stdin | Reads the password from standard input, for scripts. |
users passwd <name> [--password-stdin] | Changes the password and invalidates all of the user's sessions. |
users totp <name> enable | Enables two-step verification and prints the otpauth:// URI and the secret key. |
users totp <name> disable | Disables two-step verification. |
users unlock <name> | Unlocks an account locked after failed sign-ins. |
users list | Lists users with roles, TOTP, state (active, locked or over the limit) and last sign-in, and how many users the license allows. |
users remove <name> | Deletes a user. |
Examples (Windows syntax; on Linux and Docker use the prefixes from section 4):
.\SqlStudio.Gateway.exe users add mario.rossi
.\SqlStudio.Gateway.exe users totp mario.rossi enable
.\SqlStudio.Gateway.exe users list
Scripted creation on Linux:
cd /opt/sqlstudio-gateway && printf '%s\n' "$PASSWORD" | sudo -u sqlstudio env Gateway__DataDirectory=/var/lib/sqlstudio-gateway dotnet SqlStudio.Gateway.dll users add ci-bot --password-stdin
You can run user commands while the service is running: the gateway rereads the user file automatically.
User names and passwords
- User names: 1 to 64 characters; letters, digits and
. _ - @. Names are not case-sensitive. - Passwords: at least 12 characters (maximum 256), must not contain the user name, at least 4 different characters.
- Passwords are stored as Argon2id hashes. Give each person their initial password through a secure channel and change it with
users passwdwhen needed.
Roles
Every account has the user role; --admin adds the admin role. users list shows the roles. What a user can do inside SQL Server always depends on the SQL login or Entra identity they connect with.
Two-step verification (TOTP)
- Enable it with
users totp <name> enable. The command prints anotpauth://URI and the secret key for manual entry. The secret is not shown again. - Hand the secret to the user securely. The user adds the account to any standard authenticator app (time-based codes, 6 digits, 30 seconds), either from the URI (or a QR code generated from it) or by typing the secret key.
- The user signs in: after the correct password the app asks for the Verification code (TOTP).
Each code can be used only once. If a user loses the device, run users totp <name> enable again to issue a new secret (the old one stops working), or disable it. Changing the password, the TOTP setting or removing the user invalidates the user's existing sessions at the next request.
admin role.Lockout and sign-in limits
- After 5 failed attempts the account is locked for 15 minutes. The lock persists across restarts. Unlock it earlier with
users unlock <name>. - At most 5 sign-in attempts per minute for each IP address and user name, and 30 requests per minute per IP address to the anonymous sign-in endpoints. Excess attempts get HTTP 429.
- The error message is the same for a wrong password and an unknown user, and unknown names are "locked" too, so the gateway does not reveal which accounts exist.
- Every outcome (success, wrong password, unknown user, locked, limit exceeded, wrong code) is written to the audit log.
The thresholds can be changed with the Gateway:RateLimit:* settings (section 11).
Sessions
After sign-in the app receives a 15-minute access token and a refresh token that rotates at every renewal. A session stays alive while it is used within 12 hours (sliding) and lasts at most 7 days; then the user signs in again. A refresh token that is reused (a sign of theft) revokes the whole session.
10. Allowed servers
Every connection, and every Failover Partner, is checked against Gateway:AllowedServers before any socket is opened. Without this list the gateway would be a bridge into your internal network. With an empty list in Production every connection is refused (and the gateway logs a warning at startup).
Before matching, the server name is normalized: lowercase, tcp: prefix removed, . and (local) become localhost, the port follows the comma and the instance follows the backslash.
Pattern syntax
| Pattern | Matches | Does not match |
|---|---|---|
sql01 | sql01, SQL01,1433, sql01\PROD | sql01.contoso.local |
sql01,1433 | sql01,1433 | sql01, sql01,1434 |
sql01\PROD | sql01\prod | sql01, sql01\DEV |
*.database.windows.net | srv.database.windows.net | a.b.database.windows.net, database.windows.net |
**.contoso.local | a.b.contoso.local | contoso.local.evil.com |
10.0.0.* | 10.0.0.5 | 10.0.0.5.evil.com, 10.0.1.5 |
sql0? | sql01, sql0a | sql10 |
*does not cross dots (so10.0.0.*cannot accept10.0.0.5.attacker.com); use**for several domain levels.- The
np:(named pipes) andlpc:(shared memory) prefixes are always refused. - Write each entry in the same form users type in the app. If users type
sql01.contoso.local, the patternsql01does not match it. - Matching by name trusts the gateway server's DNS. Use reliable internal DNS, or IP address patterns.
Examples
In appsettings.Production.json:
"AllowedServers": [ "sql01.contoso.local", "sql02.contoso.local,1433", "sqlcluster.contoso.local\\PROD", "*.database.windows.net", "10.20.30.*" ]
As environment variables (Docker, systemd):
Gateway__AllowedServers__0=sql01.contoso.local
Gateway__AllowedServers__1=sql02.contoso.local,1433
Gateway__AllowedServers__2=*.database.windows.net
Restart the gateway after changing the list. The app shows whether the allow-list is active in the gateway profile (Server allow-list).
Gateway:AllowAnyServer=true disables the list completely. Do not use it in production.11. Configuration reference
Where settings come from
The gateway reads its settings, in increasing order of priority, from:
appsettings.json(defaults, do not edit);appsettings.Production.json(your settings, in the install folder);- environment variables;
- the command line.
Values are validated at startup: an invalid value stops the gateway with a message that names it. The gateway runs in the Production environment (the systemd unit sets ASPNETCORE_ENVIRONMENT=Production explicitly). Do not set any other environment on a production server.
Environment variable syntax
Replace each : with a double underscore __. Array items take an index.
Gateway__Https__Port=9443
Gateway__AllowedServers__0=sql01.contoso.local
Gateway__AllowedServers__1=*.database.windows.net
Gateway__License__Key=SQLS-XXXXX-XXXXX-XXXXX-XXXXX
Gateway__Https__Certificate__Password=<pfx-password>
All settings
| Key | Default | Description |
|---|---|---|
Gateway:DataDirectory | OS application-data folder + SqlStudioGateway (/data in Docker) | Data directory (section 1). Always set it explicitly for a service: the default of a service account points inside its profile. |
Gateway:License:Key | – | License key, as an alternative to license activate (section 4). |
Gateway:License:ServerUrl | https://sqlstudio.geolox.it/store | License server. Do not change unless support asks you to. |
Gateway:AllowedServers | [] | Allow-list of SQL Servers (section 10). |
Gateway:AllowAnyServer | false | Disables the allow-list. Not recommended in production. |
Gateway:AllowIntegratedSecurity | false | Allows the authentication types that use the gateway's identity: Windows Authentication, Entra Integrated, Managed Identity, Entra Default (section 12). |
Gateway:MaxConnectionsPerUser | 25 | Server connections open at the same time per user. |
Gateway:ConnectionIdleTimeoutMinutes | 120 | Idle connections are closed automatically and their credentials removed from memory. |
Gateway:MaxUploadBytes | 2147483648 (2 GB) | Maximum size of an uploaded file. |
Gateway:UploadRetentionMinutes | 60 | Uploaded files are deleted after this time. |
Gateway:MaxUploadsPerUser | 20 | Uploaded files kept at the same time per user. |
Gateway:MaxRequestBodyBytes | 10485760 (10 MB) | Maximum request body size (uploads excluded). |
Gateway:MaxResultRows | 5000000 | Hard limit of rows per result set in the query editor. |
Gateway:TaskRetentionHours | 24 | How long completed background tasks and their result files are kept. |
Gateway:MaxConcurrentTasks | 8 | Background tasks run in parallel; the others wait in a queue. |
Gateway:Https:Address | * | Listening interface: *, localhost or an IP address. |
Gateway:Https:Port | 8443 | HTTPS port. The gateway never listens on HTTP. |
Gateway:Https:Certificate:Path / :Password | – | PFX/PKCS#12 file and its password. Set the password only through an environment variable or a secret store. |
Gateway:Https:Certificate:StoreName / :StoreLocation / :Thumbprint | My / LocalMachine / – | Certificate from the Windows certificate store. |
Gateway:Jwt:Issuer / :Audience | sqlstudio-gateway / sqlstudio-app | Token values. No need to change them. |
Gateway:Jwt:AccessTokenMinutes | 15 | Access token lifetime (maximum 60). |
Gateway:Jwt:RefreshTokenHours | 12 | Sliding lifetime of refresh tokens. |
Gateway:Jwt:RefreshTokenAbsoluteDays | 7 | Maximum session length; then a new sign-in is required. |
Gateway:RateLimit:TokenLimit / :TokensPerSecond | 300 / 60 | Per-user token bucket on the whole API (burst / refill per second). |
Gateway:RateLimit:LoginPerMinute | 5 | Sign-in attempts per minute for each IP address and user name. |
Gateway:RateLimit:AnonymousAuthPerMinutePerIp | 30 | Requests per minute to sign-in/refresh per IP address. |
Gateway:RateLimit:LockoutThreshold / :LockoutMinutes | 5 / 15 | Account lockout after N failures, and its duration. |
Gateway:ForwardedHeaders:KnownProxies | [] | IP addresses of trusted reverse proxies. Use only if the gateway is behind a proxy that re-encrypts to HTTPS; otherwise X-Forwarded-* headers are ignored. |
Other environment variables the gateway honors: ASPNETCORE_ENVIRONMENT (keep Production), and on Linux/Docker HTTPS_PROXY and NO_PROXY for the license check.
Example appsettings.Production.json
{
"Gateway": {
"DataDirectory": "C:\\ProgramData\\SqlStudioGateway",
"AllowedServers": [
"sql01.contoso.local",
"sql02.contoso.local,1433",
"sqlcluster.contoso.local\\PROD",
"*.database.windows.net",
"10.20.30.*"
],
"AllowIntegratedSecurity": false,
"MaxConnectionsPerUser": 25,
"Https": {
"Port": 8443,
"Certificate": { "Thumbprint": "8F3C…", "StoreLocation": "LocalMachine" }
},
"RateLimit": { "LockoutThreshold": 5, "LockoutMinutes": 15 }
}
}
Omit the Certificate block if you use the certificate created by cert create. Restart the gateway after every change to this file.
12. Connect the apps
Users install SQL Studio from the App Store: on Mac (macOS 26 or later), iPhone and iPad (iOS/iPadOS 26 or later). The app is free and needs no account with us. Make sure they install SQL Studio and not SQLStudio Light, which connects to servers directly without a gateway.
Give each user: the gateway URL (https://gateway.contoso.local:8443), the certificate fingerprint, their gateway user name and initial password, and their TOTP secret if enabled.
First connection
- Configure Gateway. At first launch the Configure Gateway sheet opens. Enter a Profile name (for example "Company Gateway") and the Gateway URL,
https://gateway.contoso.local:8443(onlyhttps://URLs are accepted). Press Test Connection. When the gateway is reached, the app shows its product, version, whether the server allow-list is active and the maximum upload size. - Verify the certificate. If the certificate is not trusted by the device (self-signed or from a private CA), the app shows its SHA-256 fingerprint in the form
AA:BB:CC:…. Compare it, character by character, with the output ofcert fingerprinton the gateway. Only if they match, turn on I have verified that the thumbprint matches the gateway's and press Trust and Continue. From then on the certificate is pinned and any other certificate is refused. If the certificate is trusted by the device, the app says so and offers Also pin this certificate (recommended for internal gateways). - Sign in. In Sign In to Gateway enter the gateway User name and Password. If two-step verification is enabled, the Verification code (TOTP) field appears. The refresh token is kept only in the device Keychain: the app signs in automatically on later launches while the session is valid, and asks again when it expires.
- Connect to a server. Connect to Server opens. Enter the Server name as the gateway reaches it (
sql01.contoso.local,sql01.contoso.local,1433orsql01.contoso.local\PROD), choose the Authentication, enter the credentials and press Connect. The server must match an entry of the allow-list.
Users can configure several gateways (profiles) and switch between them with Manage Gateways... in the sign-in window or in Options › General. When the gateway certificate is renewed, they confirm the new fingerprint with Verify Certificate... in the sign-in window or Options › General › Edit....
Connect to Server options
- Server name:
server,server,port,server\instanceoradmin:server(dedicated admin connection). Named instances without a port need SQL Server Browser (UDP 1434) reachable from the gateway. The combo box lists recent servers; <Search more...> picks from registered servers and recent connections. - Encryption: Mandatory, Optional or Strict (SQL Server 2022 and Azure SQL); Trust server certificate; Host name in certificate.
- Options >>: Connection Properties (database, with <Browse for more...>; network protocol; packet size; connection and execution timeouts; application intent; multi-subnet failover; custom color), Always Encrypted (secure enclaves and attestation URL) and Additional connection parameters (
key=value;). - Additional connection parameters are limited to:
Failover Partner,Load Balance Timeout,Max Pool Size,Min Pool Size,Pooling,Connect Retry Count,Connect Retry Interval,Server SPN,Failover Partner SPN,IP Address Preference,Current Language. Keywords such asAttachDBFilename,Integrated Security,Data SourceorPasswordare refused. AFailover Partnermust also be in the allow-list. - Connection timeout: 1 to 600 seconds.
Authentication types
| Authentication | Identity used | Gateway requirement |
|---|---|---|
| SQL Server Authentication | The SQL login and password typed by the user. | None. |
| Microsoft Entra MFA | The user's Entra account. The app shows a code and opens the Microsoft sign-in page (device code flow); after sign-in the token for https://database.windows.net/ is sent to the gateway, which uses it to connect. Client ID and default tenant are set in Options › Connection. | None. |
| Microsoft Entra Password | Entra user name and password typed by the user. | None. |
| Microsoft Entra Service Principal | Application (client) ID and secret typed by the user. | None. |
| Access Token | An access token supplied by the user. | None. |
| Windows Authentication (via gateway) | The gateway's service account, not the user. | Gateway on Windows; Gateway:AllowIntegratedSecurity=true; a service account that can authenticate to the SQL Servers (gMSA or domain account). Not available in Linux containers. |
| Microsoft Entra Integrated, Managed Identity, Default | The gateway's identity. | Gateway:AllowIntegratedSecurity=true. |
The gateway never stores SQL credentials: they stay in memory for the lifetime of the connection and are erased when the connection closes (by the user, after 120 minutes of inactivity, or at shutdown).
Security on the devices
- SQL passwords, gateway tokens and Microsoft Entra refresh tokens are stored only in the device Keychain (this device only, not synced to iCloud), never in preferences or logs. Exported registered servers never include passwords.
- Options › General › Security can require Touch ID, Face ID or the device password to use saved passwords and sessions, and lock the app automatically after a period of inactivity. Lock SQL Studio (⌃⌘L on Mac) locks it immediately.
- The app runs SQL nowhere locally: it has no SQL Server driver and talks only to the gateway.
13. Using SQL Studio
SQL Studio runs on Mac, iPhone and iPad, in English and Italian (following the system language).
Object Explorer
- Object Explorer (F8): progressive tree of all connected servers with over 200 node types (databases, tables, views, programmability, Query Store, Service Broker, storage, security, server objects, replication, Always On, management, Integration Services, SQL Server Agent, XEvent Profiler) and complete context menus.
- Toolbar: connect, disconnect, stop expansion, filter (name, schema, owner), refresh, collapse all and server-side object search. Drag an object into the editor to insert its name.
- Object Explorer Details (F7): sortable list of the children of the selected node. Registered Servers (⌃⌘G): nested server groups, import and export (JSON, without passwords).
- Commands: delete, rename, enable/disable, refresh, bring online/offline, view dependencies. Starting or stopping the SQL Server or SQL Server Agent service is not available.
Query editor
- T-SQL syntax colors, light and dark themes; IntelliSense for keywords, snippets, databases, schemas, tables, columns with aliases, procedures and parameters; real-time syntax check; T-SQL formatting; over 300 templates.
- Scripts with
GO,GO nand SQLCMD mode (:setvar,:on error…); results in grid, text or file; multiple result sets; clickable XML and JSON; query options (SET, isolation, timeouts). - A dedicated session per window with transactions, Commit / Rollback and database switching; cancel execution.
- Estimated and actual execution plans with a graphical viewer, Live Query Statistics and client statistics; open and save
.sqland.sqlplanfiles.
| Mac keys | Command |
|---|---|
| ⌘N | New Query with Current Connection |
| F5 / ⌘↩ | Execute |
| ⌘. | Cancel Executing Query |
| ⌘L | Display Estimated Execution Plan |
| ⌘M | Include Actual Execution Plan |
| ⌘T / ⌘D / ⇧⌘F | Results to text / grid / file |
| ⌘/ | Comment / uncomment |
| ⌥⌘A | Activity Monitor |
The full list is in Help › Keyboard Shortcuts.
Scripting, data and design
- Script … as CREATE, ALTER, DROP, DROP and CREATE, CREATE OR ALTER, SELECT, INSERT, UPDATE, DELETE, EXECUTE, to a new window, the clipboard, a file or a SQL Server Agent job. The Generate Scripts wizard scripts objects and data to one file, one file per object, or text.
- Edit Top 200 Rows, the table designer (columns, keys, indexes, constraints, change scripts), database diagrams, Import Flat File and data import/export.
Administration dialogs
Over 130 dialogs and wizards, each with a Script button: server and database properties; backup, restore, copy and snapshots; attach, detach and shrink; logins, users, roles, schemas, keys, certificates, credentials and audits; tables, indexes and statistics; Always On availability groups; replication; SQL Server Agent jobs, schedules, alerts and operators; Resource Governor, Extended Events, Database Mail and maintenance plans; the Integration Services (SSIS) catalog; data-tier applications (extract, deploy, BACPAC export/import), which run on the gateway with Microsoft DacFx.
Backups are written to and restored from the file system of the SQL Server, which the dialogs browse with the SQL user's permissions; .bak files are not uploaded through the gateway. Local files such as BACPAC or CSV are uploaded to the gateway (up to Gateway:MaxUploadBytes) and deleted automatically.
Reports and monitoring
- 63 standard server and database reports (performance, activity, disk usage, transactions, backups, indexes, sessions).
- Query Store reports: regressed queries, overall resource consumption, top queries, wait statistics, tracked queries.
- Activity Monitor, Job Activity Monitor, Always On Dashboard, Log File Viewer, vulnerability assessment, data classification, missing indexes, live Extended Events and XEvent Profiler.
Long-running tasks
Backup, restore, DAC operations, Generate Scripts and imports run as gateway tasks. They continue on the gateway even if the user closes the app or the device sleeps. The Tasks button shows progress and messages and offers Cancel, Open in New Query (resulting script) and Save... (resulting file); a notification is sent when a task completes. The gateway runs up to Gateway:MaxConcurrentTasks tasks in parallel and keeps completed tasks and their files for Gateway:TaskRetentionHours. Only the user who started a task can see and download it.
14. Operations
Health check
GET https://gateway.contoso.local:8443/healthz(anonymous) answersHealthyand nothing else. Point your monitoring system at it.- The
healthmanagement command querieshttps://127.0.0.1:<port>/healthzon the local machine and exits with0(healthy) or1. The Docker image uses it as itsHEALTHCHECK. GET /gateway/info(anonymous) returns the version, whether the allow-list is active, the maximum upload size and the enabled authentication types. Use it to check the version after an update:curl.exe -k https://localhost:8443/gateway/info.
Logs
| Platform | Where |
|---|---|
| Windows | Event Viewer › Windows Logs › Application. In PowerShell: Get-WinEvent -LogName Application -MaxEvents 50 | Where-Object ProviderName -like '*SqlStudio*' |
| Linux | journalctl -u sqlstudio-gateway (-f to follow, --since today to filter) |
| Docker | docker logs sqlstudio-gateway (-f to follow) |
Logs never contain passwords, tokens, connection strings or query text. Error responses follow RFC 7807 and include a traceId: ask users for it and search for it in the log to find the matching entry. License problems at startup and during the 6-hour checks are logged with the reason and the support link.
Audit log
Daily JSON Lines files in <DataDirectory>/audit/audit-YYYYMMDD.jsonl, append-only with private permissions. The same events are written to the application log (category SqlStudio.Gateway.Core.Security.FileAuditLog). One line per event:
{"timestamp":"2026-10-02T08:15:30.123Z","user":"<id>","userName":"mario.rossi","action":"connection.open","target":"sql01","detail":"SqlPassword; connectionId=…","success":true,"ip":"10.0.0.20","traceId":"…"}
| Field | Meaning |
|---|---|
timestamp | UTC time of the event. |
user / userName | Gateway user id and name. |
action | auth.login, auth.refresh, auth.refresh.reuse, auth.logout, connection.test, connection.open, connection.close, connection.expire, upload.create, task.start, task.end, task.cancel, task.download, and DDL/commands recorded by the feature modules. |
target | Server or object involved. |
detail | Short detail (truncated, single line). |
success | Outcome. |
ip | Client IP address. |
traceId | Correlation id, also shown in error messages. |
The audit log never contains passwords, tokens, connection strings or SQL text. Forward it to your SIEM for retention and immutability: the local file can be modified by whoever controls the service account. auth.refresh.reuse indicates a possibly stolen refresh token and deserves an alert.
Backup and restore
Back up two things:
- the data directory (license, users, keys, certificate, audit log);
appsettings.Production.jsonfrom the install folder.
The data directory contains keys: protect backups as secrets. Examples:
# Windows (elevated PowerShell): copy with ACLs
robocopy C:\ProgramData\SqlStudioGateway D:\Backup\SqlStudioGateway /MIR /COPYALL /R:1 /W:1
Copy-Item "C:\Program Files\SQLStudio Gateway\appsettings.Production.json" D:\Backup\
# Linux
sudo tar -C /var/lib -czpf /root/sqlstudio-gateway-data-$(date +%F).tar.gz sqlstudio-gateway
sudo cp /opt/sqlstudio-gateway/appsettings.Production.json /root/
# Docker
docker run --rm -v sqlstudio-data:/data:ro -v "$PWD":/backup alpine tar -czpf /backup/sqlstudio-data.tar.gz -C /data .
Restore to the same path, for the same service account. On Windows the key ring is also protected with DPAPI for the service account, so restored keys work only for that account on that server. If the keys cannot be decrypted, the gateway reports it at startup (see Troubleshooting). To move the gateway to a new server, follow License › Seats.
Updating the gateway
An update replaces the program files only. Keep appsettings.Production.json and the data directory; the package does not contain an appsettings.Production.json, so extracting it does not overwrite yours. Do not run the install script again.
Windows
- Download the new
SQLStudio-Gateway-<version>.zipand open PowerShell as Administrator in its folder. - Stop the service:
Stop-Service SqlStudioGateway - Back up the data directory and
appsettings.Production.json(see above). - Extract over the install folder:
Unblock-File -Path .\SQLStudio-Gateway-*.zip Expand-Archive -Path .\SQLStudio-Gateway-*.zip -DestinationPath "C:\Program Files\SQLStudio Gateway" -Force - Compare the new
appsettings.Production.example.jsonwith your file for new settings. - Start and verify:
Start-Service SqlStudioGateway Set-Location "C:\Program Files\SQLStudio Gateway" .\SqlStudio.Gateway.exe health; $LASTEXITCODE curl.exe -k https://localhost:8443/gateway/info
Linux
- Stop:
sudo systemctl stop sqlstudio-gateway - Back up the data directory and
appsettings.Production.json. - Extract over the install folder:
sudo unzip -o SQLStudio-Gateway-*.zip -d /opt/sqlstudio-gateway sudo chmod -R a+rX /opt/sqlstudio-gateway - Update the unit if it changed:
sudo diff /opt/sqlstudio-gateway/extras/sqlstudio-gateway.service /etc/systemd/system/sqlstudio-gateway.service sudo cp /opt/sqlstudio-gateway/extras/sqlstudio-gateway.service /etc/systemd/system/ && sudo systemctl daemon-reload - Start and verify:
sudo systemctl start sqlstudio-gateway curl -k https://localhost:8443/gateway/info journalctl -u sqlstudio-gateway -n 50
Docker
- Build the new image from the new extracted package:
docker build -t sqlstudio-gateway -f extras/Dockerfile . - Back up the volume (see above).
- Replace the container, keeping the same volume and options:
docker stop sqlstudio-gateway && docker rm sqlstudio-gateway docker run -d --name sqlstudio-gateway --restart unless-stopped \ --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges \ -p 8443:8443 -v sqlstudio-data:/data -v /etc/machine-id:/etc/machine-id:ro --env-file /etc/sqlstudio-gateway.env \ sqlstudio-gateway - Verify:
docker inspect --format '{{.State.Health.Status}}' sqlstudio-gateway
sqlstudio-data volume during an update: without it the new container is a new installation and takes another license seat.Security hardening
Built-in protections
| Area | What the gateway does |
|---|---|
| Transport | HTTPS only, TLS 1.2/1.3; no HTTP listener and no HTTP→HTTPS redirect; HSTS; Cache-Control: no-store; no HTML and no CORS on the API. The apps pin the certificate fingerprint. |
| Sign-in | Argon2id password hashes; optional single-use TOTP codes; rate limits and persistent lockout; uniform responses that do not reveal which users exist. |
| Tokens | Signed 15-minute access tokens; single-use rotating refresh tokens (only hashes stored); reuse revokes the whole session; password, TOTP or user changes invalidate existing sessions. |
| SQL credentials | Never stored; kept in memory only for the connection; connection pools are not shared between gateway users with explicit credentials. |
| Network reach | Mandatory allow-list, also for Failover Partner; closed list of additional connection keywords; named pipes and shared memory refused; methods using the gateway's identity disabled by default. |
| Isolation | Every connection, task and upload is bound to the user who created it; other users get 404. |
| Load | Per-user rate limiting, request size limits, upload limits, bounded background tasks and result rows. |
| Storage | Private data directory (ACL / 700), atomic writes, keys encrypted at rest. |
Recommendations
- Run the gateway on a dedicated, patched host with a least-privilege service account. It concentrates SQL credentials in memory while connections are open.
- Limit exposure: publish port 8443 only to the VPN or the management network, not to the Internet.
- Keep the allow-list as tight as possible and never set
AllowAnyServerin production. Prefer reliable internal DNS or IP patterns. - Leave
AllowIntegratedSecurityoff unless you need it; if you enable it, give the gateway's account minimal rights on SQL Server. - Enable two-step verification for all users, especially
adminaccounts; remove accounts of people who leave. - Keep Mandatory or Strict encryption towards SQL Server and avoid Trust server certificate outside tests.
- Renew the certificate before it expires and distribute the new fingerprint through a separate channel.
- Protect the data directory and its backups (on Linux and Docker the keys are protected only by file system permissions).
- Forward the audit log to a SIEM and alert on
auth.refresh.reuseand repeated failed sign-ins. - Run one gateway instance per data directory.
- Restrict outbound SMB from your SQL Servers at network level: a UNC path typed in a file dialog makes the SQL Server service authenticate to that share (as
xp_dirtreewould in a query window). - If you put a reverse proxy in front of the gateway, it must re-encrypt to HTTPS; list its address in
ForwardedHeaders:KnownProxiesand allowPUTandDELETE(orPOSTwithX-HTTP-Method-Override).
15. Troubleshooting
Start with the log of your platform (section 14): the gateway logs the reason for every startup failure.
| Symptom | Cause and fix |
|---|---|
The service does not start; the log says the framework Microsoft.AspNetCore.App 10.0 was not found | ASP.NET Core Runtime 10.0 is missing. Install it and check with dotnet --list-runtimes. |
| At startup: "No TLS certificate configured" | Run cert create as the service account, or configure Gateway:Https:Certificate (section 8). |
| At startup: "The TLS certificate expired on …" or "The TLS certificate does not contain the private key." | Renew the certificate (section 8). For a corporate certificate, import it with its private key and make sure the service account can read the key. |
| At startup: an invalid configuration value is reported | Fix the named setting in appsettings.Production.json or the environment variable, then restart. |
| At startup: "Unable to decrypt the JWT signing key" | The service runs as a different account from the one that created the keys, or keys/ was lost. Run the service (and commands) as the original account or restore the data directory. As a last resort delete jwt-signing.key: all users will have to sign in again. |
Commands such as users list show no users, or a certificate created with cert create is not used | The command ran as another account or with another data directory. On Windows use the runas console; on Linux use the full sudo -u sqlstudio env Gateway__DataDirectory=… syntax; in Docker mount the same volume. |
| The gateway refuses to start: license key disabled or unknown | Check the key with license status and against your purchase email. If it is correct, contact support with the key and the installation id. |
| A user cannot sign in: "this account is beyond the limit" | The gateway has more accounts than the license allows. Remove unused accounts with users remove (users list marks the ones over the limit) or buy extra users on the Manage license page. |
users add fails: "The license allows N gateway users" | The user limit has been reached. Remove an account or buy extra users, then run the command again. |
| The gateway refuses to start: no free seat, right after moving the data directory, restoring a backup on another server or reinstalling Windows | The installation is bound to the machine it was activated on. Run license deactivate on the old server (or before reinstalling); if that is no longer possible, ask support to reset the installation, then run license activate again. |
| The gateway refuses to start: no free seat | The license is in use by another installation. Run license deactivate on the old server, or wait until the old installation frees its seat (30 days without contact), or contact support. |
| Log warning: license server unreachable; the gateway still runs | It is running on its last valid answer (up to 14 days). Allow outbound TCP 443 to sqlstudio.geolox.it; configure the Windows system proxy, or HTTPS_PROXY / NO_PROXY on Linux and Docker. license status shows when the grace period ends. |
| The gateway refuses to start: license server unreachable and last valid answer older than 14 days | Restore outbound HTTPS to sqlstudio.geolox.it as above, then start the service. |
license activate fails with a network error | Same network and proxy checks. On Linux add HTTPS_PROXY to the command after env; in Docker add -e HTTPS_PROXY=…. |
| Users see "The gateway license is not valid…" (HTTP 503) | The license has been disabled while the gateway was running (checked every 6 hours). Contact support using the link in the message. |
| A Docker container asks for activation again or uses another seat | It was started without the /data volume. Start it with -v sqlstudio-data:/data; contact support to free the extra seat if needed. |
| The download link says it has already been used or has expired | Links are valid 30 days until used; an interrupted download can be resumed for 30 minutes after clicking Download. Request a new link with the support form. |
| The app cannot reach the gateway (Test Connection fails) | Check DNS, the firewall rule for TCP 8443 and Gateway:Https:Address. From the user's network, curl -k https://gateway.contoso.local:8443/healthz must print Healthy. The URL must start with https://. |
| The app reports "The gateway certificate does not match the expected thumbprint" | The certificate changed. Run cert fingerprint and have users confirm it with Verify Certificate.... If you did not change the certificate, investigate a possible interception. |
| 403 "Server '…' is not allowed by the gateway's list of authorized servers." | Add the server to Gateway:AllowedServers in the same form used in the app, then restart. |
| 403 "Authentication using the gateway identity is disabled" | Gateway:AllowIntegratedSecurity is false (default). Use SQL Server or Entra authentication with credentials, or enable the setting (section 12). |
| Windows Authentication fails with a login error on SQL Server | The gateway's service account cannot authenticate to that server (for example a local account). Use a gMSA or domain account with a SQL login. |
Named instance (sql01\PROD) cannot be reached | SQL Server Browser (UDP 1434) is blocked between the gateway and SQL Server. Open it, or connect with server,port and allow that form. |
| 429 at sign-in | Too many attempts (5 per minute per IP and user) or the account is locked for 15 minutes. Wait, or run users unlock <name>. |
| "method not allowed" when changing database, or windows/connections that stay open | A proxy in front of the gateway (for example IIS with WebDAV, or a WAF) rejects PUT/DELETE with 405. Update the gateway (the app sends them as POST with X-HTTP-Method-Override, which the gateway accepts), or allow PUT and DELETE on the proxy (in IIS remove WebDAVModule and the WebDAV handler). |
| All connections are refused right after installation; startup warning about an empty allow-list | Gateway:AllowedServers is empty. Add your servers (section 10). |
16. Getting help
- Support form: sqlstudio.geolox.it/store/support.html (sends your request to support@geolox.it).
- Email: support@geolox.it.
Please include:
- the email address used for the purchase (or the licensee name) and, for license questions, the output of
license status(it contains the installation id); - the gateway version (from
/gateway/info) and the app version; - the system the gateway runs on (Windows Server, Linux distribution, Docker) and the device and OS version of the app;
- the SQL Server version;
- the exact text of the error message, with its
traceIdif shown, and the related lines of the gateway log.
Do not send passwords, TOTP secrets, the PFX file or the data directory. We usually reply within two business days.