Moonli
Checking service

Moonli

overview

— Connected
SYS / 01LIVE

CPU usage

—

Average server load

SYS / 02LIVE

RAM usage

—

—

SYS / 03LIVE

Disk usage

—

—

SYS / 04LIVE

Uptime

—

Server uptime

Production API usage

24H · —
Total requests — — in the last 24 hours
Tokens used — — in the last 24 hours
Requests Tokens

No production requests in this period.

Full Run

The result is a ZIP containing every applicable stage artifact. All pipelines use only the Test Calls configuration and temporary tab key.

API request

The request has not been built yet.

Result

The result will appear after the run.
01

Google connection

Full run defaults and Google endpoint

The full run uses these providers and parameters. The backend stores non-secret values in the persistent Docker volume.

Test Google API Key

Used by test stages only. It is never written to localStorage and is cleared on sign-out.

No key set for this tab
02

Prompt templates

Available placeholders: —

8 stages
01

Transcription · optional

Waiting for a request.
02

Prompt normalization

Waiting for a request.
03

Prompt builder

Waiting for a request.
04

Image generation

Waiting for a request.
05

Palette quantization

Waiting for a request.
06

Palette validation

Waiting for a request.
07

Vectorization

Waiting for a request.
08

Segmentation

Waiting for a request.
01

Routing

Google API proxy route

Route Google transcription, normalization, image generation, and API-key validation through the private Xray sidecar. The VLESS connection is stored only in the protected secrets volume and is never returned to the browser.

Routing status has not been loaded.

Saving an empty field keeps the current connection. Disable the switch to return Google requests to direct routing.

02

Appearance

Accent color

The preview updates immediately; the change is saved only when you select Apply.

03

Security

Changing the operator Access Key revokes every browser session. Android and TouchDesigner client API keys are not changed.

04

Backup and recovery

The logical ZIP excludes Google, client and updater keys, browser sessions, and .env. A pre-restore snapshot is created automatically before restore.

No snapshot selected.
05

Updates

Download an operator snapshot before installation. The local host updater uses a Unix socket and enforces checksums, an immutable image digest, server backup, health checks, and rollback. One-click updates are safe for image-only releases; Compose topology changes require an explicit repair or install.

Updater status has not been requested.

Connected Devices

—

A device is registered on its first production request with a td-######## or aa-######## identifier. Device type does not restrict pipeline selection.

The device list has not been loaded.

01

Request log

Current tab only, up to 100 entries. Secrets are redacted.

TYPEBODYTIME
02

Server audit

Up to 200 recent events; the server enforces limits of 10,000 records, 30 days, and 64 MiB.

Audit has not been loaded.

MOONLI HANDBOOK

Contents

01 · System overview 02 · Quick start 03 · Client API 04 · Device identity 05 · Pipeline stages 06 · Results and artifacts 07 · Google and prompts 08 · Operator console 09 · Backup and recovery 10 · Deployment 11 · Updates 12 · Errors 13 · Client integration
Open Standalone Guide

OPERATIONS · API · CLIENTS

Moonli Documentation

This handbook describes the deployed service, its production API, the operator console, artifacts, recovery, and the changes required in Android and TouchDesigner clients.

01 System overview

Moonli owns three tagged generation profiles. pipeline-1 and pipeline-2 complete the processed image flow in one POST /v1/generate request. pipeline-3 mirrors the production TouchDesigner flow: audio is transcribed and normalized into concise Russian by a multipart POST /v1/generate; the returned text is sent to the same endpoint as JSON, translated into an English image prompt, and used for three independent JPEG variants. The client type never selects or restricts the pipeline.

Production boundary. Nginx is the only public listener. Public traffic uses the configured HTTPS domain. Test Calls are available in production only through the authenticated operator session and require CSRF protection for mutations. The application port, databases, metrics, storage paths, legacy endpoints, updater catalog, and restore routes are not public.

Operator authentication, client authentication, device identity, and pipeline routing are separate concerns:

  • The operator signs in with an operator Access Key and receives an HttpOnly, SameSite browser session.
  • Android and TouchDesigner authenticate with a Bearer client credential.
  • X-Moonli-Device-Id identifies a persistent client installation but is not a secret.
  • The request field pipeline is the only selector for the processing profile.

02 Quick start

  1. Sign in with the operator Access Key.
  2. Open Test Calls, select the test providers and enter the exact enabled model names for transcription, normalization, Pipeline 3 prompt translation, and image generation.
  3. Enter the temporary test Google key, review the processed-pipeline prompt templates, and save the non-secret test settings.
  4. Open Production and configure each pipeline independently: Google key, enabled models, provider calls, and instructions. The backend verifies every key before persisting it.
  5. Use Test Calls to exercise individual stages or download a full-run artifact archive.
  6. Configure each real client with the HTTPS base domain and its Bearer credential. Generate and persist its device identifier before the first call.

