Commands, settings, SDK functions, anything in the docs.

Build apps

Secrets

Keep your app’s own API keys out of its code.

On this page

A secret is a value your app needs that must not be in its code, such as the API key of a service it reads. You declare its name in ledable.json, set its value once with the CLI, and read it from ctx.secrets when the app draws. The value never enters the bundle you upload, and no command shows it again.

A secret belongs to your app, so every display that runs the app uses the same value. Anything that belongs to one person, such as their own token for a service, is a setting instead: see Accounts and credentials.

Declare it

List the names in secrets. A name is upper-case letters, digits and underscores, and does not start with a digit.

ledable.json
{
  "app_id": "air-quality",
  "version": "1.0.0",
  "entry": "./index.tsx",
  "name": "Air Quality",
  "description": "The air quality where your display is.",
  "category": "weather",
  "secrets": ["AIR_API_KEY"],
  "network": { "allowed_hosts": ["api.example.com"] }
}

Only the names a version declares reach it. Declaring a secret also means the version cannot run without it: until the secret has a value, every render of that version fails.

Read it

ctx.secrets holds each declared name with its value. render reads it, and so do settings and verifyCredential, which have their own ctx.secrets. preview has none, since a preview reads nothing that can change.

TSX
const response = await fetch("https://api.example.com/v1/air?city=Berlin", {
  headers: { Authorization: `Bearer ${ctx.secrets.AIR_API_KEY}` },
});

If your app reports an error or throws one, the platform replaces the values of its secrets with *** in the message before it leaves the app. Still, do not put a secret in a message, a cache tag or anything you draw.

Set the value

Run the secret commands in the project's folder; they act on the app ledable.json names. At a terminal, secret set asks for the value without showing it. In a script, pipe the value in as one line of stdin instead:

Terminal
ledable secret set AIR_API_KEY
printf '%s\n' "$AIR_API_KEY" | ledable secret set AIR_API_KEY

The CLI seals the value on your machine before it sends it, and the store keeps it sealed: only the servers that run your app open it. A value may not be empty and holds at most 4096 bytes.

To change a value, set it again. Renders pick up the new value within about a minute; there is no new version to upload, because the value belongs to the app, not to a version.

See what is set

secret list shows the secrets that have a value, the published versions that declare each one, and under missing the names your ledable.json declares that have no value yet. A name in missing fails your next upload.

JSON
{
  "secrets": [
    {
      "name": "AIR_API_KEY",
      "updated_at": 1791216000,
      "declared_by": ["1.0.0"]
    }
  ],
  "missing": []
}

Uploading a version that declares one

Set every declared secret before you run developer upload. The store runs the version's settings, preview and a render before it publishes anything, and a declared secret without a value makes that run fail, starting with the settings check. Set the value and upload the same version number again. The checks are described in Publishing checks.

Delete one

secret delete removes a secret that no published version declares, such as one you set for a version you never shipped. A secret that a published version declares cannot be deleted, because displays keep playing the versions they have and that version would stop working. Replace its value instead.

Terminal
ledable secret delete OLD_API_KEY

In the local preview

developer dev and developer render never reach the store, so they read secrets from ledable.dev.json next to ledable.json. Put your own key there, and keep the file out of version control; developer init lists it in the project's .gitignore.

ledable.dev.json
{
  "secrets": { "AIR_API_KEY": "your-own-key" }
}

A declared secret the file does not have fails the render, as it would on LEDABLE's servers. The rest of the file is described in Local preview.