Fingerpoint Detector
Command line

Detection

Check an API channel with random challenges over several rounds, save the report, and recompute it offline.

Examples

# Pass the connection settings explicitly
npx lmfpd@latest -b https://api.example.com/v1 -k sk-xxx -m gpt-6-astra

# Read the connection settings from the environment; use Chat Completions and set the reasoning effort
npx lmfpd@latest -a cc -e high

# Run 5 detection rounds
npx lmfpd@latest -n 5

# Send only one request per round
npx lmfpd@latest -b https://api.example.com/v1 -k sk-xxx -m gpt-6-astra --count 1

# Strict mode without streaming; save the full report (credentials excluded)
npx lmfpd@latest -s -ns --timeout 180 --output result.json

# Analyze a saved report again offline without calling a model
npx lmfpd@latest --input result.json --json

# Also run the tokenizer probe (about 12 more requests)
npx lmfpd@latest -a cc --tokenizer

Options

OptionDescriptionDefault
-b, --baseurl URLBase URL or complete endpoint. The CLI adds the endpoint path if it is missing$BASE_URL
-k, --apikey KEYAPI key$API_KEY
-m, --model MODELModel to request$MODEL
-a, --api TYPEresponses, chatcompletion (or cc), or message. Any prefix worksresponses
-e, --effort LEVELReasoning effort, such as low or high, or a provider-specific valueNot sent
-t, --service-tier TIERService tier: flex, default, fast, or ultrafast. Any prefix works. Aliases: slow (flex), f (fast), uf (ultrafast), priority (fast), standard (default)default, not sent
-ns, --no-streamUse a plain JSON response instead of SSESSE
--timeout SECONDSDeadline for the first SSE byte. In JSON mode, deadline for the complete response. Fractional seconds are supported120
--count NSamples per round, 1–3. Fewer than 3 samples give a ranking only3
-p, --parallel NConcurrent samples per round, 1–3, capped at --count. Does not change the total number of requests3
-n, --repeat NSequential detection rounds1
-s, --strictDisable automatic truncation and require 3 complete, valid answersOff
--challenges FILEReuse saved challenges in every round. The count must match --countRandom
--bank FILEUse a custom reference bank. The detector is read from shared_detector.bin in the same directoryBundled bank
--tokenizerRun the tokenizer probe. Sends about 12 more requests. The ranking is the same with or without itOff
--max-probes NProbe text limit for the tokenizer probe, 4–60. Use only with --tokenizer20
--tokenizer-bank FILEUse a custom tokenizer bank. Use only with --tokenizer or --inputBundled tokenizer bank
--input FILEAnalyze saved outputs offline—
--output FILESave all rounds, request settings, challenges, received text, and results as JSON—
--jsonWrite JSON to stdout instead of the terminal interface—
--no-update-checkDisable background update checksOn

An origin without a path, such as https://api.example.com, uses /v1. A base URL with a path preserves that path and appends the selected endpoint. Messages requests use x-api-key and anthropic-version headers. Messages sends the service tier as speed with the anthropic-beta: fast-mode-2026-02-01 header; Responses and Chat Completions send it as service_tier. Detection requests omit the optional output token limit for Responses and Chat Completions. Messages requests keep max_tokens: 8192, which that API requires.

Sampling behavior

Each round generates --count random challenges with the shared challenge generator (default: 3). The prompts stay unchanged to keep the sampling method consistent. -p 1 only serializes the requests; -n 1 runs only one round. For a fixed single challenge, use --count 1 --challenges one-challenge.json. The file must contain a one-element array whose element has id, prompt, and expected_count. A mismatched array length is rejected before any requests are sent.

A streaming sample stops after it receives the requested number of complete integers. The interface marks it Capped. JSON responses with excess numbers are trimmed after receipt. The raw text received before cancellation remains in the sample record. An incomplete final integer does not trigger truncation. One or two valid samples still produce a ranking, but without confidence scores. Confidence calibration requires three valid samples.

A naturally completed sample must contain at least max(80, ceil(expected_count × 0.55)) valid numbers, matching the scorer. A refusal, provider error, or unfinished response is rejected unless relaxed mode already stopped it at the requested count. Failed samples remain visible and are preserved in the results.

All samples in a round, including failed requests, must settle before the next round starts. The concurrency limit applies per round and is capped at the sample count. Detection does not retry automatically. A failed round does not prevent later rounds from running.

The timeout starts when each request is dispatched. For SSE, the first nonempty response-body chunk clears the timer, including a heartbeat or metadata event. Response headers alone do not clear it. No client deadline or Bun idle timeout remains after SSE starts. In JSON mode, the entire response must arrive before the deadline.

Press q or Ctrl+C to cancel active requests. Queued requests and later rounds do not start. With --output, samples received before cancellation are saved. If a tokenizer probe is still running after the last round, the same keys stop only the probe, and all results stay. Redirected output contains a static report, and progress goes to stderr: each sample, each round's leading candidate as soon as the round is scored, and the probe result with --tokenizer. --json keeps stdout limited to machine-readable results.

Results and offline recomputation

bun run fpd --output result.json
bun run fpd --input result.json
bun run fpd --input result.json --strict --json

--input accepts a saved report, a report with outputs, an array of one to three { "text": "...", "expected_count": 300 } objects, or the result.json of a sampling batch. Sampling batches are grouped by condition into rounds of up to three answers. Reports with several rounds are analyzed one round at a time. Strict recomputation rejects samples recorded as truncated or with unknown completion. A plain output array cannot verify its original network completion status. --count applies only to online detection and cannot be combined with --input.

When all three samples are valid and the calibration matches the detector, the result is labeled Calibrated ranking and each candidate has a confidence value. When the calibration does not match, the result is labeled Uncalibrated ranking: it gives the ranking only, and confidence is unavailable. The terminal interface shows the latest ranking and summaries of recent rounds. After at least one scored round, a dimmed final line links the GitHub repository. JSON retains every round. The "most frequent candidate" counts round winners; it is not a combined probability. Confidence is relative to models in the reference bank and does not establish the upstream model's identity. When a custom reference bank does not match its detector, detection stops with an error that asks you to run fpd retrain --data-dir DIR.

Each round is scored from the number fingerprint alone, when its samples finish. With --tokenizer, the tokenizer probe result shows in a separate block as reference only. It does not change the candidates, their order, or the confidence. With --input, a tokenizer probe saved in the report shows in the same block. Reading it sends no requests.

If the smallest valid number of a sample is 200 or more, the interface warns that the sample has an abnormal distribution and the result is unreliable. The prompt causes it: with random challenges, a new run draws new prompts; with --challenges, replace the matching prompts in the file.

Exit codes

Exit codeMeaning
0All rounds produced a ranking (a partial ranking in relaxed mode also counts as success)
1Invalid input, or any round could not be scored
130Canceled, including a tokenizer probe stopped after the last round
143Received SIGTERM

Last updated on

On this page