> ## Documentation Index
> Fetch the complete documentation index at: https://c42d5cb-opencomputer.manicule.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For every page in one file, fetch https://c42d5cb-opencomputer.manicule.dev/llms-full.txt (large).
> To search and read these docs as tools, connect the MCP server at https://c42d5cb-opencomputer.manicule.dev/api/mcp.
> Every agent surface: https://c42d5cb-opencomputer.manicule.dev/.well-known/llms-manifest.json

# Secrets and variables (/slack/secrets-and-variables)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 1788 · updated: 2026-11-12 -->
Related: [Threads](/slack/threads.md), [Shared threads](/slack/shared-threads.md), [Linked and spawned threads](/slack/linked-and-spawned-threads.md), [Mid-turn messages](/slack/mid-turn-messages.md), [Permission modes](/slack/permission-modes.md), [Approvals, questions and plans](/slack/approvals-questions-plans.md)

> A session reads secrets and variables as environment variables; secrets are entered in a private Slack form and redacted from its posts

|          | Entered                 | Shown in Slack | Redacted |
| -------- | ----------------------- | -------------- | -------- |
| Secret   | In a private Slack form | No             | Yes      |
| Variable | In a message            | Yes            | No       |

Slack stores messages and the model reads them, so a secret never goes in a message.

Secrets need `SP_STORE_KEY` set on the [gateway](/self-hosting/gateway); without it the commands answer `Secrets are not available: the gateway has no SP_STORE_KEY`.

## Set a secret

<Steps>
  <Step title="Ask for the form">
    Send `!secret NAME`, or `!secret NAME file` for a value that should arrive as a file. The gateway answers with a message only you can see, holding an **Enter NAME** button.

    <Frame>
      <img src="/_assets/4fb181a485f7580730e2388957cde2858187ad7466c3e7da252e027283c47430" alt="The answer to !secret STAGING_DB_URL, visible only to the sender, with an Enter STAGING_DB_URL button" width="1552" height="904" />
    </Frame>
  </Step>

  <Step title="Fill in the form">
    Enter the value (several lines allowed, up to 3000 characters), choose a scope under **Available to**, and tick **Provide as a file** if needed.

    <Frame>
      <img src="/_assets/76c094267c422c27b4c6b08944f52366d69c309454bb6ca19e254f76d980d3b8" alt="The Set a secret form: Name, Value, Available to with three scopes, the Provide as a file option, and Cancel and Save" width="1552" height="1738" />
    </Frame>
  </Step>

<Step title="Save">The value goes from Slack to the gateway, not through the conversation or the model. Running sessions in scope receive it at once.</Step>
</Steps>

| Action                              | How                                                                                                                                                              |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| List names per scope (never values) | `!secret`                                                                                                                                                        |
| Remove from a scope                 | `!secret rm NAME [session\|me\|workspace]`                                                                                                                       |
| Session asks for a credential       | The gateway posts `This session needs $NAME`, the reason, and a button in the thread. Anyone on the people list can fill in the form. A runner can ask 10 times. |

<Frame>
  <img src="/_assets/8ed5c715efe604385dc72b5fe1d8bdf92a1f8d2b2dad596a8b370c4dcfd84e12" alt="A session asking for a secret in its thread: This session needs $STAGING_DB_URL, the reason, and an Enter STAGING_DB_URL button" width="1552" height="1046" />
</Frame>

Every form of the command: Slack commands.

## Names

Names match `^[A-Za-z_][A-Za-z0-9_]{0,63}$`. Refused names:

* `PATH`, `HOME` and similar
* names starting with `SP_`, `CLAUDE`, `ANTHROPIC_`, `GIT_`, `LD_`, `DYLD_` or `BASH_FUNC`
* proxy and CA bundle variables

## Scopes

| Scope     | In the form       | In commands | Which sessions receive it                          | Lifetime                                                                                                   |
| --------- | ----------------- | ----------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Session   | This session only | `session`   | The session of the thread the form was opened from | Deleted 30 days after the session was last active                                                          |
| Person    | Just me           | `me`        | Every session the person owns                      | Until removed                                                                                              |
| Workspace | Workspace         | `workspace` | Every session                                      | Until removed. Only admins see the option and can set or remove it; see [People](/self-hosting/configure). |

* When the same name is set in more than one scope, the narrowest wins.
* A secret you set with **Just me** in someone else's thread does not reach that session: a session receives the person-scope secrets of its owner.

<Note>Not in the open-source code yet (runner isolation). Each session's runner will run in its own rootless container with its own secrets. Today all sessions on the computer run as one Unix user, so a session can read another session's secret files and process environments, and so can every person who can direct a session. The form says so.</Note>

## In the session

| Form                  | What the session gets                                                                                           |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| Default               | `$NAME` holds the value.                                                                                        |
| **Provide as a file** | `$NAME` holds the path of a file with the value. Use for a service account JSON, a kubeconfig or a private key. |

A secret set, changed or removed during a turn takes effect in the next shell command the session runs.

The gateway stores secrets in its SQLite database, encrypted with AES-256-GCM under a key derived from `SP_STORE_KEY`.

## Redaction

The runner replaces every secret value it knows with `[secret:NAME]` in everything it posts to Slack, and refuses to post a file or card that contains one. It does not cover:

* a value shorter than 6 characters
* a line of a multi-line value quoted on its own, when the line is shorter than 16 characters
* a value that is encoded, split, reversed or drawn in an image
* a value sent anywhere other than Slack: a commit, a request, a log on the computer

## Variables

`!var NAME value [session|me|workspace]` sets a variable and `!vars` lists names and values. Every form of the command: Slack commands.

* The default scope is the session of the thread the command is sent in, and `me` where there is no session.
* Scopes, name rules, the workspace admin rule and the 3000-character limit are the same as for secrets.
* A variable cannot replace a secret of the same name in the same scope; a secret can replace a variable.
* When a name or value looks like a credential, the gateway does not keep it until **Set it anyway** is pressed.

<Frame>
  <img src="/_assets/4313ea962467715b33965348192d77d08f570c0a20469692d8fb9ab7cd7a2f20" alt="Two !var commands and their answers, visible only to the sender: one variable set, one not set because its name sounds like a credential, with a Set it anyway button" width="1552" height="1016" />
</Frame>
