Skip to content

Ananto30/pushy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pushy

Pushy is a standalone server to send real-time messages to clients using Server-Sent Events (SSE) or WebSockets. It allows clients to listen to a list of channels and servers (publishers) to post events to them.

Core Concepts

  • Channels: Named streams that clients can subscribe to for receiving events. We prefer dot notation (e.g., news.sports) for channel names, so wildcards can be used (e.g., news.*).
  • Clients: HTTP clients through SSE or WebSocket connections that subscribe to channels and receive real-time updates.
  • Publishers/backends: Authorized entities that can send events to channels, usually internal services/backends.

Flow Diagram

 Client              Publisher              Pushy Cluster
 (SSE or WS)         (Your backend)         ┌──────────────────┐
                                            │ Server 1         │
                                            │ Server 2         │
                                            │ Server 3         │
 (1)                 (2)                    │ + Redis          │
 Request ────────>   Create Token ────────> └──────────────────┘
 Token for           with specific             │
 sse connection      channels permissions      │
                                               │
                                               │
                     (3) Return Token <────────┘        
                      │
 (4)                  │
 Connect <────────────┘
 with Token       
 Listen ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌> Events Stream
 (SSE or WS)  <╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ (Real-time updates)
               
                     (5) Publish
                     Message ───────────────> Channel
               
 (6) Receive
 Update ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ Phoenix.PubSub distributes
 Real-time <╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ to all subscribers

📋 Requirements

  • Elixir ~> 1.14
  • Erlang/OTP 23+
  • Docker & Docker Compose (optional, for containerized deployment)

🚀 Quick Start with Docker Compose

The fastest way to get Pushy running with Redis is using Docker Compose.

1. Start the Services

docker compose up -d

This starts:

Default credentials are set in the docker-compose.yml:

  • AUTH_SECRET_KEY: your_secret_key_here
  • PUBLISHER_CLIENT_ID: publisher_id
  • PUBLISHER_CLIENT_SECRET: publisher_secret

2. Create a Client Token

Generate a token using your publisher credentials:

curl -X POST http://localhost:4001/token \
  -H "Content-Type: application/json" \
  -H "X-Client-ID: publisher_id" \
  -H "X-Client-Secret: publisher_secret" \
  -d '{
    "sub": "user123",
    "channels": ["news", "sports", "weather"],
    "meta": {
      "name": "John Doe",
      "email": "john@example.com"
    }
  }'

Response:

{
  "token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..."
}

Save this token for the next steps.

3. Subscribe to a Channel (SSE)

Open a Server-Sent Events connection to listen to messages:

curl -X GET http://localhost:4001/channels/stream?channels=news \
  -H "Authorization: Bearer <your-token-here>"

This will keep the connection open and stream events as they arrive.

3b. Subscribe to a Channel (WebSocket)

Alternatively, you can use WebSocket:

# Using websocat (install via: cargo install websocat)
websocat ws://localhost:4001/channels/ws?channels=news \
  -H "Authorization: Bearer <your-token-here>"

4. Publish a Message to a Channel

In another terminal, publish a message:

curl -X POST http://localhost:4001/publish/news \
  -H "Content-Type: application/json" \
  -H "X-Client-ID: publisher_id" \
  -H "X-Client-Secret: publisher_secret" \
  -d '{
    "data": {
      "title": "Breaking News",
      "content": "Some important content here"
    }
  }'

The subscribed client will receive the message in real-time! Also try sending on a different server at 4002, it will still be delivered, thanks to Phoenix.PubSub.

5. Check metrics

Pushy exposes Prometheus metrics at /metrics endpoint. You can check them with:

curl http://localhost:4001/metrics

6. Stop the Services

docker compose down

🛑 Understanding Token Claims & Access Control

Token Claims

When you create a token, it contains:

  • sub: Subject identifier (e.g., user ID)
  • channels: Array of channels the user can access
  • meta: Optional metadata object

Access Control

  • Clients: Can only subscribe to and publish messages to channels listed in their token's channels array
  • Publisher: Uses client credentials (X-Client-ID and X-Client-Secret headers) to create tokens with specific channel permissions

Channel Wildcards

When creating a token, you can use wildcards in channel names:

curl -X POST http://localhost:4001/token \
  -H "Content-Type: application/json" \
  -H "X-Client-ID: publisher_id" \
  -H "X-Client-Secret: publisher_secret" \
  -d '{
    "sub": "user456",
    "channels": ["news.*", "sports.updates"],
    "meta": {}
  }'

If you have many publishers, it is better to name channels with publisher prefix (e.g., publisher1.news, publisher2.sports) and create tokens with publisher-specific channel permissions. This way, you can easily manage access control and avoid accidental cross-publisher access.


💻 Development Setup

1. Clone the Repository

git clone https://github.com/ananto30/pushy.git
cd pushy

2. Install Dependencies

mix deps.get

3. Compile the Project

mix compile

4. Set Environment Variables

export AUTH_SECRET_KEY=your_secret_key_here
export PUBLISHER_CLIENT_ID=publisher_id
export PUBLISHER_CLIENT_SECRET=publisher_secret

Optionally, configure Redis (defaults to localhost:6379):

export REDIS_URL=redis://localhost:6379

5. Test

mix test

6. Run the Development Server

mix run --no-halt

The server will start on port 4000 by default.


🐳 Docker Deployment

Building the Image

The project includes a multi-stage Dockerfile that builds an optimized production image.

Important: Never bake secrets into image layers with --build-arg. Instead, pass AUTH_SECRET_KEY and other secrets at runtime via environment variables or your orchestrator's secret mechanism.

Running with Docker

docker run -p 4000:4000 \
  -e AUTH_SECRET_KEY=$AUTH_SECRET_KEY \
  -e PUBLISHER_CLIENT_ID=$PUBLISHER_CLIENT_ID \
  -e PUBLISHER_CLIENT_SECRET=$PUBLISHER_CLIENT_SECRET \
  -e REDIS_URL=redis://your-redis-host:6379 \
  pushy:latest

Client Libraries

There is a simple JavaScript client library to connect to Pushy servers using SSE. See the client/sse-client.js file for usage details.

PS: I am not a JS expert, the clients are solely generated by LLMs 😅

Examples

See the example directory for sample scripts to listen to channels and publish events.

Notes

Proxy / nginx notes

When putting a reverse proxy in front of Pushy, ensure you do not buffer the upstream response and increase timeouts. Example nginx snippet:

location /channels/ {
   proxy_pass http://pushy:4000;
   proxy_http_version 1.1;
   proxy_set_header Connection '';
   proxy_buffering off;
   proxy_read_timeout 3600s;
}

📄 License

This project is licensed under the MIT License.

About

Simple & efficient real-time communication server

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors