The seekrit-run launcher
seekrit-run is a compiled, single-file version of seekrit run built for
machines. It authenticates with a service token, fetches the bound
environment's encrypted secrets, decrypts them locally, layers them under
any .env files and the live process environment, and then execs your
command. That's all it does — no config files, no caching, no daemon.
SEEKRIT_TOKEN=skt_… seekrit-run -- ./start-server
Use the Node seekrit CLI for human workflows (login, key setup, managing
secrets). seekrit-run is service-token only and read-only at runtime:
the smallest thing that can turn a token into an environment and hand off to
your process.
Degrades gracefully
Fetching seekrit secrets is best-effort. If the token is missing or
malformed, or the API can't be reached, seekrit-run logs a warning to stderr
and still runs your command with just the .env overlay and the live
environment:
seekrit-run: continuing without seekrit-managed secrets: could not reach the seekrit API: …
This means the same launcher works whether or not seekrit is reachable — locally
without a token, in CI, or in an offline sandbox — so you can wire it into an
image's entrypoint unconditionally. Only genuinely local problems stop it: a
usage error, an explicit --env-file that can't be read, or a command that
can't be started. The seekrit run subcommand of the Node CLI behaves the same
way.
Note what that degraded start actually gives you: the command runs, but
without its managed secrets. If your app needs them, it will fail somewhere
further in — usually deeper and less obviously than a failure at startup. Add
--cache when you would rather it keep running on the last secrets it saw.
Surviving an outage with --cache
Off by default. With --cache, each successful resolve is written to disk and a
later run falls back to it when the API cannot be reached:
seekrit-run --cache -- ./start-server
seekrit-run: could not reach the seekrit API: … — using cached secrets fetched 6m ago
Only the encrypted response is stored — the same ciphertext and wrapped data
keys the API serves. Decrypting it still needs this token, so the file is no
more sensitive than the token beside it (it is written 0600 in a 0700
directory). Live resolve is always tried first, and a refused resolve
(401/403) deletes the entry rather than falling back to it, so revoking a
token still takes effect on the next run. Entries expire after
--cache-max-age (default 24h).
In a container, point it at a mounted volume so it survives a restart:
seekrit-run --cache --cache-dir /var/cache/seekrit -- ./start-server
Full behavior, including the two trade-offs it makes, is in the CLI reference.
Why use it instead of the CLI?
- Tiny & static. One ~1–2 MB file with no runtime dependencies — no Node,
no OpenSSL, no system CA bundle. TLS roots are compiled in, so it runs
unchanged in
distroless,alpine, andscratchimages. - Same behavior as
seekrit run. Identical layer precedence,.envparsing, and${OTHER_SECRET}reference expansion; its decryption is verified bit-for-bit against the browser/CLI crypto. - Clean process model. On Unix it replaces itself with your command
(
execvp), so signals and exit codes pass through exactly. On Windows it spawns, waits, and forwards the exit code.
Install
The install script detects your OS and architecture, downloads the matching
binary from run.seekrit.dev, verifies its SHA-256 checksum, and drops it on
your PATH:
curl -fsSL https://run.seekrit.dev/install.sh | sh
It installs to /usr/local/bin if that's writable, otherwise ~/.local/bin.
Override the destination, pin a version, or force a target with environment
variables:
# Pin a version and install somewhere specific.
curl -fsSL https://run.seekrit.dev/install.sh \
| SEEKRIT_RUN_VERSION=0.2.0 SEEKRIT_RUN_INSTALL_DIR="$HOME/bin" sh
| Variable | Default | Description |
|---|---|---|
SEEKRIT_RUN_VERSION | latest | Version to install, e.g. 0.2.0. |
SEEKRIT_RUN_INSTALL_DIR | /usr/local/bin or ~/.local/bin | Where to put the binary. |
SEEKRIT_RUN_TARGET | auto-detected | Force a Rust target triple. |
Manual download
Prefer to fetch it yourself? Every release is published under a versioned and a
latest/ path, each with a .sha256 alongside. On Linux the fully static
musl build runs anywhere (glibc, alpine, distroless, scratch):
# Pick your target: {x86_64,aarch64}-{unknown-linux-musl,unknown-linux-gnu,apple-darwin}
curl -fsSL -O https://run.seekrit.dev/latest/seekrit-run-aarch64-apple-darwin.tar.gz
curl -fsSL -O https://run.seekrit.dev/latest/seekrit-run-aarch64-apple-darwin.sha256
shasum -a 256 -c seekrit-run-aarch64-apple-darwin.sha256
tar xzf seekrit-run-aarch64-apple-darwin.tar.gz
install -m 0755 seekrit-run /usr/local/bin/
Windows ships as a .zip (seekrit-run-x86_64-pc-windows-msvc.zip).
Usage
# Token from the environment (or a .env file); resolves org/app/env from it.
SEEKRIT_TOKEN=skt_… seekrit-run -- pnpm start
# Token via flag; everything after -- is the command, verbatim.
seekrit-run --token skt_… -- node --enable-source-maps server.js
# Swap one composed group's slice for this run.
seekrit-run --with auth-providers=staging -- ./app
# See where each variable resolved from (stderr; names only, never values).
seekrit-run --explain -- true
Precedence
Highest wins — identical to seekrit run:
process env > .env files > app-environment secrets > group secrets
The live process environment always wins, so a value exported in the shell (or by your orchestrator) overrides anything seekrit resolves.
Options
| Flag | Default | Description |
|---|---|---|
-t, --token <skt_…> | SEEKRIT_TOKEN (env or .env) | Service token. |
--api-url <url> | SEEKRIT_API_URL or https://api.seekrit.dev | API base URL. |
-e, --env-file <path> | .env | A .env file to overlay (repeatable). |
--no-env-file | Do not load the default .env. | |
--with <group=env> | Override one composed group's slice (repeatable). | |
--explain | Print each variable's source to stderr. | |
--no-interpolate | Leave ${OTHER_SECRET} references as literal text. | |
--cache | off | Fall back to the last-known-good encrypted response when the API is unreachable. Also SEEKRIT_CACHE=1. |
--no-cache | Override SEEKRIT_CACHE=1 for this run. | |
--cache-dir <path> | SEEKRIT_CACHE_DIR, else $XDG_CACHE_HOME/seekrit | Where cached responses live. |
--cache-max-age <d> | 24h | How stale a cached response may be and still be used. |
HTTPS_PROXY / ALL_PROXY are honored for egress-proxied networks.
In a container
Keep Node out of your runtime image entirely. The simplest way is to copy the
binary out of the published image — seekritdev/run is multi-arch and is just
the static binary on scratch (~2 MB) — into a minimal runtime, and make it the
entrypoint:
FROM seekritdev/run:latest AS seekrit
FROM gcr.io/distroless/static
COPY --from=seekrit /seekrit-run /usr/local/bin/seekrit-run
ENTRYPOINT ["seekrit-run", "--"]
CMD ["./start-server"]
Pin a release tag (e.g. seekritdev/run:0.3.0) for reproducibility, or
:edge for the latest main.
Prefer not to depend on Docker Hub? Fetch the static binary in a build stage instead — pin the version and verify the checksum for reproducible images:
# --- fetch seekrit-run: pinned + checksum-verified --------------------------
FROM alpine:3 AS seekrit
ARG SEEKRIT_RUN_VERSION=0.2.0
ARG TARGET=x86_64-unknown-linux-musl
RUN apk add --no-cache curl && cd /tmp \
&& curl -fsSL -O https://run.seekrit.dev/v${SEEKRIT_RUN_VERSION}/seekrit-run-${TARGET}.tar.gz \
&& curl -fsSL -O https://run.seekrit.dev/v${SEEKRIT_RUN_VERSION}/seekrit-run-${TARGET}.sha256 \
&& sha256sum -c seekrit-run-${TARGET}.sha256 \
&& tar xzf seekrit-run-${TARGET}.tar.gz -C /usr/local/bin
# --- your runtime image: fully static, so distroless/static is enough -------
FROM gcr.io/distroless/static
COPY --from=seekrit /usr/local/bin/seekrit-run /usr/local/bin/seekrit-run
ENTRYPOINT ["seekrit-run", "--"]
CMD ["./start-server"]
docker run --rm \
-e SEEKRIT_TOKEN="$SEEKRIT_TOKEN" \
your-image ./start-server
Inject at runtime, never at build time. Secrets fetched during docker build
can be baked into an image layer. seekrit-run only ever puts values into the
child process's environment — nothing touches disk.
Exit codes
| Code | Meaning |
|---|---|
2 | Usage error (unknown flag, no command). |
1 | An explicitly named --env-file could not be read. |
127 | The command could not be started. |
| child | Otherwise, the command's own exit code. |
A missing/malformed token, a revoked token or missing key grant, and network
failures are not fatal — seekrit-run warns and runs the command without
the managed secrets (see Degrades gracefully).
See the CLI reference for the condensed version, and Service tokens for how to mint the token this binary needs.