Mock providers can complete every test stage without Google, including through the authenticated Test Calls surface in a production deployment. Production client pipelines never use those mock selections.

03 Production client API

Every request requires a Bearer credential, a stable device identifier, and an idempotency key. Use a fresh UUID for each logical operation and reuse it only when retrying that exact operation. All three pipelines use /v1/generate: pipeline 3 sends audio first and then sends the returned normalized text with a new operation UUID.

Text input

POST /v1/generate HTTP/1.1
Host: moonli.example.com
Authorization: Bearer <client-api-key>
X-Moonli-Device-Id: td-02941846
Idempotency-Key: 4c4058a6-fd85-4cf3-bf18-7fa475dd0ceb
Content-Type: application/json

{
  "type": "text",
  "pipeline": "pipeline-1",
  "text": "A red tree on a small island, without people"
}

Audio input

POST /v1/generate HTTP/1.1
Host: moonli.example.com
Authorization: Bearer <client-api-key>
X-Moonli-Device-Id: aa-26093758
Idempotency-Key: f78a3993-04c5-40d0-bc46-60dbc154106e
Content-Type: multipart/form-data; boundary=...

type=audio
pipeline=pipeline-2
audio=<binary audio file>

Do not send palette, provider, stage, or table-selection fields. Unknown pipeline tags—including misspellings such as pipelien-2—are rejected. The server validates declared and streamed audio size before provider work begins.

Retry rule. A transport failure may occur after the server has completed a paid generation. Retry with the same idempotency key. A matching request returns the stored artifact; a different payload with the same key returns IDEMPOTENCY_CONFLICT.

04 Device identity and access

Before its first production request, a client creates an identifier and stores it in installation-local persistent storage:

ClientFormatExample
TouchDesignertd- + 8 random digitstd-02941846
Android appaa- + 8 random digitsaa-26093758

The server registers a valid identifier on first use and counts every production API attempt. The Devices page shows its type, registration time, lifetime request count, and status. Blocking a device rejects future requests with DEVICE_BLOCKED before the pipeline starts; unblocking restores access. Either prefix may call any pipeline. TouchDesigner stores one device identifier in .moonli/device_id.txt and reuses it for both pipeline-3 operations.

Deleting application data may generate a new identity, so client storage should survive ordinary restarts and updates. The identifier does not replace the Bearer credential and must not be treated as authentication.

05 Pipeline stages

StagePurposeInput → output
1 · TranscriptionOptional for audio requests.Audio → source text
2 · NormalizationRemoves greetings, filler, and test commentary; Pipelines 1–2 produce English while Pipeline 3 produces Russian.Source text → normalized phrase
3 · Prompt builderApplies the pipeline template, palette contract, canvas, and exclusions.Normalized phrase → technical prompt
4 · GenerationRequests an image from the configured provider and converts the response to PNG.Prompt → provider PNG
5 · QuantizationMaps every visible pixel to the versioned palette in CIE Lab and optionally removes isolated speckles.Provider PNG → strict-palette PNG
6 · ValidationRequires exact palette colors and the expected alpha contract with zero tolerance.PNG → validation report
7 · VectorizationBuilds palette-safe vector paths where required by the output profile.PNG → SVG
8 · SegmentationCreates the ordered fixed-slot layer package for layered output.SVG/PNG → layer ZIP

pipeline-1 publishes the validated raster PNG. pipeline-2 continues through vectorization and segmentation and publishes a twelve-slot layered package. pipeline-3 intentionally stops after generation: its first operation performs transcription and Russian normalization, while its second operation translates the prompt into English and launches three independent image generations without quantization, palette validation, vectorization, or segmentation. The Test Calls page exposes the processed stages independently and lets an operator copy or download its output and pass it into the next stage.

06 Results and artifacts

pipeline-1 returns Content-Type: image/png with the filename moonli.png. pipeline-2 returns application/vnd.moonli.layers+zip with the filename moonli-layers.zip. pipeline-3 normalization returns only the normalized Russian UTF-8 text. pipeline-3 generation translates that text into an English prompt and returns application/zip containing exactly image_1.jpg, image_2.jpg, and image_3.jpg; every member is a 1024×1024 RGB JPEG.

Successful pipeline-1 and pipeline-2 responses include X-Moonli-Run-Id, X-Moonli-Result-SHA256, X-Moonli-Device-Id, and X-Idempotent-Replay. Clients must stream the body to a staging file, verify the SHA-256 response digest, validate the expected media type, and atomically publish the completed file. The pipeline-3 TouchDesigner integration validates the in-memory ZIP, JPEG dimensions, names, and uniqueness before atomically replacing the three files.

A pipeline-2 package contains:

manifest.json
composite.png
layers/00.png
...
layers/11.png

