Architecture

MicroLog is split across two deployment targets that communicate over the internet. No log data is stored on the cloud server — only authentication metadata lives there. All logs land on your local machine.

Deployment targets

  • Server (cloud VPS) — runs mosquitto, ingest-go, and hub. Holds a SQLite DB for auth and alert rules only.
  • Local PC — runs collector and dashboard. All logs land in ./local-logs/<projectId>/<serverId>/YYYY-MM-DD.log as NDJSON.
  • Monitored server(s) — runs agent-go as a Docker container. Can be the same machine as the cloud VPS or any other server.

Data flow

agent-go → [MQTT TLS :8883] → mosquitto → [MQTT plain :1883] → ingest-go ↓ validates API key (SQLite) ↓ republishes to live/{projectId}/{serverId}/{kind} hub ← [MQTT plain :1883] ←──────────────────────────────────────── ↓ buffers last 600 metric points per server ↓ evaluates alert rules every 30 s ↓ detects offline servers every 5 s (15 s threshold) dashboard ← [WebSocket :4001] ← hub collector ← [WebSocket :4001] ← hub → writes NDJSON to ./local-logs/

MQTT topics follow the pattern raw/{serverId}/{kind} (agent → ingest) and live/{projectId}/{serverId}/{kind} (ingest → hub → subscribers). The kind is one of metrics, heartbeat, logs, or containers.

Components

agent-go

Go monitored server

Runs as a Docker container on every server you want to monitor. Reads /host/proc for CPU, memory, load, disk I/O, and network stats. Tails /host/log/syslog and auth.log. Optionally polls the Docker socket for container state. Publishes all events to mosquitto over MQTT with TLS.

Source: apps/agent-go/  ·  MQTT topic: raw/{serverId}/{kind}

mosquitto

eclipse-mosquitto:2.0 server

MQTT broker. Exposes two listeners: :8883 (external, TLS) for agents, and :1883 (internal Docker network, plain) for ingest and hub. Access is controlled via a password file and an ACL file that grants agents write-only access to raw/# and restricts hub to read-only on live/#.

ingest-go

Go server

Subscribes to raw/# on mosquitto. For each message, validates the serverId against SQLite (with a 60 s in-memory cache). On success, injects projectId into the payload and republishes to live/{projectId}/{serverId}/{kind}. Rejected messages are silently dropped.

Source: apps/platform/ingest-go/

hub

Bun server · :4001

Central broker and HTTP API. Subscribes to live/# via MQTT. Buffers the last 600 metric points per server in memory. Runs alert rules from DB every 30 s. Detects offline servers every 5 s (15 s heartbeat threshold). Broadcasts all events to connected dashboard and collector clients over WebSocket.

Also serves: / (landing), /docs, /register, /api/login/request, /api/login/poll, /magic, /servers, /agent-install.sh, /agent-bundle.tar.gz.

collector

Bun local PC

Connects to the hub as a WebSocket client. Writes every received event to NDJSON log files at ./local-logs/{projectId}/{serverId}/YYYY-MM-DD.log. Runs indefinitely and handles hub reconnection automatically.

Source: apps/platform/collector/collector.ts

dashboard

Vanilla JS/HTML local PC · :5178

Static frontend served by bun x serve. Connects to hub via WebSocket using a JWT obtained via magic-link login (/api/login/request + /api/login/poll). Displays real-time charts (CPU, memory, disk, network), container status cards, and a live log stream. No build step — plain JS files.

Source: apps/dashboard/frontend/

Environment variables

agent-go

VariableDefaultDescription
API_KEY—Required. API key assigned to this server in the DB.
SERVER_ID—Required. UUID of this server in the DB.
MQTT_BROKERssl://gramastudio.cl:8883MQTT broker address for the agent.
MQTT_AGENT_PASSagent-secretPassword for the shared agent MQTT user.
PROC_ROOT/host/procMount point for the host /proc filesystem.
LOG_ROOT/host/logMount point for the host /var/log filesystem.
DOCKER_CONTAINERS—all or comma-separated container names to monitor.
DOCKER_SOCKET/var/run/docker.sockPath to the Docker Unix socket inside the container.

ingest-go

VariableDefaultDescription
MQTT_BROKERtcp://mosquitto:1883Internal broker address.
MQTT_USERingestMQTT username for ingest.
MQTT_PASS—Required. MQTT password for ingest.
SQLITE_PATH/app/data/microlog.dbPath to the SQLite database.
AUTH_DEBUG0Set to 1 to log auth decisions.

hub

