Skip to main content
Markdown-OS uses WebSockets to push real-time file change notifications from the server to connected clients. This enables live updates when markdown files are modified externally.

Connection

WebSocket Endpoint

Protocol: WebSocket (RFC 6455) Authentication: None required Example Connection:
The server accepts the WebSocket connection immediately and adds the client to an internal broadcast hub.

Message Protocol

Client to Server

Clients can send any text message to keep the connection alive. The server does not process client messages but requires the connection to receive data to stay open. Keepalive Pattern:
The server ignores message content from clients. Sending messages is only necessary to prevent connection timeouts.

Server to Client

The server sends JSON messages when markdown files are modified externally by other processes or editors.

File Changed Event

Broadcast when a watched markdown file is modified, created, or moved. Message Type: file_changed File Mode Fields:
string
required
Always "file_changed"
string
required
Updated markdown content of the file
Folder Mode Fields:
string
required
Always "file_changed"
string
required
Relative path of the changed file
string
Updated markdown content (included if the file is readable)

Examples

File Mode

When running in single-file mode, the server watches one markdown file and broadcasts its content when changed. Server Message:
Client Handler:

Folder Mode

When running in folder mode, the server watches all markdown files in the directory tree and broadcasts changes with the file path. Server Message (with content):
Server Message (without content): If the file cannot be read, content is omitted:
Client Handler:

Event Triggers

The server uses Watchdog to monitor file system events. Monitored Events:
  • File modified (content changed)
  • File created (new markdown file)
  • File moved (renamed or relocated)
Filtering:
File Mode: Only changes to the target file trigger notifications.Folder Mode: Only markdown files (.md, .markdown) within the workspace trigger notifications.
Throttling: Events are throttled to 200ms intervals to prevent notification spam during rapid edits. Self-Write Suppression: The server ignores file system events for 500ms after processing a POST /api/save request to avoid echoing the client’s own saves.

Connection Lifecycle

Connection Established

  1. Client initiates WebSocket handshake to /ws
  2. Server accepts connection via websocket.accept()
  3. Server adds client to internal WebSocketHub
  4. Client is now subscribed to all file change broadcasts

Connection Maintenance

The server keeps the connection open by continuously awaiting messages:
Clients should send periodic keepalive messages or rely on browser/library defaults.

Connection Closed

Client-initiated close:
  1. Client closes WebSocket connection
  2. Server receives WebSocketDisconnect exception
  3. Server removes client from WebSocketHub
Server-initiated close:
  1. Server detects stale connection during broadcast
  2. Server catches RuntimeError when sending fails
  3. Server removes client from WebSocketHub
There is no explicit close handshake or goodbye message. Clients are silently removed when disconnected.

Broadcasting

The server uses a fan-out pattern to broadcast messages to all connected clients.

Broadcast Flow

  1. File system event detected by Watchdog observer
  2. Callback scheduled on event loop via loop.call_soon_threadsafe()
  3. Validation checks if event should be ignored (self-write, wrong file, throttled)
  4. Content read from disk (skipped if read fails)
  5. JSON payload constructed with type, file, and content
  6. Broadcast to all clients in WebSocketHub._clients set
  7. Stale clients removed if send fails with RuntimeError

Concurrency

The WebSocketHub uses an asyncio.Lock to protect the client set during:
  • Adding new connections
  • Removing disconnected clients
  • Iterating for broadcasts

Error Handling

Client Errors

Connection refused:
Unexpected close:

Server Errors

File read failure: If the server cannot read a changed file, it broadcasts the event without content:
Clients should handle missing content by refetching via GET /api/content. Broadcast failure: If sending to a client fails, the server silently removes that client and continues broadcasting to others.

Complete Example

React Hook

Python CLI Monitor


Technical Details

Implementation

  • Framework: FastAPI WebSocket support
  • File watcher: Watchdog library
  • Concurrency: asyncio with thread-safe event scheduling
  • Hub pattern: Single WebSocketHub manages all client connections

Performance

  • Broadcast latency: Typically < 50ms from file system event to client delivery
  • Scalability: Tested with 100+ concurrent WebSocket connections
  • Memory: ~1KB per connected client

Limitations

  • No message acknowledgment or delivery guarantees
  • No replay/history of missed events
  • No selective subscriptions (all clients receive all events)
  • File mode only watches a single file; folder mode watches all .md files

See Also