Ferrum Family · Desktop API client

Ferrum Anvil

Put your APIs to the test.

A local-first API client for desktop and the command line. Build requests, run repeatable tests and load tests, and troubleshoot Ferrum Edge with explanations grounded in what actually happened on the wire. No account, works offline.

v0.1.1 · unsigned preview Windows · macOS · Linux Desktop app + CLI PolyForm Noncommercial
Status
v0.1.1 previewPublished October 1, 2026; unsigned installers
Ships as
Desktop app + CLIWindows, macOS and Linux, plus the anvil command
Built with
Rust + Tauri 2React 19 and TypeScript renderer
Ferrum Edge
Catalogs for 0.9.8, 0.9.7 and 0.9.5Works with any API; no gateway required
Ferrum Anvil v0.1.1 is an unsigned preview. Verify downloads with SHA256SUMS. macOS installers are not Developer ID signed or notarized and Windows installers have no code-signing certificate. In-app updates use verified minisign signatures; this does not code-sign the installers. See preview downloads or Getting Started. Screenshots remain historical pre-release captures.
Ferrum Anvil diagnosing an HTTP 502 Bad Gateway returned by a Ferrum Edge gateway. Transport completed and the application failed. The first finding, Gateway could not prepare a connection to the backend, is marked Likely, scoped to the gateway-to-backend leg, owned by the gateway operator and based on the connection_failure token. It lists what the finding does not prove, including that TLS failed, that DNS failed, or that the caller's client certificate is wrong.
Pre-release build. A request through a local Ferrum Edge 0.9.7 gateway whose backend refuses connections. Anvil says Likely, not Confirmed, names the leg and who can act, and lists what the gateway's token does not prove. Captured on macOS by the automated desktop test suite.

One Engine to Send, Test and Explain

One Rust engine prepares, sends, observes and diagnoses every request. The desktop app, the anvil command-line client, the collection runner and the load worker all use it.

Every protocol you need

HTTP/1.1, HTTP/2 and HTTP/3, WebSocket, gRPC, gRPC-Web, SSE, TCP/TLS and UDP/DTLS, with live interactive sessions where the protocol allows them.

Findings from evidence

Deterministic rules read DNS, connect, TLS and protocol evidence. Each finding states its confidence, the leg it is about, who can act, what it does not prove and what to try next.

Load tests

Arrival-rate, virtual-user and fixed-iteration workloads with a load unit per protocol, HDR latency percentiles, a timeline, run comparison and HTML, JSON or CSV reports.

Auth, TLS and mesh identity

API key, Basic, Bearer, JWT, OAuth 2.0 with PKCE, Ferrum HMAC, DPoP, mTLS, private CAs, SPIFFE identities and HBONE tunnels, with verification on by default.

Local first and encrypted

Offline, with no account and no cloud sync. Everything you create is encrypted on disk, and the profile locks on demand, when idle or when the system sleeps.

Import and share safely

Import OpenAPI, WSDL, Postman, Insomnia, cURL and HAR with a preview. Exports leave secrets out by default, and nothing imported ever runs on its own.

Three Things Anvil Is For

1 · Build and send

Requests you can repeat

Workspaces with nested folders, saved requests with immutable revisions, environments, variables and history.

  • WebSocket (with optional permessage-deflate), gRPC, gRPC-Web and SSE, each also over HTTP/3
  • Mesh and edge testing: HBONE tunnels, SPIFFE Workload API identities (X.509-SVID and JWT-SVID), SNI override, CONNECT-UDP (MASQUE) and PROXY protocol v1/v2
  • Opt-in 0-RTT early data for replay-safe requests, with 425 Too Early handled as RFC 8470 describes
  • Import OpenAPI 2.0–3.2, WSDL 1.1, Postman, Insomnia, cURL and HAR
  • Collection runner with datasets, chained extraction and JUnit, HTML or JSON reports
2 · Understand the failure

Findings from evidence

Deterministic rules read typed evidence instead of rewording status codes.

  • DNS, connect and TLS phases, dispatch state, HTTP/2 signals, body completeness, gRPC trailers, WebSocket close codes
  • Each finding states its confidence, the leg it is about, who can act, what it does not prove, alternatives and next steps
  • Transport, application, assertions and dispatch are reported separately
