Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Configuration & security

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.

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.

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.

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.

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.

DemoMode turns the whole app into a public playground — see Demo mode. It forces both ReadOnly and DisableAi on.

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.

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.