Visitar URL original
feat: update to quicknode-sdk 0.9.0 and expose the new Admin operations (DX-7540) by johnpmitsch · Pull Request #61 · quicknode/cli · GitHub
Skip to content

feat: update to quicknode-sdk 0.9.0 and expose the new Admin operations (DX-7540) - #61

Merged
johnpmitsch merged 8 commits into
mainfrom
dx-7540-update-qn-to-quicknode-sdk-090-and-expose-the-new-admin
Oct 30, 2026
Merged

johnpmitsch merged 8 commits into
mainfrom
dx-7540-update-qn-to-quicknode-sdk-090-and-expose-the-new-admin

Conversation

@johnpmitsch

@johnpmitsch johnpmitsch commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

tl;dr

Bumps quicknode-sdk to 0.9.0 and exposes everything the new Admin contracts offer: per-IP rate limits, response logging, structured request filters, client certificates, method blacklists, range limits, blocked IPs, windowed usage/traffic/error summaries, the account activity log, and retained transactions. Create-style security verbs now print the record they created, so a new token's value is visible immediately.

Closes DX-7540

Impact

  • Breaking SDK alignment, no CLI breaking changes. Every existing command keeps its flags and exit codes. Seven verbs (security token create, referrer/ip/domain-mask/jwt add, tag add, rate-limit set) now also print the created record on stdout (table by default, SDK JSON under -o json); stderr notes are unchanged.
  • New flags on existing verbs: --limit-by-ip <true|false> (rate-limit set, method-create, method-update; set may send the flag alone), --response-logging (security set-options), --percentile (endpoint metrics, metrics endpoint), --pattern/--pattern-file (request-filter create|update), --wide credit/byte columns on the legacy usage by-* tables.
  • New verbs: endpoint security request-filter list, client-cert list|add|enable|disable|remove, method-blacklist show|set|clear, range-limit show|set|clear, blocked-ip list|add|remove; endpoint errors; usage credits|rpc|streams|webhooks; metrics traffic|errors.
  • New nouns: qn activity-log list|filters|show (aliases activity-logs, activity) and qn transaction list|show (aliases transactions, tx).
  • Gated (--yes, exit 5 non-interactively): client-cert remove, client-cert disable, blocked-ip remove, method-blacklist clear, range-limit clear.
  • Rendering: security show / endpoint show list hsts, cors, response_logging, a BLOCKED_IPS section, and structured filter matchers with a PARAMS column; bulk tables show each failed item's ERROR; method-list shows LIMIT_BY_IP.
  • Errors: HTTP 403 renders as "forbidden … (feature or plan access)" instead of the 401 "unauthorized" text. Exit code stays 2.
  • README and qn agent context document all of the above.

CLI examples

Every new or changed command, with fake ids. Reads auto-retry; mutations never do. Gated verbs show --yes as a script would pass it.

Modified commands

# rate limits: caps and the per-IP flag are independent; the flag may go alone
qn endpoint rate-limit set ep-1 --rps 100 --rpm 5000 --limit-by-ip true   # prints effective rps/rpm/rpd
qn endpoint rate-limit set ep-1 --limit-by-ip false                        # sends {"limit_by_ip": false}
qn endpoint rate-limit method-create ep-1 --interval second --method eth_call --rate 10 --limit-by-ip true
qn endpoint rate-limit method-update ep-1 rl-1 --limit-by-ip false
qn endpoint rate-limit method-list ep-1                                     # new LIMIT_BY_IP column

# security options and views
qn endpoint security set-options ep-1 --response-logging enabled
qn endpoint security show ep-1            # hsts, cors, response_logging rows; BLOCKED_IPS section; PARAMS column
qn endpoint show ep-1                     # same rows plus security.blocked_ips count

# create-style verbs now print the created record (table, or SDK JSON with -o json)
qn endpoint security token create ep-1                     # ID + TOKEN
qn endpoint security referrer add ep-1 https://app.example.com
qn endpoint security ip add ep-1 192.0.2.10
qn endpoint security domain-mask add ep-1 rpc.example.com
qn endpoint security jwt add ep-1 --public-key-file key.pem --kid k1 --name ci
qn endpoint tag add ep-1 prod                              # association ID, TAG_ID, LABEL, ENDPOINT_ID
qn endpoint security token create ep-1 -o json | jq -r .data.token

# request filters: method whitelist (as before) or a custom JSON pattern
qn endpoint security request-filter create ep-1 --method eth_call --method eth_blockNumber
qn endpoint security request-filter create ep-1 \
  --pattern '{"method":[{"prefix":"eth_"}],"jsonrpc":["2.0"]}'
qn endpoint security request-filter create ep-1 --pattern-file filter.json
cat filter.json | qn endpoint security request-filter update ep-1 rf-2 --pattern-file -

# endpoint metrics: percentile for response-time metrics
qn endpoint metrics ep-1 --period day --metric response_time_over_time --percentile 95
qn metrics endpoint ep-1 --period day --metric response_time_over_time --percentile 99

# bulk results carry each failed item's error
qn endpoint bulk pause ep-1 ep-2 --yes      # ID / OK / ERROR

# legacy usage tables: credit and byte breakdowns under --wide
qn usage by-endpoint --from 30d --wide
qn usage by-chain --wide

New security verbs

qn endpoint security request-filter list ep-1               # ID / WHITELIST / PATTERN

qn endpoint security client-cert list ep-1
qn endpoint security client-cert add ep-1 --certificate-file client.pem
qn endpoint security client-cert add ep-1 --certificate "$(cat client.pem)"
qn endpoint security client-cert enable ep-1
qn endpoint security client-cert disable ep-1 --yes          # gated
qn endpoint security client-cert remove ep-1 ab:cd:ef --yes  # gated

qn endpoint security method-blacklist show ep-1
qn endpoint security method-blacklist set ep-1 --method eth_sendRawTransaction --method debug_traceTransaction
qn endpoint security method-blacklist set ep-1 --methods eth_sendRawTransaction,debug_traceTransaction
qn endpoint security method-blacklist clear ep-1 --yes       # gated

qn endpoint security range-limit show ep-1                   # limits + supported methods
qn endpoint security range-limit set ep-1 --limit eth_getLogs=1000 --limit eth_getFilterLogs=500
qn endpoint security range-limit clear ep-1 --yes            # gated

qn endpoint security blocked-ip list ep-1
qn endpoint security blocked-ip add ep-1 192.0.2.0/24
qn endpoint security blocked-ip remove ep-1 bip-1 --yes      # gated

Windowed summaries

# --from is required (RFC-3339, 1h, 7d, now); --to defaults to now
qn usage credits --from 24h
qn usage credits --from 2026-01-01T00:00:00Z --to 2026-02-01T00:00:00Z
qn usage rpc --from 1h
qn usage rpc --from 7d --group-by method,endpoint --limit 20
qn usage rpc --from 24h --chain ethereum --network mainnet --method eth_call --tag-id 7
qn usage streams --from 7d --group-by stream
qn usage streams --from 24h --stream-id st-1
qn usage webhooks --from 7d --group-by webhook --webhook-id wh-1

qn metrics traffic --from 24h --group-by endpoint
qn metrics traffic --from 1h --endpoint-name ep-1 --network mainnet
qn metrics errors --from 1h --kind rpc --group-by code,method
qn metrics errors --from 24h --kind http --status-code 429 --endpoint 123 --endpoint 456
qn endpoint errors ep-1 --from 1h --kind rpc --group-by code
qn endpoint errors ep-1 --from 24h --kind http --status-code 500

# paging: the table ends with "next page: --cursor <c>"; the cursor travels alone
qn usage rpc --cursor eyJvZmZzZXQiOjEwMH0
qn usage rpc --from 1h -o json | jq -r .data.next_cursor

Activity log and transactions

qn activity-log list
qn activity-log list --type endpoint --endpoint-id 123 --from 7d
qn activity-log list --query prod --actor 7c9e6679-7425-40de-944b-e07fc1f90ae7 --limit 50 --offset 50
qn activity-log filters                                      # actor ids and target types for --actor/--type
qn activity-log show 7c9e6679-7425-40de-944b-e07fc1f90ae7
qn activity ls                                               # aliases: activity-logs, activity

