# Giving a secret its value

> Declaring `app.Secrets` names a secret; it does not give it a value, and an app cannot run until something does. `osy secret set` supplies the value on your own machine — it writes the project's gitignored `.secrets` file, which every compile re-applies, and applies it straight away to a running local app. `osy secret list` shows which of your declared secrets still have no value here.

<!-- id: local-giving-a-secret-its-value · area: local · stability: stable · html: https://osysharp.com/reference/local/giving-a-secret-its-value/ -->

## Summary        {#summary}

[declaring secrets (app.Secrets)](https://osysharp.com/reference/config/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.

```console
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 here
```

## Signature      {#signature}

```console
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    {#description}

### Where the value is kept   {#secrets-file}

`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.

```text
Anthropic=sk-ant-...
```

### A multi-line value, such as a PEM key   {#from-a-file}

Some credentials are not one line. A private key for
[`Crypto.SignRs256`](https://osysharp.com/reference/function/crypto-sign/) — Snowflake key-pair auth, a Google service account, a GitHub App —
arrives as a PEM file of twenty-odd lines. Point at the file:

```bash
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:

```text
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   {#listing}

```console
osy secret list
```

lists 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   {#deployed}

`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)](https://osysharp.com/reference/config/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       {#examples}

Give a declared API key its value and see the result:

```console
$ 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:

```console
$ osy secret set Anthropic
Value for Anthropic: ********
```

A name your app does not declare is refused, with the near miss:

```console
$ osy secret set Anthropi sk-ant-...
✗ 'Anthropi' is not declared in `app.Secrets` — did you mean 'Anthropic'?
  This app declares: Anthropic
```

## See also       {#see-also}

[declaring secrets (app.Secrets)](https://osysharp.com/reference/config/secrets/) — declaring the secrets your app uses, and referencing one by its `Secret.Name` handle.

[Compiling your app](https://osysharp.com/reference/local/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](https://osysharp.com/reference/agent/llm-calls/) — `app.Llm`, where each model's client takes a declared secret as its `ApiKey`.
