Skip to content

moq-token-cli: treat - as stdin/stdout, not a literal file path#1490

Merged
kixelated merged 5 commits into
mainfrom
claude/silly-beaver-dcfd5e
May 24, 2026
Merged

moq-token-cli: treat - as stdin/stdout, not a literal file path#1490
kixelated merged 5 commits into
mainfrom
claude/silly-beaver-dcfd5e

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

moq-token-cli generate --out - silently created a file literally named - instead of writing to stdout, which made the obvious pipeline footgun-prone:

moq-token-cli generate --out - | wrangler secret put ROOT_KEY
# wrangler reads 0 bytes, stores an empty secret, app logs "ROOT_KEY is not configured"

This PR makes - mean stdin/stdout consistently and guards the cases where that would collide with another stream.

Changes

  • generate: --out - and --public - now write to stdout (existing default-no---out stdout behavior is unchanged).
  • sign: --key - now reads the JWK from stdin, so generate --out - | sign --key - works.
  • Errors:
    • generate errors if both keys would land on stdout (--out - with --public -, or --public - with --out unset and no --out-dir).
    • verify --key - errors with a clear message since stdin is already reserved for the token.
  • The --out-dir / --public-dir flags are unaffected (a directory of stdout doesn't make sense).

Test plan

  • cargo build -p moq-token-cli clean
  • cargo clippy -p moq-token-cli --all-targets -- -D warnings clean
  • generate --out - prints to stdout
  • generate --public - --out file.jwk prints the public key, writes the private to file
  • generate --public - (no --out) errors instead of jamming both keys to stdout
  • generate --out - | sign --key - --root test --subscribe "" produces a valid token
  • verify --key - errors with the reserved-stdin message
  • File-based roundtrip (generate --out f.jwksign --key f.jwkverify --key f.jwk) unchanged

🤖 Generated with Claude Code

Previously, `moq-token-cli generate --out -` silently created a file
literally named `-`, which made `generate --out - | wrangler secret put`
pipe 0 bytes to wrangler (and then wrangler happily stored an empty
secret).

- `--out` / `--public` write to stdout when set to `-`
- `--key` for `sign` reads from stdin when set to `-`
- error if both keys would land on stdout (`--out -` + `--public -`,
  or `--public -` with `--out` unset)
- error on `verify --key -` since stdin is reserved for the token

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fd06d254-5fdc-4d8b-83e4-2800e3fae17a

📥 Commits

Reviewing files that changed from the base of the PR and between b7c4e71 and a518b7e.

📒 Files selected for processing (1)
  • rs/moq-token-cli/src/main.rs

Walkthrough

The CLI now treats - as stdin/stdout for key and token I/O. New helpers (is_dash, write_key, read_key, read_token) handle - routing and trimming. Generate accepts --out/--public as - and validates it won't write both keys to stdout. Sign loads the signing key via read_key(&key) (accepting -). Verify adds --in (default -), rejects the case where both --key and --in are -, then loads the key with read_key and the token with read_token.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: treating - as a special path for stdin/stdout instead of a literal file path in the moq-token-cli tool.
Description check ✅ Passed The description is directly related to the changeset, explaining the problem (literal - file creation), the solution (stdin/stdout support), specific changes to each subcommand, error handling, and test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/silly-beaver-dcfd5e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
rs/moq-token-cli/src/main.rs (3)

173-177: ⚡ Quick win

Add error context for clearer diagnostics.

While the stdin guard (lines 173-175) is correct, the subsequent operations on lines 176-177 would benefit from error context to make failures more actionable.

As per coding guidelines, prefer anyhow::Context for error conversion.

📝 Suggested improvement
 		if is_dash(&key) {
 			anyhow::bail!("--key cannot read from stdin; stdin is reserved for the token");
 		}
-		let key = moq_token::Key::from_file(key)?;
-		let token = io::read_to_string(io::stdin())?.trim().to_string();
+		let key = moq_token::Key::from_file(key).context("failed to read verification key")?;
+		let token = io::read_to_string(io::stdin()).context("failed to read token from stdin")?.trim().to_string();
 		let payload = key.decode(&token)?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-token-cli/src/main.rs` around lines 173 - 177, The stdin guard is fine
but failures when reading the key file and reading stdin lack context; update
the calls that can fail—moq_token::Key::from_file(key) and
io::read_to_string(io::stdin())—to use anyhow::Context (e.g.,
.with_context(...)) so errors include actionable messages (mention which key
path failed and that reading stdin for the token failed), and keep the
is_dash(&key) check as-is.

87-94: ⚡ Quick win

Add error context for clearer diagnostics.

Adding .context() to the error paths would make failures more actionable. For example, line 89 could indicate serialization failure, and line 92 could show which file path failed.

As per coding guidelines, prefer anyhow::Context for error conversion.

📝 Suggested improvement
 fn write_key(key: &moq_token::Key, path: &std::path::Path) -> anyhow::Result<()> {
 	if is_dash(path) {
-		println!("{}", key.to_str()?);
+		println!("{}", key.to_str().context("failed to serialize key")?);
 		Ok(())
 	} else {
-		Ok(key.to_file(path)?)
+		key.to_file(path).context("failed to write key to file")
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-token-cli/src/main.rs` around lines 87 - 94, The write_key function
should attach context to errors using anyhow::Context: wrap key.to_str()? with
.context("failed to serialize key to string") (used when is_dash(path) is true)
and wrap key.to_file(path)? with .with_context(|| format!("failed to write key
to file: {}", path.display())) so failures from key.to_str and key.to_file (and
the provided path) produce actionable diagnostics; keep existing is_dash(path)
logic and return types unchanged.

96-103: ⚡ Quick win

Add error context for clearer diagnostics.

Each error path should include context about what operation failed. This helps users diagnose issues when reading keys from stdin or files.

As per coding guidelines, prefer anyhow::Context for error conversion.

📝 Suggested improvement
 fn read_key(path: &std::path::Path) -> anyhow::Result<moq_token::Key> {
 	if is_dash(path) {
-		let contents = io::read_to_string(io::stdin())?;
-		Ok(moq_token::Key::from_str(contents.trim())?)
+		let contents = io::read_to_string(io::stdin()).context("failed to read key from stdin")?;
+		moq_token::Key::from_str(contents.trim()).context("failed to parse key from stdin")
 	} else {
-		Ok(moq_token::Key::from_file(path)?)
+		moq_token::Key::from_file(path).context("failed to read key from file")
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-token-cli/src/main.rs` around lines 96 - 103, The read_key function
needs richer error context: wrap the io::read_to_string(io::stdin()) call with
anyhow::Context to indicate failure reading from stdin (include that it was
stdin), and wrap the moq_token::Key::from_str(contents.trim()) conversion with
Context to indicate parsing the key from stdin failed; likewise, for the file
branch wrap moq_token::Key::from_file(path) with Context to indicate failure
reading/parsing the key file and include the path in the message. Update
read_key (which uses is_dash, io::read_to_string, moq_token::Key::from_str, and
moq_token::Key::from_file) to use anyhow::Context on each fallible call so
errors surface clear diagnostic text.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@rs/moq-token-cli/src/main.rs`:
- Around line 173-177: The stdin guard is fine but failures when reading the key
file and reading stdin lack context; update the calls that can
fail—moq_token::Key::from_file(key) and io::read_to_string(io::stdin())—to use
anyhow::Context (e.g., .with_context(...)) so errors include actionable messages
(mention which key path failed and that reading stdin for the token failed), and
keep the is_dash(&key) check as-is.
- Around line 87-94: The write_key function should attach context to errors
using anyhow::Context: wrap key.to_str()? with .context("failed to serialize key
to string") (used when is_dash(path) is true) and wrap key.to_file(path)? with
.with_context(|| format!("failed to write key to file: {}", path.display())) so
failures from key.to_str and key.to_file (and the provided path) produce
actionable diagnostics; keep existing is_dash(path) logic and return types
unchanged.
- Around line 96-103: The read_key function needs richer error context: wrap the
io::read_to_string(io::stdin()) call with anyhow::Context to indicate failure
reading from stdin (include that it was stdin), and wrap the
moq_token::Key::from_str(contents.trim()) conversion with Context to indicate
parsing the key from stdin failed; likewise, for the file branch wrap
moq_token::Key::from_file(path) with Context to indicate failure reading/parsing
the key file and include the path in the message. Update read_key (which uses
is_dash, io::read_to_string, moq_token::Key::from_str, and
moq_token::Key::from_file) to use anyhow::Context on each fallible call so
errors surface clear diagnostic text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60e03129-e130-4090-b45e-9703333bdee6

📥 Commits

Reviewing files that changed from the base of the PR and between f9eb9db and e7d6cd4.

📒 Files selected for processing (1)
  • rs/moq-token-cli/src/main.rs

CodeRabbit nitpick: bare IO/JSON errors from from_file/to_file leave the
user guessing which file failed. Wrap with anyhow context that includes
the path, and route verify through read_key so it inherits the same.

Context on to_str() (infallible-in-practice) and on stdin reads
(self-describing) intentionally skipped — would be noise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Addressed in 13bb504:

  • write_key / read_key: wrapped to_file / from_file with with_context(|| format!("...{path}")) so failures name the offending path.
  • read_key (stdin branch): from_str gets .context("failed to parse key from stdin").
  • Verify now goes through read_key (after the dash guard) and inherits the same context, dropping the duplicate from_file call.

Skipped intentionally:

  • to_str() — serde_json::to_string on a well-formed Key is infallible in practice; context would be noise.
  • read_to_string(stdin) for the token in Verify — the underlying I/O error is self-explanatory and the surrounding command already implies the source.

(Written by Claude)

kixelated and others added 2 commits May 24, 2026 14:49
Previously `verify` always pulled the token from stdin, which forced
the key onto a file (`--key -` was rejected). With `--in <PATH>`
(default `-`), the token source is explicit and `--key -` works as
long as the token isn't also on stdin.

Existing `verify --key <file> < token` invocations still work because
`--in` defaults to `-`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rs/moq-token-cli/src/main.rs`:
- Around line 77-83: Update the doc comment for the key argument to clarify that
`--key -` (stdin) is only supported when the token comes from a file (i.e.,
`--in` is not `-`). Edit the doc string above the key field (the `key: PathBuf`
arg) to something like: "Path to the key file. Use `-` for stdin, but only when
`--in` is not `-` (token must come from a file)." This makes the behavior
consistent with the guard around token handling in the verify flow that checks
the token source.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef9b64f0-328a-4939-a36f-135b2cb1a8b9

📥 Commits

Reviewing files that changed from the base of the PR and between 13bb504 and b7c4e71.

📒 Files selected for processing (1)
  • rs/moq-token-cli/src/main.rs

Comment thread rs/moq-token-cli/src/main.rs Outdated
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kixelated kixelated enabled auto-merge (squash) May 24, 2026 22:05
@kixelated kixelated merged commit 2915730 into main May 24, 2026
1 check passed
@kixelated kixelated deleted the claude/silly-beaver-dcfd5e branch May 24, 2026 22:15
This was referenced May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant