Use Oxys DB Studio in a browser
Run the web gateway on your machine and use the app in a browser: one WebSocket, every database connection made by the gateway.
The browser version is the desktop app compiled to WebAssembly. The page itself reaches nothing but the web gateway, a small program that runs next to it: the gateway opens the MongoDB connections, SSH tunnels and files, with the same code as the desktop app, and the page talks to it over a single WebSocket. Your passwords and drivers never reach the browser.
Start it
The gateway listens on 127.0.0.1 on a free port and opens your browser on an address such as
http://127.0.0.1:41234/#token=3f9c…. The part after # is an access token made for this launch
only. The page keeps it for the tab, removes it from the address bar, and offers it when it connects.
Do not share that address. Stop the gateway with Ctrl+C.
The gateway uses your usual Oxys DB Studio folders, so your saved connections are there. Only one page is connected at a time: opening the address in another tab disconnects the first one.
From a source checkout, cargo xtask web --open builds the WebAssembly bundle and starts the gateway
on it.
Options
| Option | Effect |
|---|---|
--open | open the page in the default browser |
--bind ADDR:PORT | where to listen (default 127.0.0.1:0) |
--assets DIR | folder with oxysdb_web.js and oxysdb_web_bg.wasm |
--tls-cert FILE, --tls-key FILE | serve HTTPS and WSS with this PEM certificate and key (reloaded when they change) |
--tls-pq prefer or require | post-quantum key exchange policy (see TLS) |
--origin URL | an address browsers will use, such as https://db.example.com:8443 (repeatable) |
--staging DIR | where files you open or save in the browser are kept during a session (default: the system’s temporary folder) |
--max-file-mib N | the largest file you can open or save in the browser (default 1024) |
--files-quota-mib N | all the files of one session together (default 2048) |
--allow-local-processes | let pages run this machine’s programs (IntelliShell, external tools, SQL drivers) and use its key files |
--ai-endpoint URL | an AI provider address pages may use besides the providers’ own (repeatable) |
--allow-network-settings | let pages change the gateway’s proxy settings |
Opening and saving files
File dialogs are your browser’s. When you open a file, such as an import source, a pipeline or a script, the browser asks you to choose it on your computer and uploads it to the gateway, for your session only. The banner shows the upload’s progress, and the dialog continues on its own when the file is there. Most dialogs do. If one does not, click its button again once the upload is done.
When you save a file, such as an export or a script, no file dialog opens: when the export or save is done, the file comes back to your browser as a download, under the name it was given.
Folders work too. When a dialog asks for a folder to open, such as a mongodump folder to import,
the browser asks you to choose a folder and uploads every file in it (hidden files are left out),
keeping its sub-folders. If one file cannot be uploaded, nothing of the folder is kept. When a dialog
asks for a folder to save into, such as the export’s target folder, a mongodump folder export or
Save To Disk of several GridFS files, no dialog opens: when the export is done, the whole folder
comes back as one download, a zip file named after the folder.
The page never names a file on the gateway’s machine. A path typed into a file field is refused, and imports and exports read and write only the session’s own folder. That folder is private to the gateway’s user and deleted when the session ends. Commands that would reach other files of the gateway’s machine, such as log folders, fonts, reports, task definitions and Load the connections from a local folder, are not available in the browser.
Programs and key files
A browser page cannot start programs on the gateway’s machine: IntelliShell, script tasks, tasks,
external mongodump and mongorestore, SQL drivers and startup scripts say they are not available
(imports and exports use the built-in tools). A connection cannot use the machine’s TLS certificates,
SSH keys, known_hosts file or SSH agent either: choose the key file with Browse…, which uploads it.
Nor can it sign in with the machine’s own identity: AWS without an access key, a cloud workload
identity (OIDC ENVIRONMENT) or Kerberos, whose tickets and keytabs are the machine’s.
When you save the connection, the gateway keeps each uploaded key file (root CA file, client certificate, SSH private key) in its credential store, with the connection, like a saved password: the field then shows the file’s name and says the gateway keeps it. The page can never read a kept key back, and exports, connection strings and shared connections never include it. A kept key goes only where it was saved for: if you change the connection’s server, SSH server or security settings, the gateway forgets the keys and you upload them again. To connect, the gateway writes the key to a private file that only it can read, removed when your session ends. Through an SSH tunnel, the connection keeps its own list of known host keys: the first time, you are asked to accept the SSH server’s key, and it is remembered for that connection. Oracle, DB2 and Sybase connections, which need a helper program, are not available. The menus of the browser version hide what the gateway refuses.
The gateway’s proxy settings, and the AI Helper’s proxy, belong to whoever runs it: a page cannot
change them unless the gateway runs with --allow-network-settings. Everywhere, a saved password
goes only to the server it was saved for: changing a proxy, a connection’s server, an SSH server or an
AI provider’s address forgets the saved password or key, and asks for it again.
Saved passwords and passphrases never reach the page: exporting connections with their passwords, copying a connection string with its password and sharing a connection with its password are not available in the browser.
The AI Helper sends the gateway’s API key only to the providers’ own addresses. To use another
address, such as a company proxy, start the gateway with --ai-endpoint https://llm.example.com.
--allow-local-processes lifts this. Use it only on a gateway that serves you alone: IntelliShell runs
mongosh, which can read and run anything the gateway’s account can, so anyone who holds the page’s
address and token can then do everything that account can do on that machine. It cannot be used
with --public-demo.
Scheduled tasks
A browser page has no scheduler of its own: in the browser, Schedule hands the task to the remote task runner, which runs it on its schedule, whether a page is open or not. The Task schedule editor says so. When you click Save, the task, its schedule and the connections it uses go to the runner, and the status bar says the task is scheduled on the remote task runner. If the desktop app had a local schedule for that task, that schedule is turned off, so the task never runs twice. The task runs on the runner’s machine, so its files and exports are read and written there, in the runner’s files folder, not on your computer and not on the gateway’s.
The runner is set up by whoever runs the gateway, on the gateway’s machine: in the desktop app,
Preferences > Remote Task Runner (its address and access token), or with oxysdb-cli run runner.configure. A page cannot see or change that setting, because the runner receives the saved
passwords of the tasks’ connections. Until a runner is set up, Save says what to configure and
changes nothing.
In the browser, the Tasks view lists your tasks and their runs, schedules them on the runner, sends
them with Run on Remote Runner, and shows, runs and deletes the runner’s tasks. Running a task in
the gateway itself, editing, unscheduling and deleting tasks are not available, even with
--allow-local-processes. The public demo has no tasks.
When you open the Task schedule editor again for a task the runner holds, it shows the runner’s schedule, and says so. If the runner does not answer, the editor opens on a new schedule and says why.
Serving it to other machines
To bind anything other than the loopback address, you must pass a certificate. When you bind
0.0.0.0, you must also pass --origin with the name on the certificate. Without them, the gateway
refuses to start. Anyone who has the printed address can use your connections, so send it only over
a channel you trust.
What the gateway enforces
- The WebSocket is accepted only from the gateway’s own page (
Origin), and only with the token. - Requests for any other host name are refused, which blocks DNS-rebinding attacks.
- The page may connect to the gateway only, and no other site may frame it (Content-Security-Policy).
- Messages are limited in size and rate. A page that exceeds a limit is told why and disconnected.
- Read-only connections refuse writes in the engine, as in the desktop app.
The website’s demo
The demo on this website is the same browser version, served by a gateway in public demo mode against a MongoDB server used for nothing else:
Behind a reverse proxy, bind the gateway to an address only the proxy reaches, and give both the
header and the proxy’s address or network: --client-ip-header X-Forwarded-For --trusted-proxy 10.0.0.5 (or 10.0.0.0/24). The header is ignored on connections from anyone else, so give the proxy
a fixed address: otherwise every visitor counts as the proxy, and one of them can fill the demo.
For a container’s health check, give the gateway a listener of its own on the loopback,
--health-port 127.0.0.1:9090, and ask it with oxysdb-web-gateway --health-check 127.0.0.1:9090.
Visitors cannot reach or fill it. In the public demo, if the gateway stops responding for 30
seconds, it exits with an error, so a restart policy (restart: unless-stopped) brings it back. A
paused machine does not count. Other gateways never exit on their own.
The gateway reads the page’s files when it starts: restart it after rebuilding them. If gzip and
brotli are installed, cargo xtask demo also writes compressed copies, which the gateway sends to
browsers that accept them. The page asks for the files under a folder named after their content
(/pkg/<hash>/), so browsers can keep them until the next build. Behind a reverse proxy, let the
proxy serve those files from disk, as the sample does: its deploy-bundle.sh publishes each build
into a folder of its own, then restart the gateway. The TLS key file must be
readable by its owner only (chmod 600).
The file holds the administrator’s connection string and must be readable by its owner only
(chmod 600). Each visitor gets a MongoDB database of their own, with a user that may only read and
write that database (and set its validators), filled with invented sample data. Visitors never share a database, and none can
see another’s. The page has exactly one connection, to that database.
Inside that database a visitor uses the app as on the desktop: documents, queries, aggregations, SQL Query, indexes, schema, explain plans, value search, Data Compare between their own collections, saved queries and Collection History. What the session keeps (Collection History, comparison results, saved queries) is in its own small folder on the gateway, never shared, bounded, and removed when the session ends: Collection History keeps at most 2 MiB (512 KiB per action, larger deletes and updates ask to go ahead without keeping their documents), comparisons keep at most 4 MiB of results, and saving is refused once the folder holds 6 MiB.
Adding or editing connections, importing and exporting files, the shell, tasks, the AI helper,
Map-Reduce (it needs JavaScript on the server, which the demo server runs without), users, roles,
the profiler, server status and current operations, preferences, and anything else that would reach
files, programs or other servers are not available in the demo. Their menu entries and toolbar
buttons stay disabled, with a tooltip saying so (“Not available in the online demo — download the
app”), and the gateway refuses them, whatever the page sends. Server-side JavaScript, $out and
$merge are refused, wherever they are written. Every query, aggregation and explain is limited to 10 seconds,
pipelines cannot spill to disk, and longer writes are stopped. A visitor can keep at most 12 tabs
open, and each session’s writes and pages are bounded.
| Option | Default | Effect |
|---|---|---|
--demo-max-sessions N | 50 | visitors at once |
--demo-per-ip N | 5 | new sessions per address in 10 minutes (an IPv6 /64 counts as one address) |
--demo-live-per-ip N | 2 | sessions open at once per address |
--demo-idle-minutes N | 30 | a session without activity ends |
--demo-max-minutes N | 120 | a session ends whatever happens |
--demo-size-cap-mib N | 64 | past this size, the visitor’s database becomes read-only |
--demo-scratch DIR | temporary folder | where the sessions’ small working folders go; prefer a disk with a quota |
--demo-allow-no-time-limit | off | serve even if the server cannot limit reads’ time (MongoDB older than 8.0) |
--client-ip-header NAME | none | the header your reverse proxy sets to the visitor’s address |
--trusted-proxy IP[/N] | none | the proxy’s address or network, the only one whose header is believed (repeatable) |
--health-port ADDR | none | a plain health listener on the loopback, GET /healthz |
--demo-memory-mib N | 2048 | the gateway’s memory past which new demos wait and the newest ends |
--demo-allow-server-js | off | serve even if the server runs JavaScript (not recommended) |
When a session ends, its database and user are dropped. A database past 64 MiB, 40 collections or 200 indexes becomes read-only. When the gateway starts, and every 10 minutes while it runs, it drops any demo database no session owns. For that reason, run only one gateway per demo server.
Prepare the demo’s MongoDB server for strangers. deploy/public-demo/ in the source has a sample
mongod.conf and compose.yaml that do all of this, with a reverse proxy in front (its
README.md lists the steps). The compose file builds the gateway’s container image from
deploy/docker/web-gateway.Dockerfile (docker compose build gateway, linux/amd64 or linux/arm64),
or uses the one OXYSDB_GATEWAY_IMAGE names. The image runs as a non-root user without a shell, and
holds the page’s files too; the sample runs it as the owner of its secrets (OXYSDB_DEMO_UID,
OXYSDB_DEMO_GID). Its
make-secrets.sh writes the passwords, the replica set key and the gateway’s certificate into
secrets/, which git ignores. Its make-volume.sh makes the data’s filesystem. The server’s
start script creates the replica set and the administrator whenever they are missing, so the demo
comes back by itself after a restart, even on an empty volume. It refuses a data folder that
make-volume.sh did not mark, so MongoDB never writes to the host’s own disk by mistake:
- MongoDB 8.0 or later. The gateway refuses to start otherwise.
security.javascriptEnabled: false. The gateway checks this and refuses to start otherwise.setParameter: allowDiskUseByDefault: false, so sorts and groups stop at their memory limit instead of writing to disk. The gateway checks this too. The sample also lowers those limits.- A small WiredTiger cache (
storage.wiredTiger.engineConfig.cacheSizeGB). - Its own disk filesystem with a size limit, not tmpfs: tmpfs counts against the container’s memory and comes back empty after a restart. This is the hard cap: the gateway stops each visitor at 64 MiB, but only the filesystem’s size protects the server as a whole.
- A small oplog (
replication.oplogSizeMB), counted in that size. - A private network that only the gateway can reach.
- A short expiry for change stream pre-images (
changeStreamOptions).
Keyboard shortcuts
The browser version has the same keymap as the desktop app. While the app has the focus, the page keeps the shortcuts it binds for itself: Ctrl+E opens the value editor instead of the address bar, Ctrl+R refreshes the view instead of the page, F5 runs the query. Shortcuts the app does not bind stay the browser’s (zoom with Ctrl++, the developer tools, F11), and Ctrl+C, Ctrl+V, Ctrl+X copy and paste through the browser. If you remove a shortcut in Preferences › Keymap, the browser gets it back.
A few shortcuts never reach a web page: the browser keeps them for its own windows and tabs. The browser version binds an alternative instead. The menus show it, and Preferences › Keymap lists it after the shortcut it replaces:
| Shortcut the browser keeps | In the browser version | Used for |
|---|---|---|
| Ctrl+N | Alt+N | Connect (Connection Manager), New Task |
| Ctrl+Shift+N | Alt+Shift+N | Add new stage (Aggregation Editor) |
| Ctrl+T | Alt+T | Hide/show columns, New mapping |
| Ctrl+F4, Ctrl+W | Alt+W | Close tab |
| Ctrl+Shift+W | Alt+Shift+W | Close tab without asking |
| Ctrl+PgDn, Ctrl+PgUp | Alt+PgDn, Alt+PgUp | Next tab, previous tab |
On macOS, ⌘ replaces Ctrl and ⌥ replaces Alt. While you type in a text field on macOS, ⌥ with a letter types its character, as usual. If you give one of these alternatives to another action in the keymap, it belongs to that action.
In Chrome and Edge, two modes give the page the shortcuts above as well: full screen (F11), where the browser keeps only the keys that leave full screen or quit, and the page opened in its own app window (installed, or a shortcut opened as a window), where it keeps none. The alternatives keep working there. We have not checked this in Firefox or Safari.
Limits
The system clipboard and opening files with other programs work as well as the browser allows. Scheduled tasks run on the remote task runner, not in the gateway (see above). An upload under way when the connection drops must be started again.
When the connection drops (a network change, a laptop waking up), the page says so and reconnects on its own, a few times over about two minutes, to the same session: your open connections and tabs are still there. An action you started just as the connection dropped may say it was not delivered: run it again. If the gateway was restarted, or the page was away longer than that, reload the page.