Configuration & security
Settings
Section titled “Settings”| Setting | Environment variable | Default |
|---|---|---|
ShinyDocDbMyAdmin:DataDirectory |
SHINYDOCDBMYADMIN_DATA |
~/.shinydocdbmyadmin |
ShinyDocDbMyAdmin:SecretKey |
SHINYDOCDBMYADMIN_KEY |
generated into <data>/secret.key |
ShinyDocDbMyAdmin:ReadOnly |
ShinyDocDbMyAdmin__ReadOnly |
false |
ShinyDocDbMyAdmin:DisableAi |
ShinyDocDbMyAdmin__DisableAi |
false |
ShinyDocDbMyAdmin:DemoMode |
ShinyDocDbMyAdmin__DemoMode |
false |
ShinyDocDbMyAdmin:FrameAncestors |
ShinyDocDbMyAdmin__FrameAncestors |
unset (embedding refused) |
Connections themselves are configuration too — see where connections come from.
The data directory
Section titled “The data directory”Holds the tool’s own SQLite document store (connection profiles and saved queries — dogfooding the
library) plus any uploaded database files, under <data>/uploads/<profile-id>/.
In the container it’s /data, which is why the docker run examples mount a volume there. Drop the
volume and the UI starts empty every time.
Connections handed over by a host are never written to that store at all — they live in memory for as long as the process does.
The terminal front end defaults to the same directory and reads the same
store, so a connection saved in one appears in the other. It adds two files of its own there:
tui-settings.json (theme and page size — preferences that belong to one front end, not to the shared
surface) and shinydocdb.log.
Secret key
Section titled “Secret key”SecretKey encrypts the secret-bearing parts of a saved profile — connection strings, SQLCipher keys and
assistant API keys — with AES-GCM. Either a base64 32-byte key or a passphrase.
The key is per instance. A web app in a container and a terminal tool on the host generate different
ones, so they cannot read each other’s saved secrets even pointed at the same volume — set SecretKey to
the same value in both if you want them to, or move connections with a
passphrase-encrypted bundle, which is
what it is for.
From an Aspire AppHost, WithSecretKey takes a parameter, which is the
supported way to pass one without putting it in the compose file.
Read-only
Section titled “Read-only”ReadOnly blocks every write path for every host-provided connection, which is the setting you want
whenever the tool is pointed at something you didn’t create five minutes ago. Individual saved connections
carry their own flag — see read-only
connections for exactly what it closes.
Disable AI
Section titled “Disable AI”DisableAi removes the assistant entirely — no tab, no settings link, and
the services aren’t registered at all, so the routes refuse to render rather than being merely unlinked.
Demo mode
Section titled “Demo mode”DemoMode turns the whole app into a public playground — see Demo
mode. It forces both ReadOnly and DisableAi on.
Frame ancestors
Section titled “Frame ancestors”Blazor’s interactive server render mode blocks embedding by default, sending both
Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN. That is the right
default here: a page that can read and write your databases should not be loadable into someone else’s.
FrameAncestors opts out. Its value becomes the frame-ancestors policy verbatim — or any (equivalently
*) to allow every embedder — and the X-Frame-Options header is dropped alongside,
since that one has no directive to relax and would otherwise block the embed on its own.
The Docker Desktop extension is what this exists for: it sets any on
the container it creates so the app can render inside its tab. Leave it unset for anything reachable
beyond your own machine — giving up clickjacking protection on a tool with no authentication in front of
it is not a trade worth making.
Safety
Section titled “Safety”There is no authentication in this tool. It is a database client, and it assumes the network in front of it is doing the access control — put it behind whatever you’d put a database admin console behind.
What it does defend against:
Destructive clicks. Deleting a connection and bulk-deleting documents each take two clicks. Passwords are masked wherever a connection string is displayed.
Other people’s bytes. Blob payloads come from whoever wrote the document, so serving them from our own
origin is a real XSS surface. Every response carries nosniff and a default-src 'none' CSP, and
anything not on the short raster-image allowlist is sent as an attachment rather than rendered — SVG
deliberately included, because it’s a document format that can carry script. Document values are
HTML-escaped everywhere they’re rendered.
A quietly wrong audit trail. Writes to a temporal-mapped type record a version, stamped with the actor
shiny-docdb-myadmin — see History.
Secrets in an export. The connection bundle leaves secrets out by default, and encrypts them under a passphrase you type when you opt in.


