# API keys

Project Settings → API Keys issues the keys everything outside the console
authenticates with. The type select silently rewrites the rest of the form, and
nothing on it says what separates the three types.

## The type decides how the key is checked

| Type                            | How it is checked                                                                 |
| ------------------------------- | --------------------------------------------------------------------------------- |
| **CLI read/write**              | Holding the key is the permission. Full read and write over the project's files.  |
| **SDK readonly**                | Holding the key is the permission. Read-only.                                     |
| **Scoped (custom permissions)** | The key carries its own permissions and is checked against them on every request. |

For the first two, nothing narrows the key. A **scoped** key — the type the form
opens on — is the only one that can be given less than everything. Project
**viewers** have no API Keys entry in Project Settings, and the console API
returns them only the read-only keys an app can ship with: SDK readonly keys and
the Browser (read-only) key.

Every new project starts with four keys:

- **CLI** (CLI read/write) reads and writes everything.
- **SDK Development** (SDK readonly) reads everything.
- **Workflow Execution Key** (scoped) reads `dcupl.lc.json`, `data/**`,
  `models/**`, `transformers/**`, `operators/**` and `scripts/**`, and writes
  `data/**` and `models/**`.
- **Browser (read-only)** (scoped) reads `dcupl.lc.json`, `models/**`,
  `data/**`, `transformers/**` and `operators/**`, and writes nothing.

The Workflow Execution Key's value is also the project variable
`dcupl_api_key`; it stays on the server. **Browser (read-only)** is the key for
browser apps: it reads only through the dcupl CDN, and the console API refuses
it (`403`). Seeded keys don't count toward your plan's key limit (the counter on the API Keys page still includes them). Projects created before these four existed keep the keys they had, which count toward the limit: CLI and SDK Development and, on all but the oldest projects, a Workflow Execution Key that reads only `dcupl.lc.json`, `data/**` and `models/**`.

The Workflow Execution Key is also the key a **dcupl Instance** step starts
with: a step added in the workflow editor carries the key's id as
`config.apiKey` in its **Init Options**, and deploying the workflow resolves the
id to the key, so the step's loader can fetch a private project's files. That is
why the key reads `transformers/**`, `operators/**` and `scripts/**`. A step
written into the template by hand has no key until you add one. On a project
created before the four seeded keys, the preset is that project's own Workflow
Execution Key, which reads only `dcupl.lc.json`, `data/**` and `models/**`; a
project without a key named Workflow Execution Key gets no preset, and its steps
start with no key at all. If such a project is private, put the UID of a key that
reads every file its loader declares in the step.

A key cannot be edited: to change its type or patterns, create a new key and
delete the old one. Replacing the Workflow Execution Key this way leaves the old
value in `dcupl_api_key` and the old key's id in the workflow steps that use it:
update both, then redeploy those workflows. **Set custom value** on the form
lets you choose the key string (at least 16 characters; letters, digits, `-` and
`_`); otherwise one is generated.

## A scoped key carries globs over your files

A scoped key's **file read patterns** are globs matched against paths in your
project. Its **file write patterns** cover write, delete, move and copy. The
form starts a scoped key with `dcupl.lc.json`, `data/**` and `models/**` as read
patterns and no write pattern; add `transformers/**`, `operators/**` or
`scripts/**` when the app loads them. Two further switches, **Execute
Workflows** and **Create Runners**, are capabilities rather than paths. Execute
Workflows decides which keys a workflow's request trigger can require: the
trigger's key picker lists only scoped keys with it switched on. **Create Runners** has no effect yet: no service checks it.

A path that no pattern matches is simply left out of the key. There is no
warning in the form, because the form does not know what the key will be used
for.

## The globs must cover everything the app loads

```mermaid
flowchart LR
  K["Scoped key"] --> LC["dcupl.lc.json"]
  LC --> RES["every declared resource<br/>models · data · transformers · scripts · operators"]
  RES --> G{"path matches<br/>a read pattern?"}
  G -->|yes| OK["loads"]
  G -->|no| FAIL["403 — that resource is missing from the run"]
```

A project is **private** when **Private CDN** is on for it: the CDN then serves
its files only to requests that carry one of the project's keys. New projects
are private by default. On a project without Private CDN the CDN serves the
files without a key, so a scoped key's read patterns do not limit what an app
can load. The console does not show the setting. Once on, it stays on through
plan changes; a project created before projects became private by default stays
public unless its plan includes Private CDN.

A key is only tested when something loads with it. The loader fetches your
`dcupl.lc.json` **and every resource it declares**, so on a private project one
uncovered path — a `scripts/` folder, say — answers `403` at load time, long
after the key looked fine in this form. The loader records that resource as an
`InvalidResource` loader error and loads the rest, so the app comes up without
it: no scripts, or a model with no rows. Only an uncovered `dcupl.lc.json` fails
the whole load. The Browser (read-only) key, for example, does not cover
`scripts/**`: a browser app whose loader declares `script` resources under
`scripts/` needs a scoped key of its own.

Think about **coverage**, not syntax. List the resource URLs in your loader
config and check each one against the patterns before you save.

## Who uses a key

- **The SDK** — an app loading a private project passes the key to the loader,
  which appends it to every hosted resource request. A browser app ships its key
  to every visitor, so give it the **Browser (read-only)** key (or another scoped
  key that reads only what the app loads), and keep keys that can write on a
  server.
- **The CLI** — a CLI read/write key authenticates `dcupl files` push and pull
  and the `dcupl workflow` commands (`deploy`, `test`, `history`, …); the
  console API accepts only this key type for workflow deployment.
- **Workflows** — a dcupl Instance step loads the project with the key in its
  **Init Options** (`config.apiKey`, preset to the Workflow Execution Key in the
  editor when the project has one; for another key, put that key's UID, shown
  in its details on the API Keys page, in its place). A dcupl Files step carries
  a key of its own. A scoped key needs the matching file patterns. A request
  trigger with authentication accepts the keys selected on it, and the trigger
  offers only scoped keys with Execute Workflows.
- **REST APIs** — a deployed instance loads its data with the key configured on
  it. The template form's **Api Key** picker offers SDK readonly keys and scoped
  keys that have at least one read pattern (the limit is the picker's: a
  template edited as JSON deploys with any key it holds). A scoped key must
  cover everything the instance loads. The form warns only when the template has
  endpoints and the key cannot read the script files the template's application
  schedules (or `scripts/**` while it schedules none); it never checks loader
  data or models.

## Next steps

- [Loader configuration](/console/loader) — the list of paths a scoped key has to cover
- [REST APIs](/console/rapi) — where an instance's key is configured
- [Workflows](/console/workflows) — steps that authenticate with a project key
- [Load from the console](/sdk/guides/load-from-console) — use the Browser key in your own app
