Skip to content

Ananto30/pushy

Repository files navigation

Pushy

Pushy is a simple server to send SSE (Server-Sent Events) to HTTP clients. It allows you to listen to a list of channels and 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 that connect to the server to listen for events on specific channels.
  • Publishers: Authorized entities that can send events to channels, usually internal services.
  • Flow: Deploy Pushy server(s) connected to a Redis instance for pub/sub with same or different client_id and client_secret. Publishers a.k.a trusted services create JWT tokens with allowed channels and hand them to clients. Clients connect to Pushy server with the token and listen to events on allowed channels.

Flow Diagram

 Client        Publisher          Pushy Cluster
 (Js or        (Internal          ┌──────────────────┐
 any sse)      or trusted)        │ Server 1         │
                                  │ Server 2         │
                                  │ Server 3         │
 (1)           (2)                │ + Redis          │
 Request ────> Get Token ────────>└──────────────────┘
 Token         with specific             │
               channels                  │
                                         │
               (3) Return Token <────────┘        
                  │
 (4)              │
 Connect <────────┘
 with Token       
 Listen ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌> Events Stream
 (SSE)  <╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ (Real-time updates)
               
               (6) Publish
               Message ──────────> Channel
               
 (7) Receive
 Update ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ Phoenix.PubSub distributes
 Real-time <╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ to all subscribers

📋 Requirements

  • Elixir ~> 1.12
  • 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/news/stream \
  -H "Authorization: Bearer <your-token-here>"

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

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. 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": {}
  }'

💻 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 client is 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

Open-source Pusher alternative

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published