# Glb Compressor Server

> HTTP server for compressing GLB/glTF 3D models via REST API and SSE streaming. Use when building web integrations, setting up a compression microservice, or adding real-time compression progress to a frontend. Use when this capability is needed.

- Skill: `tomevault-io/glb-compressor-server` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/glb-compressor-server`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/glb-compressor-server/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/glb-compressor-server

---


# glb-compressor Server

HTTP compression server built on `Bun.serve()`. Provides synchronous and
streaming (SSE) compression endpoints with full CORS support.

## Starting the Server

```sh
# Production (from installed package)
glb-server

# Development (hot reload)
bun run dev

# Custom port
PORT=3000 bun run start
```

Default port: `8080` (override via `PORT` env var).

## Endpoints

### `GET /healthz`

Health check. Returns `200 ok`.

### `POST /compress`

Synchronous compression. Returns compressed GLB binary.

**Accepts:** `multipart/form-data` (field: `file`) or raw
`application/octet-stream`.

**Query params / form fields:**

- `preset` - Compression preset: `default`, `balanced`, `aggressive`, `max`
- `simplify` - Mesh simplification ratio `(0, 1)`, e.g. `0.5`

Form fields override query params when both are provided.

**Response headers:**

| Header                 | Description                        |
| ---------------------- | ---------------------------------- |
| `X-Request-ID`         | UUID tracking this request         |
| `X-Original-Size`      | Input file size in bytes           |
| `X-Compressed-Size`    | Output file size in bytes          |
| `X-Compression-Method` | `gltfpack` or `meshopt`            |
| `X-Compression-Ratio`  | Percentage reduction (e.g. `84.1`) |
| `Content-Disposition`  | Suggested download filename        |

**Example:**

```sh
# Multipart upload
curl -X POST -F "file=@model.glb" \
  "http://localhost:8080/compress?preset=aggressive" \
  -o compressed.glb

# Raw binary upload
curl -X POST --data-binary @model.glb \
  -H "Content-Type: application/octet-stream" \
  "http://localhost:8080/compress?preset=balanced" \
  -o compressed.glb
```

### `POST /compress-stream`

SSE streaming compression with real-time progress. **Multipart only.**

**Content-Type:** `text/event-stream`

**SSE events:**

| Event    | Data shape                                                                            |
| -------- | ------------------------------------------------------------------------------------- |
| `log`    | `{ message: string }`                                                                 |
| `result` | `{ requestId, filename, data (base64), originalSize, compressedSize, ratio, method }` |
| `error`  | `{ message, requestId, code }`                                                        |

Stream closes after `result` or `error` event.

**Example (JavaScript):**

```js
const form = new FormData();
form.append('file', glbFile);

const response = await fetch(
	'http://localhost:8080/compress-stream?preset=aggressive',
	{ method: 'POST', body: form },
);

const reader = response.body.getReader();
const decoder = new TextDecoder();

// Parse SSE events from the stream
while (true) {
	const { done, value } = await reader.read();
	if (done) break;
	const text = decoder.decode(value);
	// Parse "event: <type>\ndata: <json>\n\n" format
	console.log(text);
}
```

## Error Responses

All errors return structured JSON:

```json
{
	"error": { "code": "ERROR_CODE", "message": "Human-readable message" },
	"requestId": "uuid"
}
```

**Error codes:**

| Code                   | HTTP | Description                         |
| ---------------------- | ---- | ----------------------------------- |
| `NO_FILE_PROVIDED`     | 400  | No file in form data                |
| `INVALID_GLB`          | 400  | Failed GLB magic byte validation    |
| `FILE_TOO_LARGE`       | 413  | Exceeds 100 MB limit                |
| `INVALID_CONTENT_TYPE` | 415  | Non-multipart on streaming endpoint |
| `COMPRESSION_FAILED`   | 500  | Pipeline error during compression   |

## CORS

Full CORS enabled on all endpoints. Exposed headers: `X-Request-ID`,
`X-Original-Size`, `X-Compressed-Size`, `X-Compression-Method`,
`X-Compression-Ratio`.

## Limits

- **Max file size:** 100 MB
- **gltfpack timeout:** 60 seconds per file

## Architecture Notes

- `import.meta.main` guard: safe to import `server/main.ts` as a library without
  starting the server.
- Request ID (`X-Request-ID`) is generated per request and logged server-side.
- The `/compress-stream` endpoint sends the compressed GLB as base64 in the
  `result` SSE event (~33% larger than binary). For large files near the 100 MB
  limit, prefer `/compress`.

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/kjanat) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-15 -->

