Repository navigation
Service Asset Baker
Asset Baker is Atlas' containerized Blender headless HP→LP bake worker. It turns AI-generated high-poly meshes into clean low-poly GLBs with a baked BaseColor map and a tangent-space normal map. Raw img2mesh output has interpenetrating shells, texture distortion and no normal map. The pipeline is voxel-remesh → decimate → fresh Smart-UV → selected-to-active bake of color and normal from the original.
It is a separate service from the Asset Worker. Asset Worker (Node, glTF-Transform) welds, simplifies and compresses. It cannot voxel-remesh, regenerate UVs or bake textures and normals; those steps need Blender. Both use the same content-addressed MinIO artifact schema. Asset Baker serves its own asset-baker.localhost route; the Backend gateway has no bake route. The intended chain is generate → image→3D → bake (this) → optimize (asset-worker).
It is disabled by default (ASSET_BAKER_SOURCE=disabled); the supported enabled mode is container-cpu. Cycles bakes on CPU for determinism and portability (CI, Linux), and Docker on macOS cannot pass Metal into a container.
Bake time depends on the mesh and the texture size; Atlas has not published measured bake times. ASSET_BAKER_TIMEOUT_SECONDS (default 600) caps each bake. GPU (container-gpu) and managed localhost wait for separate lifecycle and performance evidence. The image holds a full Blender install, and the wizard offers it only in the gen-ai-creative and all tracks.
There is no MCP surface, by design: this is a deterministic batch stage driven by job parameters. The blender-mcp add-on also refuses to start under blender -b. For agent-driven Blender, use Blender MCP.
| Surface | URL | Notes |
|---|---|---|
| Direct API | http://localhost:${ASSET_BAKER_PORT} |
Host-side FastAPI service when ASSET_BAKER_SOURCE=container-cpu. |
| Kong alias | http://asset-baker.localhost:${KONG_HTTP_PORT} |
Requires ./start.sh --setup-hosts; generated only when the service is enabled. |
| Internal API | http://asset-baker:8096 |
Used by sibling containers through ASSET_BAKER_ENDPOINT. |
| Health | GET /health |
Returns 200 only when the configured Blender executable is available; otherwise returns 503. |
| Metrics | GET /metrics |
Prometheus request counters and duration histograms; intentionally unauthenticated for in-network scraping. |
Every route except GET /health and GET /metrics requires Authorization: Bearer ${ASSET_BAKER_API_TOKEN}. Atlas generates the token on first startup; requests fail closed with 503 if authentication is not configured.
| Variable | Default | Purpose |
|---|---|---|
ASSET_BAKER_SOURCE |
disabled |
Enables the containerized worker when set to container-cpu. |
ASSET_BAKER_IMAGE |
python:3.12.9-slim |
Base image; the Dockerfile downloads pinned Blender on top. |
ASSET_BAKER_BLENDER_VERSION |
4.3.2 |
Pinned headless Blender (≥ 4.3) installed in the image; bundles Python + numpy. |
ASSET_BAKER_BLENDER_SHA256 |
4da1c956...a5592e6 |
Published SHA-256 verified before the pinned Blender archive is extracted. |
ASSET_BAKER_PORT |
computed | Host port assigned by Atlas' topology allocator. |
ASSET_BAKER_API_TOKEN |
generated | Bearer token required by upload, reference-bake, and artifact-download routes. |
ASSET_BAKER_ALLOWED_INPUT_BUCKETS |
(blank) | Optional comma- or space-separated MinIO bucket allowlist. Blank follows MINIO_BUCKET_ASSET_INPUTS. |
ASSET_BAKER_ARTIFACT_DIR |
/data/artifacts |
Local cache path for baked outputs and direct downloads. |
ASSET_BAKER_MINIO_ENABLED |
true |
Writes baked GLB + textures to MinIO when enabled. |
ASSET_BAKER_MINIO_BUCKET |
asset-baker |
Output bucket for content-addressed artifacts. |
ASSET_BAKER_MINIO_ENDPOINT |
auto-managed | Internal MinIO S3 API endpoint. |
ASSET_BAKER_MINIO_ACCESS_KEY / ASSET_BAKER_MINIO_SECRET_KEY
|
empty | Optional credential override; compose otherwise uses the generated MINIO_ASSET_BAKER_* scoped account. |
ASSET_BAKER_TARGET_TRIS |
39000 |
Default low-poly triangle budget (overridable per request). |
ASSET_BAKER_TEX_SIZE |
2048 |
Default baked texture resolution (square). |
ASSET_BAKER_CANONICAL_SIZE |
4.0 |
Canonical max-dimension each mesh is scaled to before remesh. |
ASSET_BAKER_BRIGHTNESS_MIN |
0.05 |
Mean-brightness QA gate; a baked color mean below this is refused. |
ASSET_BAKER_TIMEOUT_SECONDS |
600 |
Hard wall-clock ceiling per bake subprocess. |
ASSET_BAKER_MAX_UPLOAD_MB |
200 |
Maximum accepted GLB input size (MiB). Declared uploads above this limit plus 2 MiB return 413 before body parsing. Other oversized inputs fail before baking. |
ASSET_BAKER_CONCURRENCY |
1 |
Maximum concurrent bakes; default 1 (one bake saturates CPU). |
Enable from the CLI with:
./start.sh --asset-baker-source container-cpuThe default ASSET_BAKER_SOURCE=disabled keeps the Blender worker and its image build out of normal starts until a creative or 3D workflow needs it.
The image installs a pinned, checksum-verified headless Blender at build time (services/asset-baker/app/Dockerfile). The build is reproducible and source-host-independent:
| Property | Value |
|---|---|
| Version |
ASSET_BAKER_BLENDER_VERSION (default 4.3.2) |
| Artifact | blender-<version>-linux-x64.tar.xz |
| Integrity |
ASSET_BAKER_BLENDER_SHA256 (default 4da1c956…a5592e6), verified with sha256sum -c before extraction |
| Architecture |
linux/amd64 (x86_64) only — Blender ships no official linux-arm64 build; the Dockerfile fails fast with an actionable message on any other architecture. The compose service pins platform: ${ASSET_BAKER_PLATFORM:-linux/amd64}, so an arm64 host (Apple Silicon) builds and runs it under emulation |
| License | Blender is licensed under the GNU GPL: its source files carry GPL-2.0-or-later, and the v4.3.2 source tree also ships the GPL-3.0 text. It runs as a separate headless subprocess (blender -b) and is not linked into Atlas code. Atlas has not settled what redistributing a built image requires; that is open item R-BUILD in the license inventory
|
Download source. download.blender.org returns HTTP 403 to automated clients. The build therefore fetches from the official Blender mirror network and tries each mirror in turn: ftp.nluug.nl, mirror.clarkson.edu, mirrors.ocf.berkeley.edu, mirrors.dotsrc.org. The SHA-256 is verified whatever the host, so one mirror outage does not break the build. A clean docker build needs no developer cache. A build-time smoke step asserts that blender --version matches the pin.
Updating the pin.
- Download
blender-<version>-linux-x64.tar.xzandblender-<version>.sha256from a mirror above. - Set
ASSET_BAKER_BLENDER_VERSIONto the new version, in.envor theservice.ymldefaults. - Set
ASSET_BAKER_BLENDER_SHA256to its published checksum in the same place.
Input rules are the same as for the Asset Worker (§4.1): a self-contained binary glTF 2.0 file whose buffers[].uri and images[].uri are data: URIs only. Other input returns 400 before Blender runs.
POST /assets/bake accepts multipart/form-data:
curl -H "Authorization: Bearer ${ASSET_BAKER_API_TOKEN}" \
-F file=@mesh.glb \
"http://localhost:${ASSET_BAKER_PORT}/assets/bake"| Field | Required | Notes |
|---|---|---|
file |
yes | Raw .glb file (plain GLB, not EXT_meshopt_compressed). |
target_tris |
no | Low-poly triangle budget for the decimate stage. Defaults to ASSET_BAKER_TARGET_TRIS. |
tex_size |
no | Baked BaseColor/Normal texture resolution (square). Defaults to ASSET_BAKER_TEX_SIZE. |
canonical_size |
no | Canonical max-dimension normalization scale. Defaults to ASSET_BAKER_CANONICAL_SIZE. |
mode |
no |
bake (full HP→LP + texture bake, default) or skip (foliage bypass — normalize + export, no remesh/bake). |
POST /assets/bake/ref accepts JSON:
{
"input": {"bucket": "raw-assets", "key": "incoming/cottage.glb"},
"params": {"target_tris": 15000, "tex_size": 2048, "mode": "bake"}
}The bucket must be in ASSET_BAKER_ALLOWED_INPUT_BUCKETS; other buckets return 403 before MinIO is contacted. Write inputs to raw-assets with the ingest credentials described in Asset Worker §4.2.
Both endpoints return a content-addressed artifact envelope — the baked LP GLB plus its BaseColor/Normal textures:
{
"status": "succeeded",
"sha256": "<lp-glb-sha256>",
"artifact": {
"storage": "minio",
"bucket": "asset-baker",
"key": "bake/<sha256>.glb",
"uri": "s3://asset-baker/bake/<sha256>.glb",
"content_type": "model/gltf-binary"
},
"textures": [
{"role": "basecolor", "storage": "minio", "bucket": "asset-baker", "key": "bake/<sha>.png", "content_type": "image/png"},
{"role": "normal", "storage": "minio", "bucket": "asset-baker", "key": "bake/<sha>.png", "content_type": "image/png"}
],
"summary": {"mode": "bake", "faces_in": 98000, "tris_out": 15000, "shells_kept": 1, "color_mean": 0.34, "duration_s": 72.0},
"download_url": null
}When ASSET_BAKER_MINIO_ENABLED=false, artifacts are stored under ASSET_BAKER_ARTIFACT_DIR/bake/<sha256>.{glb,png} and download_url=/assets/artifacts/<sha256>.glb. A skip (foliage) bake emits textures: [] and color_mean: null.
Failure statuses:
-
400: empty or non-GLB input. -
401: bad bearer token.503: no token configured. -
413: input overASSET_BAKER_MAX_UPLOAD_MB. -
422: bake failed, including the black-bake QA gate, or invalid parameters (for example amodeother thanbake/skip, or an out-of-rangetarget_tris). -
429: worker busy (bounded concurrency).504: bake timeout. -
/assets/bake/refonly:404for a missing input object,403for one the service account cannot read.
The Kong route's read/write timeout is ASSET_BAKER_TIMEOUT_SECONDS + 30 s, so the gateway's 300 s default does not cut a long bake.
The worker runs one headless Blender subprocess (blender -b -P bake.py) per request. It enforces input size, timeout, concurrency (ASSET_BAKER_CONCURRENCY, default 1) and temp-file cleanup. The bake.py pipeline runs per source:
-
Import → join → canonical-normalize. Apply parent transforms, join all meshes, and scale to
canonical_sizebefore remeshing. Raw GLBs are not meter-scale, so the voxel heuristics fail without this step. The base rests atz=0. - Voxel-remesh fuses the interpenetrating shells/flaps that cause tilt/floaters into one watertight surface.
-
Debris-shell drop removes loose fragments below
MIN_SHELL_FACES. -
Decimate to
target_tris(skipped if already under). - Uniform smooth shading (hard normals corrupt cage baking) + clear seams.
- Smart-UV Project by angle + pack — seam-based unwrap fails on remeshed surfaces.
- Two-pass selected-to-active bake of DIFFUSE/COLOR and TANGENT normal from the original into fresh textures. The passes are tight and far-fallback, with a numpy composite of missed texels.
-
Mean-brightness QA gate, then export
<name>_LP.glb+BaseColor.png+Normal.png.
Correctness rules (each is a tested invariant):
-
Metallic neutralization — img2mesh sources ship
metallic=1.0; a fully-metallic BSDF has no diffuse, so COLOR bakes come out pure black. The worker neutralizes metallic on the bake source in-memory only. -
Mean-brightness gate — a baked color mean below
ASSET_BAKER_BRIGHTNESS_MINis a silently-failed bake and is refused with a non-zero result (422), never shipped. Re-enforced at the worker boundary. - Canonical-scale normalization before remeshing (above).
- Smart-UV by angle only — seam-based unwrap fails on remeshed surfaces.
-
Foliage bypass (
mode=skip) — thin leaf-shell meshes fragment into shards under remesh+decimate, so foliage is normalized and re-exported without remesh/bake. Rule: buildings/props →bake; foliage →skip. - Per-source isolation + JSON summary — one bad asset never aborts a batch; a machine-readable summary drives the gate and content-addressing.
Outputs are SHA-256 content-addressed and written to MinIO (bake/<sha256>.{glb,png}) or the local artifact cache, with the Asset Worker schema. The service is separate from the media gateway. Image-to-3D flows send raw provider GLBs to this worker; adapters do not embed bake logic.
Support tier: experimental — Capability contract declared; no cited cold-start, workflow, or upgrade qualification run yet (evidence at v0.1.0).
| Capability | Status | Verification | Notes |
|---|---|---|---|
| Headless high-to-low-poly texture baking | supported | tested | Atlas runs the voxel-remesh, decimate, Smart-UV, color-bake, normal-bake, and black-output QA pipeline through pinned Blender on Cycles CPU. |
| Bounded authenticated bake API | supported | tested | Upload, MinIO-reference, and artifact routes require the generated bearer token and enforce input-size, concurrency, and subprocess-timeout bounds; health and metrics remain public. |
| Content-addressed bake artifacts | supported | tested | Successful GLBs and textures use SHA-256 keys in the scoped MinIO bucket by default, with an explicitly selected local-artifact fallback. |
| GPU and native ARM64 baking | not-supported | tested | The only source is the CPU container and its pinned Blender archive is linux/amd64-only; the dormant bake-script GPU path is not an Atlas source variant. |
-
400 Input must be a GLB file: use a.glbfilename and a binary GLB payload. Decompress EXT_meshopt GLBs to plain GLB first (Blender 4.3 can't import meshopt). -
422 baked color is black: the mean-brightness QA gate fired — typically ametallic=1source (neutralized automatically, but check the source's textures) or dead UVs. The bake is refused rather than shipping a black asset. - Thin foliage fragments into shards: pass
mode=skip(foliage bypass) — buildings/props bake, foliage skips. -
429 Bake worker is busy: bounded concurrency (ASSET_BAKER_CONCURRENCY, default 1) rejected a concurrent bake; retry after the in-flight bake finishes. -
504bake timeout: raiseASSET_BAKER_TIMEOUT_SECONDSor lowertex_size/target_trisfor very heavy assets. -
401 Invalid Asset Baker bearer token: passAuthorization: Bearer ${ASSET_BAKER_API_TOKEN}. -
403 Input bucket is not allowed: add the bucket toASSET_BAKER_ALLOWED_INPUT_BUCKETS(see Asset Worker §7). - Dependency check fails with "Asset Baker requires MinIO": the container waits on
minioandminio-init, and thegen-ai-creativetrack leaves MinIO off. Add--minio-source container(for example./start.sh --track gen-ai-creative --asset-baker-source container-cpu --minio-source container), declareMINIO_SOURCE: containerin a consumer manifest, or disable Asset Baker. - MinIO upload failure: confirm
MINIO_SOURCE=container,ASSET_BAKER_MINIO_BUCKET, and the generatedMINIO_ASSET_BAKER_*credentials. - Kong alias missing: confirm
ASSET_BAKER_SOURCE=container-cpu, run./start.sh --setup-hosts, and regenerate routes through the normal startup flow.
| Service | Category |
|---|---|
| minio | data |
| Service | Category |
|---|---|
| kong | infra |
| prometheus | infra |

Open the full-size diagram for a full-screen view.
- Backend media operations should call Asset Baker after hosted image-to-3D providers return raw GLB, before Asset Worker's optimize pass. This forms the
generate → image→3D → bake → optimizechain. - The backend media-operation contract (
/media/operations/{id}, statesqueued|running|succeeded|failed|cancelled|timeout) exists. Asset Baker does not use it yet; bakes are synchronous, bounded requests.
- A
creative-3dwizard track should split fromgen-ai-creativeonce the 3D fleet (gateway, image→3D, bake, segment, terrain) reaches ~5+ services.
- GPU baking (
container-gpu, Cycles OPTIX/CUDA) is a fleet-scale optimization, deferred until separate performance evidence exists. Thebake.py--gpupath exists but is not exposed as a source variant. - Managed host
localhost(a/Applications/Blender.appon the developer machine, mirroring the ComfyUI managed-MPS pattern) is deferred until separate lifecycle evidence exists.
Atlas documentation.
- 1. Overview
- 2. Quick Start
- 3. Core Concepts
- 4. Tracks
-
5. Services
- 5.1. Service Catalog
-
5.2. Service Guides
- 5.2.1. airflow
- 5.2.2. asset-baker
- 5.2.3. asset-worker
- 5.2.4. backend
- 5.2.5. backup
- 5.2.6. blender-mcp
- 5.2.7. celery
- 5.2.8. chatterbox
- 5.2.9. cloud-providers
- 5.2.10. cloudflared
- 5.2.11. comfyui
- 5.2.12. crawl4ai
- 5.2.13. doc-processor
- 5.2.14. docling
- 5.2.15. docling-lightrag-adapter
- 5.2.16. fal
- 5.2.17. globals
- 5.2.18. grafana
- 5.2.19. hermes
- 5.2.20. iceberg-rest
- 5.2.21. jenkins
- 5.2.22. jupyterhub
- 5.2.23. kong
- 5.2.24. label-studio
- 5.2.25. langfuse
- 5.2.26. lightrag
- 5.2.27. litellm
- 5.2.28. llm-graph-builder
- 5.2.29. local-deep-researcher
- 5.2.30. loki
- 5.2.31. mcp-servers
- 5.2.32. minio
- 5.2.33. mlflow
- 5.2.34. multi2vec-clip
- 5.2.35. n8n
- 5.2.36. neo4j
- 5.2.37. ollama
- 5.2.38. open-webui
- 5.2.39. openclaw
- 5.2.40. otel-collector
- 5.2.41. parakeet
- 5.2.42. prometheus
- 5.2.43. ray
- 5.2.44. redis
- 5.2.45. redpanda
- 5.2.46. searxng
- 5.2.47. spark
- 5.2.48. speaches
- 5.2.49. stt-provider
- 5.2.50. supabase
- 5.2.51. supavisor
- 5.2.52. tei-reranker
- 5.2.53. tempo
- 5.2.54. tika
- 5.2.55. trino
- 5.2.56. trueforge
- 5.2.57. tts-provider
- 5.2.58. verba
- 5.2.59. vllm-metal
- 5.2.60. weaviate
- 5.2.61. zeppelin
- 5.3. Provider And Extension Guides
- 6. Configuration
- 7. Operations
- 8. Architecture
- 9. Development
- 10. Reference