store_secret
Generated automatically from the published contract sources.
Build metadata: Registered in this build. Registration describes the default dispatcher in this build. It does not guarantee that a caller has the required credential or grant, that a tool is listed for that connection, or that the required service is configured.
A script calls this action over HTTPS at POST https://turnzero.ai/api/v1/actions/store_secret, with a bearer credential and the action's payload as the JSON body.
Contract description
Store a secret value under a name. This tool takes no value, since a host may record a tool's arguments in its transcript. A call naming no `generate` stores nothing: it answers the state `awaiting_value` with `command`, one line that sends the value from the developer's machine, and `command_windows`, the same line for Windows. Run the one for your system once, as given, before the answer's `expires_at`.
Name `value_file`, a file on that machine that holds the value, and the command reads it there, so your tool can run the line itself. Name none, and the command asks for the value at a terminal, so the builder runs the line in a terminal of their own.
For a value nobody chooses, such as a session signing secret, name `application` and `generate` (`base64url_32` or `hex_32`): the platform creates 32 random bytes in custody at that scope in this call, and answers the form, never the value.
With `application`, an optional `environment` (`development` or `production`; absent, `production`) chooses which environment's scope of that application holds the value. One name may stand at both environment scopes of one application, each with its own value. Without `application` the value is held at the account scope, which the egress gateway reads for both environments and no binding reads. Otherwise a name stays at the scope it first landed at: storing it at the account scope, at another application's scope, or from the account scope at an application's is refused `scope_fixed`.
The value is write-only — no tool ever shows it again — and everything else refers to it by name. An outside API's key is declared with `declare_upstream` where you may edit the client to read its base URL and key from settings, and bound in the manifest's `settings` only where its host is fixed in code you must not change. The rest is on the page /cloud/reference/actions/store-secret/, which `read_documentation` reads as `page` and the platform's origin serves.
More about this action
Storing an existing name again at the same scope replaces its value; `rotate_secret` does the same and refuses a name that was never stored.
The line pipes a short-lived grant for that one write to the turnzero-cloud command, which reads the value on that machine and sends `POST /api/v1/actions/store_secret` on the origin this server answers at. The command takes the value from no command line, and prints neither the value nor the grant.
A stored value reaches code in one of two ways. A value an upstream names is applied by the egress gateway at its edge and never enters the container, so the code calls the gateway to use it. A value the manifest's `settings` bind is injected into the container as that setting at the environment's next deploy or promote, and at a `restart_application` where the running copy already carries the binding. A name may stand at both environment scopes so development can hold a test key and production a live one.
A call naming `generate` refuses a name that stands at its scope `secret_exists` and never replaces a value. No tool or person ever reads a created value, so a secret an outside service must also hold, such as a webhook's signing secret, is supplied through `value_file` or at a terminal instead. A created value is replaced by storing a new name with `generate`, binding the setting to it, deploying, and deleting the old name with `delete_secret`; a later store or rotation carrying a value replaces it for good. A store that would add a name to an application's environment scope already holding 100 names is refused `secret_count_limit`.
Access and action metadata
{
"name": "store_secret",
"resource": "secret",
"tier": "reversible",
"clients": [
"bearer",
"browser_session"
],
"summary": "Put a value into custody under a declared name. The value travels as `POST /api/v1/actions/store_secret` on the origin this server answers at, never as a tool's argument, since a host may record a tool's arguments in its transcript. A call naming `generate` and an application has the platform create the value in custody and answers none. A call naming no `value` and no `generate`, through a tool or on that route under a bearer credential of your own, stores nothing. It answers `command`, one line that sends the value from the developer's machine under a short-lived grant, and `command_windows`, the same line for Windows. The turnzero-cloud command the line runs reads the value from the file `value_file` names or at a terminal, never from its command line, and prints neither the value nor the grant.",
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"openWorldHint": false
}
}
MCP catalog entry
{
"name": "store_secret",
"tier": "reversible",
"scenario": "CHI-L0-08",
"summary": "Store a secret value under a name. This tool takes no value, since a host may record a tool's arguments in its transcript. A call naming no `generate` stores nothing: it answers the state `awaiting_value` with `command`, one line that sends the value from the developer's machine, and `command_windows`, the same line for Windows. Run the one for your system once, as given, before the answer's `expires_at`.\n\nName `value_file`, a file on that machine that holds the value, and the command reads it there, so your tool can run the line itself. Name none, and the command asks for the value at a terminal, so the builder runs the line in a terminal of their own.\n\nFor a value nobody chooses, such as a session signing secret, name `application` and `generate` (`base64url_32` or `hex_32`): the platform creates 32 random bytes in custody at that scope in this call, and answers the form, never the value.\n\nWith `application`, an optional `environment` (`development` or `production`; absent, `production`) chooses which environment's scope of that application holds the value. One name may stand at both environment scopes of one application, each with its own value. Without `application` the value is held at the account scope, which the egress gateway reads for both environments and no binding reads. Otherwise a name stays at the scope it first landed at: storing it at the account scope, at another application's scope, or from the account scope at an application's is refused `scope_fixed`.\n\nThe value is write-only — no tool ever shows it again — and everything else refers to it by name. An outside API's key is declared with `declare_upstream` where you may edit the client to read its base URL and key from settings, and bound in the manifest's `settings` only where its host is fixed in code you must not change. The rest is on the page /cloud/reference/actions/store-secret/, which `read_documentation` reads as `page` and the platform's origin serves.",
"owners": [
"EGW-L0-03",
"MAN-14",
"SCRT-L0-01",
"SCRT-L0-08",
"SEC-L0-07",
"SEC-L0-20",
"SEC-L0-21"
]
}
request
| JSON pointer | Description and constraints |
|---|---|
| "" (root) | Type: object Required fields: ["name"] |
| / |
The name to store under: a letter or digit first, then letters, digits, `_` or `-`, up to 64 characters. A name keeps the scope it was first stored with, except that it may stand at both environment scopes of one application, each holding its own value. Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
The secret value. Sent once; never read back. Taken on the HTTP action route alone: the MCP tool declares no `value`. A call naming no `value` and no `generate`, through the MCP tool or on the HTTP route under your own credential, stores nothing: it answers the state `awaiting_value` and the command that sends the value from your machine. A browser session's call must carry it, or name `generate`. Type: string Minimum length: 1 Maximum length: 20000 HTTP action route only, never an MCP tool argument: true |
| / |
Optional, on a call naming no `value` alone: the file on your machine that holds the value, absolute or relative to the folder the command runs in, which the answered command reads. Absent, the command asks for the value at a terminal. The platform never reads the path. Keep the file outside the application's folder, which a deploy zips, and delete it once the command ends 0. A path holding a control character, a double quote, `$`, a backtick, `%`, `!`, `&`, `|`, `<`, `>`, `^`, a typographic double quote, a doubled backslash, or a trailing backslash is refused `invalid_request`. So is `~` or a path opening with `~/` or `~\`, which no shell expands in double quotes. So is one named beside `value` or beside `generate`. Type: string Minimum length: 1 Maximum length: 1024 Pattern: ^(?:[^"$`%!&\|<>^\u201c-\u201e\u0000-\u001f\u007f\\]|\\[^"$`%!&\|<>^\u201c-\u201e\u0000-\u001f\u007f\\])+$ |
| / |
Optional, with `application`: the platform creates the value itself in custody, for a value nobody chooses, such as a session or webhook signing secret, and the call answers none. It is 32 random bytes, `base64url_32` as 43 base64url characters for most uses, `hex_32` as 64 lowercase hexadecimal characters where a library reads a key in hex. It lands at the one environment scope `application` and `environment` name, and no action ever answers it. A name that stands at that scope is refused `secret_exists`, and a created value is replaced by a new name. Named beside `value` or `value_file`, or with no `application`, it is refused `invalid_request`. Type: string Allowed values: ["base64url_32","hex_32"] |
| / |
An application id, to hold the secret at that application's scope; omitted, the secret is held at the account scope. Type: string |
| / |
Optional, and it rides `application`: the environment whose scope of that application holds the secret, `development` or `production`; absent, `production`. Ignored where `application` is absent, because the account scope carries no environment. Type: string Pattern: ^(development|production)$ |
response
| JSON pointer | Description and constraints |
|---|---|
| "" (root) | Type: object Required fields: ["contract_version","secret"] |
| / |
Required value: 1 |
| / |
The short reference the platform recorded this call under, ten lowercase hexadecimal characters, the value the call’s record row carries; quote it when reporting the call. Type: string Pattern: ^[0-9a-f]{10}$ |
| / |
Type: object Required fields: ["name","scope","stored"] |
| / |
Type: string |
| / |
Type: string |
| / |
True on the answer to a call that carried a value or named `generate`: the value is in custody. False with the state `awaiting_value`, where nothing is stored until the answered command runs. Type: boolean |
| / |
Type: boolean |
| / |
Present where the call named `generate`: the form the platform created the value in. The value itself is never answered. Type: string Allowed values: ["base64url_32","hex_32"] |
| / |
Type: ["string","null"] |
| / |
The environment of the application scope that holds the value; null at the account scope. Type: ["string","null"] |
| / |
Present on the answer to a call naming no `value` and no `generate` alone: `awaiting_value`. The call stored nothing and minted a short-lived grant for the one write the answered command makes. Type: string Pattern: ^awaiting_value$ |
| / |
Present with the state `awaiting_value`: when the grant in the command stops serving, as an ISO 8601 instant. A command run after it is refused `secret_grant_expired`, or `authentication_required` once the platform has removed the grant's record, and a new call naming no `value` answers a new one. Type: string |
| / |
Present with the state `awaiting_value`: one line, for macOS and Linux. It reads `echo <grant> | npx -y <origin>/packages/turnzero-cloud-<version>.tgz secret store --name <name>`, then `--application <application id> --environment <environment>` where the call named an application, and `--origin <origin>` where the origin is not `https://turnzero\.ai\`\. It ends with `--value-file "<value_file>"`, or with `--value-prompt` where the call named no `value_file`. Run it once, as given, from the folder a relative `value_file` is relative to. The turnzero-cloud command reads the grant on its standard input, reads the value from the file or at the terminal, and sends it once. It ends 0 where the value was written, 2 where that is unknown, and 3 where nothing was written. Its one write spends the grant; a second write under it is refused `secret_grant_spent`, and a write naming another name or scope `secret_grant_not_admitted`. Type: string |
| / |
Present with `command`: the same line for every Windows shell, with `npx.cmd` where its head says `npx`. On Windows, run this one in `command`'s place, once, as given. The two are one command line, so a run of either spends the grant. Type: string |
| / |
Present with the state `awaiting_value` where the tool call's arguments carried a `value`. It says that the platform neither read nor stored that value, that a host may keep a tool call's arguments in its transcript, and that the builder should treat the value as exposed. It names neither the value nor its length. Type: string |
| / |
Type: string |
| / |
$ref: #/shapes/page |
Complete payload contract
{
"request": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$",
"description": "The name to store under: a letter or digit first, then letters, digits, `_` or `-`, up to 64 characters. A name keeps the scope it was first stored with, except that it may stand at both environment scopes of one application, each holding its own value."
},
"value": {
"type": "string",
"minLength": 1,
"maxLength": 20000,
"x-wire-only": true,
"description": "The secret value. Sent once; never read back. Taken on the HTTP action route alone: the MCP tool declares no `value`. A call naming no `value` and no `generate`, through the MCP tool or on the HTTP route under your own credential, stores nothing: it answers the state `awaiting_value` and the command that sends the value from your machine. A browser session's call must carry it, or name `generate`."
},
"value_file": {
"type": "string",
"minLength": 1,
"maxLength": 1024,
"pattern": "^(?:[^\"$`%!&\\|<>^\\u201c-\\u201e\\u0000-\\u001f\\u007f\\\\]|\\\\[^\"$`%!&\\|<>^\\u201c-\\u201e\\u0000-\\u001f\\u007f\\\\])+$",
"description": "Optional, on a call naming no `value` alone: the file on your machine that holds the value, absolute or relative to the folder the command runs in, which the answered command reads. Absent, the command asks for the value at a terminal. The platform never reads the path. Keep the file outside the application's folder, which a deploy zips, and delete it once the command ends 0. A path holding a control character, a double quote, `$`, a backtick, `%`, `!`, `&`, `|`, `<`, `>`, `^`, a typographic double quote, a doubled backslash, or a trailing backslash is refused `invalid_request`. So is `~` or a path opening with `~/` or `~\\`, which no shell expands in double quotes. So is one named beside `value` or beside `generate`."
},
"generate": {
"type": "string",
"enum": [
"base64url_32",
"hex_32"
],
"description": "Optional, with `application`: the platform creates the value itself in custody, for a value nobody chooses, such as a session or webhook signing secret, and the call answers none. It is 32 random bytes, `base64url_32` as 43 base64url characters for most uses, `hex_32` as 64 lowercase hexadecimal characters where a library reads a key in hex. It lands at the one environment scope `application` and `environment` name, and no action ever answers it. A name that stands at that scope is refused `secret_exists`, and a created value is replaced by a new name. Named beside `value` or `value_file`, or with no `application`, it is refused `invalid_request`."
},
"application": {
"type": "string",
"description": "An application id, to hold the secret at that application's scope; omitted, the secret is held at the account scope."
},
"environment": {
"type": "string",
"pattern": "^(development|production)$",
"description": "Optional, and it rides `application`: the environment whose scope of that application holds the secret, `development` or `production`; absent, `production`. Ignored where `application` is absent, because the account scope carries no environment."
}
}
},
"response": {
"type": "object",
"required": [
"contract_version",
"secret"
],
"properties": {
"contract_version": {
"const": 1
},
"reference": {
"type": "string",
"pattern": "^[0-9a-f]{10}$",
"description": "The short reference the platform recorded this call under, ten lowercase hexadecimal characters, the value the call’s record row carries; quote it when reporting the call."
},
"secret": {
"type": "object",
"required": [
"name",
"scope",
"stored"
],
"properties": {
"name": {
"type": "string"
},
"scope": {
"type": "string"
},
"stored": {
"type": "boolean",
"description": "True on the answer to a call that carried a value or named `generate`: the value is in custody. False with the state `awaiting_value`, where nothing is stored until the answered command runs."
},
"resupplied": {
"type": "boolean"
},
"generated": {
"type": "string",
"enum": [
"base64url_32",
"hex_32"
],
"description": "Present where the call named `generate`: the form the platform created the value in. The value itself is never answered."
},
"application": {
"type": [
"string",
"null"
]
},
"environment": {
"type": [
"string",
"null"
],
"description": "The environment of the application scope that holds the value; null at the account scope."
}
}
},
"state": {
"type": "string",
"pattern": "^awaiting_value$",
"description": "Present on the answer to a call naming no `value` and no `generate` alone: `awaiting_value`. The call stored nothing and minted a short-lived grant for the one write the answered command makes."
},
"expires_at": {
"type": "string",
"description": "Present with the state `awaiting_value`: when the grant in the command stops serving, as an ISO 8601 instant. A command run after it is refused `secret_grant_expired`, or `authentication_required` once the platform has removed the grant's record, and a new call naming no `value` answers a new one."
},
"command": {
"type": "string",
"description": "Present with the state `awaiting_value`: one line, for macOS and Linux. It reads `echo <grant> | npx -y <origin>/packages/turnzero-cloud-<version>.tgz secret store --name <name>`, then `--application <application id> --environment <environment>` where the call named an application, and `--origin <origin>` where the origin is not `https://turnzero.ai`. It ends with `--value-file \"<value_file>\"`, or with `--value-prompt` where the call named no `value_file`. Run it once, as given, from the folder a relative `value_file` is relative to. The turnzero-cloud command reads the grant on its standard input, reads the value from the file or at the terminal, and sends it once. It ends 0 where the value was written, 2 where that is unknown, and 3 where nothing was written. Its one write spends the grant; a second write under it is refused `secret_grant_spent`, and a write naming another name or scope `secret_grant_not_admitted`."
},
"command_windows": {
"type": "string",
"description": "Present with `command`: the same line for every Windows shell, with `npx.cmd` where its head says `npx`. On Windows, run this one in `command`'s place, once, as given. The two are one command line, so a run of either spends the grant."
},
"note": {
"type": "string",
"description": "Present with the state `awaiting_value` where the tool call's arguments carried a `value`. It says that the platform neither read nor stored that value, that a host may keep a tool call's arguments in its transcript, and that the builder should treat the value as exposed. It names neither the value nor its length."
},
"detail": {
"type": "string"
},
"page": {
"$ref": "#/shapes/page"
}
}
}
}
Shared contracts
- Refusals: every refusal, by surface, with its cause and its remedy
- schemas/wire_error.schema.json
- schemas/wire_errors.json
- schemas/action_payloads.json (includes shared shapes)
- management_api_contract.md