sq config keyring migrate

Part of the sq config keyring command group; see Secrets for the broader picture.

migrate is the one keyring command a typical user is likely to run. It moves plaintext credentials out of sq.yml and into the OS keyring in bulk. For each source it writes the source’s full Location (the connection string, credentials and all) to a fresh opaque keyring entry, then rewrites that source’s location in sq.yml to a bare ${keyring:<id>} placeholder. The driver type stays in the driver: field; the keyring entry holds the whole DSN.

Back up your config first

migrate rewrites sq.yml in place and keeps no backup of its own. Before a real run, export your current config so you can restore it if anything looks wrong:

$ sq config export -o sq.bak.yml

sq config export copies the config verbatim, including the inline credentials migrate is about to move, so the backup is a complete pre-migration snapshot. If you want a self-contained copy with every secret resolved in-line (for example, before moving to a machine whose keyring won’t hold these entries), add --expand. Note that this writes every secret in plaintext:

$ sq config export --expand -o sq.bak.yml

What gets migrated

Name a single source by handle, or pass --all for the whole collection:

$ sq config keyring migrate @sakila
$ sq config keyring migrate --all

Sources with nothing to move are skipped automatically, each with its reason shown in the output (skips are hidden unless you pass -v):

  • File or document sources with no embedded credentials, such as CSV, SQLite, or Excel: no credentials to migrate.
  • A connection URL that carries no password: no password to migrate.
  • A location that already uses a ${...} placeholder, so re-runs are idempotent: already has a placeholder.
  • A malformed location or placeholder, surfaced rather than stamped into the keyring: malformed location: <reason> or malformed placeholder syntax: <reason>.

Preview, then confirm

Every run targets either a single source by handle or the whole collection with --all. Two flags control how it behaves, and both work with either target:

  • --dry-run previews the plan and changes nothing (mints no IDs, writes nothing).
  • --yes skips the confirmation prompt, for non-interactive use.

--dry-run: preview without changes

# Preview a single source
$ sq config keyring migrate @sakila --dry-run

# Preview the whole collection
$ sq config keyring migrate --all --dry-run

The output is an aligned table listing only the sources that will migrate. Sources with nothing to move (file paths, no password, already migrated) are omitted to keep the list focused; pass -v to show them with their skip reason. If nothing is eligible, migrate says so and makes no changes:

HANDLE            STATUS   DETAIL
@sakila/pg        migrate  ${keyring:<new-id>}
@sakila/local/pg  migrate  ${keyring:<new-id>}

The confirmation prompt and --yes

A real run prints the same plan, then prompts before changing anything:

Proceed with migration? [y/N]

Answer y/yes to proceed, or n/no (or just press Enter, the [y/N] default) to cancel. Cancelling touches nothing (no keyring writes, no sq.yml change) and exits non-zero, so a script can tell a declined migration from a completed one. Only y/yes/n/no (case-insensitive) are accepted; any other answer, or no answer at all, is an error and exits non-zero without retrying. Pass --yes to skip the prompt, which is what you want in scripts:

# Migrate one source without prompting
$ sq config keyring migrate @sakila --yes

# Migrate everything without prompting
$ sq config keyring migrate --all --yes

JSON output (--json) is non-interactive: it skips both the preview and the prompt and applies directly, so --yes is implied.

How changes are applied

migrate runs under a config lock (so it won’t race another sq process) and is atomic: a --all run either migrates every eligible source or changes nothing. It writes each source’s keyring entry and rewrites its location in memory, then saves sq.yml once for the whole batch. If any step fails, the run is rolled back: every keyring entry it wrote is deleted, every location is restored, sq.yml is left untouched, and the command exits non-zero. A failed migration never leaves your config half-converted.

Reference

For each source (or one specified by handle), write its
Location URL to the OS keyring at a fresh opaque ID and replace the
Location with a bare ${keyring:<id>} placeholder. The driver type stays
in the driver: field; the keyring entry holds the entire DSN.

Sources skipped automatically:
  - Non-URL locations (file paths, sqlite, Excel, etc.)
  - URLs with no password component
  - Locations that already contain a ${...} placeholder

Use --dry-run to preview without making any changes. Use --yes to skip
the confirmation prompt.

Usage:
  sq config keyring migrate [@HANDLE]

Examples:
  # Preview the migration
  $ sq config keyring migrate --all --dry-run

  # Migrate every source without prompting
  $ sq config keyring migrate --all --yes

  # Migrate a single source
  $ sq config keyring migrate @sakila

Flags:
      --all         Migrate every source
      --dry-run     Show planned changes, make no writes
      --yes         Skip the confirmation prompt
  -t, --text        Output text
  -j, --json        Output JSON
  -h, --header      Print header row (default true)
  -H, --no-header   Don't print header row
      --help        help for migrate

Global Flags:
      --config string         Load config from here
      --debug.pprof string    pprof profiling mode (default "off")
      --error.format string   Error output format (default "text")
  -E, --error.stack           Print error stack trace to stderr
      --expand                Resolve ${scheme:path} placeholders to their underlying values
      --log                   Enable logging
      --log.file string       Log file path (default "$HOME/Library/Logs/sq/sq.log")
      --log.format string     Log output format (text or json) (default "text")
      --log.level string      Log level, one of: DEBUG, INFO, WARN, ERROR (default "DEBUG")
  -M, --monochrome            Don't print color output
      --no-progress           Don't show progress bar
      --no-redact             Don't redact passwords in output (deprecated, use --reveal)
      --reveal                Show secret values in output (don't redact passwords; print keyring values)
  -v, --verbose               Print verbose output