Each layer is a full-canvas RGBA PNG. Transparent pixels are exactly (0,0,0,0); visible pixels are fully opaque and use exactly the assigned slot color. Unused slots remain present as transparent images with used:false. The manifest records contract version, run ID, canvas, palette order, member paths, usage flags, and SHA-256 values. The backend verifies ZIP safety, member checksums, dimensions, colors, alpha, ordering, and pixel-perfect recomposition before publishing.

The operator full-run download is a diagnostic archive rather than the production response. Pipelines 1 and 2 contain the applicable original text or audio, normalized text, technical prompt, generated PNG, quantized PNG, validation report, vector output, and layer artifacts. Pipeline 3 contains the original text or audio, optional transcription, normalized Russian text, translated English prompt, three JPEG variants, the exact production image ZIP, and an execution trace; skipped stages are explicitly marked not_applicable. Every archive records the selected pipeline and input type.

07 Google providers and prompt templates

Each pipeline has its own persistent Google API key on the Production page. Keys are verified against Google, written atomically to pipeline-specific files in the dedicated secrets volume, and never placed in .env, localStorage, logs, API responses, logical backups, or release files. The browser receives only configured status. Deleting a key disables Google-backed calls for that pipeline until a replacement is saved.

If Google rejects the server region, Configuration → Routing can send every Google provider request—including Pipeline 3 prompt translation—through the internal Xray sidecar. Paste a VLESS Reality/TCP/Vision connection, enable the switch, and save. The secret is stored only in moonli_secrets; the browser can see configured status but cannot retrieve the value. Disabling routing restores direct Google connections and retains the private VLESS value.

The key entered from Test Calls is intentionally temporary: it exists only in the current tab's memory and is used by individual stages and Full Run for all three pipelines. Test endpoints never fall back to a Production key or Production pipeline configuration. Refreshing or signing out clears it.

Prompt normalization is a separate provider step. Pipelines 1 and 2 normalize to concise English before Prompt Builder applies their technical template. Pipeline 3 normalizes to concise Russian for the TouchDesigner response, then its second operation translates that phrase into English without adding details. Available placeholders are listed beside the template editor; both processed-pipeline templates must be non-empty before saving.

08 Operator console

  • Overview reports CPU, RAM, disk, uptime, request counts, token counts, and a 24-hour usage chart.
  • Test Calls owns the temporary Google connection, prompt templates, complete diagnostic archive flow for all three pipelines, and isolated processing stages. It never consumes Production configuration or keys. An authenticated operator may use either mock or Google test providers in production.
  • Configuration controls private Google API routing, appearance, security, backup, and updates.
  • Production contains three independent pipeline sections for persistent Google keys, model selection, provider configuration, and instructions. Pipeline 3 also provides a collapsed integration kit containing both complete client requests and both ready-to-paste TouchDesigner scripts.
  • Devices lists registered installations and blocks or unblocks API access.
  • Activity contains a redacted, tab-local request log and the retained server audit stream.

Changing the operator Access Key requires the current key, enforces the minimum length, updates the persistent verifier, and revokes every active browser session. It does not rotate client or updater credentials.

09 Backup, restore, and retention

A logical snapshot is a ZIP using format moonli-logical-backup, schema version 2, with a manifest and SHA-256 for every member. It includes runs, usage statistics, registered devices and block state, non-secret settings, the operator verifier, retained audit events, and referenced completed artifacts.

It deliberately excludes Google keys, optional VLESS routing state, client and updater credentials, cookies, sessions, .env, staging files, and release files. Back up the secrets volume separately using an encrypted host-level disaster-recovery procedure.

Before mutation, restore checks the 128 MiB compressed and 256 MiB uncompressed limits, compression ratios, member count, duplicate names, paths, links, schema, and all digests. It creates a pre-restore snapshot and reapplies it if restore fails. The service keeps the five newest restore points. A successful restore revokes browser sessions, so sign in again afterward.

Retention baselines are 10,000 audit events / 30 days / 64 MiB estimated payload, up to 1,000,000 usage rows / 365 days, and three rotated container log files of 10 MiB each.

10 Domain-only deployment

  1. Point the production hostname's A/AAAA record to the server and allow inbound TCP 80/443.
  2. Install a trusted certificate and private key at the configured Nginx mount paths.
  3. Create independent random operator, client, catalog, restore, and updater credentials. Never copy a workstation .env to the server.
  4. Configure the exact hostname, allowed hosts, Google providers, and enabled model names.
  5. Validate Compose, install the stack, then save the required per-pipeline Google API keys and models from the authenticated Production page.
  6. The internal vless-proxy service has no host port. Configure it from Configuration → Routing only when the server's direct region cannot reach Google.