3 · Test under load

Load with the same requests

Open (arrival rate), closed (virtual users) and fixed-iteration workloads over saved requests, one protocol per plan.

  • A load unit per protocol: HTTP requests, gRPC calls and streams, SSE streams, WebSocket sessions, TCP exchanges and UDP/DTLS exchanges
  • Runs in a separate worker process with the same request preparation as Send
  • Balanced counts, HDR latency percentiles, a timeline, generator health and run comparison
  • Every run needs an explicit authorization acknowledgement

Real Results, Nothing Mocked

Screenshots from the native app, driven by the automated desktop test suite against a real Ferrum Edge 0.9.7 gateway and local test servers on macOS.

A normal call

Every result kept separate

A GET through a local Ferrum Edge 0.9.7 gateway returns 200. The summary keeps transport, application, tests and dispatch as separate results, next to timing, size and protocol.

  • Transport completed, application success
  • Tests not run, dispatch sent
  • Pretty-printed body, rendered as inert text
Ferrum Anvil request editor after sending GET to a local Ferrum Edge gateway at 127.0.0.1 port 18080, path ok/echo. The response shows 200 OK over HTTP/1.1 with transport completed, application success, tests not run and dispatch sent, and the Body tab shows the pretty-printed JSON echo of the request.
Pre-release build. Captured by the automated desktop test suite.
A frontend certificate issue

Confirmed, because Anvil saw it

Here the failure is on your own connection: the server's certificate chains to a throwaway test CA that the TLS profile does not trust. Anvil observed this directly, so it says Confirmed.

  • Records that nothing was dispatched
  • Scoped to your connection, owned by the caller
  • Never offers disabling verification as the default fix
Ferrum Anvil diagnosis of an HTTPS request to a local test server: no response, transport failed, application not evaluated, dispatch not dispatched. The finding The server certificate is not trusted is marked Confirmed, scoped Your connection to destination, owned by the caller, and explains that the certificate issued by a throwaway test CA does not chain to a trusted authority in the TLS profile. Lists of what it does not prove and other possibilities follow.
Pre-release build. The test server's certificate is signed by a throwaway CA created for the test.
A saved load report

Counts that balance

A completed fixed-iteration run, with latency percentiles from merged HDR histograms and timeouts counted separately.

  • Started, completed and failure counts that add up
  • Status codes and redacted failure samples
  • JSON, CSV, timeline CSV and HTML exports
Ferrum Anvil load report for a completed run named E2E smoke load: 300 iterations started and 300 requests completed, with zero transport failures, timeouts, application failures and assertion failures. It names the load unit (HTTP requests) and defines what completed, success and latency mean, counts HTTP/3 fallback attempts separately, and shows latency percentiles for successful sends, status code counts, no failure categories, JSON, CSV, Timeline CSV and HTML export buttons, and notes explaining the workload model, the connection mode and how latency is measured.
Pre-release build. The figures come from a 300-request automated smoke run against a local test server on the same machine. They are not a benchmark.

Evidence From Every Leg

Anvil observes its own connection directly. Through a declared Ferrum Edge gateway it also reads the gateway's markers, and keeps claims about the gateway-to-backend leg at the confidence that evidence allows.

Ferrum AnvilDesktop app, CLI, runner and load worker on one engine
Ferrum EdgeOptional. A declared gateway profile turns on catalog matching
Your backendOr any API server, reached directly

The same bytes everywhere

A manual Send, a collection run and a load iteration share one request preparation, so what you test is what you send.

Findings name the leg

A finding says whether it is about your connection or the gateway-to-backend leg, and who can act on it: you or the gateway operator.

Gateway knowledge is opt-in

Catalog matching happens only for a destination you declare as a Ferrum gateway. Everything else is diagnosed from Anvil's own observations.

Works With Any API. Knows More About Ferrum Edge.

Works with any API

Nothing in Anvil requires Ferrum Edge. Requests, tests, load runs and the core diagnostics work against any server, because findings come from what Anvil observed on its own connection: name resolution, TCP, TLS, the bytes it wrote, protocol signals, status and body completeness.

Enhanced diagnostics with Ferrum Edge

