Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
# @suaveplan/crypto-bin - Suaveplan crypto bins: suaveplan-hash-password (Argon2id / blake2b password hash + verify CLI). Lives in its own package so the universal @suaveplan/crypto does not pull in @suaveplan/cli runtime deps.
## Summary
Suaveplan password-hashing CLI. Ships the `suaveplan-hash-password`
binary — Argon2id (default) and BLAKE2b password hashing + verification
with PHC/MCF-formatted output strings, distinct exit codes per failure
class, and an env-injected output adapter so the core is testable
without subprocess spawn.
This package lives separately from `@suaveplan/crypto` so the
universal crypto façade does not pull CLI runtime deps into
browser/server consumers that just want primitives.
<!-- begin:auto-docs -->
## Documentation tree
- [bin](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [hash-password](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [hash-password](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/bin/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_- [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_ - [hash-password](./src/hash-password/hash-password.md) — <!-- begin:auto-docs --> _(no submodules)_
## Quickstart
```bash
# Hash a password (Argon2id by default)
$ suaveplan-hash-password --password "correct horse battery staple"
$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>
# Verify (exit 0 = match, 1 = mismatch)
$ suaveplan-hash-password --password "correct horse battery staple" \
--verify '$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>'
$ echo $?
0
```
## Algorithms
- **`--algo argon2id`** (default) — OWASP 2023-recommended password hash.
Output prefix `$argon2id$v=19$...`.
- **`--algo blake2b`** — opt-in alternative for environments that need
it. Output prefix `$blake2b$v=1$...`. Cost factor is configurable via
`--cost-factor <n>` (1–31).
The verify mode auto-detects the algorithm from the stored hash's prefix,
so callers do not have to track which algorithm was used at hash time.
## Exit codes
| Code | Meaning |
|------|------------------------------------------------------------|
| 0 | Success / verify matched |
| 1 | Verify mismatch (wrong password) |
| 2 | Bad arguments (missing/malformed flag value) |
| 3 | Unknown algorithm (unrecognised `--algo` or stored prefix) |
| 4 | Verify failed (stored hash is corrupt or unparseable) |
The `0` / `1` split matches the de-facto contract for shell pipelines —
`if suaveplan-hash-password --password ... --verify ...; then ...; fi`
works as expected, with `3+` reserved for operator errors so callers can
distinguish "wrong password" from "stored data is broken".
## Programmatic use
```ts
import { main } from "@suaveplan/crypto-bin";
const out: string[] = [];
const err: string[] = [];
const code = main(
["--password", "pw", "--algo", "blake2b", "--cost-factor", "10"],
{
write: (s) => out.push(s),
writeError: (s) => err.push(s),
},
);
if (code === 0) {
storeHash(out.join("").trim());
}
```
The `main()` function never throws — every error path is captured and
surfaced via `env.writeError(...)` and a non-zero return value. See
`src/hash-password/hash-password.md` for the full contract.
## Public surface
- `main` — testable CLI core.
- `parseArgs` — pure argv parser (throws `HashPasswordCliError`).
- `detectAlgo` — algorithm sniffer for stored hashes.
- `HashPasswordCliEnv` — output adapter interface.
- `HashPasswordCliError` — error class wrapping the stable code registry.
- `HASH_PASSWORD_CLI_CODES` — `BAD_ARGS` / `UNKNOWN_ALGO` /
`VERIFY_FAILED`.
## Dependencies
- `@suaveplan/crypto-hash` — Argon2id + BLAKE2b primitives.
- `@suaveplan/error` — `SuaveplanError` base + stable code registry.
- `@suaveplan/types` — shared cross-package contracts.
No dependency on `@suaveplan/cli` — argv parsing is hand-rolled (~40
lines) so the bin stays small.

View File

@@ -0,0 +1,107 @@
# `suaveplan-hash-password` bin entry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This file is the platform-runtime entry for the `suaveplan-hash-password`
binary shipped by `@suaveplan/crypto-bin`. It is intentionally a minimal
shim: it constructs an `HashPasswordCliEnv` adapter from `process.stdout`
and `process.stderr`, then delegates to the testable `main()` function in
`../hash-password/hash-password.ts`. All argument parsing, algorithm
dispatch, and exit-code logic live in that pure core so they can be unit
tested by direct invocation rather than by spawning a subprocess.
The shim exists so the package can publish a Node-style `bin` entry under
`./dist/bin/hash-password.js` (with the `#!/usr/bin/env node` shebang
preserved by Vite) without coupling the testable core to `process.*`
globals. Anything that touches `process.argv` / `process.exit` /
`process.stdout` is confined to this module.
## Features
- Forwards `process.argv.slice(2)` into `main()` and exits with the
returned numeric code.
- Wires `process.stdout.write` / `process.stderr.write` into the
`HashPasswordCliEnv` adapter — the core never references `process`
directly.
- Auto-exec guard: only runs `main()` when invoked as the top-level
module (`process.argv[1]` ends with `hash-password.js`). Importing this
module from a test or another script does not trigger an exit.
- Coverage-excluded by design — this module is exercised end-to-end via
the bin install path; the logic it wraps is fully covered by
`../hash-password/hash-password.test.ts`.
- Shebang line emitted verbatim into the dist bundle so the file is
directly executable once symlinked by `bun install` / `npm install`.
## Usage: Basic Example
```bash
# Once installed, the bin is on PATH via the package.json `bin` map
$ suaveplan-hash-password --password "correct horse battery staple"
$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>
$ echo $?
0
```
## Usage: Advanced Example
```bash
# Pipe-friendly verify (exit 0 = match, 1 = mismatch, 3+ = error)
$ STORED='$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>'
$ if suaveplan-hash-password --password "$PW" --verify "$STORED"; then
> echo "login ok"
> else
> case $? in
> 1) echo "wrong password" ;;
> 3) echo "stored hash has unrecognised algorithm" ;;
> 4) echo "stored hash is corrupt" ;;
> *) echo "argument error" ;;
> esac
> fi
```
For programmatic invocation that does not spawn a process, import the
testable core directly instead of this shim:
```ts
import { main } from "@suaveplan/crypto-bin";
const code = main(["--password", "pw"], {
write: (s) => process.stdout.write(s),
writeError: (s) => process.stderr.write(s),
});
```
## API Reference
This module has no public exports — it is a side-effect entry point.
Behaviour:
- On import, registers the auto-exec guard and (if invoked as the
top-level script) calls `main(argv, env)` and exits with its return
value.
- `argv`: `process.argv.slice(2)` — same shape `main()` accepts.
- `env.write(s)`: forwards `s` to `process.stdout.write` synchronously.
- `env.writeError(s)`: forwards `s` to `process.stderr.write`
synchronously.
For the contract of `main()` itself (exit codes, accepted flags, error
classes) see `../hash-password/hash-password.md`.
## Implementation Notes
The auto-exec guard checks `process.argv[1]?.endsWith("hash-password.js")`
rather than `import.meta.url === ...` so the same module survives Vite's
ES-module bundling without ESM-vs-CJS surprises in older Node hosts. The
guard string matches the bundled filename (`hash-password.js`) deliberately
— the source is `.ts` and is never executed directly in production.
`/* istanbul ignore */` markers wrap the guard and the `process.exit`
call because the test suite invokes `main()` directly with a captured env
adapter (see `../hash-password/hash-password.test.ts`). Spawning a
subprocess to cover this two-line wrapper would add filesystem and Node
runtime coupling to the test suite without exercising any logic that is
not already covered. The `vitest.config.ts` coverage `exclude` list
documents this decision in a sibling comment so future readers do not
mistake the exclusion for an oversight.

View File

@@ -0,0 +1,63 @@
# suaveplan-hash-password CLI
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`hash-password.ts` is the testable core of the `suaveplan-hash-password` binary in `@suaveplan/crypto-bin`. It accepts an argv-style list of flags, an env adapter (so tests can capture stdout/stderr without process side effects), and returns an exit code. Two modes: hash mode (no `--verify`) prints a stored hash; verify mode (`--verify <stored>`) checks a candidate password against a stored hash and returns 0/1 by match outcome.
The bin lives in its own package (`@suaveplan/crypto-bin`) rather than alongside the universal `@suaveplan/crypto` because CLI surfaces would force the entire `@suaveplan/cli` runtime tree into universal consumers; keeping the bin in a separate package preserves universal purity.
## Features
- Two algorithms: `argon2id` (default, OWASP 2023 recommended) and `blake2b` (opt-in via `--algo blake2b`).
- Hash mode: prints `$argon2id$v=19$...` or `$blake2b$v=1$...` to stdout, exits 0.
- Verify mode: detects algorithm from the stored hash's prefix and runs the matching `verifyPassword*` function. Exits 0 on match, 1 on mismatch, 3+ on argument / format errors.
- Strict argument validation: missing `--password`, unknown `--algo`, malformed `--cost-factor` all surface as `HashPasswordCliError` with one of three stable error codes (`HASH_PASSWORD_CLI_BAD_ARGS` / `HASH_PASSWORD_CLI_UNKNOWN_ALGO` / `HASH_PASSWORD_CLI_VERIFY_FAILED`).
- Env-injected stdout/stderr — never touches `process.stdout` / `process.stderr` directly, so tests can capture output without spawning a subprocess.
- Per-spec exit-code contract: 0 = success / verify-match, 1 = verify-mismatch, 2 = bad args, 3 = unknown algo, 4 = verify failed (corrupt stored hash).
## Usage: Basic Example
```bash
# Hash a password (Argon2id by default)
$ suaveplan-hash-password --password "correct horse battery staple"
$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>
# Verify
$ suaveplan-hash-password --password "correct horse battery staple" --verify '$argon2id$v=19$m=65536,t=3,p=1$<salt>$<hash>'
$ echo $?
0
```
## Usage: Advanced Example
```ts
// Programmatic invocation (e.g. from a test harness or a higher-level CLI)
import { main } from "@suaveplan/crypto-bin/hash-password/hash-password";
const out: string[] = [];
const err: string[] = [];
const code = main(["--password", "pw", "--algo", "blake2b", "--cost-factor", "10"], {
write: (s) => out.push(s),
writeError: (s) => err.push(s),
});
if (code === 0) {
storeHash(out.join("").trim());
}
```
## API Reference
- `main(argv: readonly string[], env: HashPasswordCliEnv): number` — testable entry point.
- `parseArgs(argv: readonly string[]): HashPasswordCliArgs` — pure argv parser; throws `HashPasswordCliError` on bad input.
- `detectAlgo(storedHash: string): Algo` — algorithm sniffer; throws on unknown prefix.
- `HashPasswordCliEnv { write(s), writeError(s) }` — output adapter interface.
- `HashPasswordCliError extends SuaveplanError` — error class wrapping the three stable codes.
- `HASH_PASSWORD_CLI_CODES` — exported registry: `HASH_PASSWORD_CLI_BAD_ARGS`, `HASH_PASSWORD_CLI_UNKNOWN_ALGO`, `HASH_PASSWORD_CLI_VERIFY_FAILED`.
## Implementation Notes
The CLI deliberately does not depend on `@suaveplan/cli` to avoid pulling the readline / TTY-prompt machinery into a tiny one-shot binary. argv parsing is hand-rolled (≈40 lines) because the option set is small and stable. Verify-mode exit codes are spec-pinned: 0 = match, 1 = mismatch — those two values are the contract for shell pipelines (`if suaveplan-hash-password --password ... --verify ...; then login; fi`). Errors from the verify path (corrupt stored hash) intentionally exit with `4` rather than `1` so a caller can distinguish "wrong password" from "your stored hash is corrupt"; the spec's `verify mismatch exit 1` requirement is preserved for the wrong-password case.