Securing a deployment¶
Presto AI Dash is built for one person's desk and one trusted network. It reads numbers, it never acts on a provider account, and it keeps your provider keys encrypted. It does not try to defend against someone who already has control of the machine or the network it runs on, so most of the work of securing it is choosing where it runs. This page says what to do, and what you accept if you do not.
The short version¶
- Run the daemon on a network you trust, and do not forward its ports to the internet.
- Let the daemon generate its tokens, or generate your own with
python -c "import secrets; print(secrets.token_urlsafe(24))". - Keep
dash.dband its key in a folder only your account can write to. - Leave the admin page on
127.0.0.1. - If a device or collector must reach the daemon from outside your LAN, put a private network such as Tailscale or a TLS reverse proxy in front of it.
Who can reach what¶
| Port | Default bind | What it gives | Who should reach it |
|---|---|---|---|
| Status, 8787 | 0.0.0.0 (the LAN) |
Your usage numbers and account names, and the one write the Presto makes: one service's alerts on or off | The Presto, the remote collectors, nothing else |
| Admin, 8788 | 127.0.0.1 |
Everything: accounts, provider keys, ingest tokens | You, in a browser on the same machine |
Anyone who holds the shared token can read the status payload. That includes your account names and quota percentages. They can also switch a service's alerts on or off. They cannot read a provider key or change anything else.
The shared token travels over plain HTTP. Anyone who can watch your network can read it. That is a deliberate trade for a device with no TLS stack, which is why the first rule is a network you trust. If you cannot say that of your Wi-Fi, use a private network (Tailscale, WireGuard) between the Presto's network and the daemon.
Concretely, someone who can capture traffic on that network (another device on the same Wi-Fi, a compromised router, a guest on a shared network) gets:
- the shared token, which lets them read your account names and quota percentages and switch one service's alerts on or off, from then on;
- every status payload as it is fetched.
They do not get a provider key, the admin page or any ability to act on a provider account, because none of those cross this port.
The Presto cannot use TLS, and that will not change soon. The device runs MicroPython with no certificate store, so there is no safe way to check a server's certificate on it, and encrypting without checking only hides the traffic from a passive listener. The mitigation is the network, not the device: a Wi-Fi network you control, a firewall rule, or a VPN. A TLS reverse proxy protects collectors and the admin page, but not the Presto's own fetches.
There is one shared token for every Presto. The daemon cannot tell devices apart or revoke one. If a device is lost or you stop trusting a network, rotate the shared token and update every device (see If something leaks). Per-device tokens are a possible future change, not a current feature.
Use the firewall to narrow the status port to the Presto and your collectors, for example on Windows:
New-NetFirewallRule -DisplayName "Presto AI Dash status" -Direction Inbound `
-Protocol TCP -LocalPort 8787 -RemoteAddress 192.168.1.60 -Action Allow
Tokens¶
There are three, and they are separate on purpose.
| Token | Held by | Can do |
|---|---|---|
| Shared token | The Presto | Read the status payload; set one service's alerts on or off |
| Admin token | You | Everything on the admin page |
| Ingest token | One remote collector | Write readings for one account, and nothing else |
If you let the daemon generate a token, it is long and random, and you need do nothing. If you choose your own, the daemon refuses to start with one that is under 16 characters, built from fewer than six different characters, or an example from this documentation. A token someone can guess is the one thing between the network and your usage data.
A wrong token is answered after a short pause and logged with the address it came from, never with the token. The warning appears in the log and in the admin page's Problems list. A run of them is either someone on your network guessing or a Presto or collector still holding an old token.
The temporary tokens the daemon generates when you set none are printed to the log at startup. That log is as sensitive as the token: do not paste it anywhere.
The admin page¶
The admin page binds to 127.0.0.1 and checks the Host and Origin of
every request, which is what stops a web page in your browser from driving it.
Keep it that way. To reach it from another computer, use an SSH tunnel to the
loopback address (ssh -L 8788:127.0.0.1:8788 host) instead of binding it to
the LAN.
If you must serve it on another address, the daemon requires an explicit
admin token, answers only to the names in allowed_hosts, and you should put
a TLS reverse proxy in front of it. Behind a proxy that terminates TLS, set
admin.secure_cookie = true (or DASH_ADMIN_SECURE_COOKIE=true) so the session
cookie is marked Secure and a browser never sends it over plain HTTP. Do not
turn it on for a plain-HTTP address: the browser would drop the cookie and
sign-in would not stick.
A browser session lasts seven days, and at most 25 are kept (the oldest go first). When you set the admin token yourself, changing it ends every session that was opened under the old one. With the temporary token the daemon generates, sessions deliberately survive a restart, so that Lock configuration, which ends the session in that browser only, is not needed on every start. To end all of them at once, open Settings > Browser sessions and press Sign out of every browser.
Treat admin access as the ability to run programs as the daemon's account.
An administrator can set which Codex executable the daemon starts, and the
daemon starts it on every poll. The setting must be a program named codex
on this computer, not a network share, but a file with that name can be put
anywhere, so this limits mistakes, not a determined administrator. That is by
design: the admin page is the configuration. It is why it must not be
exposed. The Codex process is started without the daemon's own secrets
(DASH_* variables and the provider key variable), so setting them in the
environment does not hand them to Codex.
The database and its key¶
dash.db holds your accounts, your provider keys (encrypted), your readings
and your browser sessions. dash.db.key holds the key that decrypts them.
The daemon creates both so only your account can use them, and it tightens an older database when it starts. On Windows that means your account, the system and the administrators; on Linux and macOS, mode 0600.
That protects the files, not the folder. Anyone who can write to the folder
can delete dash.db and put their own there, and a database they control
decides which program the daemon runs and which address your saved keys are
sent to. The daemon warns at startup, in the log and the Problems list, when
the folder is writable by Everyone, Authenticated Users or Users (Windows) or
by group or other users (Linux and macOS). Heed it:
- Keep the database somewhere only you can write, for example
%LOCALAPPDATA%\presto-ai-dash\dash.dbon Windows or~/.local/share/presto-ai-dash/dash.dbelsewhere, withdaemon.database. Create the folder first. The key file moves with the database by default, and an existing database and its key must be moved together. - On Windows, a folder under a data drive such as
D:\usually lets every signed-in user write to it. The profile folder does not. - Other accounts include the sandbox accounts that some coding agents run under. If an agent can write to your working folder, it can write there.
- Do not keep the database in a folder that is synced or shared.
The warning looks at the broad groups. It does not list a named account or a custom group that has write access, so read the folder's permissions yourself if you share the machine.
The database is not checked for tampering. Someone who can change it can change the daemon's configuration, because the daemon trusts what it finds there and does not sign its own rows. The control today is the one above: keep it in a folder only you can write to, and treat the Problems warning as a fault to fix, not a notice to ignore.
Back up the database and the key separately, and keep both. The daemon
will not start without the key it encrypted with. If the key is missing or
wrong, or a stored credential no longer decrypts with it, startup stops with
an error and the daemon neither makes a replacement key nor changes any
stored record. Restore the original key (the key file, or DASH_ENCRYPTION_KEY)
and start again. There is no automatic key rotation; to change a provider key,
replace it on the admin page. To keep the key off the disk entirely, set it as
DASH_ENCRYPTION_KEY (python -m presto_ai_dash.app --generate-key prints
one), and store it in your password manager or your service manager's secret
store.
Docker¶
The image runs as a non-root user and holds only the installed package. To keep it that way:
- Publish the status port on the LAN address only, not on every interface:
set
DASH_PUBLISH_ADDRESS=192.168.1.50in.env(it becomes"192.168.1.50:8787:8787"incompose.yaml). On Linux, Docker's own firewall rules bypassufwandfirewalld, so a published port is open to the whole network the host is on, whatever the host firewall says. - Leave the admin port published on
127.0.0.1only, as the example does. - The tokens in
.envare passed as environment variables, which anyone who can rundocker inspector read/procon the host can see. Keep that group small, or give the daemon the tokens as files instead (DASH_ADMIN_TOKEN_FILE,DASH_SHARED_TOKEN_FILE,DASH_ENCRYPTION_KEY_FILE, for example from Composesecrets:), which keeps them out ofdocker inspect. The compose file requiresDASH_SHARED_TOKEN, so a temporary one is never written to the container's log. - The data volume belongs to the container's
dashuser and its files are readable by that user only.
Remote collectors¶
A collector on another machine reports with its own ingest token, which can write one account's readings and nothing else. It travels over plain HTTP unless you put TLS in front. Over a network you do not control, use a VPN or a TLS reverse proxy. Revoke a token in the admin page when a machine is retired or lost, and create a new one for the replacement.
If something leaks¶
| What | Do |
|---|---|
| The shared token | Set a new daemon.shared_token, restart the daemon, and put the same value in the Presto's private config.py as described in Set up the Presto |
| The admin token | Set a new admin.token and restart. Every session opened under the old token ends. If you use the generated token, press Sign out of every browser in Settings as well |
| An ingest token | Revoke it in the admin page and create a new one |
dash.db and dash.db.key together |
Assume every stored provider key is exposed. Replace each key at the provider, then remove and re-add it |
| The log, if it held a temporary token | Set fixed tokens so the next start prints none |
What this does not cover¶
Exposing either port directly to the internet, hosting for several users, and protecting against an attacker who already runs code as your account on the daemon's machine. In each of those the tokens and the database are not the thing standing in the way.
See also¶
- Privacy: what is read, stored and sent
- Ports and endpoints
- Configuration keys and environment variables
- Install with Docker