rtb-credentials 0.6.4

OS keychain credential storage. Part of the phpboyscout Rust toolkit.
Documentation
---
title: Environment variables
description: The one variable rtb-credentials reads on its own, the variables a tool names through config, and how empty values are treated.
tags:
  - reference
  - configuration
  - ci
---

# Environment variables

There are two distinct categories, and conflating them causes most of
the confusion about what this crate reads.

## `CI` is the only variable the crate reads on its own

| Variable | Read by | Effect |
|---|---|---|
| `CI` | `Resolver::resolve` and `Resolver::probe` | When exactly `true`, a `literal` credential is refused |

Nothing else is consulted without a tool naming it.

### The comparison is exact and case-sensitive

```rust
std::env::var("CI").as_deref() == Ok("true")
```

Only the lowercase string `true` triggers refusal:

| Value of `CI` | Literal refused? |
|---|---|
| `true` | **yes** |
| `1` | no |
| `TRUE` | no |
| `True` | no |
| `yes` | no |
| unset | no |

A pipeline that sets `CI=1` gets no protection from this check. If you
rely on the refusal, confirm the value your platform actually sets, or
set `CI=true` yourself in the job.

### Which platforms set it

The crate's own documentation names GitHub Actions, GitLab CI, CircleCI
and Buildkite as platforms following the `CI=true` convention.

Others vary — some set a different value, some set nothing and expose
only a product-specific variable such as `JENKINS_URL`. The crate
deliberately does not detect those: matching `CI_*` prefixes or
vendor-specific names produces false positives in developer shells that
export similar variables for unrelated reasons.

Setting `CI=true` explicitly in a job is the reliable way to get the
behaviour on any platform.

### Turning the refusal off locally

A developer shell that exports `CI=true` for some other tool will find
literal credentials refused. Any value other than `true` restores them —
`CI=false` is the value the crate's own error help suggests, and
unsetting the variable works equally well.

```bash
CI=false mytool run
```

## Variables named by configuration

The `env` and `fallback_env` keys of a
[`CredentialRef`](credential-ref.md) each hold the *name* of a variable
to read. These are chosen by the tool and its operator, not by this
crate.

`fallback_env` is conventionally an ecosystem-wide name that the tool
did not invent:

| Example | Belongs to |
|---|---|
| `ANTHROPIC_API_KEY` | Anthropic tooling generally |
| `GITHUB_TOKEN` | GitHub tooling generally |

These are **examples of the convention, not defaults**. The crate has no
built-in list and no implicit lookup: a credential with no `env` and no
`fallback_env` is never resolved from the environment, whatever
variables happen to be set.

## `EnvStore` reads the variable named by its `account` argument

Used directly rather than through a `Resolver`, `EnvStore` takes the
variable name as its **`account`** parameter and ignores `service`
entirely:

```rust
store.get("ignored", "MYTOOL_TOKEN").await?;  // reads $MYTOOL_TOKEN
```

## A variable that is set but empty counts as found

`std::env::var` returns `Ok("")` for a variable set to an empty value,
and neither the resolver nor `EnvStore` checks for it.

```bash
export MYTOOL_TOKEN=""
```

With that exported, a reference naming `MYTOOL_TOKEN` in `env` resolves
successfully to an empty `SecretString`. The chain stops there: the
keychain, literal and fallback legs are never tried, because the first
leg was a hit.

The failure surfaces later, wherever the empty secret is used — usually
as a `401` from an API rather than as a credential error. An exported
but empty variable shadows every lower-precedence source, which makes
`export MYTOOL_TOKEN=` a surprisingly effective way to break a working
configuration.