🚀 Live Demo Available: Check out Ghostboard in action (read-only demo).
Ghostboard is a lightweight, self-hosted solution for real-time synchronized text sharing. This repository includes a WebSocket server for syncing text across multiple clients and a command-line client for retrieving or updating the shared text.
This project is aimed at self-hosters who want to quickly and easily share text between devices. There is no encryption or security—it is not suitable for deployment on the internet or other untrusted networks. Ghostboard creates a simple webpage that accepts text, which is mirrored across all connected instances. A command-line client is also provided for interacting with the text without requiring a graphical interface.
-
Server:
- Serves a webpage with a real-time synchronized text field.
- Clients see live updates as text is typed.
- Text remains synchronized across all connected clients.
- Dynamically create multiple boards based on the URL path. Each unique subdirectory (e.g., /test or /example) hosts an independent board with its own synchronized text field.
- Added Dark Mode and improved styling. Refined CSS for better readability and transitions. Implemented an iOS-style toggle switch for theme switching. Added simple icon.
- New Feature (v3.0.0): Nonintrusive corner tabs with quarter-circle expansions:
- Left Tab: Displays project information (name, author, version).
- Right Tab: Expands to a clickable GitHub link with a large icon.
- New Feature (v3.0.0): WebSocket connection handling improvements, including error messages and a reload option.
- New Feature (v3.1.0): FULL MARKDOWN SUPPORT- thanks to /u/jack3308 for the suggestion!
- New Feature (v3.2.0): BUNDLED NGINX IN DOCKER! Can simply open port 80 now and not worry about complex reverse proxy routing.
- New Feature (v3.4.0): REST API support for updating and retrieving text.
-
Client (Legacy):
- Command-line tool to retrieve or update the shared text.
- Supports WebSocket servers using IPs or domain names.
- Still supported but superceded by REST API
-
Dockerized:
- Both server and client are available as prebuilt Docker images for ease of deployment.
- Docker (for containerized deployment)
- Python 3.8+ (if running the scripts locally)
-
Pull the prebuilt server image:
docker pull thehelpfulidiot/ghostboard-server
- Use tag
latest-arm64
for arm release.
- Use tag
-
Run the server container:
docker run --rm -p 8080:8080 -p 8765:8765 thehelpfulidiot/ghostboard-server
As of v3.2.0, one could instead simply open port 80 and it will work fine. Above is deprecated:
docker run --rm -p 8080:80 thehelpfulidiot/ghostboard-server
-
Access the server:
- Open
http://<server-ip>:80
in your browser.
- Open
-
Navigate to the
server/
directory:cd server
-
Install dependencies:
pip install -r requirements.txt
-
Run the server:
python3 server.py
-
Access the server:
- Open
http://<server-ip>:8080
in your browser. - All text changes will synchronize in real time.
- Open any page to create a new and separate board e.g.
http:<server-ip>:8080/new-board
.
- Open
You can update the text on a board using a POST request.
Examples:
-
Update text from a file:
curl -X POST "http://ghostboard-server:port" -d "text=$(cat example.txt)"
-
Update text directly:
curl -X POST "http://ghostboard-server:port" -d "text=example"
You can retrieve the current text from a board using a GET request.
Examples:
-
Save the text to a file:
curl "http://ghostboard-server:port?get_text=true" > ghostboard.txt
-
Print the text to the terminal:
curl "http://ghostboard-server:port?get_text=true"
These commands work with dynamically created boards by appending the desired board path to the URL, such as http://ghostboard-server:port/board-name
.
-
Pull the prebuilt client image:
docker pull thehelpfulidiot/ghostboard-client
- Use tag
latest-arm64
for arm release.
- Use tag
-
Retrieve the current text:
docker run --rm thehelpfulidiot/ghostboard-client <server-ip>
-
Update the shared text:
docker run --rm thehelpfulidiot/ghostboard-client <server-ip> "New text to share"
-
Update text from a file:
cat text.txt | docker run --rm -i thehelpfulidiot/ghostboard-client <server-ip> -
-
Update file from text:
docker run --rm -i thehelpfulidiot/ghostboard-client <server-ip> > text.txt
-
Navigate to the
client/
directory:cd client
-
Install dependencies:
pip install -r requirements.txt
-
Retrieve the current text:
python3 text_client.py <server-ip>
-
Update the shared text:
python3 text_client.py <server-ip> "New shared text"
python3 client/text_client.py 192.168.1.1
Output:
Hello, World!
python3 client/text_client.py example.com "New shared text"
Output:
Text updated successfully.
cat text.txt | python3 client/text_client.py example.com -
Output:
Text updated successfully.
python3 client/text_client.py example.com > text.txt
Output:
Connecting to: wss://example.com:443/ws.
As of v3.2.0, this part is largely obsolete. One can simply point a reverse proxy to port 80 on the container and make sure websockets are enabled and it should work.
Ghostboard can function behind a reverse proxy, requiring only a single exposed port for both HTTP and WebSocket traffic. To configure your reverse proxy:
- Route
/
traffic to the HTTP server (<server-ip>:8080
). - Route
/ws
traffic to the WebSocket server (<server-ip>:8765
). - Route
/ws/
traffic to the WebSocket server (<server-ip>:8765
).
Example configuration for Nginx:
location / {
proxy_pass http://<server-ip>:8080;
}
location /ws {
proxy_pass http://<server-ip>:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /ws/ {
proxy_pass http://<server-ip>:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
error_page 404 /index.html;
- IP Addresses: If you provide an IP address (e.g.,
192.168.1.1
), the client defaults tows://<ip>:8765
. - Domain Names: If you provide a domain name (e.g.,
example.com
), the client defaults towss://<domain>:443/ws
.
Contributions are welcome! Feel free to open an issue or submit a pull request to improve the project.
This project is licensed under the MIT License.