Concepts
Seven short ideas. Read them once and the CLI, the dashboard and the MCP server all make sense.
Services
A service is one app or deployment: its variables, how each applies, and the tokens that can read it. You make one per thing you run (web, worker, scraper-eu).
The dashboard and CLI still say process for a service: dynv admin add, DYNV_PROCESS, the Processes page. Same object. Names are encrypted in your browser, so we only ever see a random id.
Variables
A variable is a NAME=value pair. Names are letters, digits and _, cannot start with a digit or DYNV_, and are at most 256 bytes. Names and values are both encrypted on your machine before they leave it.
A service's whole configuration, names included, is stored as one encrypted document, so we cannot tell how many variables it has.
Live (hot) or restart, per variable
Every variable has an apply mode. A service's default is restart.
| Mode | What happens on a change | Your code |
|---|---|---|
restart | dynv run stops your process (SIGTERM, then SIGKILL after --grace seconds) and starts it again with the new env. About a second end to end. | No changes. Reads process.env, os.environ, anything. |
hot | Nothing restarts. dynv run updates a loopback agent, and the new value is there the next time your code asks. | Reads through the SDK (or the local agent). A plain process.env read in a child started by dynv run keeps the value it started with. |
If one change touches any restart variable, the process restarts once. If it touches only hot ones, it does not. Set the mode when you write, or change it later:
dynv admin set web PROXY_SERVER=http://proxy.example:8000 --hot
dynv admin mode web restart DATABASE_URLRemoving a variable is remembered with a signed marker, so a deleted variable never falls back to a stale value from the environment your container started with.
Shared variables
A shared group holds variables you define once, for example PROXY_SERVER. You attach the group to the services that should use it. Change a value in the group once and every attached running service gets it live, hot or restart per variable, the same as today.
When the same name exists in more than one place, the service's own variable wins, then a group attached later, then a group attached earlier. Shared groups are encrypted the same way as everything else, so we still cannot read them.
Create one from Variables → New variable in the dashboard, or with dynv admin shared create and dynv admin shared attach. Detaching a service, or revoking one of its tokens, rotates the group's key, so it loses access immediately. A service needs a dynv from the current installer to receive shared variables; older versions keep working but ignore them.
Variable references
A variable can be marked as a template and built from other variables. ${NAME} is the value of another variable of the same service, including one from an attached shared group, and $$ is a literal $. Change DB_HOST once in a group and every service's DATABASE_URL follows, live.
dynv admin set web --hot --template 'DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/app'
dynv admin show web # the template, the (masked) result, and its apply modeOnly variables you mark as templates are resolved, so existing values that contain $ keep their meaning. Templates are resolved on your machines after decryption, so we still see nothing. A template can use other variables of the service and its groups, never the environment your container started with. A template is restart if it or anything it uses is restart. Dynv refuses a change that would break a template: a cycle, a name that doesn't exist, or removing a variable a template needs. If a template still breaks in a running service, the service keeps its last good value and logs the variable's name.
Tokens
Dynv has no usernames and passwords for machines. Access is by token, and there are two kinds.
| Token | Looks like | Who holds it | What it can do |
|---|---|---|---|
| Service token | dynv_pt_… | One deployment. It becomes DYNV_TOKEN. | Read exactly one service. Never write. Cannot see any other service: the keys for the others are never sealed to it. |
| Master token | dynv_mt_… | You. Your recovery kit is one; so is each passkey, internally. | Open your account's keys. A writer master token can change everything; a recovery or viewer one can only read. See roles. |
- One token per deployment (or per replica set), so a leaked one is revoked alone. Each shows when it was last seen.
- Shown once. We store only a one-way hash of what proves you hold it, so we cannot show it again or recover it.
- Revoking is immediate, and the service's data key is rotated in the same step, so nothing written afterwards is readable by the revoked holder. The running service keeps its last good env; revoking never takes a service down. Revoking does not un-leak what the holder already saw: rotate the upstream secret too.
dynv runremovesDYNV_TOKENfrom your app's environment. Only the supervisor holds it.
Scoped agent tokens for automation are Coming soon. Today an AI agent uses your master token through the local MCP server, with limits you set.
Members and roles
Invite people from Members in the dashboard. The invite is a link whose secret sits after the #, which browsers never send to a server; it works once and expires (24 hours at most for a viewer). There are three roles:
| Role | Can | Cannot |
|---|---|---|
| Owner | Everything: create and delete services, change variables, mint and revoke tokens, invite people. | - |
| Recovery | Read and export everything. Revoke any master token (to cut off a compromised owner). | Write, invite. |
| Viewer (invited member) | Read and export everything. Revoke their own devices. | Write, invite, revoke anyone else. |
Zero-knowledge: what we can and can't see
Encryption happens in your browser, the CLI or the MCP server, with keys derived from your tokens. The token secret is never sent to us; only a value derived from it that proves you hold it.
| We can see | We cannot see or do |
|---|---|
| Random ids, how many services and tokens you have, timestamps, which token fetched when and from which IP, rounded-up blob sizes (1 KiB buckets), audit events, and your email's keyed hash plus the address itself while sending you a code. | Variable names or values. Service names. Token labels. How many variables a service has. Your passkey secret or any token secret. We also cannot forge or alter a service's variables: they are signed with a key we do not hold. |
The audit log therefore records token, service id, time and IP, but never a variable name.
Runs when we're down
Dynv is not in your request path. Your service reads its env at start and then listens for changes.
- Already running: keeps its env. Changes resume when Dynv is back.
- Restarts while Dynv is down, with a cache: start with
--cache /path/dynv.cache(orDYNV_CACHE).dynv runboots from the last version it received. The file holds ciphertext only, mode0600. - Restarts while Dynv is down, no cache: it starts with the environment it already has (what your platform injects). Add
--requireto fail instead. - Stale or forged data: versions are signed and only go up.
dynv runrefuses an older version, or different content at a version it has seen, and keeps the last good env.
dynv run --cache /var/lib/myapp/dynv.cache -- node dist/api.jsLimit: a server could withhold updates indefinitely, and a process with no cache can be served an old (but validly signed) version once at first boot. Both are covered in Security.
If you lose your keys
That is the cost of zero-knowledge: we cannot recover a lost master token or your recovery kit. Keep the recovery kit offline, and create a second recovery token with dynv admin master --out recovery.token if you want a spare.