VariableDefaultDescription
HUB_PORT4001HTTP/WebSocket port.
SQLITE_PATHauto-detectedResolved in order: env → /app/data/microlog.db → ./platform/database/microlog.db.
MQTT_BROKERmqtt://mosquitto:1883Internal broker address.
MQTT_HUB_USERhubMQTT username for hub.
MQTT_HUB_PASS—MQTT password for hub.
HUB_WRITE_ALERTStrueSet to false in production to skip writing alert log files.
RESEND_API_KEY—Optional. Enables email notifications via Resend.

collector

VariableDefaultDescription
HUB_URLws://localhost:4001WebSocket URL of the hub.
PROJECT_ID—Required. UUID of the project to subscribe to.
LOG_ROOT./local-logsRoot directory for NDJSON log files.

Admin CLI

All commands run with bun run apps/platform/admin/cli.ts <command> [flags].

# Bootstrap: create user + project + server in one step bun run apps/platform/admin/cli.ts bootstrap \ --email admin@example.com \ --password secret \ --project-name Core \ --server-name prod-1 # Invite tokens (used for self-registration via /register) bun run apps/platform/admin/cli.ts create-invite --base-url http://gramastudio.cl:4001 # Individual resource creation bun run apps/platform/admin/cli.ts create-user --email <email> --password <pass> bun run apps/platform/admin/cli.ts create-project --user-id <uuid> --name "Core" bun run apps/platform/admin/cli.ts create-server --project-id <uuid> --name "prod-1" --hostname api-1 bun run apps/platform/admin/cli.ts create-alert --project-id <uuid> --rule cpu --threshold 90 --duration 30 # List resources bun run apps/platform/admin/cli.ts list-users bun run apps/platform/admin/cli.ts list-projects bun run apps/platform/admin/cli.ts list-servers bun run apps/platform/admin/cli.ts list-alerts

Alert --rule values match metric keys from the agent payload: cpu, memory, diskUsage, load, etc. The special rule offline triggers on heartbeat timeout (no threshold required).

Scripts

All scripts live in scripts/, load a .env file from the project root if present, and prompt interactively for any missing required values. Run them from the root of the repo.

start-server.sh

VPS — server stack

Launches mosquitto + ingest-go + hub via infra/docker-compose.server.yml. Before starting, checks that the three TLS certificate files (chain.pem, fullchain.pem, privkey.pem) exist in infra/mosquitto/certs/ and aborts with instructions if any are missing. Prompts interactively for MOSQUITTO_INGEST_PASS and MOSQUITTO_HUB_PASS.

Accepts -d to run in background.

init-mosquitto.sh

VPS — run once

Creates the ingest, hub and agent MQTT users inside the running mosquitto container and reloads the password file. Run after start-server.sh on first deploy, or whenever passwords change. Prompts for the three passwords interactively.

The agent password set here must match the MQTT_AGENT_PASS used in start-agent.sh.

start-agent.sh

Monitored server

Builds the Go agent image from apps/agent-go/ (requires the repo to be present on the server), removes any existing microlog-agent container, and starts a new one. Prompts for API_KEY, SERVER_ID, MQTT broker, agent password, and optionally which Docker containers to monitor.

Default broker: ssl://gramastudio.cl:8883. Accepts -d to run in background.

start-dashboard.sh

Local PC

Launches collector + dashboard via infra/docker-compose.local.yml. Prompts for HUB_URL (default: ws://log.gramastudio.cl:4001), PROJECT_ID, and optionally an auth token. The collector writes NDJSON logs to ./local-logs/; the dashboard is served at http://localhost:5178.

Accepts -d to run in background.

Self-hosted setup sequence

# 1. On your VPS — one command sets up everything bash <(curl -s http://gramastudio.cl:4001/server-install.sh) # 2. On each monitored server HUB_HTTP=http://<your-vps-ip>:4001 bash <(curl -s http://<your-vps-ip>:4001/agent-install.sh) # 3. On your local PC HUB_HTTP=http://<your-vps-ip>:4001 HUB_WS=ws://<your-vps-ip>:4001 bash <(curl -s http://<your-vps-ip>:4001/local-install.sh)

Docker Compose

FileTargetServices
infra/docker-compose.server.yml Cloud VPS mosquitto, ingest (Go), hub (Bun)
infra/docker-compose.local.yml Local PC collector, dashboard

Database schema

The SQLite schema lives in apps/platform/database/schema.sqlite.sql and is auto-applied on first run if the users table is absent. Tables: users → projects → servers (holds api_key), alerts, invite_tokens, config.