Release candidate — 1.0.0-rc.6
StitchAPI
GuidesAuth

Secret resolvers

Resolve secrets lazily at call time with env() and secretsFile() instead of hard-coding them.

Every auth strategy takes a secret as a reference, not a value, and resolves it at call time — reach for env() and secretsFile() so the declaration carries a capability, not a credential you have to commit.

Example

import { , , ,  } from 'stitchapi';

// env() reads process.env.API_TOKEN when the stitch is called, not now.
const  = ({
    : 'https://api.example.com',
    : '/me',
    : (('API_TOKEN')),
});

// secretsFile() resolves from ~/.stitch/secrets.json, falling back to the env var.
const  = ({
    : 'https://api.example.com',
    : '/billing',
    : (('BILLING_TOKEN')),
});

The token is never read at declaration time, so the secret never enters the stitch's source — only the name of where to find it does.

Options

A secret is a string | (() => string). Every strategy — bearer, apiKey, basic, oauth2 — accepts one and resolves it the moment a request goes out, so the same declaration works across processes and machines without baking a value in.

env(name) returns a thunk that reads process.env[name] at call time and throws missing env var <name> if it is unset — failing loudly at the call rather than silently sending an empty token.

secretsFile(name) reads ~/.stitch/secrets.json if present and the key is there, otherwise falls back to the environment variable of the same name, and throws if neither has it.

~/.stitch/secrets.json is unencrypted plaintext JSON. Restrict its permissions (chmod 600 ~/.stitch/secrets.json), add it to your .gitignore, and never commit it. For production workloads prefer a proper secrets manager or env() backed by your deployment platform's secret injection.

Why call-time resolution matters: the value is fetched only when a call is made, so you never commit a secret and the stitch holds a capability rather than the credential itself — the capability boundary the product is built on. You can pass a plain string (discouraged — that hard-codes the value), or any () => string thunk of your own, so plugging in a vault or secrets manager is just supplying a different resolver.

Anti-pattern: don't pass a secret as a plain string — it is read at declaration time and lives in your source and history, collapsing the capability back into a committed credential; pass env() or secretsFile() instead so the value resolves at call time and the stitch carries a capability, not a credential. See Capability, not credential.

See also

On this page