FERRULE LAB TERMINAL  v2.6 · tty1
MEM 640K OK · CLOCK 21:04 · LINK sin1 ── ok
CH1 EDGE ── armed    CH2 AUTH ── armed
CH3 QUEUE ── armed   CH4 FN ── armed   CH5 DB ── armed
TRIG ▲ CH1 · 5 ms/div · persistence ON
READY.

Functions you can follow

Ferrule runs your webhooks and edge functions next to your users and records every request as a trace, layer by layer. A slow checkout is one keystroke from its cause.

npm i @ferrule/sdk
The Ferrule lab bench at night: a putty-coloured amber terminal reading FUNCTIONS YOU CAN FOLLOW, a two-channel oscilloscope with five traces, and a stack of green-bar listing paper. A green-bar fanfold listing rising out of the line printer, the session printed on it.
PLATE 1tty1 and the bench scope, 21:04the same session, sent to the printer
TIME/DIV
5 ms
TRIG
▲ CH1 EDGE
CURSOR
X ---- Y ----
SWEEP
scroll ▾ to send a request

power --on tty1

Cold boot, every time

The amber terminal after power-on: the boot log reads READY, then FUNCTIONS YOU CAN FOLLOW and the install line npm i @ferrule/sdk.
PLATE 1Bthe raster opens from a line, then the session prints

ferrule trace --follow

One request.
Five channels.

tty1 · ~/shopexample trace

curl -X POST https://shop.example.com/api/checkout \

curl -X POST \

https://shop.example.com/api/checkout \

-H "Authorization: Bearer $SHOP_KEY" \

-d '{"cart":"c_8Q2","email":"[email protected]"}'

CH1 edgesin1 · tls1.3 · /api/checkout4ms

CH2 authfr_live_…3a9 · orders:write2ms

CH3 queueorders · key ck_7Q · depth 03ms

CH4 fncheckout.ts · warm · 3 logs31ms

CH5 dbcarts.get + orders.insert9ms

201 CREATED · tr_9f2c41 · 41ms

{"id":"ord_7Kq2Lm","status":"created",

"total":48.00,"trace":"tr_9f2c41"}

  1. CH1EdgeTerminates TLS in the nearest region and routes by path.
  2. CH2AuthChecks the key and its scopes before any code runs.
  3. CH3QueueHolds the job with an idempotency key, so a retry never charges twice.
  4. CH4FunctionRuns your TypeScript. Every log line lands in the trace.
  5. CH5DatabaseReads and writes are spans too, with the query attached.
capture tr_9f2c41Δt 41 ms
PLATE 2the capture builds as the request passes each channel

ferrule scope --live

Measure it
yourself.

Fire a request at the bench scope. Each channel is one layer, each pulse is time spent there. Turn TIME/DIV until the slow one fits, then drag the two cursors across it. Δt is the answer.

request
POST /api/checkout · warm
total
41.0 ms
Δt
31.0 ms
between cursors
CH4 function
Close-up of the bench oscilloscope's screen: a green graticule with five amber and green traces.
PLATE 3the same capture on the bench scope
FERRULE SC-55-channel trace scope
A
B
TIME/DIV5 ms

Example captures. Drag the cursors or focus them and use the arrow keys; the knob turns with a drag, a click or the arrow keys.

ferrule sdk --lang all

Same function.
Your language.

Every SDK hands your handler the same context: a logger that writes into the trace, a queue with retries, and a database client whose queries become spans. Or skip the SDK and call the HTTP API.

man 3 ferrule-sdk

functions/checkout.ts

import { ferrule } from "@ferrule/sdk";

export default ferrule.fn("checkout", async (req, ctx) => {
  const { cart, email } = await req.json();
  const items = await ctx.db.carts.get(cart);   // span: CH5
  ctx.log("cart loaded", { items: items.length });

  const order = await ctx.queue.send("orders", { cart, email }, {
    idempotencyKey: req.headers.get("idempotency-key"),
    retries: 3,
  });
  return Response.json({ id: order.id, status: "created" }, { status: 201 });
});

ferrule inspect --when it-breaks

