@beesolve/aws-env-resolver
Resolve local development environment from CloudFormation and Secrets Manager, cache it, and inject it into a spawned command.
[!WARNING] Pre-1.0 - unstable API. This package is
0.xand under active development. Breaking changes can land in any0.xrelease without a major version bump. Pin an exact version and review the changelog before upgrading. Once the API stabilizes and the package reaches1.0.0, it follows semantic versioning and this notice is removed.
macOS-only secure cache. Secrets are cached in the macOS login keychain. On other platforms secrets are fetched on every run and never written to disk. The CLI requires Bun.
Install
bun add -d @beesolve/aws-env-resolver
npm install --save-dev @beesolve/aws-env-resolver
CLI
aws-env-resolver [--stage <stage>] [options] -- <command...>
Put flags before --. Bun drops a leading --, so with no flags the first non-flag argument starts the command.
| Flag | Description |
|---|---|
--stage <stage> | Optional. Picks aws-env.config.<stage>.* first, passed to a factory, keys caches. |
--target <name> | Target to resolve when the config defines several. Optional with exactly one target. |
--config <path> | Config module or JSON (default: aws-env.config.{ts,js,mjs,json} in the cwd). |
--profile <name> | AWS profile; overrides the config. |
--refresh | Bypass caches and re-fetch from AWS. |
--purge | Delete cached values for this stage (runs <command> only if given). |
--trusted | Store the keychain item so reads are silent (no prompt). See below. |
--help | Show help. |
The trust list of a keychain item is set when the item is written. With --trusted the resolver writes the item on every run, so an item first stored without the flag is upgraded: that run prompts once and later runs are silent. To switch to trusted mode without running anything, pass --trusted with no command:
aws-env-resolver --stage stg --trusted
Exit codes: 0 success, 1 runtime error, 2 invalid arguments. When a command runs, its exit code is returned.
Library (@beesolve/aws-env-resolver)
| Export | Description |
|---|---|
resolveEnv | (options: ResolveEnvOptions) => Promise<Record<string, string>>. Does not mutate process.env. |
purgeCache | ({ projectKey, stage?, target?, config }) => Promise<void>. Deletes the cache file and the keychain item. |
ResolveEnvOptions | config, resources, secrets?, projectKey, stage?, target?, refresh?, cacheTtlMs?, trusted?. |
ResolverConfig | profile?, region, stackName (one name or a list), secretId?, accountId?, staticEnv. |
ResourceMapping | envVar, logicalIdPrefix, resourceType?, stackName?, transform?. |
TemplateMapping | envVar, parts: literal strings and resource references (logicalIdPrefix, ...), concatenated. |
SecretMapping | envVar, jsonKey? (defaults to envVar). |
ResourceTransform | A function (physicalId, { region, accountId }) => string or a TransformTag. |
TransformTag | { kind: "lambdaArn" }, for JSON config. |
TransformContext | { region, accountId }. |
EmittedConfig | JSON config shape produced by the CDK emitter: projectKey and targets (name to EmittedTarget). |
EmittedTarget | One target's config, resources, secrets?. |
emittedConfigSchema | Valibot schema for EmittedConfig. |
emittedTargetSchema | Valibot schema for EmittedTarget. |
defaultTarget | "default", the target used when none is named. |
CDK (@beesolve/aws-env-resolver/cdk)
Requires the optional peers aws-cdk-lib and constructs.
| Export | Description |
|---|---|
envOf | (construct) => Record<string, DevEnvValue>. The env of the Lambda(s) under a construct. |
secret | ({ secretId, jsonKey? }) => EnvSecret. A Secrets Manager value; jsonKey defaults to the name. |
generateDevEnv | (props: DevEnvProps) => EmittedConfig. Turns the composed env into a mapping; no I/O. |
persistDevEnv | (config, { path }) => void. Writes JSON, creating parent directories. |
devEnv | generateDevEnv then persistDevEnv; optional stage and path (default aws-env.config[.<stage>].json). |
defaultConfigPath | (stage?) => string. The file name devEnv writes without a path. |
DevEnvProps | projectName, env, profile?. |
DevEnvValue | string | EnvResource | EnvSecret. |
The dev env is a plain object you compose: spread envOf(...) for each construct whose env the local process needs, then add literals and secret(...). Later keys win, so spread order is precedence and two embedded constructs that set the same name to different resources never conflict; you pick one. envOf throws when Lambdas under one construct disagree on a name. All values must share one region. Ref and Fn::GetAtt Arn are supported; other intrinsics throw. Env vars set from secret.secretValueFromJson(key) (a {{resolve:secretsmanager:...}} dynamic reference) are emitted as secret mappings automatically. The emitter writes a recipe (env var to logical-id prefix), never resolved physical names.
Config
The CLI loads the first of aws-env.config.<stage>.ts, .js, .mjs, .json (with --stage), then aws-env.config.ts, .js, .mjs, .json, in the cwd. A module exports an options object or a (stage) => options factory (sync or async). A JSON file is the CDK-emitted form: { projectKey, targets }. With one target --target is optional; with several it is required. A module factory receives (stage, target). A flat JSON file without targets (the pre-targets shape) loads as the default target. Generated config goes to .json, hand-written config goes to .ts.
When an env var appears in several sources, the later one wins: config.staticEnv, then CloudFormation resources, then secrets.
Caching
- Non-secret values:
os.tmpdir()/aws-env-resolver/<projectKey>-<stage>-<fingerprint>.json, 24 hour TTL by default (cacheTtlMs). The file records a hash of the resource mapping, so values cached for a different mapping (for example after regenerating the config) are a miss and get re-fetched. - Secrets: macOS keychain, service
aws-env-resolver:<projectKey>:<stage>:<fingerprint>, one item holding all secrets as JSON. The fingerprint hashesregion,stackName, andsecretId, so the cache and keychain share one identity independent of the working directory. A non-default target also enters the cache file's fingerprint (not the keychain's), so targets sharing a stack keep separate resource caches. - Default mode stores items with an empty trust list, so macOS may prompt once on read.
--trustedstores them with-T /usr/bin/securityfor silent reads. - Steady state makes zero AWS calls.
- On non-macOS platforms there is no secure cache and secrets are fetched on every run.
IAM permissions
cloudformation:ListStackResourcessecretsmanager:GetSecretValuests:GetCallerIdentity, only when an ARN transform runs withoutconfig.accountId
See DOCS.md for the how-to guides.