MEMAR How-To Guide

Everything you need to run your fleet.

Get running in minutes.

Pick your install method. Docker is the fastest path; bare-metal is recommended for production.

# Clone and start
git clone https://github.com/stratum-industries-foss/memar
cd memar
cp .env.example .env
# Edit .env — set MEMAR_JWT_SECRET at minimum:
# MEMAR_JWT_SECRET=$(openssl rand -hex 32)
docker compose up -d
# Get the auto-generated admin password:
docker compose logs memar | grep -i password

MEMAR listens on :8000 (mapped from internal :9000).

# Bare-metal / systemd — no Docker
cd ~/apps/memar/backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cd ../frontend && npm install && npm run build

# Set MEMAR_JWT_SECRET (required) in /etc/memar.env — SMTP and Stripe
# credentials are optional and only needed if you use password-reset email
# or the built-in billing routes.
sudo systemctl enable --now memar   # your own unit file, pointing at
                                     # 'uvicorn app.main:app' in backend/.venv

The installer validates health at /api/healthz and auto-rolls back on failure.

# Backend
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
MEMAR_JWT_SECRET=$(openssl rand -hex 32) \
  uvicorn app.main:app --port 9000 --reload

# Frontend — separate terminal
cd frontend
npm install
npm run dev  # dev server at http://localhost:5173

Backend at :9000, Vite dev server at :5173 with HMR.

First-run checklist

1

Open the UI

Navigate to http://localhost:8000 (or your configured MEMAR_PUBLIC_URL).

2

Log in as admin

Admin password is printed in container logs on first run. Username: admin (or MEMAR_ADMIN_USER).

3

Add your first device

Settings → Devices → Add device. See Adding Devices for the config format.

4

Activate your license

Free tier supports 5 devices. For more, see License Activation.

Adding devices.

Devices are defined in a YAML config file. The default path is config/devices.yaml — override with MEMAR_CONFIG_PATH.

# config/devices.yaml
printers:
  - id: voron-1
    name: "Voron 2.4"
    protocol: moonraker
    base_url: http://192.168.1.100:7125
    api_key: null          # optional — required for some protocols
    machine_type: fdm
    enabled: true
    tags: [production, bay-1]

  - id: my-cnc
    name: "Shapeoko Pro"
    protocol: grbl
    base_url: tcp://192.168.1.101:23
    machine_type: cnc_router
    enabled: true

Config fields

