---
title: Secrets
description: Project secrets in 1Password, attached to a project by reference and never written to disk.
---

A project's secrets (API keys, database URLs…) live in 1Password, and
[Varlock](https://varlock.dev) hands them to the project when it runs. No
secret is written to a `.env` file or exported in the shell.

```sh
dot apply packages    # installs Varlock
```

## Where they live

Project secrets go in a 1Password vault kept for development, apart from
your logins. `dot.toml` names it:

```toml
[secrets]
vault = "Dev"
```

A project never stores a value: its `.env.schema` holds a reference, and
Varlock resolves it when the project runs. Which secrets a project needs,
and with what permissions, is the project's own documentation.

## Add a secret to a project

### 1. Create it with the least it needs

Give each secret only what its job requires, and one secret per job, so
revoking one breaks nothing else. A job that only reads gets a read-only
token; a test environment gets a test key. Creating it usually happens on
the provider's website; each provider's guide has the exact steps
([GitHub token](/docs/your-projects/secrets/github)).

### 2. Store it

```sh
dot secret add myapp-payments     # name it <project>-<use>
```

It asks for the value without showing it (or reads it from a pipe:
`pbpaste | dot secret add myapp-payments`), stores it in the vault as an API
Credential tagged `dotfiles`, and prints its reference,
`op://Dev/myapp-payments/credential`. The value never becomes a command
argument or a file, so it's not in your shell history or the process list.
It refuses a name that already exists.

```sh
dot secret list                   # names, dates and references; never values
dot secret update myapp-payments  # a new value, same reference
```

### 3. Attach it to the project

A project commits a `.env.schema`: which variables exist, their types, and
where each value comes from. `varlock init` creates it, and its header loads
the 1Password plugin. Then, from the project's folder:

```sh
dot secret attach myapp-payments PAYMENTS_KEY
```

This adds the variable as a reference, never a value:

```sh
# @plugin(@varlock/1password-plugin)
# @initOp(allowAppAuth=forEnv(dev))
# ---
APP_URL=http://localhost:3000

# @sensitive
PAYMENTS_KEY=op(op://Dev/myapp-payments/credential)
```

`dot secret attach` refuses a variable the schema already has, and a schema
without the plugin.

### 4. Run with it

```sh
varlock explain PAYMENTS_KEY  # how one variable resolves, without its value
varlock load                  # validate the schema and show what resolves
varlock run -- pnpm dev       # run a command with the values injected
```

A Next.js project uses Varlock's integration instead
(`@varlock/nextjs-integration`), so `next dev` and `next build` load the
schema themselves. Local overrides (`.env.local`) belong in each project's
`.gitignore` and never hold a secret: that's what the schema is for.

## At runtime

- **Only that process gets the secrets.** `varlock run` resolves the values
  (1Password asks for Touch ID) and injects them into the command it starts;
  your shell never has them.
- **Sensitive values are redacted** in output that's piped, such as logs.
- **Cache:** resolved values can be cached in `~/.config/varlock/cache/`,
  encrypted with a Secure Enclave key that needs Touch ID.

## Telemetry

Varlock sends anonymous usage analytics unless they're turned off. Here
that's a personal value:

```toml
[varlock]
telemetry = "disable"    # or "enable"
```

`DO_NOT_TRACK=1` also turns it off for a single run.

## Where each secret lives

| Secret                          | Lives in                           | Reaches its user through |
|---------------------------------|------------------------------------|--------------------------|
| App secrets (API keys, DB URLs) | 1Password, development vault       | Varlock, per process     |
| GitHub token for `gh`           | 1Password                          | the `gh` shell plugin, per command |
| Vercel token for `vercel`       | 1Password                          | the `vercel` shell plugin, per command |
| A project's Vercel OIDC token   | nowhere: issued per run, 12 hours  | Varlock, through the `vercel` plugin ([Vercel](/docs/your-projects/accounts/vercel#a-projects-oidc-token)) |
| SSH key                         | 1Password                          | the SSH agent            |
| `dot.toml` backup               | 1Password                          | `dot conf restore`       |

## Further reading

- [Varlock: introduction](https://varlock.dev/getting-started/introduction/)
- [The `.env.schema` format (@env-spec)](https://varlock.dev/env-spec/overview/)
- [Varlock's 1Password plugin](https://varlock.dev/plugins/1password/)
- [1Password secret references](https://developer.1password.com/docs/cli/secret-reference-syntax/)
- [1Password service accounts](https://developer.1password.com/docs/service-accounts/), for a job that runs unattended on a server
