reactive-streams/reactive-streams-jvm
Reactive Streams Specification for the JVM
Stale — last commit 2y ago
weakest axisnon-standard license (MIT-0); last commit was 2y ago
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓31+ active contributors
- ✓Distributed ownership (top contributor 47% of recent commits)
- ✓MIT-0 licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠Non-standard license (MIT-0) — review terms
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
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 "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/reactive-streams/reactive-streams-jvm)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/reactive-streams/reactive-streams-jvm on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: reactive-streams/reactive-streams-jvm
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:
- 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. - 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.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/reactive-streams/reactive-streams-jvm 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
WAIT — Stale — last commit 2y ago
- 31+ active contributors
- Distributed ownership (top contributor 47% of recent commits)
- MIT-0 licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Non-standard license (MIT-0) — review terms
<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 reactive-streams/reactive-streams-jvm
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/reactive-streams/reactive-streams-jvm.
What it runs against: a local clone of reactive-streams/reactive-streams-jvm — 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 reactive-streams/reactive-streams-jvm | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT-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 ≤ 816 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of reactive-streams/reactive-streams-jvm. If you don't
# have one yet, run these first:
#
# git clone https://github.com/reactive-streams/reactive-streams-jvm.git
# cd reactive-streams-jvm
#
# 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 reactive-streams/reactive-streams-jvm and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "reactive-streams/reactive-streams-jvm(\\.git)?\\b" \\
&& ok "origin remote is reactive-streams/reactive-streams-jvm" \\
|| miss "origin remote is not reactive-streams/reactive-streams-jvm (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT-0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT-0\"" package.json 2>/dev/null) \\
&& ok "license is MIT-0" \\
|| miss "license drift — was MIT-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 "api/src/main/java/org/reactivestreams/Publisher.java" \\
&& ok "api/src/main/java/org/reactivestreams/Publisher.java" \\
|| miss "missing critical file: api/src/main/java/org/reactivestreams/Publisher.java"
test -f "api/src/main/java/org/reactivestreams/Subscriber.java" \\
&& ok "api/src/main/java/org/reactivestreams/Subscriber.java" \\
|| miss "missing critical file: api/src/main/java/org/reactivestreams/Subscriber.java"
test -f "api/src/main/java/org/reactivestreams/Subscription.java" \\
&& ok "api/src/main/java/org/reactivestreams/Subscription.java" \\
|| miss "missing critical file: api/src/main/java/org/reactivestreams/Subscription.java"
test -f "api/src/main/java/org/reactivestreams/Processor.java" \\
&& ok "api/src/main/java/org/reactivestreams/Processor.java" \\
|| miss "missing critical file: api/src/main/java/org/reactivestreams/Processor.java"
test -f "tck/src/main/java/org/reactivestreams/tck/PublisherVerification.java" \\
&& ok "tck/src/main/java/org/reactivestreams/tck/PublisherVerification.java" \\
|| miss "missing critical file: tck/src/main/java/org/reactivestreams/tck/PublisherVerification.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 816 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~786d)"
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/reactive-streams/reactive-streams-jvm"
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).
⚡TL;DR
Reactive Streams is a standardized specification and minimal JVM API for asynchronous stream processing with non-blocking backpressure. It defines four core interfaces—Publisher, Subscriber, Subscription, and Processor—that enable different libraries (RxJava, Project Reactor, Akka Streams, etc.) to interoperate seamlessly when exchanging data across asynchronous boundaries without forcing unbounded buffering. Multi-module Gradle build: api/ contains the core four interfaces in api/src/main/java/org/reactivestreams/ (Publisher.java, Subscriber.java, Subscription.java, Processor.java), with Java 9+ Flow adapters in api/src/main/java9/; examples/ provides reference implementations (RangePublisher, AsyncSubscriber, etc.) in examples/src/main/java/org/reactivestreams/example/unicast/; test suites live in examples/src/test/. No separate TCK module visible in file list, but RELEASE-NOTES.md references it.
👥Who it's for
JVM library maintainers (RxJava, Reactor, Akka developers) who need a common contract for composable async streams; backend engineers building reactive microservices that must handle high-throughput data with bounded memory; framework authors integrating multiple streaming libraries that need to speak the same protocol.
🌱Maturity & risk
Highly mature and stable. Version 1.0.4 is released on Maven Central; the specification is 7+ years old with established, production adoption across major frameworks. CI is present (.github/workflows/ci.yml), and the API surface is intentionally minimal and locked (no breaking changes expected). This is a foundational standard, not an active development project—stability is the goal.
Standard open source risks apply.
Active areas of work
Repository is in maintenance mode. The spec is complete and stable (v1.0.4). The file structure shows examples for reference implementations (unicast sync/async publishers and subscribers) and CI automation, but no active feature development. The Relicensing.txt file suggests past governance clarity work. Current activity is validating conformance via TCK and supporting implementations in downstream libraries.
🚀Get running
git clone https://github.com/reactive-streams/reactive-streams-jvm.git
cd reactive-streams-jvm
./gradlew build
./gradlew :examples:test
Daily commands:
This is not a runnable application. To validate your implementation: ./gradlew :examples:test runs the reference examples. To build and publish locally: ./gradlew build publishToMavenLocal. No dev server; instead, add org.reactivestreams:reactive-streams:1.0.4 and org.reactivestreams:reactive-streams-tck:1.0.4 as test dependencies in your project to verify conformance.
🗺️Map of the codebase
api/src/main/java/org/reactivestreams/Publisher.java— Core Publisher interface defining the contract for asynchronous data sources with backpressure support.api/src/main/java/org/reactivestreams/Subscriber.java— Core Subscriber interface defining the contract for consuming reactive streams with demand signaling.api/src/main/java/org/reactivestreams/Subscription.java— Core Subscription interface managing the relationship between Publisher and Subscriber, enabling backpressure control.api/src/main/java/org/reactivestreams/Processor.java— Processor interface combining Publisher and Subscriber roles, essential for stream transformation pipelines.tck/src/main/java/org/reactivestreams/tck/PublisherVerification.java— TCK verification suite for testing Publisher implementations against the specification guarantees.tck/src/main/java/org/reactivestreams/tck/SubscriberBlackboxVerification.java— TCK blackbox verification for testing Subscriber implementations without internal state inspection.api/src/main/java9/org/reactivestreams/FlowAdapters.java— Adapters bridging java.util.concurrent.Flow (Java 9+) to Reactive Streams interfaces for interoperability.
🛠️How to make changes
Create a new Publisher implementation
- Create new Java class extending or implementing Publisher interface (
examples/src/main/java/org/reactivestreams/example/unicast/RangePublisher.java) - Implement subscribe(Subscriber) method, create Subscription with onSubscribe callback (
examples/src/main/java/org/reactivestreams/example/unicast/AsyncIterablePublisher.java) - Handle backpressure via Subscription.request(long) and cancel() in Subscriber callbacks (
api/src/main/java/org/reactivestreams/Subscription.java) - Create test class extending PublisherVerification from TCK (
examples/src/test/java/org/reactivestreams/example/unicast/RangePublisherTest.java) - Implement createPublisher(long elements) and maxElementsFromPublisher() test methods (
tck/src/main/java/org/reactivestreams/tck/PublisherVerification.java)
Create a new Subscriber implementation
- Create new Java class implementing Subscriber interface (
examples/src/main/java/org/reactivestreams/example/unicast/SyncSubscriber.java) - Implement onSubscribe(Subscription) to store subscription and request initial demand (
api/src/main/java/org/reactivestreams/Subscriber.java) - Implement onNext(T), onError(Throwable), onComplete() lifecycle methods (
examples/src/main/java/org/reactivestreams/example/unicast/AsyncSubscriber.java) - Create blackbox or whitebox verification test class (
examples/src/test/java/org/reactivestreams/example/unicast/SyncSubscriberTest.java) - Extend SubscriberBlackboxVerification or SubscriberWhiteboxVerification from TCK (
tck/src/main/java/org/reactivestreams/tck/SubscriberBlackboxVerification.java)
Verify implementation against TCK specification
- Choose appropriate TCK verification base class (Publisher/Subscriber/Processor) (
tck/src/main/java/org/reactivestreams/tck/PublisherVerification.java) - Extend verification class and implement abstract factory methods (
examples/src/test/java/org/reactivestreams/example/unicast/RangePublisherTest.java) - Set up TestEnvironment with timing parameters if needed (
tck/src/main/java/org/reactivestreams/tck/TestEnvironment.java) - Run test suite via gradle test; all TCK rules execute automatically (
build.gradle)
🔧Why these technologies
- Java interfaces with minimal prescriptive methods — Enables maximum implementation flexibility across JVM languages while defining essential backpressure contract
- TestNG-based TCK test framework — Provides comprehensive spec verification without forcing specific assertion libraries or test runners on implementers
- Gradle multi-module build — Cleanly separates specification (api), verification (tck, tck-flow), and examples into independently versioned artifacts
- Java 9+ Flow adapters (FlowAdapters) — Bridges Reactive Streams (community spec) with java.util.concurrent.Flow (JDK standard) for seamless interop
- OSGi bundle metadata (bnd) — Enables deployment in modular environments while remaining compatible with traditional classpath
⚖️Trade-offs already made
-
Four minimal interfaces (Publisher, Subscriber, Subscription, Processor) instead of richer abstractions
- Why: Maximizes spec clarity and reduces implementation burden for vendors while ensuring universal interoperability
- Consequence: Higher-level operators (map, filter, etc.) must be added by implementations or libraries like RxJava, Project Reactor
-
Specification does not include error recovery, timeout, or retry semantics
- Why: Focuses on the fundamental backpressure contract; policy-level concerns defer to implementations
- Consequence: Implementations must independently document and provide these patterns
-
TCK is verification-only; does not provide implementation utilities
- Why: Keeps specification neutral and testable without coupling to any reference implementation
- Consequence: Developers must implement Publishers/Subscribers from scratch or adopt a framework (RxJava, Akka Streams, etc.)
-
Separate tck and tck-flow modules; Flow TCK requires Java 9+
- Why: Allows pre-Java 9 environments to use core Reactive Streams without Flow dependencies
- Consequence: Java 8 projects use reactive-streams core
🪤Traps & gotchas
No external runtime dependencies, which is intentional—this is a pure spec. Trap: Java version detection in build.gradle—FlowAdapters and java9 srcDir only compile on Java 9+; projects on Java 8 get a reduced API. Trap: the TCK test suite is not in this repo; it's documented in CONTRIBUTING.md and RELEASE-NOTES.md but the actual conformance tests live separately (you pull them as a test dependency). Trap: 'Subscription' callbacks must be non-blocking and thread-safe; naive implementations often violate this (see CONTRIBUTING.md for the specification rules).
🏗️Architecture
💡Concepts to learn
- Backpressure — The core problem Reactive Streams solves: a fast producer must respect the subscriber's request(n) demand signal to avoid unbounded buffering and memory exhaustion, making it essential to understand how subscription.request() gates onNext() calls.
- Non-blocking asynchrony — Reactive Streams mandates that all callbacks (onNext, onError, onComplete, request) must be non-blocking and thread-safe; understanding this constraint is critical to avoid deadlocks and data races in implementations.
- Publisher-Subscriber pattern — The fundamental interaction model where a Publisher calls subscribe() with a Subscriber, receives a Subscription, and uses it to send items; this push-based model with explicit demand is different from traditional pull-based iteration.
- Bounded queue / resource pool management — The motivation behind backpressure: reference implementations in examples/ (e.g., RangePublisher) use bounded internal queues and only emit onNext() when subscription.request() is called, preventing queue overflow.
- Flow adapters (Java 9+ compatibility) — Java 9 added java.util.concurrent.Flow with the same four interfaces; FlowAdapters bridge Reactive Streams to Flow, critical for projects needing to work across Java versions and interop with java.util APIs.
- TCK (Technology Compatibility Kit) — A conformance test suite (not in this repo but referenced) that validates an implementation's Publisher and Subscriber against the spec rules; understanding TCK requirements ensures your implementation is interoperable.
- Mono vs. stream protocols — Reactive Streams does not prescribe what type of stream (single-item, multi-item, infinite) a Publisher emits; understanding this flexibility explains why both Mono (single value) and Flux (many values) can conform to the same interface.
🔗Related repos
ReactiveX/RxJava— Major conforming implementation of Reactive Streams; adds composition operators (map, filter, etc.) on top of the core Publisher/Subscriber contract.reactor/reactor-core— Project Reactor—another major conforming implementation used heavily in Spring WebFlux; demonstrates Reactive Streams applied to practical async I/O.akka/akka— Akka Streams uses Reactive Streams as its foundation for actor-based reactive processing; shows how the spec integrates with actor models.eclipse/microprofile-reactive-messaging— Builds on Reactive Streams to standardize async messaging in microservices; a higher-level specification that assumes Reactive Streams compliance.reactivex/rxjs— JavaScript sibling project inspired by similar principles (async, non-blocking, backpressure) though not formally compatible—good reference for the philosophy across ecosystems.
🪄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 TCK (Technology Compatibility Kit) tests for java.util.concurrent.Flow adapters
The FlowAdapters.java class in api/src/main/java9 bridges reactive-streams with JDK 9+'s java.util.concurrent.Flow API, but there are no dedicated whitebox/blackbox verification tests for these adapters. The tck-flow module has verification classes (FlowPublisherVerification, FlowSubscriberBlackboxVerification, etc.) but no tests specifically validating the adapter conversions between the two specifications. This is critical since adapters are error-prone and must maintain backpressure semantics.
- [ ] Create tck-flow/src/test/java/org/reactivestreams/tck/flow/FlowAdaptersVerificationTest.java
- [ ] Add test cases for Publisher → Flow.Publisher adaptation
- [ ] Add test cases for Subscriber → Flow.Subscriber adaptation
- [ ] Add test cases for roundtrip conversions (Publisher → Flow.Publisher → Publisher)
- [ ] Verify backpressure semantics are preserved across adapter boundaries
- [ ] Run against existing TCK verification suites to ensure compliance
Add CI workflow for Java version matrix testing (Java 8, 11, 17, 21)
The .java-version file and conditional jdkFlow flag in build.gradle indicate version-specific code paths, but .github/workflows/ci.yml likely only tests a single Java version. The api module has dual source directories (src/main/java and src/main/java9) for FlowAdapters support. A matrix CI workflow would ensure the build, api compilation, TCK tests, and examples work across all supported Java LTS versions, catching version-specific regressions early.
- [ ] Review current .github/workflows/ci.yml configuration
- [ ] Create or extend CI workflow with Java version matrix [8, 11, 17, 21]
- [ ] Ensure FlowAdapters source inclusion logic (jdkFlow detection) is tested for Java 8 and 9+
- [ ] Run 'gradle build' and 'gradle check' across all matrix versions
- [ ] Verify tck and examples modules execute successfully on each version
- [ ] Document in CONTRIBUTING.md which Java versions are officially supported
Add integration tests for example implementations against TCK verification suites
The examples module contains unicast Publisher/Subscriber implementations (RangePublisher, AsyncSubscriber, etc.) but the test coverage appears limited to unit tests in examples/src/test. These example implementations should be verified against the reactive-streams TCK to ensure they correctly implement the specification. This would improve example quality, serve as a template for library authors, and catch specification violations in the reference implementations.
- [ ] Create examples/src/test/java/org/reactivestreams/example/unicast/TCKVerificationTests.java
- [ ] Make RangePublisher extend PublisherVerification from TCK
- [ ] Make AsyncSubscriber extend SubscriberWhiteboxVerification from TCK
- [ ] Add verification test case for AsyncIterablePublisher (currently only has AsyncRangePublisherTest unit test)
- [ ] Document in examples/README.md that implementations pass TCK verification
- [ ] Ensure all examples pass backpressure, subscription, and cancellation TCK tests
🌿Good first issues
- Add Javadoc code examples to
api/src/main/java/org/reactivestreams/Publisher.javaandSubscriber.javashowing a minimal Publisher-Subscriber handshake with backpressure—the repo has examples/ but inline docs are sparse for the core interfaces. - Create a
examples/src/main/java/org/reactivestreams/example/reference implementation showing a multi-threaded producer (e.g., ThreadPoolPublisher) that respects bounded queues and backpressure request(n), with matching tests—currently all examples are single-threaded or async but not true concurrent. - Add a diagram or ASCII art to README.md or CONTRIBUTING.md showing the Publisher → subscribe(Subscriber) → onSubscribe(Subscription) → request(n) → onNext(item) lifecycle—critical for newcomers but not visually documented anywhere.
⭐Top contributors
Click to expand
Top contributors
- @viktorklang — 47 commits
- @rkuhn — 5 commits
- @akarnokd — 5 commits
- @Scottmitch — 4 commits
- @sullis — 4 commits
📝Recent commits
Click to expand
Recent commits
a625d3a— Merge pull request #552 from rsvoboda/rm.empty.file (ktoso)648706d— Remove empty TCKVerificationSupport.java (rsvoboda)f361483— Merge pull request #541 from reactive-streams/wip-fix-540-Bundle-SymbolicName-√ (rkuhn)793cd16— Merge pull request #544 from Scottmitch/timeout_fix (rkuhn)10c6a25— TestEnvironment: Fix timeout arithmetic (Scottmitch)613ebcb— Fixing #540 by manually overriding the Bundle-SymbolicName for the BND plugin (viktorklang)944163a— Preparing for releasing 1.0.4 (viktorklang)899f17f— Merge pull request #532 from akarnokd/patch-3 (rkuhn)1f0cc2b— Do not build on JDK 13. (akarnokd)1744f5e— Merge pull request #516 from sullis/github-actions-ci (rkuhn)
🔒Security observations
The Reactive Streams JVM repository demonstrates good overall security posture as a specification library with minimal runtime dependencies and no apparent code injection risks. The codebase is primarily interface definitions and examples with no database queries, file operations, or network handling that could introduce common web vulnerabilities. Main concerns are limited to supply chain considerations (gradle-wrapper.jar inclusion), documentation URL security (HTTP vs HTTPS), and the absence of an explicit security policy. The project would benefit from strengthening dependency management practices and establishing a formal vulnerability disclosure process.
- Medium · Gradle Wrapper JAR Included in Repository —
gradle/wrapper/gradle-wrapper.jar. The gradle-wrapper.jar file is committed to the repository at gradle/wrapper/gradle-wrapper.jar. While Gradle wrapper is a common practice, binary JAR files in version control should be verified for authenticity and can present supply chain risks if compromised. Fix: Verify the integrity of gradle-wrapper.jar against official checksums. Consider implementing commit hooks to prevent accidental inclusion of unsigned binaries. Document the process for updating the wrapper securely. - Low · Missing Security Headers in Documentation —
api/build.gradle (Bundle-DocURL). The project documentation and website references (Bundle-DocURL: http://reactive-streams.org) use HTTP instead of HTTPS. While this is metadata, it could lead users to insecure resources. Fix: Update all documentation URLs to use HTTPS. Ensure the official project website enforces HTTPS and HSTS headers. - Low · No Dependency Version Pinning Visible —
build.gradle, api/build.gradle, examples/build.gradle, tck/build.gradle, tck-flow/build.gradle. The build.gradle file does not show explicit dependency version constraints in the provided snippet. This could lead to transitive dependency vulnerabilities if not managed properly. Fix: Implement explicit dependency version management using a BOM (Bill of Materials) or constraint blocks. Use tools like dependabot or Snyk to monitor for vulnerable dependencies. - Low · Missing Security Policy Documentation —
Repository root. No visible SECURITY.md file in the root directory for responsible disclosure of security vulnerabilities. Fix: Create a SECURITY.md file with clear instructions for reporting security vulnerabilities responsibly, including preferred contact methods and expected response timeframes.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.