docs: add LaunchDaemon architecture reference for M5Max128/M5Max48 collaboration

This commit is contained in:
M5Max128
2026-05-27 01:12:37 +08:00
parent 6967b99142
commit 955282e587

View File

@@ -0,0 +1,421 @@
---
title: LaunchDaemon Architecture (M5Max128 Reference)
version: 1.0
date: 2026-05-27
author: M5Max128
status: reference
---
# LaunchDaemon Architecture Reference
> **Scope**: M5Max128 local configuration (resource-managed binaries)
> **Note**: M5Max48 uses build-from-source approach via start_momentry.sh. Both approaches are valid and independent.
## Overview
| Machine | Approach | Status |
|---------|----------|--------|
| M5Max128 | LaunchDaemon + resource binaries | Reference document |
| M5Max48 | start_momentry.sh + build from source | Main branch |
## Architecture Principles
```
/Library/LaunchDaemons/ (system-level, boot before login)
├── com.momentry.postgresql.plist (P1, no dependency)
├── com.momentry.redis.plist (P1, no dependency)
├── com.momentry.qdrant.plist (P2, no dependency)
├── com.momentry.mongodb.plist (P2, no dependency)
└── com.momentry.gitea.plist (P3, depends on PostgreSQL)
Experimental services:
└── com.momentry.startup.plist (LLM, Embedding, Playground, etc.)
```
## Key Design Points
### 1. Binary Location
All binaries are resource-managed under `/Users/accusys/momentry_resources/bin/`:
| Service | Binary Path |
|---------|-------------|
| PostgreSQL | `/Users/accusys/pgsql/18.3/bin/postgres` |
| Redis | `/Users/accusys/momentry_resources/bin/redis-server` |
| Qdrant | `/Users/accusys/momentry_resources/bin/qdrant` |
| MongoDB | `/Users/accusys/momentry_resources/bin/mongod` |
| Gitea | `/Users/accusys/momentry_resources/bin/gitea` |
### 2. Root Boot → User Execution
LaunchDaemons run at boot (root), but use `UserName` key to switch to user:
```xml
<key>UserName</key>
<string>accusys</string>
```
### 3. Unified Log Path
All logs go to `/Users/accusys/momentry/logs/`:
```xml
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/<service>.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/<service>.error.log</string>
```
## Plist Templates
### PostgreSQL
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.postgresql</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/postgresql</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/pgsql/18.3/bin/postgres</string>
<string>-D</string>
<string>/Users/accusys/momentry/var/postgresql</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/postgresql.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/postgresql.error.log</string>
</dict>
</plist>
```
### Redis (ACL Authentication)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.redis</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/redis</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_resources/bin/redis-server</string>
<string>--port</string>
<string>6379</string>
<string>--bind</string>
<string>0.0.0.0</string>
<string>--aclfile</string>
<string>/Users/accusys/momentry/etc/redis/users.acl</string>
<string>--dir</string>
<string>/Users/accusys/momentry/var/redis</string>
<string>--logfile</string>
<string>/Users/accusys/momentry/logs/redis.log</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/redis.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/redis.error.log</string>
</dict>
</plist>
```
### Redis ACL File
Location: `/Users/accusys/momentry/etc/redis/users.acl`
```
user default on sanitize-payload ~* &* +@all >accusys
user accusys on sanitize-payload ~* &* +@all >accusys
```
**Redis 8.x Authentication**:
```bash
# Old (deprecated): redis-cli -a accusys ping
# New (recommended): redis-cli --user default --pass accusys ping
```
### Qdrant
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.qdrant</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/qdrant/</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_resources/bin/qdrant</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>QDRANT__STORAGE__STORAGE_PATH</key>
<string>/Users/accusys/momentry/var/qdrant/</string>
<key>QDRANT__SERVICE__HOST</key>
<string>0.0.0.0</string>
<key>QDRANT__SERVICE__HTTP_PORT</key>
<string>6333</string>
<key>HOME</key>
<string>/Users/accusys</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/qdrant.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/qdrant.error.log</string>
</dict>
</plist>
```
### MongoDB
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.mongodb</string>
<key>UserName</key>
<string>accusys</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_resources/bin/mongod</string>
<string>--dbpath</string>
<string>/Users/accusys/momentry/var/mongodb</string>
<string>--logpath</string>
<string>/Users/accusys/momentry/logs/mongodb.log</string>
<string>--port</string>
<string>27017</string>
<string>--bind_ip</string>
<string>0.0.0.0</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/mongodb.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/mongodb.error.log</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/mongodb</string>
</dict>
</plist>
```
### Gitea (with Wrapper Script)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.gitea</string>
<key>UserName</key>
<string>accusys</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry/var/gitea</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_core/scripts/start_gitea.sh</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/accusys</string>
<key>GITEA_WORK_DIR</key>
<string>/Users/accusys/momentry/var/gitea</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/logs/gitea.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/logs/gitea.error.log</string>
</dict>
</plist>
```
## Wrapper Script: start_gitea.sh
Gitea depends on PostgreSQL. Wrapper script ensures PostgreSQL is ready:
```bash
#!/bin/bash
PG_BIN="/Users/accusys/pgsql/18.3/bin"
GITEA_BIN="/Users/accusys/momentry_resources/bin/gitea"
GITEA_CONFIG="/Users/accusys/momentry/etc/gitea/app.ini"
MAX_WAIT=60
WAITED=0
# Wait for PostgreSQL
while ! "$PG_BIN/pg_isready" -q 2>/dev/null; do
if [ $WAITED -ge $MAX_WAIT ]; then
echo "ERROR: PostgreSQL not ready after $MAX_WAIT seconds"
exit 1
fi
sleep 2
WAITED=$((WAITED + 2))
done
# Start Gitea
"$GITEA_BIN" web --config "$GITEA_CONFIG"
```
## Install Script: install_launchdaemons.sh
```bash
#!/bin/bash
PLIST_DIR="/Users/accusys/momentry_core/momentry_runtime/plist"
DAEMON_DIR="/Library/LaunchDaemons"
LOG_DIR="/Users/accusys/momentry/logs"
mkdir -p "$LOG_DIR"
DAEMONS=(
"com.momentry.postgresql"
"com.momentry.redis"
"com.momentry.qdrant"
"com.momentry.mongodb"
"com.momentry.gitea"
)
for daemon in "${DAEMONS[@]}"; do
plist_name="${daemon}.plist"
src="${PLIST_DIR}/${plist_name}"
dest="${DAEMON_DIR}/${plist_name}"
if launchctl list "$daemon" >/dev/null 2>&1; then
sudo launchctl unload -w "$dest" 2>/dev/null
fi
sudo cp "$src" "$dest"
sudo chown root:wheel "$dest"
sudo chmod 644 "$dest"
sudo launchctl load -w "$dest"
done
```
## Comparison: M5Max128 vs M5Max48
| Aspect | M5Max128 | M5Max48 |
|--------|----------|---------|
| **Approach** | LaunchDaemon (system-level) | start_momentry.sh (user script) |
| **Binaries** | Resource-managed (`momentry_resources/bin/`) | Build from source (`services/*/target/`) |
| **PostgreSQL data** | `/Users/accusys/momentry/var/postgresql` | `/Users/accusys/pgsql/data` |
| **Redis auth** | ACL file (`users.acl`) | `--requirepass` (deprecated) |
| **LLM path** | Resource binary | `/Users/accusys/llama/bin/` |
| **Gitea** | Independent LaunchDaemon | Not in startup script |
| **MongoDB** | Independent LaunchDaemon | Not in startup script |
## Installation Steps (M5Max128)
```bash
# 1. Ensure directories exist
mkdir -p /Users/accusys/momentry/logs
mkdir -p /Users/accusys/momentry/var/{postgresql,redis,qdrant,mongodb,gitea}
# 2. Install LaunchDaemons (requires sudo)
sudo /Users/accusys/momentry_core/scripts/install_launchdaemons.sh
# 3. Verify services
/Users/accusys/pgsql/18.3/bin/pg_isready
/Users/accusys/momentry_resources/bin/redis-cli --user default --pass accusys ping
curl http://localhost:6333/healthz
curl http://localhost:3000/
# 4. Reboot test
sudo reboot
# 5. Post-reboot verification
launchctl list | grep com.momentry
```
## Notes
1. **Independence**: M5Max128's LaunchDaemons do not conflict with M5Max48's startup script. Each machine has its own approach.
2. **Resource Management**: M5Max128 uses pre-built binaries from `momentry_resources/bin/`, avoiding build dependencies.
3. **Redis ACL**: Redis 8.x uses ACL authentication, not `--requirepass`. This is the modern approach.
4. **Gitea Wrapper**: Essential because Gitea depends on PostgreSQL. The wrapper ensures PostgreSQL is ready before starting Gitea.
---
## Version History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2026-05-27 | M5Max128 | Initial reference document |