When it breaks,
you are already watching.

  1. (1)

    Traces, without setup

    Every request becomes a trace on its own. Layers, log lines and queries share one timeline, and the slow span is already marked.

  2. (2)

    Replay it against your fix

    Pick a failed request, change the code, send the same request again to a preview. Same body, same headers, a new result.

  3. (3)

    Retries that can't double-charge

    Queued jobs retry with backoff and carry their idempotency key. A job that keeps failing lands in a dead-letter queue you can read.

  4. (4)

    A preview for every push

    Each branch gets its own URL, secrets and traces. Merge, and production runs the build you already tested.

trace POST /api/checkout41 ms
request41
edge sin14
auth2
queue.send3
checkout.ts31
db carts.get6
db orders.ins3
+14ms  cart loaded   items=3
+26ms  price check   currency=USD
+38ms  order queued  id=ord_7Kq2Lm
replay tr_51ad07500 → 201
functions/checkout.ts
$ ferrule replay tr_51ad07 --to fix-empty-cart
  before  500  12 ms   TypeError: items is undefined
  after   201  38 ms   ok
queue orders · job ck_7Qcharged once
10:41:02  attempt 1   FAIL  502 payments provider
10:41:04  backoff     ....  2 s, same key
10:41:04  attempt 2   FAIL  timeout after 10 s
10:41:14  backoff     ....  8 s, same key
10:41:22  attempt 3   OK    order created
──────────────────────────────────────────
dead-letter queue: empty
ferrule deploys lsshop
branchcommitstateurl
fix/empty-carta41c9e2previewfix-empty-cart.shop.example.com
main7d0b113prodshop.example.com
feat/gift-notese92f5a0building…
chore/deps13ab0c4previewchore-deps.shop.example.com

ferrule plans --requests 5.2M

Pay per request.
Never per seat.

estimateexample prices
5.2 million

log scale · function time up to 50 ms per request included

plan      PRO
base      $20.00
overage   0.0M × $0.40 = $0.00

$20/mo

Pro

10M included

Plans, example prices
planpricerequeststraces keptextras
HOBBY$01M7 daysone project
PRO$20/mo10M, then $0.40/M30 daysreplays, unlimited previews
SCALEtalk to usvolumeyou choosededicated regions, SSO, support channel

ferrule status --days 90

Uptime you can check.

EXAMPLE DATA · all systems normal

Example median latency by region
regionp50state
sin1 singapore18 msnormal
fra1 frankfurt21 msnormal
iad1 virginia19 msnormal
gru1 são paulo27 msnormal

ferrule log --oneline -5

Changelog

  1. a41c9e2cli

    Replays from the CLI

    ferrule replay tr_… sends a past request to any preview and prints both responses side by side.

  2. 7d0b113traces

    Database spans carry the query plan

    Slow queries keep their plan in the trace, folded until you open it.

  3. e92f5a0sdk

    Go SDK, first stable release

    Same context as the TypeScript and Python SDKs: logger, queue and database client.

  4. 13ab0c4queues

    Dead-letter queues you can read

    Failed jobs keep body, headers and every attempt. Requeue one or all.

  5. 5c2e8f1deploys

    Per-branch secrets

    Previews read their own secrets, so a test key never reaches production.

man 7 ferrule-changelog

ferrule help faq

Questions

Do I have to change my code to get traces?

No. Requests through Ferrule are traced at the edge, auth and queue layers on their own. The SDK adds your log lines and database queries to the same trace.

Where does my function run?

In the region closest to the caller by default. Pin a function to one region when it needs to sit next to its database.

What counts as a request?

One HTTP call or one delivered webhook. Retries of a queued job count once, however many attempts it takes.

Can I bring my own database?

Yes. Point the database client at Postgres or MySQL with a connection string; queries still show up as spans.

How long are traces kept?

Seven days on Hobby, thirty on Pro, as long as you need on Scale. Export any trace as JSON.

Is Ferrule a real product?

No. Ferrule is a fictional example for an Inazda UI template. Swap in your own product, prices and links.

npx ferrule init

Deploy in five minutes.Read your first trace in six.

npx ferrule init
entercreate a free account

Connection to ferrule closed.