Declare a destination as a Ferrum gateway integration profile, naming its release, and Anvil also matches responses against that release's source-audited compatibility catalog: 540 client-observable outcomes for 0.9.8, 538 for 0.9.7 and 528 for 0.9.5.

Supported versions: Ferrum Edge 0.9.8 (the default for new gateway profiles), 0.9.7 and 0.9.5. A profile that names another release gets no catalog: Anvil reports ferrum.catalog.unavailable, matches no outcomes, and reads the tokens only with the coarse meaning every audited release shares.

A Ferrum-looking header is not enough

Any server can send X-Gateway-Error. Without a declared gateway profile, Anvil reports such a header as an unverified marker and does not attribute the response to a gateway.

What a Diagnosis Can and Cannot Tell You

Every finding carries its own confidence. Anvil reports what the evidence supports, lists the alternatives it cannot rule out, and never guarantees a root cause.

ConfidenceMeaning
ConfirmedDirectly observed, for example a refused TCP connection or an untrusted certificate on your own connection.
LikelyStrong but indirect or spoofable evidence. Every claim derived from a Ferrum Edge marker stops here.
UnknownThe evidence cannot separate the listed causes, so all of them are shown.
ConflictingThe evidence points in different directions.

Gateway Error Tokens Stay Coarse

Ferrum Edge 0.9.7 and 0.9.5 expose seven public tokens; 0.9.8 adds request_timeout. Each one covers several internal causes, so Anvil explains the category and does not turn a token into a precise cause.

TokenWhat Anvil saysWhat Anvil never claims from the token alone
connection_failureThe gateway could not set up a connection to the configured backend (DNS, TCP, TLS, pool and similar).That TLS failed, that DNS failed, or that your client certificate is wrong (the gateway uses its own identity).
backend_timeoutThe gateway's backend deadline elapsed.That the backend received the request, or that it is slow rather than unreachable.
request_timeout (0.9.8)The route deadline expired before a backend held the current attempt.That no earlier retry reached a backend, or which phase used the budget.
backend_errorThe backend path failed or the gateway refused locally.That the backend returned this error.
circuit_breaker_openThe gateway's breaker for this backend is open.That the backend is down right now.
overloadThe gateway shed load, was draining, or hit its response-transform ceiling.CPU pressure.
config_staleThe data plane's configuration fence is stale.Which configuration change is missing.
concurrency_limitA concurrency limit rejected the request.The limit value or the cause of the load.

Markers are capped at “likely”

On Ferrum Edge 0.9.7 and 0.9.5 a backend can inject X-Gateway-Error and X-Gateway-Upstream-Status on some paths, so marker-based claims stay at likely even for a declared gateway over verified TLS. Edge 0.9.8 strips backend marker copies, but the response can still traverse another proxy. Edge v0.9.9+ provides opt-in diagnostic references; Anvil v0.1.1 has no catalog or authenticated resolver for those releases, so its gateway findings remain capped at likely.

Absence proves nothing either

A missing marker does not prove a response came from the backend. A 403 alone never proves a WAF, because the WAF and bot-detection default bodies are byte-identical and backend 403s look the same.

How the diagnostics are checked

The tagged failure lab supports checksum-pinned Ferrum Edge 0.9.8 (default), 0.9.7 and 0.9.5 binaries on loopback. Every scenario runs with and without a trusted gateway profile, and lookalike backend responses must not be blamed on the gateway. Skipped scenarios are never counted as passes. The tagged lab defines 14 profiles, including MCP. Refer to the published release evidence for platform and run results; these pages do not certify a fresh lab run.

Read the Diagnostics Guide →

Local first

Offline, no account, locked when you leave

Anvil needs no sign-in, no Ferrum account and no cloud sync. What you create or observe is encrypted on disk with XChaCha20-Poly1305; only structural fields such as ids and timestamps stay in the clear.

  • Start without a password: the data key goes to the OS keychain (macOS Keychain, Windows Credential Manager or a Secret Service such as GNOME Keyring or KWallet); where none exists, Anvil asks for a passphrase and never stores data unencrypted
  • Or use a passphrase, with a recovery key shown once. Better on a shared computer
  • Lock with a button, ⌘/Ctrl+L, an idle timeout or system sleep
  • Enforced by the backend: locking drops the data key, clears cached OAuth tokens, Workload API identities, TLS session tickets and pooled connections, and stops running requests, sessions and load workers
