FiberAudit
FiberAudit audits Ruby and Rails code for operations that can block the thread running a Fiber scheduler. Version 0.2.1 includes static analysis and an explicit, observational runtime audit.
Safety disclaimer: FiberAudit does not prove that an application is fiber-safe. Static findings are hypotheses, and absence of runtime events does not establish complete coverage. FiberAudit does not emit unconditional
PASS.
Requirements and installation
FiberAudit v0.2.1 supports Ruby 3.3 and 3.4.
gem install fiber_audit
Or add it to a bundle and run bundle install:
gem "fiber_audit", require: false
Quick start
Run from a project directory or any directory beneath it:
fiber-audit static
FiberAudit walks upward to find the nearest Gemfile, gems.rb, or
config/application.rb. It loads .fiber-audit.yml from that root when the
file exists.
fiber-audit static [--format text|json] [--config PATH] [--out PATH]
[--min-severity LEVEL] [--no-color]
fiber-audit runtime [--config PATH] [--out DIRECTORY]
[--sampling-rate RATE] [--no-fail-open] -- COMMAND [ARGUMENTS...]
fiber-audit list-rules
fiber-audit explain FA1001
fiber-audit version
Output defaults to text on a TTY and JSON when piped. --out PATH defaults to
JSON, writes only the report to that file, and prints a one-line confirmation.
Explicit --format always wins.
Shipped rules
| ID | Detects | Default severity |
|---|---|---|
| FA1001 | Blocking subprocess operations | high |
| FA1002 | Thread#join and Thread#value |
high |
| FA1003 | Thread-oriented synchronization | medium |
| FA1004 | Thread-local state access | high/medium |
| FA1005 | Explicit IO.select/Kernel.select |
medium |
| FA1006 | Direct socket construction | medium |
| FA1007 | Synchronous HTTP in request-like contexts | high |
Use fiber-audit explain <RULE_ID> for exact targets and remediation.
Runtime audit
The explicit runtime command observes only a command supplied after --; it
never executes source fragments discovered by static analysis:
fiber-audit runtime -- bundle exec rspec
Each observed Ruby process writes a separate owner-only JSONL session under
tmp/fiber-audit-runtime by default. Targeted probes observe the operations
represented by FA1001–FA1007: subprocess calls, thread waits, synchronization,
thread-local access, explicit select, direct sockets, and synchronous HTTP.
Events contain canonical operation names, monotonic duration, and a conservative
project-relative callsite. They never contain commands, URLs, addresses, ports,
headers, payloads, responses, exception data, or thread-local keys and values.
Libraries such as Open3, Monitor, Socket, Net::HTTP, and OpenURI may be loaded
after runtime boot; FiberAudit rescans only these known targets after require.
Rails execution contexts (:request, :middleware, :job, :websocket) are
captured automatically when Rails integration is active. A bounded, PID-aware
fiber-local context stack tracks the current execution context during probe
observations. Rails boundaries are wrapped via prepend hooks that become inert
after deactivation or fork, preserving application semantics without interfering
with normal Rails operation. The integration supports late loading: hooks are
installed when Rails components become available, even after runtime boot.
Static/runtime correlation remains future work.
The scheduler watchdog records one bounded start/completion pair when its scheduler-owned heartbeat stops progressing past the configured threshold. Scheduler-friendly waits should continue heartbeats. A session also records an explicit watchdog state:
watchdog_active— a heartbeat ran under an installed scheduler;watchdog_absent— no scheduler was observed;watchdog_unsupported— a scheduler could not safely host the heartbeat;watchdog_disabled— watchdog policy disabled observation.
Absent or unsupported monitoring, a clean session, and absence of stall events are not proof of fiber safety. Native work that holds Ruby's GVL can also prevent the watchdog thread from running until that work returns.
Configuration
Copy .fiber-audit.example.yml to .fiber-audit.yml in the project root.
Paths and globs are rooted at the detected project. An explicit --config
path is resolved from the directory where the command was invoked.
static:
include:
- app/**/*.rb
- lib/**/*.rb
- config/**/*.rb
exclude:
- vendor/**/*
- tmp/**/*
- db/schema.rb
suppressions_path: .fiber-audit-suppressions.yml
rules:
FA1007:
enabled: false
FA1003:
severity: low
report:
formats: [text, json]
min_severity: low
runtime:
redaction:
mode: strict
sampling:
rate: 0.1
watchdog:
enabled: true
heartbeat_interval_ms: 25
stall_threshold_ms: 100
max_frames: 20
fail_open: true
--min-severity overrides report.min_severity for one run. Severity ordering
is critical, high, medium, low, info; findings below the threshold
are omitted and do not affect the exit code. The default low threshold keeps
informational findings silent.
Suppressions
Every suppression requires a non-empty reason. Directive-looking text inside strings, heredocs, or regular expressions is ignored.
Suppress one line:
system(command) # fiber-audit:disable FA1001 -- trusted maintenance command
Suppress a block:
# fiber-audit:disable FA1003 -- protected legacy boundary
mutex.synchronize { update_record }
# fiber-audit:enable FA1003
A separate YAML file can suppress by rule and optionally by symbol or
operation. Point static.suppressions_path at the file:
suppressions:
- rule: FA1001
symbol: Reports::Generator#call
operation: Open3.capture3
reason: isolated worker process with an external timeout
Missing reasons and invalid configuration return exit code 2.
Static statuses
FAIL— at least one critical or high finding.REVIEW— a medium finding, or a non-informational low/unknown-confidence finding.PASS_WITH_WARNINGS— only low or informational findings.NO_FINDINGS— no findings at the configured threshold.
FiberAudit never emits unconditional PASS in v0.2.1.
Exit codes
| Code | Meaning |
|---|---|
| 0 | No active finding at or above the configured threshold |
| 1 | One or more active findings at or above the threshold |
| 2 | Invalid options, configuration, analysis, or report output |
| 3 | Reserved; never emitted by v0.2.1 |
Source parse errors are included in report data while analysis continues on other files.
See ARCHITECTURE.md for implementation boundaries, runtime architecture, and explicitly deferred correlation work.