Do not expose application ports 8000 or 18000. Port 18000 is loopback-only for readiness and local updater operations. Normal domain TLS is the intended boundary; VPN and mTLS are not required. Nginx allows the UI, health endpoint, client generation endpoint, and allow-listed operator routes. Unknown hosts receive 421 and concealed routes return 404.

docker compose --env-file /opt/moonli/.env \
  -f docker-compose.yml -f compose.production.yml config
docker compose --env-file /opt/moonli/.env \
  -f docker-compose.yml -f compose.production.yml up -d --wait
curl --fail https://moonli.example.com/health

Generation can take several minutes. Nginx uses a 300-second upstream timeout, so Android and TouchDesigner clients must use a call/read timeout of at least 300 seconds and keep TLS certificate verification enabled.

11 Updates and rollback

The root-owned host updater is based on psewdon1m-exocortex/updater at the pinned reviewed commit. The web application receives only its Unix socket and never receives the Docker socket. Moonli may request a semantic release version but cannot provide arbitrary commands, repositories, image names, URLs, or services.

A valid release is named moonli-vX.Y.Z and includes moonli-release.json, a checksummed Compose bundle, and an immutable image digest. Before mutation the application creates a logical backup. The updater validates the release, pulls the image, updates version state atomically, and runs loopback and public health checks. Failure restores the previous image/version and invokes backup restore.

Update limitation. One-click updates are for image-only releases. Changes to Compose, Nginx topology, mounted paths, or host units require the reviewed repair/install procedure after downloading an operator-held snapshot.

12 Errors and troubleshooting

API failures use a stable envelope and never expose a traceback or partial artifact:

{
  "error": {
    "code": "PALETTE_VALIDATION_FAILED",
    "message": "Unable to generate an image that matches the allowed palette."
  }
}
CodeAction
INVALID_DEVICE_IDGenerate and persist the required prefix plus exactly eight digits.
DEVICE_BLOCKEDUnblock the installation on Devices or investigate why it was disabled.
NO_VISUAL_SUBJECTSpeech was recognized, but it did not say what should be drawn. Ask for a concrete visual subject; do not retry automatically.
UNAUTHORIZEDCheck the Bearer client credential; the device ID is not a credential.
GOOGLE_KEY_NOT_CONFIGUREDSave the persistent key on Production.
PROMPT_NORMALIZATION_FAILEDVerify the normalization provider, model access, endpoint, key, and timeout.
PROMPT_TRANSLATION_FAILEDVerify the Pipeline 3 translation instruction, model access, endpoint, key, and timeout.
GENERATION_IN_PROGRESSWait for Retry-After, then retry the identical request and idempotency key.
IDEMPOTENCY_CONFLICTUse a new key for a changed input or pipeline.
RATE_LIMITEDHonor HTTP 429 and Retry-After.
PALETTE_QUANTIZATION_FAILED / PALETTE_VALIDATION_FAILEDInspect the generated and quantized PNGs in a full-run archive; validation requires exact colors and alpha.

Other stable codes include INVALID_INPUT, AUDIO_TOO_LARGE, TRANSCRIPTION_FAILED, PROMPT_BUILD_FAILED, IMAGE_GENERATION_FAILED, OUTPUT_VALIDATION_FAILED, RESULT_EXPIRED, and INTERNAL_ERROR. Use the run ID to correlate the client response with the redacted server audit.

13 Android and TouchDesigner integration

The backend repository does not modify either client project. Apply these integration changes in their own codebases:

TouchDesigner

  • Add one external, diffable HTTP client module beside the canonical Player.toe.
  • Persist a securely randomized td-######## identifier in installation-local configuration.
  • For pipeline 3, send audio as multipart to /v1/generate, then send the returned normalized text from op('answer') as JSON to the same endpoint.
  • Use the same persisted device ID for both calls, a fresh UUID for each operation, a 300-second timeout, and the same UUID only when retrying that operation.
  • Validate the in-memory generation ZIP, atomically save image_1.jpg through image_3.jpg, and reload all three matching Movie File In TOPs.
  • Preserve the production DAT's index increment, two sound pulses, and two water-queue commands.

Android

  • Add Internet permission, a pinned streaming HTTP client, an HTTPS-only base domain, and Android Keystore-backed per-device credentials.
  • Persist a securely randomized aa-######## identifier with DataStore.
  • Implement the single generation endpoint with text JSON and audio multipart, 300-second timeouts, cancellation, and idempotent retry.
  • For layer ZIPs, enforce Zip Slip defenses, size/member limits, CRC, manifest schema, checksums, dimensions, colors, alpha, and safe atomic storage.
  • Load the twelve local layers in manifest order. Do not poll individual backend stages.

Both client types may request any pipeline. Separate per-device credentials remain recommended even though the pipeline and device prefix do not impose authorization roles.

Google API Key

The value lasts only until refresh or sign-out and is sent as a secret test-call header. Configure the persistent production key separately on the Production page.