Ferrum Anvil lock screen with the Ferrum Anvil logo and its tagline Put your APIs to the test, and a notice that reads Locked (manually). Active runs were stopped. Below it are a profile selector for a passphrase profile, a passphrase field, an Unlock button, and links to use the recovery key or create a new profile.
Pre-release build. The lock screen after a manual lock.

Protocols and Authentication

Support is rated per use: sending and automation, interactive sessions, and load tests.

ProtocolSend and automateInteractive sessionLoad tests
HTTP/1.1, HTTP/2 (TLS or h2c)YesRequest/responseYes: one HTTP request per unit
HTTP/3 (forced, or automatic fallback)Yes; a fallback is recorded as its own attemptRequest/responseYes: fallback attempts are counted separately, and their time is part of the request's latency
0-RTT early data (opt-in, off by default)Yes: QUIC 0-RTT for HTTP/3, and TLS 1.3 early data with the HTTP/1.1-only or HTTP/2-only policy. Only GET, HEAD and OPTIONS, plus PUT, DELETE or TRACE if you list them; a 425 Too Early gets at most one retry, after the handshakeRequest/responseRefused
WebSocket (HTTP/1.1 Upgrade; HTTP/2 or HTTP/3 extended CONNECT)Yes; over HTTP/3 it needs wss:// and never falls back to another bootstrap. Optional permessage-deflate (RFC 7692), off by default and chosen per request; the message size limit applies after decompressionYesYes: one WebSocket session per unit, counted as opened, rejected or not opened, with messages and close codes; round trips only when the request expects replies
gRPC unary and server streaming (HTTP/2, TLS or h2c)YesNo, by designYes: unary calls counted by status code, where a missing status is never success; server streams counted per stream. Persistent mode reuses a pooled channel per virtual user
gRPC client streaming and bidirectional (HTTP/2, TLS or h2c)YesYesRefused
gRPC over HTTP/3 (all four call modes)Yes; forced HTTP/3 never uses TCP, and with fallback a separate HTTP/2 attempt is recorded only if QUIC failed before the call was sentClient streaming and bidirectional, as over HTTP/2Unary and server streaming, as over HTTP/2; the other modes are refused
gRPC-Web, binary and text (HTTP/1.1, HTTP/2 or HTTP/3)Unary and server streaming only, because the protocol has no client stream; a body without a trailer frame is reported incomplete, never successNo; there is no client stream to driveYes, as native gRPC
Server-sent events (HTTP/1.1, HTTP/2 or HTTP/3)YesReceive onlyYes: one stream per unit, with events, time to first event and how it ended; automatic reconnect is refused
TCP, TCP+TLSYesYesYes: one TCP exchange per unit, with frames and bytes each way and whether the expected frames arrived
UDP, DTLS 1.2/1.3YesYesYes: one UDP or DTLS exchange per unit. Datagrams sent and received are counted separately; silence is “no response observed”, never a delivery claim
UDP and DTLS through an HTTP/3 CONNECT-UDP proxy (MASQUE, RFC 9298)Yes; DTLS runs end to end with the target inside the tunnelYes, as for UDP and DTLSRefused
PROXY protocol: v1/v2 header on TCP, TCP+TLS and HTTP-family requests; v2 datagram envelope on UDP and DTLSYes. On HTTP/1.1, HTTP/2, WebSocket, gRPC, gRPC-Web and SSE over TCP, the header is sent once per new connection, and pooled connections are reused only by requests with the same header. Refused over HTTP/3 and through any proxy. The datagram envelope can be authenticated as Ferrum Edge defines itYes, as for the carried protocolYes, for load units over TCP: one header per new connection
Mesh client: HBONE tunnels (HTTP/2 CONNECT over mTLS)Yes, carrying HTTP, WebSocket, gRPC, SSE and raw TCP, and UDP and DTLS as a Ferrum Mesh datagram tunnel; the endpoint can be verified by SPIFFE ID or trust domain (X.509-SVID). Tunnel-leg failures get their own hbone.* findings and never blame the destinationAs for the carried protocolRefused for UDP and DTLS, and for HTTP or gRPC in persistent mode; other load through HBONE has not been exercised

permessage-deflate through Ferrum Edge

