Skip to content

MMMarcinho/mesync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mesync

Lightweight, filesystem-native cross-machine message sync daemon.

No Redis. No RabbitMQ. Just files and HTTP. Single static binary.

Architecture

A single machine can run both roles simultaneously — the hub and its own client share the same process boundary cleanly:

                        ┌──────────────────────────────────┐
                        │             Machine 1             │
                        │                                   │
                        │   ┌─────────────┐ ┌───────────┐  │
                        │   │  Hub server │ │  Daemon   │  │
                        │   │ (mesync     │◄►│ (mesync   │  │
                        │   │  serve)     │ │  start)   │  │
                        │   └──────┬──────┘ └───────────┘  │
                        └──────────┼───────────────────────┘
                                   │
                   ┌───────────────┼───────────────┐
                   ▼                               ▼
        ┌──────────────────┐           ┌──────────────────┐
        │    Machine 2     │           │    Machine 3     │
        │  (mesync start)  │           │  (mesync start)  │
        └──────────────────┘           └──────────────────┘

How it works:

  • Machine 1 runs mesync serve (hub) and mesync start (client) at the same time
  • Machine 1's client points MESYNC_SERVER_URL at http://localhost:8080 — it syncs with its own hub
  • Machines 2 and 3 point at Machine 1's hub IP and sync the same way
  • A broadcast from any machine reaches all others except itself
  • A targeted message (--target machine-2) goes only to that host

How the daemon works

Each client machine polls every 5 seconds:

  1. Outbox scan — JSON files in ~/.daemon/outbox/ are sent to the hub server, then archived to sent/ (or failed/ on error).
  2. Inbox pull — New messages from the server are written to ~/.daemon/inbox/ and a matching hook script is executed if present.
  3. Cleanup — Expired files in sent/ are removed automatically.
~/.daemon/
├── config.toml   # optional configuration file
├── outbox/       # local → server (pending)
├── inbox/        # server → local (received)
├── sent/         # archived successful sends
├── failed/       # failed sends (retry manually)
└── hooks/        # executable scripts triggered on inbox messages

Install

cargo build --release
# binary at ./target/release/mesync
sudo cp target/release/mesync /usr/local/bin/

Quick start

On the hub machine (Machine 1):

# Start the hub server in the background
mesync serve &

# Start the daemon (connects to the local hub)
export MESYNC_SERVER_URL=http://localhost:8080
mesync start

On each additional client machine (Machine 2, 3 ...):

export MESYNC_SERVER_URL=http://machine1:8080
mesync start

Send a message from any script:

mesync send task.event --payload '{"job": "build", "repo": "myapp"}'
mesync send notify.alert --priority high --target machine-2 --payload '{"msg": "disk full"}'

Inspect queues:

mesync list outbox
mesync list inbox
mesync list failed
mesync retry          # re-queue failed messages
mesync flush sent     # clear sent archive

Configuration

Settings are resolved in this order (highest priority first):

  1. Environment variable
  2. ~/.daemon/config.toml
  3. Built-in default

config.toml

Place at ~/.daemon/config.toml (or $MESYNC_BASE_DIR/config.toml):

# Hub server to connect to
server_url = "http://hub.internal:8080"

# Override the hostname used for message routing
# host_id = "my-machine"

# Seconds between daemon poll cycles (default: 5)
poll_interval = 10

# How long before sent/ files are deleted, in seconds (default: 86400 = 24h)
sent_ttl = 3600

# HTTP request timeout in seconds for send/pull/ack calls (default: 10)
http_timeout = 15

Environment variables

Variable Default Description
MESYNC_SERVER_URL http://localhost:8080 Hub server URL
MESYNC_HOST_ID system hostname Identity for message routing
MESYNC_BASE_DIR ~/.daemon Root directory for queues and config
MESYNC_POLL_INTERVAL 5 Seconds between daemon ticks
MESYNC_SENT_TTL 86400 Seconds before sent files are purged
MESYNC_HTTP_TIMEOUT 10 HTTP request timeout in seconds

Message format

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "task.event",
  "source": "hostname",
  "target": null,
  "priority": "normal",
  "created_at": "2024-01-15T10:30:00.123456+00:00",
  "payload": {}
}
Field Values Notes
priority high / normal / low Controls send order within a poll cycle
target hostname or null null broadcasts to all registered hosts

Hook execution

Place an executable file at ~/.daemon/hooks/<message.type>. It receives the full message JSON on stdin:

#!/bin/bash
# ~/.daemon/hooks/task.event
msg=$(cat)
echo "$msg" | jq .payload | my-worker

Design notes

  • Atomic writes — messages are written via rename(2) so no partial files are ever visible.
  • Idempotent delivery — UUID per message; inbox rejects duplicates before ack'ing.
  • Ordered delivery — files named YYYYMMDDTHHmmss_<uuid>.json for natural sort order.
  • Single binarycargo build --release produces one self-contained executable.

About

Lightweight, filesystem-native cross-machine message sync daemon

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages