# 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 ```bash # 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._\-] ```