Repository navigation
Releases: transmitworks/tropel
Release list
tropel v0.6.0
The release that makes tropel installable as a dependency. Every crate is
published to crates.io, so a consumer — KnockPort first — depends on
version = "0.6.0" instead of a git SHA.
The API is unstable pre-1.0. tropel-sdk is deliberately NOT in this
lockstep: it is the contract a third-party adapter author depends on, in its
own repo with its own history, and its version tracks its own API.
- Added — tropel reads KnockPort collections
- Changed — the whole crate tree is published
- Fixed —
publish-dry-run.shcried wolf on a first full-tree publish
Full notes for 0.6.0
The release that makes tropel installable as a dependency. Every crate is
published to crates.io, so a consumer — KnockPort first — depends on
version = "0.6.0" instead of a git SHA.
The API is unstable pre-1.0. tropel-sdk is deliberately NOT in this
lockstep: it is the contract a third-party adapter author depends on, in its
own repo with its own history, and its version tracks its own API.
Added — tropel reads KnockPort collections
tropel-input-knockport. tropel could already import Postman, Bruno,
Insomnia, HAR, OpenAPI, k6 and .http — every competitor's format — and not
the one format both products own, so tropel run my-collection/ failed on it.
The format is a directory, not a file (knockport.yaml +
requests/**/*.yaml), so the adapter uses parse_with_path — the hook that
already existed for k6's module resolution. It follows the client's own rules
rather than convenient ones: the filename is the identity (KP-101, so no
id: keys and a missing name: takes the file stem), order: is
authoritative and lists filenames, files absent from order: still import
rather than being dropped, and disabled rows are not sent. Scripts and
assertions are reported, not silently dropped — tropel's script realm is
the pm/k6 surface, and running a KnockPort script there would not be the same
script.
Changed — the whole crate tree is published
- 35 crates at 0.6.0, with the binary,
tropel-weband the four npm
packages.version-lockstep.shreports all seven surfaces agreeing. - 28
publish = falselines removed. Six of the seven crates KnockPort
links were opted out of the registry, which is why it had to pin a git SHA. - 14 crates gained a
description— cargo refuses to publish without
one, so this was the hard prerequisite. - Every workspace
tropel-*declaration carries aversion. A path-only
dependency cannot be published at all: consumers resolve the version.
0.6.0 rather than 0.5.6 because the release is breaking — tropel-auth's
EdgeGrid error type went TropelError → String and its clock/CSPRNG
helpers became feature-gated; Request gained a proxy field. Pre-1.0 that
is a minor bump by this repo's own policy.
Fixed — publish-dry-run.sh cried wolf on a first full-tree publish
It read "no matching package named X found" as a hard failure. Cargo words
"a workspace sibling is not on crates.io yet" two ways and only the other one
was recognised, so the first full-tree run reported "BLOCKED: 12 of 35 …
the gate is NOT met" for the entirely normal pre-publish state. The guard
against a real failure is unchanged: the named crate must be a workspace
member whose own version is the one being asked for.
It also now prints the topological publish order. Sequencing 35 crates by
hand is how a version number gets burned on a failed publish.
Install
Linux builds are static musl — they run unmodified in Alpine, distroless
and scratch containers.
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.6.0/tropel-v0.6.0-x86_64-unknown-linux-musl.tar.gz | tar xz
sudo install -m755 tropel-v0.6.0-x86_64-unknown-linux-musl/tropel /usr/local/bin/Other platforms, checksum verification, building from source
# Linux arm64
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.6.0/tropel-v0.6.0-aarch64-unknown-linux-musl.tar.gz | tar xz
# macOS Apple Silicon
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.6.0/tropel-v0.6.0-aarch64-apple-darwin.tar.gz | tar xz
# macOS Intel
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.6.0/tropel-v0.6.0-x86_64-apple-darwin.tar.gz | tar xzWindows (PowerShell):
Invoke-WebRequest -Uri "https://github.com/transithq/tropel/releases/download/v0.6.0/tropel-v0.6.0-x86_64-pc-windows-msvc.zip" -OutFile tropel.zip
Expand-Archive tropel.zip -DestinationPath .macOS binaries are unsigned, so Gatekeeper quarantines a downloaded archive.
Clear it with xattr -d com.apple.quarantine ./tropel.
curl -fsSLO https://github.com/transithq/tropel/releases/download/v0.6.0/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing # macOS: shasum -a 256 -cFrom source (Rust 1.94+): cargo build --release puts it at
./target/release/tropel.
Each tropel-v0.6.0-<target> archive carries tropel, tropel-controller and
tropel-agent; tropel-wasm-v0.6.0.tar.gz is the browser tier
(core-wasm, input-wasm, runtime-wasm, shims, also on npm at
0.6.0); SHA256SUMS covers every asset.
Full changelog ·
Docs ·
Issues · Apache-2.0
tropel v0.5.5
Tropel is an open-source load-testing framework in Rust. It runs
Postman collections, HAR files, OpenAPI specs, Bruno and
Insomnia exports, and k6 scripts as load tests — a native Rust hot
path with an embedded QuickJS engine for script execution.
Pre-1.0: the API is unstable. k6 parity is actively expanding and the
tropel-sdksurface still changes between minor versions. Pin an exact
version if you depend on it.
Install
Pick your platform. Linux builds are static musl — they run unmodified in
Alpine, distroless and scratch containers.
# Linux x86_64
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.5.5/tropel-v0.5.5-x86_64-unknown-linux-musl.tar.gz | tar xz
sudo install -m755 tropel-v0.5.5-x86_64-unknown-linux-musl/tropel /usr/local/bin/
# Linux arm64
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.5.5/tropel-v0.5.5-aarch64-unknown-linux-musl.tar.gz | tar xz
sudo install -m755 tropel-v0.5.5-aarch64-unknown-linux-musl/tropel /usr/local/bin/
# macOS Apple Silicon
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.5.5/tropel-v0.5.5-aarch64-apple-darwin.tar.gz | tar xz
sudo install -m755 tropel-v0.5.5-aarch64-apple-darwin/tropel /usr/local/bin/
# macOS Intel
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.5.5/tropel-v0.5.5-x86_64-apple-darwin.tar.gz | tar xz
sudo install -m755 tropel-v0.5.5-x86_64-apple-darwin/tropel /usr/local/bin/Windows (PowerShell):
Invoke-WebRequest -Uri "https://github.com/transithq/tropel/releases/download/v0.5.5/tropel-v0.5.5-x86_64-pc-windows-msvc.zip" -OutFile tropel.zip
Expand-Archive tropel.zip -DestinationPath .macOS note: binaries are unsigned, so Gatekeeper will quarantine a downloaded
archive. Clear it with xattr -d com.apple.quarantine ./tropel.
Verify your download
curl -fsSLO https://github.com/transithq/tropel/releases/download/v0.5.5/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing # macOS: shasum -a 256 -cFrom source (Rust 1.94+)
cargo build --release # ./target/release/tropelAssets
| asset | contents |
|---|---|
tropel-v0.5.5-<target>.tar.gz / .zip |
tropel, tropel-controller, tropel-agent + LICENSE, README |
tropel-wasm-v0.5.5.tar.gz |
the browser/edge tier — core-wasm, input-wasm, runtime-wasm, shims |
SHA256SUMS |
checksums over every asset above |
Targets: x86_64/aarch64 Linux (musl), x86_64/aarch64 macOS,
x86_64 Windows (MSVC).
Quick start
tropel run collection.json --vus 50 --duration 2m # Postman, HAR, OpenAPI, k6, Bruno, Insomnia
tropel inspect collection.json # preview without sending traffic
tropel extensions # list the input formats this binary shipsFull docs: docs/ — CLI
reference, executors, scripting, metrics, extensions, distributed execution.
What's in it
- Seven executors — constant-vus, ramping-vus, shared/per-vu iterations,
constant & ramping arrival rate, externally-controlled, with graceful
stop/ramp-down, think time and pacing - Postman
pm.*scripting — tests, assertions, variable scopes,
setNextRequest,sendRequest, custom metrics - k6-style scripting —
http.*,check(),group(),sleep(),
Counter/Gauge/Rate/Trend; exportedoptionshonoured - HDR-histogram metrics — p50/p90/p95/p99, sub-timings, tag-scoped
aggregation, thresholds with k6-compatible abort semantics - Streaming outputs — NDJSON, StatsD, InfluxDB, Prometheus, OTLP
(protobuf + gzip), plus stdout/JSON/CSV reporters - Distributed — k6-style execution segments for multi-node runs
- Extensible —
tropel-sdk+tropel build --with <crate>, plus a
sandboxed WASM plugin tier for third-party input formats
npm packages
@tropel/core-wasm@0.5.5 @tropel/input-wasm@0.5.5 @tropel/runtime-wasm@0.5.5 @tropel/shims@0.5.5
Changes in 0.5.5
Three tagged versions shipped without a changelog entry — 0.3.0 and 0.5.4 were
tagged, and the file stopped at 0.2.0. This section therefore covers
everything since 0.2.0 rather than 0.5.4 alone, because a reader upgrading
from the last documented release needs the whole span. scripts/release-notes.sh
extracts it, so it is also what the release page shows.
The API is unstable pre-1.0. tropel-sdk is at 0.4.0 and moves between
minor versions; k6 parity is still expanding.
Added — the browser and desktop tiers can now execute what they could only configure
The theme of this release. KnockPort (and any embedder) could create a
digest, Hawk, SigV4, OAuth1, WSSE or EdgeGrid auth config and every send
refused, because the signing lived behind tropel-auth's reqwest
feature and wasm32-unknown-unknown cannot compile reqwest. The rules moved
out from behind that gate and got a JS facade:
- Pure auth builders (
tropel-auth::builders, ungated): digest
challenge-response, Hawk, AWS SigV4 — including the service-derivation and
S3 double-encoding rules — and OAuth1's RFC 5849 §3.4.1.2 base-string URI
and form parsing.signers.rsbecame a thin adapter that reads fields off a
reqwest::Requestand calls in, so there is one implementation rather than
two. @tropel/core-wasmexports them:digestSign,hawkSign,
awsSigV4Sign,oauth1Sign,edgegridSign, plus
oauth1SignatureMethods()so a picker offers exactly what the signer
accepts instead of keeping its own list.edgegridSigntakes the whole
request, because the signature covers it — method, url, the named
headers' values and the body hash — and refuses a missing nonce or
timestamp rather than fabricating one: this tier has no clock and no
CSPRNG worth using, andcrypto.getRandomValuesis a better source anyway.tropel agent's/auth/signanswers for EdgeGrid and WSSE too. It had
the signers (viabuild_auth_signer, which/executealready calls) and
refused them as unknown at the endpoint the desktop tier actually calls —
one surface signing what the other called unsupported. Its refusal message
was worse than the gap: a hardcoded "supported: digest, hawk, awsSigV4,
oauth1" that went stale the moment a scheme was added, so a caller reading
it would conclude the agent could not sign something it could. Now derived
from one declaration, with a test asserting every entry appears in the
refusal.- WSSE UsernameToken and Akamai EdgeGrid (EG1-HMAC-SHA256) through the
signer builder. EdgeGrid usesyyyyMMddTHH:mm:ss+0000, not RFC 3339 — an
ISO stamp goes into the signing data verbatim and produces a 401 that
mentions neither the clock nor the format. Bodies over Akamai's 128 KiB
default are not hashed rather than truncated, because a truncated
content hash is a signature the server cannot reproduce. - OAuth1 PLAINTEXT, over TLS only. RSA-* still refuses by name rather than
silently downgrading to HMAC-SHA1.
Added — declarative assertions in Rust
assert_evaluate(response, assertions)with 28 operators, their arity,
and target resolution (status,header.*,body.*, JSON paths). Pinned
by a test that asserts the set and its order, because the editor renders
from it.@tropel/core-wasmexportsassertEvaluateand the operator table. The
regex matcher is the host'sRegExp, passed in: linking a Rust regex
would put back the 152 KB TR-434 removed, and it would be unfaithful —
JavaScript has backreferences and lookaround that Rust'sregexdoes not.- Failure messages name the target, not the assertion, so a reader learns
which field was wrong rather than which rule fired.
Added — the loopback agent grew a rules surface
tropel agent went from an execute-only endpoint to the full seam an embedder
needs: /resolve and /resolve/batch, /assert, /operators, /auth/sign,
/auth/oauth2, /variables/dynamic (and its batched twin), /constants, and
/script — which carries all four variable scopes both ways, seeds the
response a test asserts against, returns what the script did to the request,
and has a bidirectional host-callback channel so bru.runRequest works.
--exit-with-parent means a dead supervisor cannot leak a live agent.
Added — inputs and protocols
- Bruno
.brutext format parsed directly, keeping disabled entries,
vars, settings and assertions — a disabled row is deliberate local state,
and dropping it makes the checkbox meaningless. - OpenAPI declarations reach the Scenario (
ScenarioItem::contract): the
declared parameters with their types andrequiredflags, the declared
request body, and the declared responses keyed by the spec's own string
so4XXanddefaultsurvive. Pluspath_template, because building an
executable request substitutes/users/{id}into/users/exampleand the
substituted segment is indistinguishable from a literal one afterwards. - Private CA bundles on the TLS builder (
root_cert_paths,
keep_system_roots). Multi-cert bundles read every certificate —
Certificate::from_pemreads only the FIRST, so a chain handed to it whole
drops the intermediate and then fails verification in a very confusing way.
A config that would trust no CA at all is refused by name. - A proxy surface:
off/fixed/system/pac, proxy auth, and
bypass rules with exact hosts,*.suffix(subdomains only), bare*, IP
and CIDR. A malformed bypass entry is an error, not a fuzzy match, because
both ways of getting it wrong route traffic silently. PAC directive
failover ordering with a bounded TTL is implemented; PAC evaluation
(runningFindProxyForURL) still needs a JS engine and is not. - Per-request proxy and TLS:
Request::proxyalongsideRequest::certificate,
...
tropel v0.2.0
Tropel is an open-source load-testing framework in Rust. It runs
Postman collections, HAR files, OpenAPI specs, Bruno and
Insomnia exports, and k6 scripts as load tests — a native Rust hot
path with an embedded QuickJS engine for script execution.
Pre-1.0: the API is unstable. k6 parity is actively expanding and the
tropel-sdksurface still changes between minor versions. Pin an exact
version if you depend on it.
Install
Pick your platform. Linux builds are static musl — they run unmodified in
Alpine, distroless and scratch containers.
# Linux x86_64
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.2.0/tropel-v0.2.0-x86_64-unknown-linux-musl.tar.gz | tar xz
sudo install -m755 tropel-v0.2.0-x86_64-unknown-linux-musl/tropel /usr/local/bin/
# Linux arm64
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.2.0/tropel-v0.2.0-aarch64-unknown-linux-musl.tar.gz | tar xz
sudo install -m755 tropel-v0.2.0-aarch64-unknown-linux-musl/tropel /usr/local/bin/
# macOS Apple Silicon
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.2.0/tropel-v0.2.0-aarch64-apple-darwin.tar.gz | tar xz
sudo install -m755 tropel-v0.2.0-aarch64-apple-darwin/tropel /usr/local/bin/
# macOS Intel
curl -fsSL https://github.com/transithq/tropel/releases/download/v0.2.0/tropel-v0.2.0-x86_64-apple-darwin.tar.gz | tar xz
sudo install -m755 tropel-v0.2.0-x86_64-apple-darwin/tropel /usr/local/bin/Windows (PowerShell):
Invoke-WebRequest -Uri "https://github.com/transithq/tropel/releases/download/v0.2.0/tropel-v0.2.0-x86_64-pc-windows-msvc.zip" -OutFile tropel.zip
Expand-Archive tropel.zip -DestinationPath .macOS note: binaries are unsigned, so Gatekeeper will quarantine a downloaded
archive. Clear it with xattr -d com.apple.quarantine ./tropel.
Verify your download
curl -fsSLO https://github.com/transithq/tropel/releases/download/v0.2.0/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing # macOS: shasum -a 256 -cFrom source (Rust 1.94+)
cargo build --release # ./target/release/tropelAssets
| asset | contents |
|---|---|
tropel-v0.2.0-<target>.tar.gz / .zip |
tropel, tropel-controller, tropel-agent + LICENSE, README |
tropel-wasm-v0.2.0.tar.gz |
the browser/edge tier — core-wasm, input-wasm, runtime-wasm, shims |
SHA256SUMS |
checksums over every asset above |
Targets: x86_64/aarch64 Linux (musl), x86_64/aarch64 macOS,
x86_64 Windows (MSVC).
Quick start
tropel run collection.json --vus 50 --duration 2m # Postman, HAR, OpenAPI, k6, Bruno, Insomnia
tropel inspect collection.json # preview without sending traffic
tropel extensions # list the input formats this binary shipsFull docs: docs/ — CLI
reference, executors, scripting, metrics, extensions, distributed execution.
What's in it
- Seven executors — constant-vus, ramping-vus, shared/per-vu iterations,
constant & ramping arrival rate, externally-controlled, with graceful
stop/ramp-down, think time and pacing - Postman
pm.*scripting — tests, assertions, variable scopes,
setNextRequest,sendRequest, custom metrics - k6-style scripting —
http.*,check(),group(),sleep(),
Counter/Gauge/Rate/Trend; exportedoptionshonoured - HDR-histogram metrics — p50/p90/p95/p99, sub-timings, tag-scoped
aggregation, thresholds with k6-compatible abort semantics - Streaming outputs — NDJSON, StatsD, InfluxDB, Prometheus, OTLP
(protobuf + gzip), plus stdout/JSON/CSV reporters - Distributed — k6-style execution segments for multi-node runs
- Extensible —
tropel-sdk+tropel build --with <crate>, plus a
sandboxed WASM plugin tier for third-party input formats
npm packages
@tropel/core-wasm@0.2.0 @tropel/input-wasm@0.2.0 @tropel/runtime-wasm@0.2.0 @tropel/shims@0.2.0
Changes in 0.2.0
First tagged release. Versioned 0.2.0 rather than 0.1.0 across all seven
surfaces (binary, tropel-engine, tropel-web, and the four @tropel/* npm
packages — tropel-engine is what --version prints) because
@tropel/shims@0.1.0 and @tropel/runtime-wasm@0.1.0 were published on
2026-08-10 and npm versions are immutable: those artifacts still call the
__tropel_pm_* bridges this tree renamed to __tropel_trp_*, so they cannot
be paired with this runtime. 0.2.0 is the first npm set that matches the
binary.
The API is unstable pre-1.0. k6 parity is actively expanding and the
tropel-sdk surface still moves between minor versions.
Fixed — correctness
sleep()was non-functional on every declarative format (Postman, HAR,
OpenAPI,.http, Insomnia, Bruno).__tropel_native_sleepwas registered as
an async host function on an rquickjsRuntimewith no spawner, so the first
call panicked with "tried to use async function in non async runtime";
rquickjs stashed that panic and re-raised it on whichever VU next threw — a
different VU on a shared runtime. Now a blocking sleep with an absolute
deadline. This also fixesbru.sleep(ms).- Headline
http_req_duration/iteration_durationwere computed from one
metrics shard.shard_for_keyhashed the metric name and its tags,
against its own documented contract, so{url:/a}and{url:/b}landed on
different shards and the merge kept only the largest-count partial. Count,
avg, min, max and p95 were derived from a fraction of the population. http_req_failedwas the maximum of per-shard rates — 10 failures across
400 requests reported0.10instead of0.025. Now merges numerator and
denominator.output_samples_dropped/aggregator_samples_droppedwere multiplied by
SHARD_COUNT— process-global atomics read once per shard and summed, so
one output dropping 1,000 samples reported 4,000.absorb_snapshotdiscarded over-cap series without counting them, so a
controller merge that lost series still reported"unverified": false.- The QuickJS promise-rejection tracker was overwritten by every new VU.
It is a property of the runtime, not the context, so on a shared runtime one
VU's unhandled rejection was recorded into another's map — the first VU
passed silently, the second failed with an error it never raised. bru.setVar/setEnvVar/setCollectionVarwere not inverses of their
getters — they wrote withString(value)while the getters used
JSON.parse, sosetVar('id','1234')read back as the number1234and an
object read back as"[object Object]".@tropel/shimsshipped withoutk6-core.js, leaving every
@tropel/runtime-wasmembedder withoutcheck/group/Counter/Gauge/
Rate/Trend. The npm bundle list is now derived fromShim::ALLin the
Rust source, so the two cannot diverge silently.ws_*samples carriedgroup:"ws"where k6's root group is"".
Changed
- Per-VU JS shims are now selected by input format, and the bytecode cache is keyed by bundle (TR-501). The compiled-shim-bytecode cache was a single
OnceLockkeyed on nothing, so only the full default bundle could use it and every narrowed bundle paid a per-VU source parse+compile — which made shim gating a pessimisation: an http-only script measured 557,824 B/VU against the full bundle's 497,584 B/VU. The cache is now keyed by bundle identity, andShimBundle::for_formatpicks the shim set from the resolvingInputAdapter::id(). ✅MEAS (Apple M2, release, N=25, contexts sharing one worker-threadRuntime): har/openapi/http/insomnia 203,400 B/VU, content-gated http-only 261,878, postman 369,424, k6/unknown format 385,324. (An earlier revision of this entry quoted 280,480 / 336,848 / 479,952 / 497,584 — those came from a harness that summed a shared runtime's heap once per context and divided by N, reporting the whole 25-context total as a per-VU figure. Withdrawn; do not cite them.) Behaviour change: a context created bycreate_vu_js_contextno longer necessarily has all ofpm/chai/_/CryptoJS/bru—bruis dropped for every format exceptbru, and the assertion/utility libraries are dropped for the four formats whose adapters cannot emit a script.pm.jsis still loaded for every format. The k6 Driver's own shim bundle (tropel-input-k6) is unchanged. - License: dual MIT OR Apache-2.0 → Apache-2.0 only.
LICENSE-MITremoved;LICENSE-APACHEnow carries the full canonical text (previously a placeholder stub) and is copied intocrates/tropel-sdk/so published artifacts include it.deny.tomlkeepsMITon the dependency allowlist (third-party crates only).
Full changelog: CHANGELOG.md ·
Issues: github.com/transithq/tropel/issues ·
Licence: Apache-2.0