Skip to content

Skill: Upload and Check 3D Models

Teach your agent the benchy.world upload pipeline — ingest grants, multipart transfer, server file checks, policy decisions, and recovery.


v2.0.0MIT licensedbenchy-cli · uploads.benchy.world · mesh.benchy.world · benchy.world Convex

What it teaches

The pipeline skill: upload, checks, and publishing are separate states. Covers ingest grants, multipart transfer, server-enforced benchy-mesh policies, print-readiness evidence, and recovery.

Load it into Claude Code, Codex, Cursor, Grok, or any MCP-capable agent alongside a Benchy connection (setup guide), and the agent gains this entire operating manual as working knowledge.

What your agent can do

  • Run the grant → transfer → finalize ingest session, single PUT or 32 MiB multipart
  • Upload up to 250 MB per model; Public checks support glb, gltf, stl, obj, and 3mf
  • Read validation results: lifecycle, profile, execution mode, decision, coverage, and issues
  • Explain File checks passed, Needs attention, and Checks incomplete without overstating scope
  • Recover rejected models — interpret error codes, fix the bytes, publish a new version
  • Debug stuck uploads and validations with the gateway and queue checklists

Get the skill

For Claude Code, install it as a project skill:

shell
mkdir -p .claude/skills/upload-and-validate-3d-models
curl -o .claude/skills/upload-and-validate-3d-models/SKILL.md https://benchy.world/skills/upload-and-validate-3d-models.md

The full SKILL.md

The complete document, ready to copy:

upload-and-validate-3d-models.md
---
name: upload-and-validate-3d-models
description: Upload 3D models to benchy.world and understand the file-check pipeline: ingest grants, single or multipart transfer, initial preflight, server-enforced marketplace policy, explicit check coverage, print-readiness assessment, and recovery. Use when uploading model files, publishing versions, debugging checks, or explaining publishing and Shop eligibility.
title: Upload and Check 3D Models
date: 2026-07-12
author: Benchy
tags: [benchy, 3d-models, validation, mesh, cloudflare-workers, skill]
license: MIT
metadata:
  version: "2.0.0"
  schemaVersion: 2
  policyVersion: "2026.07.10.3"
  surface: "benchy-cli · uploads.benchy.world · mesh.benchy.world · benchy.world Convex"
---

# Upload and check 3D models on Benchy

Uploading, checking, and publishing are separate states. A completed upload
means the bytes were stored. It does not mean the file passed a publishing or
commerce policy.

## Pipeline at a glance

```text
world_request_model_upload
  → upload session + scoped grant
  → PUT bytes (single or multipart) to uploads.benchy.world
  → world_finalize_model_upload
  → promote into the models bucket and compute SHA-256
  → world_create_model
  → initial content preflight (fast feedback only)
  → Cloudflare Queue "benchy-mesh-validation"
  → benchy-mesh with:
      validationProfile: marketplace_publish
      executionMode: server_enforced
  → full report stored by SHA-256 + policy version
  → POST <convex-site>/mesh/apply
  → model.meshValidation updated
```

## 1. Uploading

Supported upload declarations are `glb`, `gltf`, `stl`, `obj`, `usdz`, and
`3mf`, up to 250 MB per model file. The upload session has three steps:

Public publishing checks currently support GLB, glTF, STL, OBJ, and 3MF.
USDZ is private/unlisted-only for this release and cannot request Public or
Shop purchase. STL and OBJ require an explicit coordinate-unit assumption:
`um | mm | cm | m | in | ft`.

1. **Grant** — `world_request_model_upload { format, units?, expectedBytes,
   originalFilename }` returns an upload session and a short-lived HMAC grant.
   `units` is required here for STL and OBJ and must be one of
   `um | mm | cm | m | in | ft`. The grant is
   scoped to one pending key, transfer method, and byte ceiling.
2. **Transfer** — PUT to `uploads.benchy.world` with
   `X-Benchy-Upload-Grant`. Files above the single-request threshold use 32 MiB
   multipart chunks.
3. **Finalize** — `world_finalize_model_upload` verifies the session, promotes
   the object into the models bucket, and computes SHA-256 while streaming.
   Create the model only after finalize returns its `modelKey` and exact size.

The client never chooses a public-bucket key. A stored upload remains available
to its owner even when later checks deny or cannot establish eligibility.
Creating or updating a model with Public intent also requires an explicit
AI-origin declaration (`isGenerative: true` or `false`); omission is not treated
as human-created work.

## 2. Initial preflight and server policy

The two runs have different contracts:

- **Initial preflight** uses `validationProfile: structural` and
  `executionMode: client_advisory`. It provides quick content-based format and
  framing feedback. It cannot grant Shop eligibility and cannot
  overwrite a completed server decision.
- **Marketplace policy** uses `validationProfile: marketplace_publish` and
  `executionMode: server_enforced` over the stored bytes. This is the only v2
  context that can grant Benchy Shop eligibility.

