{"id":51240225,"url":"https://github.com/avifenesh/ferrings","last_synced_at":"2026-06-29T00:00:40.866Z","repository":{"id":367983299,"uuid":"1283107780","full_name":"avifenesh/ferrings","owner":"avifenesh","description":"Linux io_uring TCP transport for Node.js","archived":false,"fork":false,"pushed_at":"2026-06-28T16:03:10.000Z","size":177,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-28T16:05:06.523Z","etag":null,"topics":["batch","high-performance","http","iouring","linux","napi","napi-rs","networking","nodejs","npm","queue","rust","tcp","transport","zcrx","zero-copy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/avifenesh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-28T14:55:49.000Z","updated_at":"2026-06-28T16:03:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/avifenesh/ferrings","commit_stats":null,"previous_names":["avifenesh/ferrings"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/avifenesh/ferrings","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avifenesh%2Fferrings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avifenesh%2Fferrings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avifenesh%2Fferrings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avifenesh%2Fferrings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avifenesh","download_url":"https://codeload.github.com/avifenesh/ferrings/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avifenesh%2Fferrings/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34906700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batch","high-performance","http","iouring","linux","napi","napi-rs","networking","nodejs","npm","queue","rust","tcp","transport","zcrx","zero-copy"],"created_at":"2026-06-29T00:00:29.370Z","updated_at":"2026-06-29T00:00:40.855Z","avatar_url":"https://github.com/avifenesh.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ferrings\n\n[![CI](https://github.com/avifenesh/ferrings/actions/workflows/ci.yml/badge.svg)](https://github.com/avifenesh/ferrings/actions/workflows/ci.yml)\n[![Release](https://github.com/avifenesh/ferrings/actions/workflows/release.yml/badge.svg)](https://github.com/avifenesh/ferrings/actions/workflows/release.yml)\n[![npm](https://img.shields.io/npm/v/ferrings)](https://www.npmjs.com/package/ferrings)\n![Node.js 22/24/26](https://img.shields.io/badge/node-22%20%7C%2024%20%7C%2026-339933)\n![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)\n\nLinux `io_uring` TCP transport for Node.js services, built in Rust with napi-rs and shipped as native npm packages.\n\nferrings is for Linux Node servers where connection concurrency, syscall pressure, and tail latency matter. It owns the listening socket, drives accept/recv/send from a Rust `io_uring` worker, and exposes both a familiar Node-style TCP facade and lower-level batched APIs.\n\n## Benchmarks\n\nMeasured on 2026-06-29 with `ferrings@0.2.13`, Intel Core Ultra 9 275HX, Linux `7.0.0-27-generic`, Node `v26.4.0`, npm `11.12.1`, Rust `1.96.0`, loopback traffic, `strace -f -c`, and an 8 MiB locked-memory limit. Absolute numbers are machine-specific; the useful signal is the same-host comparison against Node's built-in transports.\n\n| Workload | Baseline | ferrings path | Throughput | p99 latency | Server syscalls/conn |\n| --- | --- | --- | ---: | ---: | ---: |\n| Fixed-response HTTP | Node `http` | `UringHttpServer` | **2.05x** | **56% lower** | **54% fewer** |\n| TCP echo | Node `net` | native echo worker | **2.50x** | **37% lower** | **53% fewer** |\n| TCP echo | Node `net` | Node-style TCP facade | **2.23x** | **49% lower** | **37% fewer** |\n| TCP echo | Node `net` | facade batch send | **2.24x** | **40% lower** | **38% fewer** |\n\nDetailed snapshot:\n\n| Case | req/s | p50 ms | p95 ms | p99 ms | server syscalls/conn | Transport path |\n| --- | ---: | ---: | ---: | ---: | ---: | --- |\n| Node `http` | 4,101 | 11.760 | 45.954 | 53.843 | 11.725 | libuv/epoll |\n| ferrings HTTP | 8,396 | 5.893 | 22.912 | 23.825 | 5.407 | `io_uring` accept/recv + provided buffers |\n| Node `net` TCP echo | 6,371 | 8.722 | 15.944 | 22.714 | 11.019 | libuv/epoll |\n| ferrings native TCP echo | 15,941 | 3.266 | 12.762 | 14.400 | 5.200 | native echo worker + provided buffers |\n| ferrings TCP facade | 14,227 | 3.500 | 9.494 | 11.667 | 6.945 | Node-style JS facade + batched native events |\n| ferrings TCP facade batch send | 14,249 | 3.643 | 12.032 | 13.575 | 6.806 | JS facade + batched native events/sends |\n\nReproduce the table:\n\n```bash\nREQUESTS=1000 CONCURRENCY=64 QUEUE_DEPTH=64 BUFFER_COUNT=512 BUFFER_SIZE=2048 \\\nCASES=node-http,ferrings-http,node-tcp,ferrings-native-tcp,ferrings-tcp-facade,ferrings-tcp-facade-batch \\\nREPORT_PATH=artifacts/benchmark-readme-node26-2026-06-29-0.2.13.json \\\nnpm run bench:syscalls\n```\n\nRun the benchmarks on the host class you plan to deploy. TCP tail latency depends on the selected API surface, payload shape, kernel, NIC path, and queue settings.\n\n## Installation\n\n```bash\nnpm install ferrings\n```\n\nRequirements:\n\n- Linux\n- Node.js `\u003e=22`\n- `x64` or `arm64`\n- glibc or musl\n\nCI tests Node 22, 24, and 26 on Linux. Per the [Node.js release schedule](https://nodejs.org/en/about/previous-releases), Node 26 is current, Node 24 and 22 are LTS, and Node 20 is EOL.\n\nThe root package installs the matching optional native package for the current Linux target:\n\n- `ferrings-linux-x64-gnu`\n- `ferrings-linux-x64-musl`\n- `ferrings-linux-arm64-gnu`\n- `ferrings-linux-arm64-musl`\n\nIf the native binding cannot be loaded, ferrings throws `FerringsNativeLoadError` with code `FERRINGS_NATIVE_LOAD_FAILED`, the detected platform target, the supported native package names, and the original loader error.\n\n## Quick Start\n\nCreate `quickstart.js`:\n\n```js\n'use strict';\n\nconst net = require('node:net');\nconst { createTcpServer } = require('ferrings');\n\nconst server = createTcpServer(\n  {\n    host: '127.0.0.1',\n    port: 0,\n    backlog: 1024,\n    useRecvBundle: true,\n    useZeroCopySend: true\n  },\n  (connection) =\u003e {\n    connection.on('data', (data) =\u003e {\n      connection.end(Buffer.concat([Buffer.from('echo:'), data]));\n    });\n  }\n);\n\nserver.listen((info) =\u003e {\n  const client = net.createConnection({ host: info.host, port: info.port }, () =\u003e {\n    client.write('hello');\n  });\n\n  let body = Buffer.alloc(0);\n  client.on('data', (chunk) =\u003e {\n    body = Buffer.concat([body, chunk]);\n  });\n  client.on('end', () =\u003e {\n    console.log(body.toString('utf8'));\n    server.close();\n  });\n});\n```\n\nRun it:\n\n```bash\nnode quickstart.js\n```\n\nIt prints `echo:hello`. Application code handles normal JavaScript callbacks; ferrings handles the socket lifecycle, multishot receive path, sends, and shutdown on the native worker.\n\n## When To Use Ferrings\n\n- Use ferrings when a Linux-only native dependency is acceptable and TCP syscall count, tail latency, or high connection concurrency matter.\n- Use ferrings when you want a Node TCP server API backed by Linux `io_uring`, not a wrapper around Node's `net.Server`.\n- Use ferrings when application code should stay in Node.js while the accept/recv/send path runs in Rust.\n- Use ferrings when you want live transport counters for multishot accept/recv, provided buffers, recv-bundle, zero-copy send, fixed send-buffer probes, queue drops, and ZCRX readiness.\n- Use ferrings when you are preparing for ZCRX-capable NICs but need the broadly useful multishot/provided-buffer path today.\n\n## What You Get\n\n- A Node-style TCP server facade with `connection`, `data`, `close`, `write()`, `end()`, `destroy()`, `address()`, and `getConnections()`.\n- Raw and batched TCP event APIs for lower callback overhead.\n- Multishot accept/recv with provided receive buffers on ordinary recent Linux kernels.\n- Optional recv-bundle, zero-copy send, and registered send-buffer paths with capability reporting.\n- A fixed-response HTTP server for health checks and simple edge-style responses.\n- A native TCP echo server for isolating the native transport path.\n- CLI and API probes for runtime capabilities, ZCRX readiness, and installed-package health.\n- Native npm packages for x64/arm64 glibc/musl Linux targets.\n\nZCRX is the hardware-gated fast path. The default product path is the multishot/provided-buffer transport, which does not require special NIC support.\n\n## How It Works\n\nferrings creates the listening socket directly with `socket`, `bind`, and `listen`, then drives accepts, receives, sends, and shutdown from a Rust worker thread with `io_uring`.\n\nThe default receive path uses multishot accept, multishot recv, and provided buffers. JavaScript still owns the application surface:\n\n- Native-to-JS events are delivered through NAPI thread-safe callbacks.\n- JS-to-native writes go through a bounded command queue and an `eventfd` wakeup.\n- The Node-style facade exposes `connection`, `data`, `close`, `write()`, `end()`, `destroy()`, `address()`, and `getConnections()`.\n- Lower-level APIs expose connection IDs, batched events, batched sends, server counters, and active capability probes.\n\nZCRX is separate and explicitly gated because it needs kernel support, NIC header/data split, RX queue setup, flow steering or RSS isolation, and permissions.\n\n## API\n\n### Node-Style TCP\n\n```js\nconst { createTcpServer } = require('ferrings');\n\nconst server = createTcpServer((connection) =\u003e {\n  connection.on('data', (data) =\u003e connection.end(data));\n});\n\nserver.listen(0, '127.0.0.1', (info) =\u003e {\n  console.log(info);\n});\n```\n\nUse this for a familiar Node server shape over the native transport.\n\n### Raw TCP Events\n\n```js\nconst { UringTcpServer } = require('ferrings');\n\nconst server = new UringTcpServer({\n  host: '127.0.0.1',\n  port: 0,\n  useRecvBundle: true,\n  useZeroCopySend: true\n});\n\nconst info = server.start((event) =\u003e {\n  if (event.eventType === 'data') {\n    server.sendAndClose(event.connectionId, Buffer.from('pong'));\n  }\n});\n\nconsole.log(`tcp://${info.host}:${info.port}`);\n```\n\nUse this when you want direct event objects and explicit connection IDs.\n\n### Batched TCP Events And Sends\n\n```js\nconst { UringTcpServer } = require('ferrings');\n\nconst server = new UringTcpServer({ host: '127.0.0.1', port: 0 });\n\nconst info = server.startBatch((events) =\u003e {\n  const sends = [];\n  for (const event of events) {\n    if (event.eventType === 'data') {\n      sends.push({ connectionId: event.connectionId, data: event.data });\n    }\n  }\n  if (sends.length \u003e 0) {\n    server.sendBatchAndClose(sends);\n  }\n});\n\nconsole.log(`tcp://${info.host}:${info.port}`);\n```\n\nUse this when JS callback overhead matters and events can be processed in batches.\n\n### Fixed-Response HTTP\n\n```js\nconst { UringHttpServer } = require('ferrings');\n\nconst server = new UringHttpServer({\n  host: '127.0.0.1',\n  port: 0,\n  responseBody: 'hello from ferrings\\n',\n  useZeroCopySend: true\n});\n\nconst info = server.start();\nconsole.log(`http://${info.host}:${info.port}`);\n```\n\n`UringHttpServer` is a fixed-response server for health-style responses and simple edge responses. It is not an HTTP application framework.\n\n### Native TCP Echo\n\n```js\nconst { UringTcpEchoServer } = require('ferrings');\n\nconst server = new UringTcpEchoServer({\n  host: '127.0.0.1',\n  port: 0,\n  useZeroCopySend: true\n});\n\nconst info = server.start();\nconsole.log(`tcp://${info.host}:${info.port}`);\n```\n\nUse this to isolate the native TCP path from JavaScript event delivery.\n\n## Capabilities And Doctor\n\n```js\nconst { capabilities, zcrxProbe } = require('ferrings');\n\nconsole.log(capabilities());\nconsole.log(zcrxProbe({ interfaceName: 'eth0' }));\nconsole.log(zcrxProbe({\n  interfaceName: 'eth0',\n  rxQueue: 0,\n  activeRegistration: true\n}));\n```\n\nThe installed CLI exposes the same checks:\n\n```bash\nnpx ferrings capabilities --json\nnpx ferrings doctor --interface eth0 --rx-queue 0 --active --json\nnpx ferrings zcrx-probe --interface eth0 --rx-queue 0 --active --json\n```\n\n## Configuration\n\nCommon server options:\n\n| Option | Default | Applies to | Purpose |\n| --- | ---: | --- | --- |\n| `host` | `127.0.0.1` | all servers | Bind address. |\n| `port` | `0` | all servers | Bind port; `0` asks the kernel for a free port. |\n| `backlog` | `1024` | all servers | Passed to `listen(2)`, subject to host `somaxconn`. |\n| `queueDepth` | `64` | all servers | `io_uring` queue depth. |\n| `bufferCount` | `512` | all servers | Receive buffer slots. |\n| `bufferSize` | `2048` | all servers | Size of each receive buffer. |\n| `maxConnections` | `0` | all servers | `0` means unlimited tracked active connections. |\n| `idleTimeoutMs` | `0` | all servers | `0` disables native idle eviction. |\n| `tcpNoDelay` | `true` | all servers | Applies `TCP_NODELAY` to accepted sockets. |\n| `reusePort` | `false` | all servers | Applies `SO_REUSEPORT` before bind. |\n| `tcpDeferAcceptSeconds` | `0` | all servers | Applies `TCP_DEFER_ACCEPT` when positive. |\n| `socketRecvBufferSize` | `0` | all servers | `SO_RCVBUF`; `0` keeps kernel defaults. |\n| `socketSendBufferSize` | `0` | all servers | `SO_SNDBUF`; `0` keeps kernel defaults. |\n| `useRecvBundle` | `false` | TCP servers | Requests recv-bundle mode when supported. |\n| `useZeroCopySend` | `false` | all servers | Requests `IORING_OP_SEND_ZC`. |\n| `useRegisteredSendBuffer` | `false` | all servers | Requests fixed-buffer send mode. |\n| `useZeroCopyReceive` | `false` | all servers | Requests ZCRX; requires capable hardware and permissions. |\n\nTCP queue options:\n\n| Option | Default | Purpose |\n| --- | ---: | --- |\n| `commandQueueCapacity` | `65536` | JS-to-native command queue bound. |\n| `eventQueueCapacity` | `65536` | Native-to-JS event queue bound. |\n| `eventBatchSize` | `64` | Events per JS batch in `startBatch()` and the facade. |\n| `sendQueueCapacity` | `1024` | Per-connection native send backlog. |\n| `sendBufferCount` | `256` | Fixed-send pool slot count. |\n| `sendBufferSize` | `2048` | Fixed-send pool slot size. |\n\nAll servers expose live counters through `ServerInfo`, including accepted/closed/rejected connections, bytes sent/received, queue drops, receive buffer starvations, recv-bundle counters, zero-copy send counters, fixed-send misses, and ZCRX packet counters.\n\n## ZCRX\n\nZCRX support is implemented as a gated transport path. Use it only on hosts with the right kernel, permissions, NIC support, header/data split, and flow steering/RSS isolation.\n\n```bash\nnode bin/ferrings.js zcrx-probe --interface eth0 --rx-queue 0 --active --json\nZCRX_INTERFACE=eth0 ZCRX_CONNECT_HOST=\u003cnic-routed-host\u003e npm run test:zcrx\n```\n\n`test:zcrx` starts the HTTP, native TCP echo, and programmable TCP servers with `useZeroCopyReceive: true`, drives traffic through `ZCRX_CONNECT_HOST`, and requires `ServerInfo.zcrxPackets` and `zcrxBytes` to increase.\n\nFor a real NIC receive proof, avoid `127.0.0.1`; route packets through the selected NIC queue, usually from a second host or a network namespace.\n\n## Benchmarking\n\nOther benchmark entrypoints:\n\n```bash\nnpm run bench\nnpm run bench:quick\nnpm run bench:tcp\nnpm run bench:high\nREQUESTS=1000 CONCURRENCY=32 npm run bench:syscalls\n```\n\nBenchmark scripts:\n\n- [`benchmark/compare.js`](benchmark/compare.js) compares Node HTTP with `UringHttpServer`.\n- [`benchmark/quick-proof.js`](benchmark/quick-proof.js) runs a compact HTTP, TCP, and syscall proof bundle.\n- [`benchmark/tcp-echo.js`](benchmark/tcp-echo.js) compares Node TCP, the ferrings TCP facade, raw TCP, native echo, recv-bundle, and zero-copy-send variants when available.\n- [`benchmark/high-concurrency.js`](benchmark/high-concurrency.js) runs HTTP and TCP cases with higher concurrency defaults.\n- [`benchmark/syscalls.js`](benchmark/syscalls.js) uses `strace -f -c` when installed to report server-side syscalls per completed connection.\n\nSet `REPORT_PATH=artifacts/\u003cname\u003e.json` to keep machine-readable reports. Useful knobs include `DURATION_MS`, `REQUESTS`, `CONCURRENCY`, `QUEUE_DEPTH`, `BUFFER_COUNT`, `BUFFER_SIZE`, `CASES`, and `SYSCALL_CASES`. If you raise `BUFFER_COUNT`, `QUEUE_DEPTH`, or fixed send-buffer counts, raise `ulimit -l` / `RLIMIT_MEMLOCK` too.\n\n## Packages\n\nPublished packages:\n\n- [`ferrings`](https://www.npmjs.com/package/ferrings)\n- [`ferrings-linux-x64-gnu`](https://www.npmjs.com/package/ferrings-linux-x64-gnu)\n- [`ferrings-linux-x64-musl`](https://www.npmjs.com/package/ferrings-linux-x64-musl)\n- [`ferrings-linux-arm64-gnu`](https://www.npmjs.com/package/ferrings-linux-arm64-gnu)\n- [`ferrings-linux-arm64-musl`](https://www.npmjs.com/package/ferrings-linux-arm64-musl)\n\nSource development:\n\n```bash\ngit clone https://github.com/avifenesh/ferrings.git\ncd ferrings\nnpm install\nnpm test\n```\n\n## Project Health\n\n- npm package: [`ferrings`](https://www.npmjs.com/package/ferrings)\n- Changelog: [`CHANGELOG.md`](CHANGELOG.md)\n- Contributing guide: [`CONTRIBUTING.md`](CONTRIBUTING.md)\n- Code of conduct: [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)\n- Examples: [`examples/http-fixed.js`](examples/http-fixed.js), [`examples/tcp-echo.js`](examples/tcp-echo.js)\n- Benchmarks: [`benchmark/`](benchmark/)\n- Type surface: [`index.d.ts`](index.d.ts), [`native.d.ts`](native.d.ts)\n- CLI entrypoint: [`bin/ferrings.js`](bin/ferrings.js)\n- CI workflow: [`.github/workflows/ci.yml`](.github/workflows/ci.yml)\n- Release workflow: [`.github/workflows/release.yml`](.github/workflows/release.yml)\n- Security workflow: [`.github/workflows/security.yml`](.github/workflows/security.yml)\n- Security policy: [`SECURITY.md`](SECURITY.md)\n- Tests: [`test/`](test/)\n\nThere is no separate docs site yet; the README, type definitions, examples, benchmarks, and tests are the current reference material.\n\n## Release Checks\n\nUseful checks before cutting a release:\n\n```bash\nnpm run check:main-health\nnpm run check:workflows\nnpm run check:lockfile\nnpm run check:native-packages\nnpm run check:npm-names\nnpm run check:release-repository\nnpm run check:release-ready -- --full --strict\nnpm run check:release-ready -- --full --require-zcrx\nnpm run check:registry-install -- --version \"$(node -p \"require('./package.json').version\")\"\n```\n\nTag pushes that match the package version build all native artifacts, run package checks, publish to npm with the repository `NPM_TOKEN` secret, verify the published root package, native packages, integrity metadata, provenance attestations, registry signatures, and dist-tag from the npm registry, and then create or update the GitHub release. Manual `workflow_dispatch` runs can also publish when `publish=true`.\n\nRelease reruns are registry-aware: if the exact version is already published and passes `check:published`, the workflow skips the immutable `npm publish` call and keeps the registry verification step.\n\nAfter a release has propagated, this should pass:\n\n```bash\nnpm run check:published -- --tag latest --verify-tarballs\nnpm run check:main-health\n```\n\n`check:published --verify-tarballs` verifies registry metadata, provenance, signatures, dist-tags, and the downloaded npm tarball contents for the root package and every native package.\n\nFor a new release, bump the package version first; npm versions are immutable after publication, so `check:release-ready` is a release gate rather than a normal post-release main-branch check. Use `check:main-health` when validating current `main` after a release or docs/tooling follow-up.\n\n## Limitations\n\n- Linux only; there is no macOS or Windows transport.\n- Node.js `\u003e=22` is required. Node 20 is EOL and not supported.\n- This is a native addon, so kernel support and process limits affect which fast paths are active.\n- The TCP facade follows the common Node server shape, but it is not a drop-in replacement for every `net.Server` behavior.\n- `UringHttpServer` is a fixed-response server, not an HTTP application framework.\n- TLS is not implemented.\n- ZCRX requires specific NIC hardware, kernel support, queue setup, permissions, and routed traffic through the selected RX queue.\n- Registered-buffer send can be unavailable even when the kernel supports other modern `io_uring` networking features; ferrings reports that through `capabilities().registeredSendBuffer`.\n- ferrings is a `0.x` package: it is usable now, and minor releases may still adjust API names or defaults as the surface settles.\n\n## Contributing\n\nIssues and pull requests are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md).\nAt minimum, run this baseline before opening a change:\n\n```bash\nnpm install\nnpm test\nnpm run audit:deps\nnpm run check:pack\n```\n\nFor changes that touch native packaging, also run:\n\n```bash\nnpm run check:native-packages\nnpm run check:pack\n```\n\nFor type-surface changes, `npm test` runs `npm run test:types`, which compiles a consumer TypeScript smoke test against the published `.d.ts` entrypoints.\n\nFor ZCRX changes, include `npm run test:zcrx` output when you have access to capable hardware. If you do not, include `node bin/ferrings.js zcrx-probe --all --active --json` output so reviewers can see the blocker.\n\n## License\n\nLicensed under either of:\n\n- [MIT](LICENSE-MIT)\n- [Apache-2.0](LICENSE-APACHE)\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favifenesh%2Fferrings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favifenesh%2Fferrings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favifenesh%2Fferrings/lists"}