Ferrum Edge 0.9.7 and 0.9.5 strip the offer before the backend handshake, so through the gateway Anvil reports “offered, not negotiated” and the session runs uncompressed. Compressed sessions are proven against test servers and an independent implementation.

PROXY headers on HTTP requests

For listeners behind a PROXY-speaking balancer, such as HAProxy, nginx or Envoy. Ferrum Edge's HTTP listeners do not accept a PROXY header; Anvil reports the listener's answer as observed and lists a listener that does not expect the header as a possible cause.

0-RTT early data

Sent only for replay-safe methods, and session tickets live in memory only, cleared on lock. Ferrum Edge 0.9.7 and 0.9.5 accept early data only on their HTTP/3 listener, when it is enabled there; their HTTPS listener resumes sessions without it.

Known gaps

Not implemented: double HBONE, HBONE over QUIC, CONNECT-IP and WebTransport, none of which Ferrum Edge supports either; CONNECT-UDP over HTTP/2; PROXY headers over HTTP/3 or through a proxy; WebSocket extensions other than permessage-deflate; load actions that hold sessions open while sending messages at a rate; load runs through CONNECT-UDP or HBONE datagram tunnels.

Auth and TLS

Workload API identities are checked in the lab against Ferrum Edge's own Workload API, a development and test feature of the gateway, and against its jwks_auth plugin, on the supported 0.9.8, 0.9.7 and 0.9.5 releases. SPIRE itself is not in the lab.

AuthenticationDetails
API keyHeader or query parameter; query values are redacted in records.
Basic, BearerApplied to the final request on every send.
JWT signingHS, RS and ES algorithms; time claims are regenerated per send.
OAuth 2.0Client credentials, refresh, and authorization code with PKCE in the system browser. Tokens are held in memory only and cleared on lock.
Ferrum HMAC v2Signed over the final bytes with a fresh nonce per send. Legacy v1 is an explicit opt-in.
DPoPA new proof for every send.
WS-SecurityUsernameToken and user-supplied SAML.
JWT-SVIDA SPIFFE JWT-SVID as a bearer token, fetched from a SPIFFE Workload API or read from a file or a variable. Checked locally before sending: format, algorithm, SPIFFE subject, audience, expiry and, optionally, the signature against the trust domain's bundle. A failed check stops the request unless you choose to send it anyway. The token itself is never recorded.
Multi-authSeveral mechanisms on one request.
TLSVerification on by default; private CA roots; mTLS with PEM or PKCS#12 client certificates bound to hosts, or with an X.509-SVID fetched from a SPIFFE Workload API over a Unix socket (the profile's endpoint or SPIFFE_ENDPOINT_SOCKET); a verification bypass is scoped to a profile and warned about. A profile can verify the server by SPIFFE ID or trust domain (X.509-SVID) instead of its host name, and can override the SNI.

Read the Auth and TLS Guide →

Export, Import and Redaction

Export modeContentsSecrets
Share safely (default)One workspaceNone. Literal secrets become {{placeholders}} listed in the bundle manifest.
Encrypted transferOne workspaceVault secrets, encrypted with a passphrase you share separately.
Full backupEverything, including history and settingsEncrypted. Restores on a clean machine without the original keychain.

Imports never run anything

Imports are previewed before they apply, checked for size, path traversal, symlinks, archive bombs and checksums, and applied in one transaction with a rollback checkpoint. They never send requests, run scripts or start load plans, and they switch risky settings back to safe values: TLS verification bypasses, plain-HTTP marker trust, cross-origin credential forwarding, the legacy HMAC opt-in, the 0-RTT early-data opt-in and sending a JWT-SVID that failed its local checks.

Redaction everywhere a secret could leak

Secrets are redacted by name and by exact value across execution records, history, reports, exports and support bundles, and credentials are stripped on cross-origin redirects. The effective-request preview shows Authorization redacted, and response content is rendered as inert text.

Read the Portable Data Guide →

