Summary#
declaring secrets (app.Secrets) declares that your app uses a secret. It does not supply the value — a secret's value never appears
in source. osy secret set is how you supply it on your own machine, and osy secret list shows which declared
secrets still have none.
osy secret set Anthropic # prompts for the value, so it stays out of your shell history
osy secret set SnowflakeKey --file rsa_key.p8 # a multi-line value, straight from the file it arrived in
osy secret list # which secrets are declared, and which still have no value hereSignature#
osy secret list [--path <dir>] [--json]
osy secret set <NAME> [VALUE] [--file <PATH>] [--path <dir>] [--devname <name>]NAME must be a name your app declares in app.Secrets. A name it does not declare is refused — with the closest
declared name, when there is one — because a value your app cannot read is a credential stored for nothing.
Description#
Where the value is kept#
osy secret set writes the value to a file called .secrets, beside your app.osy. The format is one
NAME=value per line, so you can also edit it by hand — the two are exactly equivalent.
Anthropic=sk-ant-...A multi-line value, such as a PEM key#
Some credentials are not one line. A private key for
Crypto.SignRs256 — Snowflake key-pair auth, a Google service account, a GitHub App —
arrives as a PEM file of twenty-odd lines. Point at the file:
osy secret set SnowflakeKey --file rsa_key.p8--file is the way to set any multi-line value, and the better way to set any value that already lives in a file:
the bytes never reach your shell history. Pasting a PEM as an argument does not work in any case — it begins with
-----BEGIN, which your shell and the CLI both read as options.
Stored, such a value is quoted with its newlines escaped, so .secrets stays one line per secret:
SnowflakeKey="-----BEGIN PRIVATE KEY-----\nMIIEvw...\n-----END PRIVATE KEY-----"Editing by hand still works: an unquoted value is taken literally, exactly as before, and a double-quoted one
understands \n, \r, \t, \" and \\. Single quotes mean "no escapes at all", for a value that genuinely
contains a backslash-n.
That file is the durable source on your machine. Your app's secret values live in the app's database, and resetting
the local platform wipes them; every compile re-applies .secrets, so a reset cannot lose the values you set. This is
also why setting a value does not require a running platform: if one is running, the value is applied immediately and
takes effect without recompiling; if not, the next compile applies it.
.secrets holds credentials in plain text, so osy secret set makes sure it is ignored by git before writing to it —
adding it to your project's .gitignore if nothing already covers it. It does that even when the project is not in a
repository yet, so a repository made later still keeps the file out; osy init writes the line from the start, and the
dev page's secrets form ignores the file before it writes a value too.
Which secrets still need a value#
osy secret listlists every secret your app declares and whether it has a value here. It reads the declarations from your source, so
it answers in a project you have never compiled, with no platform running. If .secrets sets a name your app does not
declare, list reports it: a compile refuses while that is true, and applies nothing.
Values that are not yours to set#
osy secret is the LOCAL surface — your machine, your project. A deployed app's secrets are supplied by whoever
operates the platform it runs on, out of band, and are never read from a file in your project.
A user-scoped secret (new Secret("Name") { UserScoped = true }, see declaring secrets (app.Secrets)) belongs to each user of
your app rather than to the app, so each user supplies their own through the app — there is no single value to set here.
Examples#
Give a declared API key its value and see the result:
$ osy secret set Anthropic sk-ant-...
✓ Secret 'Anthropic' written to .secrets.
This file is the durable local source: `osy compile` re-applies it, so a `dev --reset` cannot lose it.
Added `.secrets` to .gitignore — it holds credentials in plain text.
Applied to the running local app.
$ osy secret list
╭───────────┬─────────╮
│ Name │ Value │
├───────────┼─────────┤
│ Anthropic │ ● set │
╰───────────┴─────────╯Omit the value to be prompted for it, so it never reaches your shell history:
$ osy secret set Anthropic
Value for Anthropic: ********A name your app does not declare is refused, with the near miss:
$ osy secret set Anthropi sk-ant-...
✗ 'Anthropi' is not declared in `app.Secrets` — did you mean 'Anthropic'?
This app declares: AnthropicSee also#
declaring secrets (app.Secrets) — declaring the secrets your app uses, and referencing one by its Secret.Name handle.
Compiling your app — the compile that re-applies .secrets to your local app.
The Llm kit — call a model, and know what each call cost — app.Llm, where each model's client takes a declared secret as its ApiKey.