Engine provenance does not confer authority. For v2, public or commerce
eligibility requires the complete current contract:

```text
schemaVersion == 2
policyVersion == 2026.07.10.3
validationStatus == completed
validationProfile == marketplace_publish
executionMode == server_enforced
decision.verdict == allow
decision.reasonCodes is empty
required decision + print-readiness checks passed at their specified coverage
no error issues
high-confidence supported-format identification after bounded parsing
full finite geometry + topology evidence; known units
sha256 + byteLength + detected format + unit context match the current model
```

During migration, stored v1 results remain readable through the explicit
adapter, but no v1 result or engine-name prefix grants current eligibility.
Historical files are queued for a current v2 decision.

### Engine selection by size

| File size | Execution path |
|---|---|
| ≤ 24 MiB | `benchy-mesh-wasm` in the Cloudflare Worker |
| 24–256 MiB | native `benchy-mesh` in an isolated Cloudflare Container |
| > 256 MiB | refused by the validation service's absolute limit |

The application enqueue includes the model id/key, SHA-256, claimed format,
policy version, profile, execution mode, unit assumption, and an idempotency
key scoped to all of those decision inputs. A creator-declared
unit may be supplied as a policy assumption for formats that do not declare
units; the report then marks `unitBasis: assumed` and emits `UNITS_ASSUMED`.

## 3. Reading schema v2

The current report uses `schemaVersion: 2` and
`policyVersion: 2026.07.10.3`:

- `identification`: `detectedFormat`, confidence, inferred media type, and
  evidence. Detection is not structural validation.
- `decision`: `verdict` (`allow | deny | inconclusive`) and stable
  `reasonCodes`.
- `checks[]`: stable check id, `status`
  (`passed | failed | not_run | unsupported`), `coverage`
  (`full | sampled | header_only | metadata_only | none`), and evidence or
  issue codes.
- `issues[]`: stable code, severity, check id, message key, display message,
  and optional typed parameters. Automations branch on codes and parameters,
  never on English messages.
- `printReadiness`: `assessment`
  (`ready | needs_attention | not_ready | not_assessed`), prerequisite checks,
  notes, and limitations. Product copy renders `needs_attention` as **Ready
  with notes** so it cannot be confused with the blocking overall **Needs
  attention** state. There is no numeric score or quality band.
- `metadata`: observations such as counts, bounds, unit basis, coverage, and
  topology. Closed-surface and edge-manifold conclusions appear only when
  explicit topology evidence supports them.
- `engines`: implementation provenance.

A temporary compatibility projection remains for old consumers. New logic must
use the v2 decision, check, issue, identification, and readiness fields.

## 4. Product-state mapping

| Contract state | User-facing language |
|---|---|
| Upload stored; checks not started | **Uploaded** |
| Pending or running | **Checking file** |
| Server marketplace `allow` | **File checks passed** |
| Server marketplace `deny` | **Needs attention** |
| Retryable server execution failure | **Checks incomplete** — no publishing decision; select **Retry checks** or replace the file |
| Other server marketplace `inconclusive` | **Checks incomplete** — no publishing decision; replace the file or contact [Benchy support](https://benchy.studio/contact) |
| Unsupported format issue | **Unsupported file type** |

Do not say “safe,” “verified,” “printable,” “watertight,” or “manifold” unless
the exact named property was checked with sufficient coverage. Do not infer a
policy decision from the engine name.

## 5. Publishing and Shop gates

- Public is an intent while checks run: the row stays unlisted until a current
  bound v2 allow arrives. Choosing Unlisted or Private cancels that intent.
- Stage a Shop product in fail-closed order: finalize the source with protected
  delivery, create the World model as Private, call
  `world_link_shop_product { ..., enableShopOnVerification: true }`, and only
  then request Public or Unlisted visibility. Purchase enables only when the
  current file is eligible and Shop confirms that the same creator owns a
  currently listed product. Any failed or stale condition keeps both purchase
  and non-owner source delivery disabled.
- Pending, client, structural, standalone, inconclusive, v1, stale-byte, and
  stale-unit results do not qualify.
- Fix a denied file and publish a new version. Retrying identical bytes under
  the same policy is idempotent; a changed decision requires changed bytes or a
  changed policy version.

## 6. Troubleshooting

- PUT returns 401/403 — the grant expired or does not match the key. Request a
  new upload session; do not reuse grants.
- PUT returns 413 — the payload exceeds the grant or must use multipart.
- Model creation cannot find the object — finalize the upload before create.
- UI remains on **Checking file** — inspect the queue consumer and
  `https://mesh.benchy.world/health`; initial preflight alone is not success.
- **Needs attention** — inspect stable issue and reason codes, repair the file,
  and publish a new version.
- **Checks incomplete** — no publishing decision was made. If the owner UI
  offers **Retry checks**, select it; otherwise use a supported
  self-contained format, replace the file, or contact
  [Benchy support](https://benchy.studio/contact).

Use and to move between pages.