A fast, lightweight, and ephemeral proximity-based data exchange service that enables secure data transfer between devices through a simple "bump" gesture. Built with Rust for high performance and reliability.
Bump is a modern solution to a common problem: how do you quickly share data between devices that are physically next to each other? While technologies like AirDrop, NFC, or QR codes exist, they often require specific hardware, complex setup, or are platform-dependent.
Bump makes sharing as intuitive as physically bumping two devices together:
- Two users with devices next to each other want to share data
- They both make a "bump" gesture with their devices
- Our service matches these gestures based on time and location
- Data is instantly exchanged between the devices
- 🚀 Platform Independent: Works on any device with a web browser
- 🎯 No Special Hardware: Uses standard sensors (GPS, accelerometer) available in most devices
- 🔒 Privacy-First: Ephemeral by design - no data storage, no tracking
- ⚡ Lightning Fast: Sub-second data exchange
- 🌐 Universal: Share any type of data - text, URLs, JSON, or encoded binary
Device A Server Device B
| | |
|------ /bump -------->| |
| |<------ /bump ---------|
| | |
| |---(match algorithm)---|
| | |
|<---- payload B ------| |
| |------ payload A ----->|
| | |
-
When a device initiates a bump, it sends a request to our
/bumpendpoint with:- Current location
- Timestamp
- Optional payload
- Optional custom matching key
-
Our service uses a sophisticated matching algorithm to pair devices based on:
- Temporal proximity (within milliseconds)
- Spatial proximity (within meters)
- Custom keys (if provided)
-
When a match is found, payloads are exchanged instantly between the devices
Our service is designed with performance and privacy in mind:
- Zero Persistence: No databases, no storage, everything happens in memory
- Auto-Cleanup: Unmatched requests automatically expire after their TTL
- Race-Condition Protected: Thread-safe queue management
- Resource Efficient:
- Configurable queue sizes
- Automatic request cleanup
- Minimal memory footprint
- Sub-millisecond matching algorithm
Visit our live demo at bump.nyn.me to try Bump in action! Make suer you have two devices to test it with :-)
Check out our open-source client implementation at github.com/codevalley/bump-me
# Clone the repository
git clone https://github.com/codevalley/bump.git
cd bump-service
# Build and run
cargo run
# The service will start on localhost:8080The unified endpoint for all bump operations. Note the doubled /bump: every
route is nested under the web::scope("/bump") prefix in src/main.rs, and
this particular handler is itself registered as #[post("/bump")] in
src/api.rs — so the full path is /bump/bump, not /bump.
curl -X POST http://localhost:8080/bump/bump \
-H "Content-Type: application/json" \
-d '{
"matching_data": {
"location": {"lat": 37.7749, "long": -122.4194},
"timestamp": 1646078423000,
"custom_key": "optional-key"
},
"payload": "https://example.com/shared-document",
"ttl": 500
}'Response (Success):
{
"status": "matched",
"sender_id": "request-id-456",
"receiver_id": "request-id-123",
"timestamp": 1646078425000,
"payload": "payload from matching request",
"message": "Match successful"
}Other endpoints: POST /bump/send, POST /bump/receive, GET /bump/health,
GET /bump/timestamp, and GET /bump/weather?lat=&long= — a server-side
proxy to OpenWeather that returns {city, temperature, weather_icon, weather_description} and never exposes the API key to clients (503 if
BUMP_OPENWEATHER_KEY is unset).
For detailed API documentation, see our API Guide.
Configure the service through environment variables (see sample.env for a
ready-to-copy reference). Every BUMP_* variable has its own per-field
default in src/config.rs, so a partial environment (only some vars set)
does not revert the rest to some other fallback:
| Variable | Description | Default |
|---|---|---|
BUMP_MAX_QUEUE_SIZE |
Maximum pending requests | 1000 |
BUMP_MAX_DISTANCE_METERS |
Maximum matching distance (meters) | 250.0 |
BUMP_MAX_TIME_DIFF_MS |
Maximum time difference (ms) | 1500 |
BUMP_DEFAULT_TTL_MS |
Default request TTL (ms) | 500 |
BUMP_TEMPORAL_WEIGHT |
Weight given to temporal proximity in match scoring | 0.7 |
BUMP_SPATIAL_WEIGHT |
Weight given to spatial proximity in match scoring | 0.3 |
BUMP_CLEANUP_INTERVAL_MS |
Interval at which expired requests are swept from the queue (ms) | 1000 |
BUMP_OPENWEATHER_KEY |
Server-side OpenWeather API key used by the /bump/weather proxy. No default — leave unset to disable the feature (the endpoint then returns 503). |
(none) |
RUST_LOG |
Log level | info |
Docker image note: the shipped
Dockerfilebakes in stricter matching overrides than the defaults above —BUMP_MAX_TIME_DIFF_MS=5000andBUMP_MAX_DISTANCE_METERS=100— to make matching reliable out of the box given server-assigned timestamps and real-world GPS accuracy. Every otherBUMP_*variable is left unset in the image and falls back to itssrc/config.rsdefault. All of these, including the two baked-in overrides, remain tunable per-deployment via Railway dashboard vars.
bump-service/
├── src/
│ ├── main.rs # Application entry point, route wiring under /bump
│ ├── api.rs # HTTP endpoint handlers
│ ├── models.rs # Data structures
│ ├── service.rs # Core matching service
│ ├── config.rs # Configuration
│ └── queue/ # Thread-safe unified request queue
│ ├── mod.rs # Module wiring + default score/threshold constants
│ ├── types.rs # Shared types: QueuedRequest, RequestType, RequestQueue trait
│ ├── q_core.rs # UnifiedQueue struct, clone semantics, constructors
│ ├── q_impl.rs # RequestQueue trait impl (add/remove/subscribe/cleanup)
│ ├── q_matching.rs # Candidate scan + atomic match commit under lock
│ └── matching.rs # Pure match-scoring function (time/distance/key)
├── docs/ # Documentation
└── tests/
└── integration/ # Integration tests (single Cargo test binary)
# Start with hot reload
cargo watch -x run
# Run tests
cargo test
# Format and lint
cargo fmt
cargo clippyThe service auto-deploys to Railway from GitHub main using the repo's
Dockerfile (multi-stage build, non-root runtime user, binds $PORT). To
deploy:
- Push to
main— Railway picks up the commit and rebuilds the image. - Configure environment variables (matching overrides,
BUMP_OPENWEATHER_KEY,RUST_LOG, etc.) via the Railway dashboard; they take precedence over the Dockerfile's baked-in defaults. - Railway health-checks the deployment against
GET /bump/health.
Warning: if main doesn't compile, the Railway build fails and
production goes down — the live URL will start returning Railway's own
"Application not found" 404 instead of the service. There is no automatic
rollback to the last good image, so keep main green. See
DEPLOYMENT.md for details.
This project is licensed under the MIT License - see the LICENSE file for details.
We welcome contributions! See our Contributing Guidelines for details.
- 📧 Email: [email protected]
