Secrets

Write, list and delete the named values your app's functions read as environment variables.

Docs menu

Each secret is a key your functions read as env.<KEY> (an API token, a signing key). A value is written and overwritten, never read back: every list answers names only.

Operations

Operation Route MCP tool What it does
secret.write PUT /v1/_secrets (or POST) substrio_secret_write Sets key to value (both required, not empty), replacing a value already there
secret.list GET /v1/_secrets substrio_secret_list Answers secrets (each {key, value: "***"}) and count
secret.delete DELETE /v1/_secrets/{key} substrio_secret_delete Removes one secret: answers {success, deleted, key}

Each write and delete is recorded in the app's audit log. Both are irreversible: the value is never readable, so nothing can put it back (no undo token), and an agent's call to either asks for its approval like any change of authority.

The CLI: substrio secrets set KEY (the value from stdin, --from-file or --from-env, never an argument), substrio secrets list, substrio secrets delete KEY (asks before it runs; --yes skips the question).

Refusals

Code Status When Field
SECRET_NOT_FOUND 404 secret.delete of a key the app does not hold key
INVALID_ARGUMENT 400 secret.write without a key or a value

Worked example: an agent sets and removes a secret

Every call is a plain MCP tools/call with the account key on the account's MCP server; app names the app.

[
  {"name": "substrio_secret_write", "arguments": {"app": "shop", "key": "API_TOKEN", "value": "..."}},
  {"name": "substrio_secret_list", "arguments": {"app": "shop"}},
  {"name": "substrio_secret_delete", "arguments": {"app": "shop", "key": "API_TOKEN"}}
]

The delete answers {"success": true, "deleted": true, "key": "API_TOKEN"}. The same delete again is refused with SECRET_NOT_FOUND and field: "key", so the agent corrects the name from secret.list rather than retrying.

Worked example: a site and a function that reads the secret, shipped with no hash

The function reads the secret as env.API_TOKEN (the function's env, a global). Commit the function and the page to the app's history as text, so you compute no hash and upload nothing, then make the version the commit answered live (version 3 here):

[
  {"name": "substrio_version_commit", "arguments": {"app": "shop", "branch": "main", "message": "with a token",
    "units": [
      {"path": "functions/status.js", "text": "export default async () => new Response(env.API_TOKEN ? 'ready' : 'no token');\n"},
      {"path": "functions/status.json", "text": "{\"routes\":[\"GET /status\"]}"},
      {"path": "site/index.html", "text": "<!doctype html><h1>Shop</h1>\n"}]}},
  {"name": "substrio_live_set", "arguments": {"app": "shop", "version": 3}}
]

See versioning ("Developing over MCP": tree, read, edit, search, commit, promote) and static sites (large files uploaded with no hash).