qn transaction list --from 1h
qn transaction list --from 24h --state failed --network ethereum-mainnet --endpoint 123 --limit 50
qn transaction list --from 7d --method eth_sendRawTransaction --hash 0xabc
qn transaction list --cursor eyJvZmZzZXQiOjEwMH0
qn transaction show ethereum-mainnet 0xabc
qn transaction show ethereum-mainnet 0xabc --payloads        # attempts + verbatim request/response text
qn tx ls --from 1h                                           # aliases: transactions, tx

Where to focus your review

  • src/commands/summary.rs — the shared --from/--to/--limit/--cursor resolution (a cursor must travel alone) and the breakdown table that shows only the --group-by dimensions the API returned (None = unselected, Some(None) = explicit null).
  • src/commands/endpoint/security/mod.rs filter_input — the --method vs --pattern exclusivity and the JSON-object check for custom filters.
  • src/commands/endpoint/ratelimit.rs set — caps and --limit-by-ip are independent; {"limit_by_ip": false} alone must survive.
  • src/errors.rs — the 401/403 split.
  • Safe to skim: the Render impls (one newtype per response, same shape as the existing ones), tests/*.rs (wiremock matchers), tests/snapshots/.

Decisions & tradeoffs

  1. Summaries reuse --from/--to (never the API's last) — same flags and parser as endpoint logs; --from 1h works everywhere. To reverse: add a --last passthrough on WindowArgs.
  2. set replaces, clear empties — for the blacklist and range limits an empty set is an argument error, so a forgotten flag can never wipe the configuration. To reverse: let set with no values send an empty list.
  3. qn endpoint errors lives in metrics.rs — the endpoint and account error routes return the same payload, so they share one renderer; endpoint/mod.rs only dispatches. To reverse: move the args and function into endpoint/mod.rs.
  4. Security module split into security/mod.rs + security/controls.rs (git mv, history preserved) — the file was 900 lines before four new sub-nouns. To reverse: concatenate.
  5. --wide for the new usage columns — default usage by-* tables stay narrow; JSON/YAML/TOON always include every field. To reverse: move the columns into the default header list.
  6. --certificate and --code accept hyphen-leading values — PEM text and RPC error codes start with -; without allow_hyphen_values clap reads them as flags (repo precedent: --end -1).

Technical notes

  • UpdateRateLimitsRequest.rate_limits is Option in 0.9.0; the CLI sends it only when a cap flag is present.
  • EndpointRequestFilter.method is Vec<serde_json::Value>; string matchers render plainly, object matchers as compact JSON, and params as compact JSON with sorted keys so tables are stable.
  • The 0.9.0 wrappers for created records and summaries have a non-optional data, so every mock for those routes returns a body.
  • Summary array filters go out as repeated bracketed keys (group_by[]=method&group_by[]=endpoint); tests match them with wiremock query_param.

How I verified it

AI: built, tested, and self-reviewed (jreview) by john-pipeline running Claude Code.

Ran:

  • cargo fmt --all -- --check passes
  • cargo clippy --all-targets -- -D warnings passes
  • cargo test passes (every suite; 93 new tests, 17 new and 3 updated table snapshots reviewed by hand)
  • cargo build --release has no warnings from this crate
  • --help output of the new commands read in a terminal

Not checked:

  • Nothing was run against the live API. Feature gating (HTTP 403 on activity logs, transactions, client certs, summaries), certificate validation, and real cursor continuation are exercised only through wiremock fixtures shaped from the SDK structs.

Confidence assessment: how likely is this to affect or break prod

Overall: low risk of breakage, small blast radius (a CLI release; no server-side state), fully reversible by pinning the previous qn version.

Concern Likelihood Why Containment
Existing commands against the live API Low The 0.8.2 → 0.9.0 SDK changes are additive on the wire for the routes the CLI already called; all pre-existing tests pass with their request matchers unchanged except the seven mutation verbs, which now decode the body the API already returned Pin the previous release; nothing to migrate
Seven verbs now decode the mutation response Low The SDK's _with_response methods target the same routes and bodies the API documents; if a body ever lacks data, the command exits 1 with a decode error after the mutation succeeded security show / tag list confirm the state; revert to the unit-returning calls
New routes with live account data Medium Fabricated fixtures cover the documented shapes, but sparse variants (nullable ids, absent breakdowns) and plan gating were not seen live Errors map to exit 2 with the new 403 text; nothing is mutated by the read routes
401/403 message split Very low Unit-tested; exit code unchanged Revert one match arm

Not verifiable from here:

  • Whether every account sees the new routes (feature or plan access) and what the live request_filters snapshots look like for accounts with custom patterns.

Adapts the CLI to the 0.9.0 Admin contracts: rate-limit updates wrap
the caps in Option, the method rate-limit, security-options, and
endpoint-metrics requests gain optional fields, and request-filter
matchers are JSON values (structured matchers render as compact JSON
in the REQUEST_FILTERS table). No command behavior changes yet.
…e fields (DX-7540)

- rate-limit set/method-create/method-update accept --limit-by-ip
  <true|false>; set may send the flag alone; method-list shows LIMIT_BY_IP
- security set-options accepts --response-logging; security show and
  endpoint show list hsts, cors, response_logging, and a BLOCKED_IPS section
- request-filter tables render structured matchers and a PARAMS column
- endpoint metrics / metrics endpoint accept --percentile (1-100)
- bulk result tables show each failed item's ERROR
- usage by-endpoint/by-chain/by-tag/by-method show credit and byte
  breakdowns under --wide
- HTTP 403 renders as forbidden (feature or plan access) instead of
  unauthorized
… effective rate limits (DX-7540)

token create, referrer/ip/domain-mask/jwt add, tag add, and rate-limit
set now call the response-returning SDK methods and emit the created
record on stdout (table by default, SDK JSON under -o json). A new
token's value is visible immediately instead of only via security show.
The stderr confirmation notes are unchanged.
…cklist, range limits, blocked IPs (DX-7540)

New verbs under qn endpoint security:
- request-filter list; create/update accept --pattern <JSON> or
  --pattern-file <PATH> (- for stdin) for custom filters, alongside the
  existing --method whitelist form
- client-cert list|add|enable|disable|remove
- method-blacklist show|set|clear
- range-limit show|set --limit METHOD=BLOCKS|clear
- blocked-ip list|add|remove

set verbs replace the whole configuration; clear is a separate verb.
client-cert remove/disable, blocked-ip remove, method-blacklist clear,
and range-limit clear are gated (--yes, or exit 5 non-interactively).
The security module moves to a directory; the controls live in
security/controls.rs. 32 integration tests, 4 table snapshots.
- qn usage credits|rpc|streams|webhooks
- qn metrics traffic|errors
- qn endpoint errors <ENDPOINT_ID>

All take --from (required; --to defaults to now), optional --group-by
and filters, --limit (1-1000), and page with --cursor, which must travel
alone because it carries the original query. Tables print the resolved
window, full-window totals, a breakdown whose columns are the selected
dimensions, and a next-page hint. Negative RPC error codes are accepted
by --code. 14 integration tests, 4 unit tests, 3 table snapshots.
… transaction nouns (DX-7540)

- qn activity-log list|filters|show (aliases activity-logs, activity):
  offset-paginated account change history with query, actor, endpoint,
  type, and time-window filters
- qn transaction list|show (aliases transactions, tx): windowed,
  cursor-paginated retained transactions; show renders the submission
  attempts and, with --payloads, each attempt's retained request and
  response text verbatim; a non-retained hash prints exists: false and
  exits 0

16 integration tests, 5 table snapshots.
README gains examples for the new security verbs, summaries, activity
log, and transactions, plus the cursor-paging rule and the new gated
verbs. CLAUDE.md's SDK notes cover the 0.9.0 contract details that bit
during this change (double-Option dimensions, integer endpoint ids,
from/to renames, non-optional data wrappers, hyphen-leading values).
…review (DX-7540)

pattern_text and read_inline_or_file are only used inside the security
module; transaction show prints the lookup's actual exists value.
@johnpmitsch
johnpmitsch marked this pull request as ready for review October 29, 2026 17:21
@johnpmitsch
johnpmitsch merged commit 36b73a9 into main Oct 30, 2026
13 checks passed
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.

2 participants