Files
small-scripts/specs/gen-secret.spec.md
Paul O'Reilly c470867039 Remove URL-unsafe characters from gen-secret charset
Remove ^, +, ~, :, @ from the allowed charset. The ^ character breaks
SQLAlchemy DATABASE_URL parsing, + becomes space in URL query strings,
: and @ are URL delimiters. The remaining charset (A-Za-z0-9._-) is
safe in URLs, database connection strings, YAML, JSON, and shell
without any encoding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:29:08 +13:00

2.3 KiB

gen-secret

Purpose

Generate a cryptographically random string that is safe to embed unquoted in bash, YAML, and JSON without escaping.

Usage

gen-secret [OPTIONS] [LENGTH]

Arguments

Argument Default Description
LENGTH 32 Number of characters in the generated secret

Flags

Flag Description
--help, -h Show usage information
--dryrun, -n Print what would happen without generating a secret

Character Set

The output uses only characters that need no escaping in bash (unquoted assignment), YAML (plain scalar), and JSON (string value):

A-Z a-z 0-9 . _ -

Excluded (unsafe in at least one context): ", ', \, `, $, !, {, }, (, ), [, ], #, %, &, |, <, >, *, ?, ;, ,, =, +, ^, ~, :, @, space, tab, newline, /

Note: ^ breaks URL parsing (e.g., SQLAlchemy DATABASE_URL). + becomes space in URL query strings. : and @ are URL delimiters (userinfo/host separators). ~ can be shell-expanded. These are excluded to ensure secrets are safe in database connection strings, URLs, and shell contexts without encoding.

Behaviour

  1. Validate that LENGTH is a positive integer.
  2. Read random bytes from /dev/urandom.
  3. Filter to the allowed character set.
  4. Output exactly LENGTH characters followed by a newline.
  5. Exit 0 on success.

Dryrun Behaviour

When --dryrun or -n is passed:

[dryrun] Would generate a 32-character secret from charset: [A-Za-z0-9._\-]

(Substituting the actual length if provided.)

No random output is produced.

Edge Cases

Case Handling
LENGTH is 0 Error: "Length must be a positive integer", exit 1
LENGTH is negative Error: "Length must be a positive integer", exit 1
LENGTH is not a number Error: "Length must be a positive integer", exit 1
No arguments Default to 32
Multiple arguments Error: "Too many arguments", exit 1

Examples

# Default 32-character secret
$ gen-secret
xQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6a

# Custom length
$ gen-secret 64
xQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6axQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6a

# Dryrun
$ gen-secret -n 16
[dryrun] Would generate a 16-character secret from charset: [A-Za-z0-9._\-]