Skip to content

Add ts keys rotate and ts keys deactivate commands - #1500

Open
dhruv8sh wants to merge 6 commits into
mainfrom
add-ts-keys-commands
Open

dhruv8sh wants to merge 6 commits into
mainfrom
add-ts-keys-commands

Conversation

@dhruv8sh

@dhruv8sh dhruv8sh commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add ts keys rotate and ts keys deactivate so operators can drive the signing-key admin endpoints from the CLI instead of hand-crafting authenticated requests.
  • Extract the authenticated admin HTTP client (auth, request/response handling) from ts cache purge into a shared admin_client module, and the service base-URL validation into url_guard::service_endpoint (the same helper Add ts keys list and ts keys verify commands #1502 adds), so cache and keys commands use one code path.
  • ts keys deactivate --delete checks the service URL and admin password before prompting, and refuses without --yes unless both stdin and stderr are terminals.
  • Surface the server's error message when a key endpoint returns 2xx with success: false, rather than reporting success.

Changes

File Change
crates/trusted-server-cli/src/admin_client.rs New shared admin client extracted from cache purge
crates/trusted-server-cli/src/url_guard.rs New shared service_endpoint base-URL join and validation
crates/trusted-server-cli/src/commands/cache/mod.rs, cache/purge.rs Use the shared admin client
crates/trusted-server-cli/src/commands/keys/mod.rs New rotate and deactivate subcommands (deactivate supports delete with y/N confirmation)
crates/trusted-server-cli/src/commands/mod.rs, lib.rs, run.rs Wire up the keys command and module
crates/trusted-server-cli/tests/keys_rotation.rs 14 integration tests covering success, failure bodies, and error paths
docs/guide/api-reference.md, docs/guide/key-rotation.md, docs/guide/cli.md, docs/guide/error-reference.md, CHANGELOG.md Document the new CLI commands

Closes

Closes #1430

Spec

Spec: Scoped by issue #1430; no separate spec document.

Overlap with sibling PR

This overlaps with the sibling PR for #1431 (ts keys list / verify, branch add-ts-keys-list-verify). Both add crates/trusted-server-cli/src/commands/keys/mod.rs and edit run.rs. This PR merges first; #1502 rebases onto it and converges on one KeysCommand and one transport. Both PRs add an identical url_guard::service_endpoint.

Open questions for reviewers

  • Delete confirmation is a y/N prompt. Should it instead require typing the kid? Resolved in review: y/N, with stdin and stderr both required to be terminals.
  • Output is plain text, not JSON. Is a --json mode wanted? Resolved in review: not in this PR.

Test plan

  • cargo test-fastly && cargo test-axum (plus test-fastly-reuse, test-cloudflare, test-spin)
  • cargo clippy-fastly && cargo clippy-axum (all 8 clippy aliases)
  • cargo fmt --all -- --check
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run (1838 passed, 1 skipped, Node 24.20.0)
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve
  • Other: CLI tests on host target (incl. 14 keys_rotation tests), template_build_digest, parity

Checklist

  • Feature PRs: spec linked above, ending in a review log of two or more agent review rounds
  • Changes follow AGENTS.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses log macros (not println!)
  • New code has tests
  • No secrets or credentials committed

Wrap the key-rotation admin routes in the CLI so the admin password comes
from TRUSTED_SERVER_ADMIN_PASSWORD instead of the command line, and guard
permanent deletion behind a confirmation. Extract the admin HTTP client
from ts cache purge so both commands share URL checks, auth and redirect
handling. Lead the key-rotation docs with the new commands.

Closes #1430
@dhruv8sh dhruv8sh self-assigned this Nov 5, 2026
@aram356 aram356 added this to the 202611 milestone Nov 5, 2026

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed 51730bd to 2937f0f across correctness, architecture, code smells, and style. No verified findings.

Checks passed: 21 keys/cache fixture tests, 12 focused unit tests, cargo clippy-cli, changed-file Rust formatting, and git diff --check. Fixture tests exercised unconfirmed deletion refusal, remote plaintext rejection, and redirect refusal.

Not verified: docs Prettier, which was unavailable; real terminal interaction; live HTTPS deployment; and full workspace suites.

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

Adds ts keys rotate and ts keys deactivate on an admin client extracted from ts cache purge. The request and response shapes match the server's RotateKeyResponse and DeactivateKeyResponse, and a 2xx with success: false is reported as a failure. One blocking issue: the --delete prompt counts as interactive when stderr is redirected, so a permanent delete can go through without the operator seeing the prompt.

All 8 inline comments below carry a one-click GitHub suggestion. Use Commit suggestion (or Add suggestion to batch for several at once) to apply them as commits on the PR branch. Each was applied alone and all together on 2937f0f72 and passed cargo fmt --check, cargo clippy-cli and the host-target CLI tests (Prettier for the docs one). The behavior changes were also checked against the built ts binary with a local fixture or a mutation run. The two findings under "Cross-cutting" have no inline anchor.

Blocking

🔧 wrench

  • --delete prompt counts as interactive when only stdin is a terminal — see inline at crates/trusted-server-cli/src/commands/keys/mod.rs:91

Non-blocking

♻️ refactor

  • Pin the exact Authorization header — see inline at crates/trusted-server-cli/tests/keys_rotation.rs:126
  • Test that --yes requires --delete — see inline at crates/trusted-server-cli/src/commands/keys/mod.rs:341
  • Check the URL and password before the delete prompt — see inline at crates/trusted-server-cli/src/commands/keys/mod.rs:189
  • Dead userinfo checks; converge with #1502's service_endpoint — see inline at crates/trusted-server-cli/src/admin_client.rs:50

🤔 thinking

  • An empty remaining_active_kids after a success means unknown, not none — see inline at crates/trusted-server-cli/src/commands/keys/mod.rs:222

⛏ nitpick

  • Show the netrc file format — see inline at docs/guide/api-reference.md:796
  • Drop the unused re-export — see inline at crates/trusted-server-cli/src/commands/cache/mod.rs:48

Cross-cutting / body-level findings

  • 🏕 Docs outside the diff still lead with curl and an inline password
    • docs/guide/error-reference.md:419-421 and :442-444 (the "Signing key not found" and "JWKS endpoint empty" fixes) still show curl … -u admin:password, the pattern #1430 removes. ts keys rotate --service https://edge.example.com fits both.
    • The command index in docs/guide/cli.md (which "summarizes native --help output") has no ts keys, ts keys rotate or ts keys deactivate rows. ts cache is missing too.
    • CHANGELOG.md [Unreleased] ### Added has no entry for the new commands.
  • 📝 On the open questions in the description
    • y/N or typing the kid: y/N matches ts audit ad-templates generate, the prompt already names the kid, and the server refuses the two unrecoverable cases (the current key, the last active key) on its own. With the stderr fix above, y/N is enough.
    • --json: not needed for this PR. 🌱 If rotation gets automated, scripts will want new_kid without parsing new kid: …. #1502 already has ts keys list --json, so a --json that prints the service's body would be consistent later.

CI Status

  • Analyze (actions): PASS
  • Analyze (javascript-typescript): PASS (both runs)
  • Analyze (python): PASS
  • Analyze (rust): PASS
  • browser integration tests: PASS
  • cargo check (cloudflare native + wasm32-unknown-unknown): PASS
  • cargo check/build/test (spin native + wasm32-wasip1): PASS
  • cargo fmt: PASS (required)
  • cargo test: PASS (required)
  • cargo test (axum native): PASS
  • cargo test (cross-adapter parity): PASS
  • cargo test (ts CLI, native) (macos-latest): PASS
  • cargo test (ts CLI, native) (ubuntu-latest): PASS
  • CI Node pin guard: PASS
  • CLAUDE.md symlink guard: PASS
  • CodeQL: PASS
  • docs-proposal scripts: PASS
  • format-docs: PASS (required)
  • format-typescript: PASS (required)
  • HTML EOF runtime (Fastly): PASS
  • integration tests: PASS
  • integration tests (Fastly EC lifecycle): PASS
  • prepare integration artifacts: PASS
  • trace runtime and browser acceptance: PASS
  • ts dev lint domains (changed lines): PASS
  • vitest: PASS
  • worker-deadlines (cloudflare): PASS
  • worker-deadlines (spin): PASS

Comment thread docs/guide/api-reference.md Resolved
@aram356 aram356 mentioned this pull request Nov 9, 2026
12 of 15 tasks
ts keys deactivate --delete now refuses without --yes unless both stdin and stderr are terminals, matching ts audit ad-templates generate, and checks the service URL and admin password before it prompts. An empty remaining_active_kids after a success prints as unknown rather than none.

Move the service base-URL join into url_guard::service_endpoint, dropping the unreachable userinfo checks, so the keys list/verify branch shares it. Drop the unused ADMIN_PASSWORD_ENVIRONMENT_VARIABLE re-export from commands::cache. Pin the exact Authorization header and test that --yes requires --delete.

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Add ts cache and ts keys rows to the CLI command index, a changelog entry for ts keys rotate and deactivate, and the netrc line format for the curl fallback. Point the error-reference key fixes at ts keys rotate instead of curl with an inline password, and say that --delete needs --yes when stdin or stderr is not a terminal.

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
@dhruv8sh

dhruv8sh commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

@aram356 Review-body points, addressed in d923873:

  • docs/guide/error-reference.md: the "Signing key not found" and "JWKS endpoint empty" fixes now use ts keys rotate --service https://edge.example.com instead of curl with an inline password.
  • docs/guide/cli.md: added ts cache, ts cache purge, ts keys, ts keys rotate and ts keys deactivate rows, taken from --help.
  • CHANGELOG.md: [Unreleased] ### Added entry for both commands, including the --yes rule for non-terminal runs.
  • Open questions: kept y/N, which is safe now that the stderr fix is in (dd93588). No --json in this PR. I struck both questions in the description.

@dhruv8sh
dhruv8sh requested a review from aram356 November 10, 2026 14:46

This branch has not been deployed

No deployments
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.

Add ts keys rotate and ts keys deactivate commands

3 participants