Media Lab Server
The local HTTP server (`127.0.0.1:8765`) for image and music generation. Architecture, API, lifecycle, and integration with the Coordinator.
Status
Implemented. The server is a standalone process that the Coordinator calls through HTTP. It runs on the local host and is not exposed to the public internet.
Purpose
The Media Lab Server is a small HTTP service that exposes the lab's image and music generation capabilities through a web UI and an HTTP API. The Coordinator calls the API to generate images and music in response to user requests; the user can also visit the web UI directly in a browser.
This page documents the server's architecture, the endpoints, the lifecycle, and the integration with the Coordinator.
Why a server, not a CLI tool
A server is preferred over a CLI tool for generation because:
- Stateful UI. A web UI can show progress, history, and downloads.
- Long-running requests. Generation can take 30 seconds to 3 minutes; an HTTP request fits naturally.
- Model host isolation. The server can be restarted independently of the Coordinator.
- Multiple clients. The user can hit the UI from a browser while the Coordinator also calls the API.
The server is the only HTTP server in the lab that exposes generation capabilities.
Listening address and port
| Field | Value | Reason |
|---|---|---|
| Host | 127.0.0.1 | Localhost only; not exposed to the LAN or WAN. |
| Port | 8765 | Reserved for the Media Lab Server. |
| Protocol | HTTP/1.1 | Simple; no TLS needed because it is localhost. |
The server is bound to localhost only. It is not
reachable from the LAN or the internet. The user can
access the UI at http://127.0.0.1:8765/ in a browser
on the same machine.
Architecture
The server is a Python process. The architecture:
The server has:
- An HTTP API that accepts job requests.
- A web UI that submits jobs and shows progress.
- A job queue that serializes generation requests.
- An image worker that calls the image model.
- A music worker that calls the music model.
- An output directory where results are written.
Endpoints
GET /api/status
Returns the server's health and current load.
Response (200):
POST /api/image
Submits an image generation job.
Request:
Response (202):
POST /api/music
Submits a music generation job.
Request:
Response (202):
GET /api/jobs/<id>
Polls the status of a job.
Response (200) for a completed image job:
Response (200) for a completed music job:
Response (200) for a running job:
GET /api/jobs
Lists recent jobs. Pagination through ?limit=N&offset=M.
Response:
GET /outputs/<file>
Returns the generated file. Static endpoint; the file path is the file name in the output directory.
GET /
The web UI. Single-page application that submits jobs and shows progress.
Job lifecycle
A job goes through these states:
| Status | Meaning |
|---|---|
pending | The job is in the queue; no worker has picked it up. |
running | A worker is generating the output. |
completed | The output is ready; outputs field is populated. |
failed | Generation failed; error field is populated. |
The state machine is in-process; the queue is an in-memory list. If the server is restarted, the queue is lost. Pending jobs are re-submitted by the client.
Image generation
The image worker calls the model provider's image API.
The default model is image-01. The supported sizes:
| Size | Use case |
|---|---|
512x512 | thumbnails, small previews |
1024x1024 | default, square images |
1536x1024 | landscape images |
1024x1536 | portrait images |
2048x2048 | high-resolution images |
The image is returned in PNG by default. The output is
written to the output directory as
img_<job_id>_<index>.png.
The average generation time is:
| Size | Average duration |
|---|---|
512x512 | 8-12 s |
1024x1024 | 12-18 s |
1536x1024 | 18-25 s |
2048x2048 | 30-45 s |
The duration depends on the provider's load.
Music generation
The music worker calls the provider's music API. The
default model is Music-2.6. The supported parameters:
| Parameter | Default | Range | Notes |
|---|---|---|---|
duration_s | 60 | 1 to 300 | Provider caps at 5 min |
instrumental | true | bool | If false, lyrics required |
lyrics | (none) | string | Required if instrumental: false |
temperature | 1.0 | 0.0 to 2.0 | Higher = more creative |
seed | (none) | integer | Reproducible outputs |
The output is MP3 by default. The output is written to
the output directory as mus_<job_id>_<index>.mp3.
The average generation time:
| Duration | Average duration |
|---|---|
| 30 s | 30-60 s |
| 60 s | 60-120 s |
| 120 s | 120-240 s |
| 300 s | 300-600 s |
Quota management
The server tracks the daily quota for image and music generation. The quota is reset at midnight UTC. The default quotas are:
| Resource | Daily quota | Source |
|---|---|---|
| Image | 50 images/day | Plan default |
| Music | 100 songs/day | Plan default |
The server rejects jobs that would exceed the quota with
429 Too Many Requests and a JSON body:
The Coordinator checks the quota before submitting a job to avoid wasted round-trips.
Output directory
The server writes outputs to
~/.openclaw/media/outbound/ by default. The
Coordinator can configure the path through the
MEDIA_LAB_OUTPUT_DIR environment variable.
The directory structure:
Old files are cleaned up by a retention job that runs daily at 03:00 local time. The default retention is 30 days.
Lifecycle
The server is supervised by launchd (macOS) or systemd (Linux). The supervisor's responsibilities:
- Start on boot. The server starts when the user logs in.
- Restart on crash. The supervisor restarts the server if it crashes.
- Reload on configuration change. The supervisor
sends
SIGHUPto reload the configuration.
Launchd plist (macOS)
The plist is in
{workspace-root}/services/media-lab-server/launchd.plist.
The launchd label is
ai.openclaw.media-lab-server. The supervisor's log is
at ~/.openclaw/media/logs/launchd.out.log.
Systemd unit (Linux)
The unit is in
/etc/systemd/system/openclaw-media-lab.service. The
unit name is openclaw-media-lab. The unit's log is at
/var/log/openclaw/media-lab.log.
Configuration
The server's configuration is in
{workspace-root}/.openclaw/media-lab-server/config.toml.
| Field | Default | Purpose |
|---|---|---|
host | 127.0.0.1 | Bind address. |
port | 8765 | Bind port. |
output_dir | ~/.openclaw/media/outbound/ | Output directory. |
retention_days | 30 | Output retention. |
image_model | image-01 | Default image model. |
music_model | Music-2.6 | Default music model. |
quota_image | 50 | Daily image quota. |
quota_music | 100 | Daily music quota. |
log_level | INFO | Log level. |
log_format | json | Log format. |
Logging
The server logs to ~/.openclaw/media/logs/server.log.
The format is JSON Lines by default. Each log entry
includes:
timestamp: ISO 8601 UTC.level:INFO,WARN,ERROR.event: the event name (e.g.,job_submitted,job_started,job_completed).job_id: the job ID.duration_ms: for completed jobs.error: for failed jobs.
Example:
Health checks
The server exposes GET /api/status for health checks.
The Coordinator and the audit scripts poll this endpoint
periodically. The expected response is 200 OK with the
server's status.
A failed health check indicates:
- The server is down (restart the supervisor).
- The server is overloaded (wait and retry).
- The model provider is down (check the provider's status page).
Failure modes
| Failure | Result |
|---|---|
| Model provider is down | Return failed with provider error. |
| Quota exhausted | Return 429 with quota info. |
| Output directory is full | Return failed with disk full error. |
| Job queue is full | Return 503 with retry-after header. |
| Server crashes | Supervisor restarts. Queue is lost. |
| Restart during job | Job is lost; client must resubmit. |
Integration with the Coordinator
The Coordinator calls the server through the API. The Coordinator's typical flow:
The Coordinator polls the job until it is completed
or failed. The polling interval is 5 seconds for image
jobs, 10 seconds for music jobs.
Security
The server is bound to localhost. It does not require authentication because the only clients are local processes. If the user wants to expose the server to the LAN (e.g., to call from a phone), the user must:
- Change the bind address to
0.0.0.0. - Add a reverse proxy (e.g., nginx) with TLS.
- Add authentication (e.g., a token in the request header).
The lab does not provide these features out of the box.