FieldTypeDescription
idstringUnique device identifier — used in webhook payloads and API paths
namestringHuman-readable display name
protocolstringAdapter protocol key — see Adapter Reference below
base_urlstringDevice endpoint URL (http://, tcp://, or opc.tcp:// depending on adapter)
api_keystring | nullAuthentication credential — adapter-specific; null if not required
machine_typestringOne of the machine types listed below
enabledboolWhether MEMAR polls this device; false = paused, not deleted
tagsstring[]Optional labels for grouping and filtering in the dashboard

Valid machine_type values

fdmslaidexcnc_millcnc_lathecnc_routerlaserplasmawaterjetvinylpick_and_placeembroiderywire_edm

18 protocol adapters — 6 proven, 12 alpha.

Each adapter translates machine-native protocols into MEMAR's unified state model. Set protocol: in your device config to the value in the card header. Proven adapters are battle-tested on real 3D-printer fleets; alpha adapters are real, working code not yet verified against every real machine.

Proven moonraker FDMIDEXSLA

Moonraker

required base_url
notes api_key optional (X-Api-Key header)
Proven octoprint FDM

OctoPrint

required base_url, api_key
notes X-Api-Key header
Proven prusa_connect FDM

Prusa Connect

required base_url, api_key
notes Cloud API; base_url is your printer UUID endpoint. Requires internet — not air-gap compatible.
Proven bambu_mqtt FDM

Bambu Labs

required base_url, serial, api_key
notes LAN mode required; api_key is 8-digit access code; MQTT TLS :8883. Unofficial, reverse-engineered protocol — can shift across Bambu firmware updates.
Proven marlin FDM

Marlin / ESP3D

required base_url, api_key
notes HTTP gcode bridge; api_key: esp3d or repetier:{token}
Proven duet FDMCNC

Duet / RRF

required base_url
notes RepRap firmware HTTP API
Proven fluidnc CNCFDM

FluidNC

required base_url
notes HTTP gcode bridge
Alpha mqtt Any

Generic MQTT

required mqtt_topic, mqtt_field_mapping
notes Custom topic schema for any MQTT-capable device — you write the field mapping
Alpha grbl CNCLaserPlasma

GRBL

required base_url
notes Supports http://, tcp://, or bare host (TCP :23)
Alpha linuxcnc CNC

LinuxCNC

required base_url
notes Requires an external REST bridge (e.g. linuxcnc-rest) — not shipped by MEMAR
Alpha mach3 CNC

Mach3

required base_url
notes Requires an external REST bridge — not shipped by MEMAR
Alpha mach4 CNC

Mach4

required base_url
notes Targets a hypothetical REST bridge — unverified against real hardware
Alpha opcua Industrial CNC

OPC-UA

required base_url
notes Format: opc.tcp://host:port/endpoint. Ships with placeholder NodeIds — reads nothing until you map them to your server.
Alpha haas CNC Mill

Haas

required base_url
notes Targets a hypothetical REST bridge — unverified against real hardware
Alpha fanuc Industrial CNC

FANUC

required base_url
notes Targets a hypothetical FOCAS HTTP bridge — unverified against real hardware
Alpha sinumerik Industrial Mill

Siemens Sinumerik

required base_url
notes Requires an external REST bridge — not shipped by MEMAR
Alpha fadal CNC Mill (VMC)

Fadal CNC

required base_url
notes Targets a hypothetical Ethernet gateway / DNC bridge — unverified against real hardware
Alpha openpcb Pick-and-Place

OpenPnP

required base_url
notes OpenPnP REST API at :8080 (configurable); no e-stop implementation yet

Activating your license.

Three methods — all hot-apply without a redeploy except the env var method.

1

Env var (recommended for Docker / systemd)

MEMAR_LICENSE_KEY=eyJhbGciOiJFZERTQSIs...

Restart the container or service after setting. The key is validated on startup and persists across restarts.

2

Admin UI (hot-apply)

Settings → License → paste key → Apply. Takes effect immediately — no restart required.

3

Auto-refresh heartbeat

Set MEMAR_LICENSE_HEARTBEAT_URL (defaults to the Stratum issuance server). MEMAR checks in hourly and auto-applies a fresh key each billing period. Air-gap installs must disable: set MEMAR_LICENSE_HEARTBEAT_URL="".

Device limits by tier

TierDevices
Free 5
Maker 15
Pro 40
Farm 150
Scale 500
Workshop 500
Business 2,000
Enterprise 10,000
Air-Gap 9,999

Free gets core monitoring and control. Every paid tier includes every MEMAR feature — the only differentiator between paid tiers is device capacity. View pricing →

Environment variable reference.

All configuration is via environment variables. Set them in .env (Docker) or /etc/memar.env (systemd).

Core 12
VariableDefaultDescription
MEMAR_JWT_SECRET (required) Session signing key. openssl rand -hex 32
MEMAR_ADMIN_USER admin Initial admin username
MEMAR_ADMIN_PASSWORD (random, logged) Initial admin password — only logged on first run
MEMAR_PUBLIC_URL http://localhost:9000 Public base URL — used in billing redirects and SSO callbacks
MEMAR_CONFIG_PATH config/devices.yaml Path to device config file
MEMAR_DATA_DIR data/ Root directory for all persisted data
MEMAR_DB_PATH data/memar.db SQLite database file path; overrides MEMAR_DATA_DIR location
MEMAR_CORS_ORIGINS (none) Extra CORS origins, comma-separated — useful behind a reverse proxy
MEMAR_OWNER_EMAIL (none) Owner contact shown in license validation responses
MEMAR_ENV production Environment tag: production or development
MEMAR_DEMO_MODE false Read-only demo mode — injects sample data
MEMAR_NO_AUTH false Disable JWT auth entirely — dev/testing only, never in production
Licensing 4
VariableDefaultDescription
MEMAR_LICENSE_KEY (none) Signed Ed25519 license key from Stratum
MEMAR_LICENSE_HEARTBEAT_URL Stratum worker Auto-refresh endpoint; set "" to disable. Air-gap installs must disable.
MEMAR_LICENSE_PUBLIC_KEY (embedded) Ed25519 public key — only override if running your own issuance server
MEMAR_LICENSE_PRIVATE_KEY (none) Ed25519 private key — only needed if you are the key issuance server
Billing 4
VariableDefaultDescription
MEMAR_STRIPE_SECRET_KEY (none) Stripe API key (sk_live_…)
MEMAR_STRIPE_PRICE_MAKER / _PRO / _FARM / _SCALE (placeholders) Monthly Stripe price IDs
MEMAR_STRIPE_PRICE_*_ANNUAL (placeholders) Annual Stripe price IDs
MEMAR_STRIPE_WEBHOOK_SECRET (none) ⚠ Do not set if using the Cloudflare Worker — causes double key issuance
Alerting & Integrations 5
VariableDefaultDescription
MEMAR_ALERT_EMAILS (none) Comma-separated email addresses for alert delivery
MEMAR_SMTP_HOST / _PORT / _USER / _PASSWORD / _FROM (none) SMTP credentials for email delivery
WEBHOOK_URL (none) Generic outbound webhook endpoint for fleet events
MEMAR_SLACK_WEBHOOK_URL (none) Slack incoming webhook URL for alerts
MEMAR_COOLANT_WEBHOOK_URL (none) Dedicated webhook for CNC coolant system alerts
Analytics 1
VariableDefaultDescription
MEMAR_FILAMENT_COST_PER_KG (none) Filament cost in USD/kg — enables cost tracking in analytics
SSO — OIDC 5
VariableDefaultDescription
MEMAR_OIDC_ISSUER (none) OIDC issuer URL (e.g. https://accounts.google.com)
MEMAR_OIDC_CLIENT_ID (none) OIDC client ID
MEMAR_OIDC_CLIENT_SECRET (none) OIDC client secret
MEMAR_OIDC_REDIRECT_URI (none) OAuth2 redirect URI — must match what is registered in the IdP
MEMAR_SSO_DEFAULT_ROLE viewer Role assigned to SSO users on first login
SSO — LDAP 3
VariableDefaultDescription
MEMAR_LDAP_SERVER (none) LDAP server URL (e.g. ldap://host:389)
MEMAR_LDAP_USER_DN_TEMPLATE (none) DN template with {username} placeholder
MEMAR_LDAP_DEFAULT_ROLE viewer Default role for authenticated LDAP users

Roles and access control.

MEMAR uses JWT (HS256) auth with three roles. Users are created via the admin UI. SSO roles map from OIDC claims or LDAP groups.

RoleCapabilities
admin Full access — device config, user management, license, audit logs, branding, SSO, webhooks
operator Device control — start, pause, resume, cancel jobs; send gcode; adjust temps; jog axes
viewer Read-only — live dashboard, metrics, job history, device status
SSO role mapping: OIDC users are assigned MEMAR_SSO_DEFAULT_ROLE on first login. LDAP users get MEMAR_LDAP_DEFAULT_ROLE. Override per-user in admin UI after first login.

Outbound event webhooks.

Configure a webhook URL and secret in admin UI → Webhooks. MEMAR signs every delivery with HMAC-SHA256. Queue: 500 events in-memory.

Events

EventFires when
printer.error Any device transitions to error state
printer.complete Print/job completes successfully
printer.offline Device stops responding to polling
printer.anomaly AI anomaly detection flags unusual behavior
cnc.spindle_overspeed Spindle RPM exceeds configured threshold
cnc.coolant_failure Coolant system failure detected
cnc.tool_change Tool change event
cnc.feed_rate_warning Feed rate anomaly detected
cnc.production_complete CNC production job completes

Payload format

{
  "type": "printer.complete",
  "printer_id": "voron-1",
  "data": {
    "state": "complete",
    "timestamp": "2026-06-26T14:22:01Z"
  }
}

Signature verification (Python)

import hmac, hashlib

expected = "sha256=" + hmac.new(
    secret.encode(),
    payload,       # raw request body bytes
    hashlib.sha256
).hexdigest()

# Constant-time compare against the X-MEMAR-Signature header
assert hmac.compare_digest(expected, request.headers["X-MEMAR-Signature"])

Overall Equipment Effectiveness.

MEMAR calculates OEE per device over the last 100 jobs. Fleet OEE is the sample-size-weighted average across all active devices.

Availability
print_time ÷ scheduled_time
Good: > 85%

How much of planned runtime the machine was actually running. Downtime, maintenance, and changeovers reduce this metric.

Performance
estimated_time ÷ actual_time
Good: > 90%

How close actual cycle time was to the estimated time. Requires M73 progress commands in gcode — null if not present.

Quality
completed ÷ attempted
Good: > 95%

The ratio of successful completions to total attempts. Failed or cancelled jobs reduce quality score.

OEE
Availability × Performance × Quality
World-class: > 85%

OEE is null unless all three components are measurable. 85%+ is the industry benchmark for world-class manufacturing; 65%+ is typically considered good.

M73 note: The M73 gcode command reports estimated print time and progress percentage. Most modern slicers (PrusaSlicer, OrcaSlicer, SuperSlicer) can emit M73 — check your slicer's gcode settings. CNC jobs rarely have M73; Performance will be null for those devices.

Common issues.

Quick fixes for the most common problems. Check the GitHub issues if yours isn't here.

Adapter shows "offline" immediately

Wrong base_url, firewall block, or API key mismatch. Test the endpoint directly: curl -H "X-Api-Key: KEY" http://host:port/api/info

License key not activating

Verify the full base64 string — no line breaks or trailing spaces. Check: docker compose logs memar | grep license

OEE shows null

Performance requires M73 progress commands in gcode. Enable "Include M73 P/R commands" in your slicer, or accept null until enough M73-enabled jobs complete.

Bambu Labs not connecting

LAN mode must be enabled in the printer's network settings. Use the 8-digit access code from the printer display, not your account password.

GRBL adapter stuck in connecting

If using TCP mode, test: telnet host 23. Ensure a GRBL bridge is running (FluidNC, CNCjs, UGS) on the target host.

Heartbeat fails after purchasing

MEMAR_LICENSE_HEARTBEAT_URL must be reachable from the host. Air-gap installs: set MEMAR_LICENSE_HEARTBEAT_URL="" in .env to disable.

Admin password not in logs

Printed only on first run (when the database is initialized). If the DB already exists, set MEMAR_ADMIN_PASSWORD in .env to override.

Webhook not firing

Queue is 500 events in-memory. Check admin UI → Webhooks → Delivery Log. Verify your endpoint returns HTTP 2xx within 10 seconds.