Rocco the raccoon mascot, waving Ringtail
Concepts

Project-scoping

Ringtail reads one project's .env.example as the manifest — and fans each key out across the environment axis.

Ringtail is project-scoped. You point it at a local project and it reads that project's .env.example as the manifest — the shopping list of every credential the project needs. The connection grid is built directly from it: providers × environments.

Only names and paths cross the boundary during selection — never file contents, nothing secret.

The .env.example format

Ringtail groups the keys in your .env.example into one provider row per section. You do not need any special format — the normal way you already write .env.example works. There are three ways a key gets grouped, in order:

1. A plain section comment (what most projects already use):

# Database
DATABASE_URL=

## Auth
BETTER_AUTH_SECRET=

# ---- Email ----
RESEND_API_KEY=

Each # Header, ## Header, or # ---- Header ---- comment starts a new row. Decorative dashes/equals/asterisks around the label are ignored, and a prose comment (one containing a URL, colon, or other sentence punctuation — e.g. # get this from the dashboard →) is left as a plain comment, not treated as a header.

2. The box-drawing section comment (Ringtail's own repos use this — still supported):

# ── Cloudflare ──
CLOUDFLARE_API_TOKEN=
CLOUDFLARE_ACCOUNT_ID=

3. No header at all — Ringtail recognizes the provider from the variable name so a bare .env.example still splits into real provider rows:

DATABASE_URL=          # → neon
BETTER_AUTH_SECRET=    # → better-auth
RESEND_API_KEY=        # → resend
CLOUDFLARE_API_TOKEN=  # → cloudflare
STRIPE_SECRET_KEY=     # → creem (billing)

Known prefixes: DATABASE_URL / POSTGRES* / PG* / NEON*neon, RESEND*resend, BETTER_AUTH*better-auth, POSTHOG* / NEXT_PUBLIC_POSTHOG*posthog, CLOUDFLARE* / CF_*cloudflare, CREEM* / STRIPE* / DODO*creem, INFISICAL*infisical. Anything unrecognized and without a header lands in an other row — still on the grid, just not auto-mapped to a recipe.

As always, only names are read — the right-hand side of .env.example holds no values, so nothing secret is ever touched.

The environment axis

local · dev · staging · prod.

  • local — your machine, .env.local, localhost. The only environment that touches your disk.
  • dev · staging · prod — deployed. Secrets go to Infisical only, never your disk. Each environment gets its own scoped keys and resources (e.g. a Neon branch per env).

Sink routing follows the axis: local → .env.local; dev / staging / prod → Infisical. syncCredential fans one acquired key out per environment so a single consent provisions the whole row.

Local credential discovery

Before asking you for anything, Ringtail scans known credential stores — .env.local, ~/.ringtail, ~/.aws/credentials, ~/.config/gh, ~/.wrangler / ~/.cloudflared, and the environment — validates what it finds, reuses complete root grants, and only prompts for real gaps. Known locations only, local-only, and transparent about what it reused. It never scans your whole disk.