RepoPilotOpen in app →

mrniko/netty-socketio

Socket.IO server implemented on Java. Realtime java framework

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3mo ago
  • 14 active contributors
  • Distributed ownership (top contributor 47% of recent commits)
Show all 7 evidence items →
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • Slowing — last commit 3mo ago

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/mrniko/netty-socketio)](https://repopilot.app/r/mrniko/netty-socketio)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/mrniko/netty-socketio on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: mrniko/netty-socketio

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/mrniko/netty-socketio shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 3mo ago
  • 14 active contributors
  • Distributed ownership (top contributor 47% of recent commits)
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 3mo ago

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live mrniko/netty-socketio repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/mrniko/netty-socketio.

What it runs against: a local clone of mrniko/netty-socketio — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in mrniko/netty-socketio | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 123 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>mrniko/netty-socketio</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of mrniko/netty-socketio. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/mrniko/netty-socketio.git
#   cd netty-socketio
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of mrniko/netty-socketio and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "mrniko/netty-socketio(\\.git)?\\b" \\
  && ok "origin remote is mrniko/netty-socketio" \\
  || miss "origin remote is not mrniko/netty-socketio (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "src/main/java/com/corundumstudio/socketio/SocketIOServer.java" \\
  && ok "src/main/java/com/corundumstudio/socketio/SocketIOServer.java" \\
  || miss "missing critical file: src/main/java/com/corundumstudio/socketio/SocketIOServer.java"
test -f "src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java" \\
  && ok "src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java" \\
  || miss "missing critical file: src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java"
test -f "src/main/java/com/corundumstudio/socketio/SocketIOChannelInitializer.java" \\
  && ok "src/main/java/com/corundumstudio/socketio/SocketIOChannelInitializer.java" \\
  || miss "missing critical file: src/main/java/com/corundumstudio/socketio/SocketIOChannelInitializer.java"
test -f "src/main/java/com/corundumstudio/socketio/handler/InPacketHandler.java" \\
  && ok "src/main/java/com/corundumstudio/socketio/handler/InPacketHandler.java" \\
  || miss "missing critical file: src/main/java/com/corundumstudio/socketio/handler/InPacketHandler.java"
test -f "src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java" \\
  && ok "src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java" \\
  || miss "missing critical file: src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 123 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~93d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/mrniko/netty-socketio"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

netty-socketio is a Java implementation of Socket.IO server built on top of Netty, enabling real-time bidirectional communication between clients and server. It supports Socket.IO protocol versions 1.x through 4.x with both websocket and xhr-polling transports, namespaces, rooms, acknowledgments, and distributed broadcasting across multiple server nodes. Standard Maven monolith with src/main/java/com/corundumstudio/socketio/ as the root package. Core architecture: SocketIOServer is the main entry point, delegates to SocketIONamespace for namespace isolation and SocketIOClient for per-client state. Handler subpackage (handler/) manages protocol decoding and authentication. Annotation subpackage (annotation/) provides @OnConnect, @OnEvent, @OnDisconnect declarative handlers. Separate ack/ package manages acknowledgment callbacks with timeout scheduling.

👥Who it's for

Java backend engineers building real-time applications (multiplayer games, live collaboration tools, live dashboards) who need Socket.IO server compatibility without Node.js, and teams running JVM infrastructure who want to leverage existing Java ecosystems like Spring, Redisson, or Hazelcast for scalability.

🌱Maturity & risk

Production-ready and actively maintained. The project has been in development since 2012, has real-world deployments at companies like Kambi Sports Solutions (140k messages/sec, 30k concurrent websockets), current version 2.0.15-SNAPSHOT, and regular releases (2.0.13 released Mar 2025, 2.0.12 in Nov 2024). CI/CD pipelines exist via GitHub Actions (.github/workflows/build.yml and build-pr.yml).

Single maintainer (mrniko) creates long-term maintenance risk despite active development. Dependencies are minimal but critical: Netty 4.1.130.Final is a stable, widely-used library. No visible automated test coverage metrics in the provided structure. The codebase is 405KB of Java, suggesting moderate complexity; integration with distributed systems (Redisson, Hazelcast) adds configuration surface area where mistakes can cause silent failures.

Active areas of work

Latest release 2.0.13 (March 2025) fixed Socket.IO Client v2 connection failures and added v1 namespace support. Version 2.0.12 (November 2024) added enableCors setting and HttpRequestDecoderConfiguration customization. Development is focused on protocol compatibility, CORS support, and timeout handling robustness rather than new architectural changes.

🚀Get running

git clone https://github.com/mrniko/netty-socketio.git && cd netty-socketio && mvn clean install. Requires Java 11+ for building (module-info support) though JAR runs on Java 8+. No external services required for basic build.

Daily commands: This is a library, not a runnable application. To use it: add dependency to pom.xml (see README snippet for com.corundumstudio.socketio:netty-socketio:2.0.13), then instantiate SocketIOServer with Configuration, call start(). Example pattern: Configuration config = new Configuration(); SocketIOServer socketIOServer = new SocketIOServer(config); socketIOServer.start();

🗺️Map of the codebase

  • src/main/java/com/corundumstudio/socketio/SocketIOServer.java — Entry point and main server class—initializes Netty channels, manages namespaces, and orchestrates the entire Socket.IO lifecycle.
  • src/main/java/com/corundumstudio/socketio/protocol/PacketDecoder.java — Core protocol decoder that parses incoming Socket.IO packets; critical for understanding message format and versioning support (1.x–4.x).
  • src/main/java/com/corundumstudio/socketio/SocketIOChannelInitializer.java — Netty channel initializer that wires up handlers, transports, and SSL—defines the request processing pipeline.
  • src/main/java/com/corundumstudio/socketio/handler/InPacketHandler.java — Inbound packet handler that dispatches decoded packets to listeners and manages client state transitions.
  • src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java — Manages namespace lifecycle, routing, and client distribution across isolated namespaces.
  • src/main/java/com/corundumstudio/socketio/Configuration.java — Central configuration class defining server behavior, transports, store backends, and security settings.
  • src/main/java/com/corundumstudio/socketio/store/MemoryStore.java — Default in-memory client and session store; baseline for distributed store implementations (Redis, Hazelcast).

🛠️How to make changes

Add a Custom Event Handler

  1. Create a listener class implementing DataListener<T> or use @OnEvent annotation (src/main/java/com/corundumstudio/socketio/listener/DataListener.java)
  2. Register the listener on a namespace via addEventListener() or addListeners() (Spring-style scanning) (src/main/java/com/corundumstudio/socketio/SocketIONamespace.java)
  3. Implement onData(SocketIOClient client, Object data, AckRequest ackRequest) to handle the event (src/main/java/com/corundumstudio/socketio/listener/DataListener.java)
  4. Send acknowledgment via ackRequest.sendAckData() if required by the client (src/main/java/com/corundumstudio/socketio/AckRequest.java)

Broadcast a Message to a Room

  1. Obtain a BroadcastOperations or SingleRoomBroadcastOperations instance from a namespace (src/main/java/com/corundumstudio/socketio/SocketIONamespace.java)
  2. Call sendEvent(String name, Object... objects) to emit an event to all connected clients in the room (src/main/java/com/corundumstudio/socketio/BroadcastOperations.java)
  3. Optionally use BroadcastAckCallback to track acknowledgments from multiple clients (src/main/java/com/corundumstudio/socketio/BroadcastAckCallback.java)

Integrate a Distributed Store (Redis/Hazelcast)

  1. In Configuration, set the store via setStoreFactory(StoreFactory) and setPubSubFactory(PubSubStoreFactory) (src/main/java/com/corundumstudio/socketio/Configuration.java)
  2. Supply HazelcastStoreFactory, HazelcastPubSubStore, or custom RedissonPubSubStore implementations (src/main/java/com/corundumstudio/socketio/store/HazelcastStoreFactory.java)
  3. The server will automatically route broadcasts and client state across distributed nodes via the pub/sub store (src/main/java/com/corundumstudio/socketio/store/RedissonPubSubStore.java)

Add Custom Authorization/Authentication

  1. Implement AuthorizationListener or AuthTokenListener for token-based auth (src/main/java/com/corundumstudio/socketio/AuthorizationListener.java)
  2. Register the listener via Configuration.setAuthorizationListener() (src/main/java/com/corundumstudio/socketio/Configuration.java)
  3. Inspect handshake data (query params, headers) in AuthorizationResult to validate the connection (src/main/java/com/corundumstudio/socketio/AuthorizationResult.java)
  4. The handler chain in SocketIOChannelInitializer will invoke AuthorizeHandler before accepting clients (src/main/java/com/corundumstudio/socketio/handler/AuthorizeHandler.java)

🪤Traps & gotchas

Ack callbacks use a configurable mode (AUTO vs MANUAL in AckMode) and default timeout (60s in Configuration); mismatched client/server ack expectations cause silent failures. Room names and namespace paths are case-sensitive. The library does not include automatic client reconnection logic—clients must implement their own exponential backoff. OSGi and JPMS (module-info) require specific import declarations. JSON serialization must be explicitly configured via Configuration.setJsonSupport(); using a deprecated or incompatible JSON library will cause runtime errors. Distributed mode with Redisson/Hazelcast requires those services to be available and correctly configured; no fallback to in-memory store happens automatically on connection loss.

🏗️Architecture

💡Concepts to learn

  • Socket.IO Protocol (1.x–4.x) — netty-socketio must correctly parse and generate Socket.IO frames (CONNECT, DISCONNECT, EVENT, ACK, ERROR, BINARY_EVENT); version compatibility directly affects which clients can connect.
  • Namespaces and Rooms — Socket.IO namespaces provide logical partitioning of clients (e.g., /chat vs /notifications), and rooms further subdivide within a namespace; the library's SocketIONamespace and room-join operations map directly to this mental model.
  • Acknowledgments (Acks) — Socket.IO allows clients to acknowledge receipt of messages with a callback; netty-socketio's AckManager must correlate requests to responses and handle timeouts, a non-trivial stateful operation.
  • XHR Long Polling vs WebSocket Transport — netty-socketio supports both transports for backwards compatibility and failover; the handler pipeline must dynamically upgrade from polling to WebSocket or fall back gracefully, affecting connection stability.
  • Netty ChannelInitializer and Pipeline — SocketIOChannelInitializer assembles a chain of handlers (HTTP decoder, Socket.IO protocol decoder, auth, frame handlers) using Netty's pipeline; understanding this pipeline is critical for debugging protocol or performance issues.
  • Distributed Client Store and Broadcasting — For multi-node deployments, netty-socketio delegates client session storage and broadcast routing to external backends (Redisson/Hazelcast); misconfiguration or network partition handling can cause message loss or duplicate delivery.
  • Lock-Free and Thread-Safe Design — netty-socketio avoids explicit locks by leveraging Netty's single-threaded channel guarantees and immutable/concurrent data structures; understanding this design prevents race conditions when adding custom handlers.
  • socketio/socket.io — The canonical Node.js Socket.IO server implementation that defines the protocol; netty-socketio implements the same protocol for JVM.
  • socketio/socket.io-client-java — Official Socket.IO Java client library; complements netty-socketio server for end-to-end JVM real-time applications.
  • netty/netty — Underlying async I/O framework that netty-socketio builds on; understanding Netty's channel, handler, and pipeline model is essential.
  • redisson/redisson — One of two primary distributed store backends (Redisson or Hazelcast) that netty-socketio plugs into for multi-node client sessions and broadcasts.
  • spring-projects/spring-boot — Common deployment target for netty-socketio servers; many users run this library as a Spring Boot bean.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for ack/AckManager.java

The acknowledgment system is a critical feature of Socket.IO (mentioned in README), but there's no visible test coverage for src/main/java/com/corundumstudio/socketio/ack/AckManager.java. This handles timeout scheduling, callback execution, and ack tracking—complex logic that needs rigorous testing to prevent regressions in distributed scenarios.

  • [ ] Create src/test/java/com/corundumstudio/socketio/ack/AckManagerTest.java
  • [ ] Add tests for ack timeout scenarios and AckSchedulerKey scheduling
  • [ ] Test concurrent ack callbacks and edge cases (duplicate acks, cancelled acks)
  • [ ] Verify integration with the scheduler and cleanup behavior
  • [ ] Run against checkstyle.xml rules to ensure code style compliance

Add integration tests for multi-transport scenarios (WebSocket + XHR polling)

The README highlights support for both xhr-polling and websocket transports, but there's no evidence of integration tests validating client behavior across transport types or fallback scenarios. This is critical for real-world deployments where clients may switch transports.

  • [ ] Create src/test/java/com/corundumstudio/socketio/integration/MultiTransportIntegrationTest.java
  • [ ] Test client connection upgrade from xhr-polling to websocket
  • [ ] Test fallback behavior when websocket unavailable
  • [ ] Verify message delivery consistency across transport switches
  • [ ] Test namespace/room operations across transport transitions

Add GitHub Actions workflow for dependency/security scanning

The repo has build-pr.yml and build.yml workflows but no automated dependency vulnerability scanning. With Netty as a core dependency (currently 4.1.130.Final) and support for multiple client stores (Redisson, Hazelcast), security scanning should be part of CI to catch CVEs early.

  • [ ] Create .github/workflows/security-scan.yml using GitHub's dependency scanning or OWASP/Snyk
  • [ ] Configure to run on push to main/master and on pull requests
  • [ ] Set workflow to fail on high-severity vulnerabilities in dependencies
  • [ ] Add step to verify Netty and client store library versions against known CVEs
  • [ ] Document the security scanning process in a SECURITY.md file (create if missing)

🌿Good first issues

  • Add integration tests for BroadcastOperations and MultiRoomBroadcastOperations in handler/ to verify message delivery across multiple rooms; currently no visible test coverage in the file listing for cross-room broadcasts.
  • Document the ack callback timeout behavior and AckMode enum with code examples in a new ARCHITECTURE.md file, as the current README lacks clarity on when and why acks timeout.
  • Add a Spring Boot auto-configuration starter class (similar to spring-boot-autoconfigure) that automatically instantiates and starts SocketIOServer from application.yml properties, reducing boilerplate for Spring users.

Top contributors

Click to expand
  • [@Nikita Koksharov](https://github.com/Nikita Koksharov) — 47 commits
  • @mrniko — 29 commits
  • @unverbraucht — 11 commits
  • @berinhardt — 3 commits
  • [@Lukáš PACLÍK](https://github.com/Lukáš PACLÍK) — 1 commits

📝Recent commits

Click to expand
  • e97fbe1 — [maven-release-plugin] prepare for next development iteration (Nikita Koksharov)
  • 355f8d4 — [maven-release-plugin] prepare release netty-socketio-2.0.14 (Nikita Koksharov)
  • 9a330bd — release updated (Nikita Koksharov)
  • 0f92a56 — [maven-release-plugin] rollback the release of netty-socketio-2.0.14 (Nikita Koksharov)
  • e1fa3a4 — release updated (Nikita Koksharov)
  • 7297b6b — [maven-release-plugin] prepare for next development iteration (Nikita Koksharov)
  • 0ab3ebb — [maven-release-plugin] prepare release netty-socketio-2.0.14 (Nikita Koksharov)
  • 7fb9f9d — Feature - HazelcastStore compatible with Hazlecast 4.x and 5.x (Nikita Koksharov)
  • e09caf5 — Merge branch 'master' of github.com:mrniko/netty-socketio (Nikita Koksharov)
  • 8aa9abb — Feature - HazelcastPubSubStore compatible with Hazlecast 4.x and 5.x (Nikita Koksharov)

🔒Security observations

  • High · Outdated Netty Dependency — pom.xml - netty.version property. The project uses Netty 4.1.130.Final. While this is a relatively recent version, it should be verified against CVE databases for any known vulnerabilities. Netty versions older than 4.1.108.Final have had security issues related to HTTP request handling and decompression. Fix: Regularly update Netty to the latest stable version and monitor security advisories. Currently use 4.1.130.Final or newer, and verify no CVEs are present in this version.
  • High · Missing Security Headers Configuration — src/main/java/com/corundumstudio/socketio/handler/ and Configuration.java. The codebase implements a Socket.IO server but there is no visible configuration for security headers (HSTS, X-Frame-Options, X-Content-Type-Options, CSP, etc.) in the file structure. This could expose clients to various attacks. Fix: Implement security headers in the HTTP response handling. Add options to Configuration for enabling HSTS, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Content-Security-Policy headers.
  • High · No Visible CORS Configuration Validation — src/main/java/com/corundumstudio/socketio/ - Missing CORS handler. Socket.IO servers are prone to CORS misconfigurations. The file structure does not show explicit CORS validation mechanisms, which could allow unauthorized cross-origin requests from malicious domains. Fix: Implement strict CORS validation with configurable allowed origins. Reject requests from unauthorized origins. Provide Configuration options to explicitly whitelist allowed domains.
  • Medium · SSL/TLS Configuration Not Verified — src/main/java/com/corundumstudio/socketio/Configuration.java. While the README mentions 'Supports SSL' capability, there is no visible enforcement of minimum TLS versions or cipher suites in the configuration files. This could allow downgrade attacks or weak cipher usage. Fix: Enforce minimum TLS 1.2 (preferably 1.3), disable weak ciphers, and provide configuration validation to prevent insecure setups. Add security documentation for SSL setup.
  • Medium · Incomplete POM File — pom.xml. The pom.xml file appears truncated at the end of the provided content ('</properti' instead of '</properties>'), indicating the full dependency list is not visible. Unknown dependencies could contain vulnerabilities. Fix: Verify the complete pom.xml file is well-formed and run dependency checks using 'mvn dependency:check' and OWASP Dependency-Check tool to identify vulnerable transitive dependencies.
  • Medium · Missing Authentication/Authorization Documentation — src/main/java/com/corundumstudio/socketio/AuthorizationListener.java and AuthTokenListener.java. Classes like AuthorizationListener.java, AuthTokenListener.java exist, but without visible implementation details, there's risk of improper authentication handling. Default configurations might be insecure. Fix: Provide clear security documentation for authentication implementation. Enforce token validation, implement rate limiting on authentication attempts, and use secure token generation methods.
  • Medium · No Visible Input Validation Framework — src/main/java/com/corundumstudio/socketio/handler/InPacketHandler.java. Socket.IO servers handle JSON payloads and arbitrary event data. No visible centralized input validation or sanitization framework in the handler components. Fix: Implement input validation for all incoming messages. Validate message size limits, JSON structure, and sanitize event names. Use a whitelist approach for allowed event types.
  • Medium · Distributed System Security Not Addressed — README mentions Redisson/Hazelcast support. The project supports distributed broadcast with Redisson and Hazelcast, but no visible security mechanisms for inter-node communication (encryption, authentication between nodes). Fix: Encrypt communication between distributed nodes. Implement mutual TLS for node-to-node communication. Document secure configuration of Redisson and Hazelcast clusters.
  • Low · Missing Rate Limiting Configuration — undefined. undefined Fix: undefined

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · mrniko/netty-socketio — RepoPilot