Skip to content

Using skret from a script or agent

Exit codes, non-interactive flags, and copy-paste recipes for running skret from CI, cron, or an AI agent.

Every skret command is non-interactive by default — the exceptions are skret delete and skret rollback without --confirm/--force (both prompt y/N) and --from-stdin at a real terminal (blocks until you send EOF, see below). Output streams are deliberate: stdout carries data (secret values, JSON, rendered templates), stderr carries status (“Set KEY”, warnings, progress). Every failure exits with a distinct, documented code you can branch on instead of parsing stderr text.

skret returns a distinct exit code per failure class, defined in pkg/skret/errors.go:

CodeConstantMeaning
0ExitSuccessOperation completed successfully
1ExitGenericErrorUnclassified error
2ExitConfigError.skret.yaml missing or invalid
3ExitProviderErrorBackend provider call failed (e.g. AWS SSM)
4ExitAuthErrorAuthentication failed
5ExitNotFoundErrorSecret does not exist
6ExitConflictErrorKey already exists (import --on-conflict=fail)
7ExitNetworkErrorNetwork/connectivity failure
8ExitValidationErrorInvalid input — bad flag combination, missing required value, experimental command not enabled
9ExitDriftskret diff --exit-code found a difference between the two sides
10ExitLeakFoundskret scan found a managed secret value in a tracked (or --staged) file
125ExitExecErrorskret run -- could not exec the command (not found on $PATH, or exec failure)

Two of these are the ones you’ll branch on most in automation:

  • skret scan exits 10 when a managed secret value shows up in a file — wire it into a pre-commit hook or a CI leak-guard step. It exits 0 when nothing is found.
  • skret diff A B --exit-code exits 9 when the two secret sets differ, the same non-zero-on-difference contract as git diff --exit-code. Without --exit-code, diff always exits 0 — it’s a report, not a gate, unless you ask it to be one.

See the Error Codes reference for the full table plus provider-specific error mappings and remediation per code.

  • Exact bytes out: skret get KEY --plain. The default get (no --plain) appends one trailing newline for terminal readability; --plain gives you the value’s exact stored bytes with nothing added — use it whenever a script or agent needs the byte-exact value (skret get TOKEN --plain > token.bin).
  • Parseable dump: skret env --format=json (also yaml, export, or the dotenv default) — all four formats round-trip byte-exact; pick json when a script needs to parse the whole environment.
  • Multi-line value in: skret set KEY --from-stdin < file.pem or skret set KEY --from-file path. Both read the entire stream/file (not just the first line), so a PEM key or multi-line JSON blob survives with every embedded newline intact.
  • A value that starts with -: pass -- before the key, or it’s parsed as a flag: skret set -- KEY '-----BEGIN PRIVATE KEY-----...'.
  • Secrets are byte-exact everywhere except run: get, env, template, and sync/import preserve every byte, including NUL, CR, and embedded newlines. skret run/skret run --watch sanitize control bytes on the way into the child process’s environment, because an OS process environment can’t carry a NUL or embedded newline. Full detail: Value fidelity.
  • Multiple config namespaces from one process: the global --config <path> flag loads a specific .skret.yaml directly, bypassing directory discovery entirely — useful for a single cron container serving several projects, each declared in its own config file. If the file does not exist the command fails with a config error; it never silently falls back to discovery. See Configuration.
Terminal window
DB_URL=$(skret get DATABASE_URL --plain)
Terminal window
skret run -- ./server

Every secret in the resolved environment is injected as a real process environment variable; skret forwards the child’s exit code (or exits 125 if the command itself can’t be found/exec’d).

Terminal window
skret env --format=json | jq -r '.DATABASE_URL'
Terminal window
skret sync --to=github,cloudflare

github needs GITHUB_TOKEN in the environment. cloudflare has no flags-only path — it must be declared under sync.targets in .skret.yaml (worker or pages) and needs CLOUDFLARE_API_TOKEN. Preview first with no writes: skret sync --to=github --dry-run prints the exact secret names it would push and exits without writing anything, saving sync state, or touching a dotenv file. Add --no-overwrite to only fill keys absent at the target (existing values are never overwritten) — the safer default for an agent driving sync unattended. See Sync.

Terminal window
skret scan --staged || { echo "a managed secret would be committed" >&2; exit 1; }

Exits 10 on a match, 0 when clean. --staged scans only staged content (git diff --cached), which is what a commit hook wants. See Scan.

Terminal window
skret import --from=dotenv --file=.env --on-conflict=skip

--on-conflict defaults to skip (silently skip keys that already exist); pass --on-conflict=fail to exit 6 (ExitConflictError) instead the first time a key collides, or --on-conflict=overwrite to replace it.

  • Leading-dash values. skret set KEY -----BEGIN... fails — skret’s flag parser reads -----BEGIN... as a flag, not a value. Use skret set -- KEY value, or avoid the problem entirely with --from-stdin/--from-file.

  • --from-stdin at an interactive terminal blocks until EOF. It reads the whole stream, not one line, so with no pipe or redirect it will hang waiting for input — type the value, then send EOF yourself: Ctrl-D on macOS/Linux, Ctrl-Z then Enter on Windows. In a script, always pipe or redirect: ... --from-stdin < file or echo -n "$VALUE" | skret set KEY --from-stdin.

  • Trailing newlines are stripped; embedded ones are not. --from-stdin and --from-file remove only a trailing run of \n bytes (so a value saved by a text editor round-trips without gaining an extra newline). A trailing \r, or any newline in the middle of the value, is left untouched. See Value fidelity for the exact byte-level rules.

  • skret history and skret rollback are experimental and gated. Both require SKRET_EXPERIMENTAL=1 in the environment; without it they exit 8 (ExitValidationError) with an explanatory message instead of running:

    Terminal window
    SKRET_EXPERIMENTAL=1 skret history DATABASE_URL
    SKRET_EXPERIMENTAL=1 skret rollback DATABASE_URL 3 --confirm