# Secret references

A plan carries a pointer to a secret, never the secret. That is what makes a plan safe to read, review, and keep in git.

Source: https://opafra.com/docs/access/secret-references

---

Opafra never stores a secret inside a plan. A plan carries a **reference**, resolved at
the moment of use, and the value goes to the host without ever being written into the
plan, the run log, or a preview.

```yaml title="a reference, not a password"
- name: Rotate the app database password
  tool: ssh.cmd
  args:
    command: "mysql -u root -p'secret://vault/prod/mysql_root' -e 'FLUSH PRIVILEGES'"
```

## The shape of a reference

```text
secret://<provider>/<namespace>/<key>
```

The provider is where the secret lives, the namespace scopes it within that provider, and
the key names it. A second scheme exists for values supplied by the infrastructure Opafra
runs on:

```text
env:VAR_NAME
```

Use `env:` for a credential the deployment provides, and `secret://` for everything a
team manages.

## Why a reference rather than encryption

Encrypting a secret in the plan still puts the secret in the plan. Every copy of that
plan, every export, every backup, and every screen showing it then contains the material,
protected by a key that also has to live somewhere.

A reference is different in kind. It is not sensitive, so a plan holding one can be:

- **Read** by anyone reviewing the automation, including people who should never see the
  credential itself
- **Kept in git**, diffed in a pull request, and reviewed like code
- **Copied between environments**, where the same reference resolves to that
  environment's own value
- **Shown in a dry run** to an approver, who sees exactly what would run without being
  handed a password

That last point is the one that matters most in practice. Review and secrecy stop being
in tension.

## Where a reference can be used

Anywhere a value is expected: a tool argument, a command, an environment variable, a
server credential.

An environment variable can be marked secret, in which case its value is stored as a
reference too. So `{{ vars.db_password }}` in a plan resolves through the environment to
the provider, and the plan names neither the secret nor the provider.

> **Important**
>
> This is what lets one plan run against staging and production unchanged. The reference
> resolves per environment, so the same plan text reaches each host with that host's own
> credential.

## Redaction

References are redacted everywhere a command or value is displayed: in a
[dry-run](/docs/running/dry-run) preview, in the live run console, in the stored run
record, and in stderr captured from a failed connection.

Redaction over-matches on purpose. Any argument whose **name** looks like a credential is
masked as well, and the same masking runs over rendered config file contents in a diff.
Hiding something that was not a secret is a smaller problem than printing one that was.

> **Danger**
>
> Nothing can redact a value it was never told was secret. A literal password typed into a
> command is written into the plan, into the run log, and into every preview of it. The
> redaction protects references and secret-named arguments, not arbitrary strings that
> happen to be sensitive.

## Tenant boundaries

A reference resolves only within its own tenant's namespace. A hand-crafted reference
pointing at another organisation's namespace is rejected rather than followed, so a
reference is not a way to probe or reach across a boundary.

## What is stored

The reference. Server credentials are held either as a reference to your provider or
encrypted at rest when Opafra holds them itself, and neither form appears in an API
response, a plan, or a log.

A generated SSH private key is the strongest case: it is shown once, when created, and no
endpoint returns it a second time.

## Next steps

- [Secret providers](/docs/access/secret-providers) for connecting Vault, Infisical, AWS or GCP
- [Plan structure](/docs/plans/structure) for how variables and references resolve