What Anvil Does Not Do Yet

  • Unsigned preview. Downloadable v0.1.1 installers and CLI archives exist. macOS and Windows installers lack platform code signing; checksums and signed updater artifacts do not replace it.
  • Published platforms. macOS x86_64 and ARM64; Windows x86_64; Linux x86_64. No Windows ARM64 or Linux ARM64 installer is published. Release evidence records native checks; minimum OS versions are not a published guarantee.
  • Diagnoses are not verdicts. Findings report confirmed, likely, unknown or conflicting evidence with alternatives. They never guarantee a root cause.
  • Coarse gateway tokens. The eight X-Gateway-Error values in the 0.9.8 catalog are explained as categories and are not turned into precise causes. Marker-based claims stay at likely.
  • Three gateway catalogs. Enhanced Ferrum diagnostics ship source-audited catalogs for Ferrum Edge 0.9.8, 0.9.7 and 0.9.5 only. The latest Edge v0.9.10 is outside this catalog set. A gateway profile that names any other release falls back to the release-independent token meanings (ferrum.catalog.unavailable), with no outcome matching; those releases have not been validated.
  • Load tests have limits. Each protocol has its own load unit, but some combinations are refused before any traffic: client-streaming and bidirectional gRPC, gRPC with server reflection, SSE with automatic reconnect, UDP and DTLS through CONNECT-UDP or HBONE tunnels, requests with 0-RTT early data, HTTP or gRPC through HBONE in persistent mode, and plans that mix protocols. No load action holds sessions open while sending messages at a rate: a WebSocket unit is connect, scripted messages, close. Each run uses one worker process on one machine.
  • Workload API coverage. SPIFFE Workload API identities are tested against Ferrum Edge's Workload API (a development and test feature of the gateway) and an independent test server, not against SPIRE. Windows named-pipe endpoints are implemented but checked only by CI.
  • 0-RTT early data scope. HTTP requests only, over direct connections, and over TCP only with the HTTP/1.1-only or HTTP/2-only policy. Session tickets are kept in memory only, so a single anvil send process never has one.
  • Social sign-in is unavailable. Google, GitHub and Facebook sign-in stay unavailable until the providers are registered, so a linked identity and the fresh-login policy cannot be used yet. They are never needed to unlock Anvil.
  • Updates are opt-in. Use Settings → Updates to check now or enable checks at launch. v0.1.1 updater artifacts use verified minisign signatures. Linux DEB/RPM installs require a manual package update.
  • OAuth tokens do not persist. They live in memory only, so a browser sign-in (authorization code with PKCE) has to be repeated after a restart or a lock.

Tech Stack

All network, TLS, auth, storage and diagnostics code is Rust. The renderer talks to it only through typed IPC and never renders a response as HTML.

RustEdition 2024, Rust 1.90+
Tauri 2Desktop shell with a Rust backend
React 19TypeScript renderer, strict CSP
Tokio + hyperAsync runtime, HTTP/1.1 and HTTP/2
quinn + h3QUIC and HTTP/3
rustlsTLS and mTLS
SQLiteLocal store, sealed with XChaCha20-Poly1305
HdrHistogramLoad-test latency percentiles

Quick Start

Use the v0.1.1 unsigned preview downloads, or build this exact release from source.

Rust (stable) Node.js 22+ and npm Linux: Tauri's WebKitGTK dependencies
  1. Clone the repository

    bash
    git clone https://github.com/ferrum-edge/ferrum-anvil
    cd ferrum-anvil
    git checkout --detach anvil-v0.1.1
  2. Build and run the desktop app

    bash
    cd apps/desktop
    npm ci
    npm run build    # the desktop crate embeds dist/ at compile time
    npx tauri dev    # run the desktop app

    On first run, choose to start without a password (OS keychain) or protect the profile with a passphrase.

  3. Or use the CLI

    bash
    cargo install --path crates/anvil-cli
    
    anvil profile create me          # passphrase via --passphrase-stdin or ANVIL_PASSPHRASE
    anvil workspace create Demo
    anvil add Demo "Health" --url https://example.com/health --folder Smoke
    anvil send Health --workspace Demo
    anvil run Demo --folder Smoke --junit report.xml

    The Getting Started guide covers the tests and the optional real-gateway failure lab.

Works With the Rest of the Stack

Licensing

Ferrum Anvil is dual-licensed: PolyForm Noncommercial 1.0.0 for personal, research, educational and nonprofit use, and a separate commercial license for commercial use, such as in a for-profit company's infrastructure, a paid product or service, a hosted service, or bundled with commercial software.