Publishing
Publishing uploads a Universe from Studio to eustress.dev. Studio bakes every Space into chunks, uploads the chunks eustress.dev does not already hold into the Universe's listing and submits it for review, and the Gallery lists it once review approves it and you have marked it Public.
01Overview
What Publishing Does
Publishing takes the Universe you are working in, with every Space inside it, and stores it on eustress.dev under a listing: a name, a description, a genre and a thumbnail. You do it from one dialog in Studio. The Eustress API at api.eustress.dev keeps the listing record and stores the world's chunks in Cloudflare R2.
Publishing stores a Universe so people can find it. It does not start servers or host sessions; where multiplayer stands is covered on the Networking page.
Before You Publish
- An open Space: publishing starts from the Space you have open and publishes the Universe that contains it. Players start in the Space you had open.
- An Eustress account: registering on eustress.dev checks a government ID and your age, then gives you an identity file named
eustress-<username>.toml. - A Studio sign-in: choose Sign In on the ribbon, browse to your identity file and press Sign In with Identity. Studio signs a challenge from the API with the file's Ed25519 private key and receives the session token that every publish request carries.
The minimum age is 18, raised where the local age of majority is higher:
19 in Canada and South Korea, 20 in Thailand, and 21 in Singapore,
Indonesia, the United Arab Emirates and Egypt. The check uses the date of
birth read from your ID document, not the one you type, and an unreadable
date is refused rather than guessed. If your computer has no usable
camera, registration shows a QR code that opens the phone capture page, /verify, with your session attached.
If the API cannot be reached when you sign in, Studio still shows you as signed in with a local identity, but the API refuses requests without a session token, so a publish fails at its first step. Sign in again once you are online.
02Publish from Studio
Opening the Dialog
Both publish commands are in the File menu:
| Command | Shortcut | What it publishes |
|---|---|---|
| Publish Universe | Ctrl+P | Every Space in the Universe, and its shared assets |
| Publish Space | Ctrl+Shift+P | Only the open Space, as an update to a published Universe |
The Roblox keymap preset gives Ctrl+Shift+P to the Properties
filter and moves Publish Space to Ctrl+Alt+Shift+P. Publish Space
works once the Universe has been published, as Updating One Space explains.
The Publish Dialog
The dialog's left side lists the files that will be published, with the open Space marked primary. The right side holds the listing:
| Field | What it does |
|---|---|
| Simulation Name | The listing name. It starts as the Universe folder's name, and Publish stays disabled while it is empty. |
| Description | The listing description. |
| Genre | All, Adventure, Building, Comedy, Fighting, FPS, Horror, Medieval, Military, Naval, RPG, Sci-Fi, Sports, Town and City, or Western. |
| Public | On by default. On: Anyone can play, once review approves. Off: Only you can access. |
| Share Source | Allow source access and reuse. Studio records the choice in the Space's publish manifest and in the review dossier; the listing on the website does not carry it yet. |
A line under the fields names the storage target, Cloudflare R2. The button reads Publish, and Publishing while the upload runs.
While It Uploads
Publish saves the Space first, shows Publishing Universe: baking every Space and uploading what changed. and does the rest on a background thread, so you can keep working. Four problems stop it before anything is uploaded:
- No open Space: Publish requires an open Space folder.
- No session: Sign in to publish.
- A Space still opening: The Space is still opening. Publish again in a moment.
- A Website reference that does not resolve: the message names the reference and the nearest candidates. See Website Service.
When nothing changed since the last publish, neither the world nor the listing text, Studio uploads nothing and reports No changes since the last publish.
Studio does not show the outcome in the editor yet. The final line goes to the
engine log, ~/.eustress_engine/logs/engine-<pid>.log: Published successfully: followed by the listing id and a summary of
the review, or Publish failed: followed by the reason. The
Projects page on eustress.dev shows the same status.
03What Gets Uploaded
The World as Chunks
Studio bakes each Space into .echk chunks: small containers of
files, each named by the BLAKE3 hash of its bytes. A Space's entities are read
from its database in their current state, so edits you have not saved to a file
still go up; files that exist only in the Space folder, such as meshes an import
wrote, are added to them. Entities are grouped by position into 256 m squares, so a
change in one part of a map changes only that part's chunk. Files with no position
(scripts, meshes, textures) share a chunk, split whenever it grows past 64 MB, and
the Universe's shared assets folder goes up in chunks of about
32 MB.
A publish leaves out:
- Hidden folders: any name starting with a dot, such as
.eustressand.git - The database files themselves:
world.fjalldbandheader.bin, whose content the chunks already carry - Operating system and temporary files:
Thumbs.db,desktop.ini, and anything ending in.lockor.tmp
A manifest lists every Space's chunks, the asset chunks, and the Space players
open first. Its BLAKE3 hash is the publish's content id, in the form blake3:<hex>. The API records it; the Player checks every chunk it
downloads against its name, so a chunk that does not match is never opened.
A chunk goes up in a single request, and a request carries at most 95 MB. Chunks are split to stay under that, but a single file larger than it cannot be split, and the publish stops naming it.
Upload and Storage
- Find or create the listing. The first publish of a Universe creates its listing and keeps the id in the
Universe's
.eustress/sync.tomlat once, so a publish that fails halfway retries into the same listing. Every later publish goes to that listing. Every listing records 10 as its player limit. - Compare. Studio sends the manifest, and the API answers with the chunks it does not hold yet.
- Upload the new chunks. Only those, one request each. The API checks each is an
.echkcontainer and stores it in Cloudflare R2 atuniverses/<id>/chunks/<hash>.echk. A republish that changed one building uploads one chunk. - Commit. The API confirms every chunk is stored at the size the manifest gives, stores the manifest, and points the listing at it. New content or new listing text puts the listing back to pending review.
- Upload the Website manifest, when the Space has a Website service. If this upload fails, the publish fails, so a website never keeps showing numbers from the previous publish.
- Upload the thumbnail. The first of
thumbnail.png,thumbnail.webpandthumbnail.jpgfound in the Universe's.eustressfolder, up to 5 MB. - Submit for review. Covered in the next section. If submission does not go through, the listing stays pending while the world is already stored.
Unless .eustress/thumbnail.png was written in the last 5
minutes, Studio captures the viewport, scales it to 512 by 288 pixels and
saves it there, replacing the file. Because the PNG is uploaded ahead of
any WebP or JPEG, the thumbnail is normally whatever the viewport shows
when you publish.
Files Publishing Writes
A publish leaves these files behind, all of them inside the Universe folder:
| File | Location | Holds |
|---|---|---|
thumbnail.png | Universe .eustress/ | The viewport capture, 512 by 288 |
publish/ | Universe .eustress/ | The baked chunks, kept so an unchanged Space is not rewritten |
.last_publish_hash | Universe .eustress/ | The BLAKE3 hash of the last committed manifest |
.last_publish_state | Universe .eustress/ | What the last publish sent, so an unchanged one is skipped |
moderation-dossier.json | Universe .eustress/ | The evidence review reads |
capture-0.png to capture-3.png | Universe .eustress/moderation/ | The review views |
sync.toml | Universe and open Space .eustress/ | The listing id (experience_id) |
publish.toml, publish-journal.toml | Universe .eustress/ | The listing fields and visibility you chose, and publish checkpoints |
04Review
What Review Reads
Review is the gate between an upload and the Gallery. It never runs your Universe: Studio builds the evidence from the live scene while it publishes, and the API judges that.
- The dossier: a measured digest of the scene (counts, bounds, hierarchy, how varied its materials and colors are, how much is left at defaults, how many parts duplicate each other), the text people will read, the scripts, the asset names, and any links or contact details found in them.
- Views of the scene: the off-screen AI camera captures the scene from several angles while the upload runs, framing all of it.
The Review Ladder
The API runs the cheapest checks first, and each layer decides how much of the next one runs:
| Layer | Reads | Can decide |
|---|---|---|
| L0 Deterministic | The stored world and the dossier digest | Flag empty or default-only scenes |
| L1 Text classifier (Jev, from TypeSafe) | The dossier text | Quarantine, hold, reject or ask for changes; it never approves |
| L2a Judge (xAI Grok) | The views and a case summary | Approve, reject or hold under the Eustress AI Guardian Policy, version 1.2 |
| L2b Agent | The case record and the moderation playbook | Settle gray-band cases through guarded tools, in at most 4 rounds |
| L3 People | Everything | Every hold and quarantine, every legal-lane call, and appeals the agent does not settle |
Two rules are enforced in code rather than in prompts: nothing is listed without a recorded decision, and no model can approve content in a hard category. Those categories are sexual content involving minors, terrorism or extremist promotion, planning of mass-casualty attacks, intimate or sexual imagery of real people shared without consent, and doxxing or targeted harassment. Only a person can clear them.
As of September 2026 this review gate is built and tested in code but not yet deployed to the live API. Review Goes Live lists the remaining steps.
Outcomes and Ratings
Studio polls the outcome for about 18 seconds and puts it in the summary line of the log:
| Status | Meaning | Summary in the log |
|---|---|---|
approved | Passed review. Listed in the Gallery if Public; otherwise only you can open it. | listed in the Gallery with the rating, or approved, private |
rejected | Not listed, with a suggested edit. | not listed and the edit |
changes_requested | Reads as directed at children under 13. Remove off-platform links, contact details, personal-data collection and chance-based or real-money mechanics, or describe it for an older audience. | changes requested before listing |
held | Waiting for a person. | held for human review |
quarantined | Under legal review. Nobody but an administrator can download it, and publishing from your account pauses until a person releases it. | under legal review |
pending | No decision yet. | review pending |
An approved listing carries one of four ratings: all_ages, teen_13, mature_17 or adult_18, with teen_13 when the judge gives none. The API accepts one
open appeal at a time for a listing that was rejected, held, quarantined or asked
for changes, with 10 to 2,000 characters of explanation. The website has no appeal
form yet.
05On the Website
The Gallery
The Gallery lists a simulation only when two things are
true: you marked it Public, and review approved it. The same rule gates the listing
page, the world download, the play request and the thumbnail. The Gallery's API
returns eligible listings newest first. Its featured shelf holds approved listings
that review marked as featured, either through the judge's quality grade or when a
reviewer approves, and never one rated adult_18.
Listing and Play Pages
Each listing has a page at eustress.dev/simulation/<id> with its
name, creator, description and visit count. The API returns a listing that is not
approved only to its author and to administrators; anyone else gets the same
not-found answer as for an id that does not exist.
The listing page's Play Now button opens a dialog titled Eustress Player Required, with a download link and a Try Again button
that opens an eustress://play/<id> link. eustress.dev/play/<id> counts a visit (the number the listing
shows) and gives the same link, with the command that opens the simulation in
the Eustress Player:
eustress-client --sim <id>The Player downloads the world's manifest and every chunk it has not cached, checks each chunk against its name, and opens the Space you had open when you published. A published simulation plays solo.
The Studio installer registers eustress:// links for
Studio, and it does not include the Player, so the link does not open a
simulation today. The Player builds from source as eustress-client.
Your Projects
The Projects page on eustress.dev lists everything you have published, with a status badge:
| Badge | Review states |
|---|---|
| Published | Approved and Public, so listed in the Gallery |
| Under Review | Pending, classifying, held, appealed, quarantined or changes requested |
| Draft | Rejected, ready to fix and publish again; a private listing that passed review also shows here |
06Updates
Publishing Again
Publishing a Universe again updates its listing: same id, same page, and only the chunks that changed go up. The API counts the listing's version up by one each time the world changes. New content goes back to review, and the listing leaves the Gallery until review approves it again, so publish when a version is ready for people to see.
When the kept id names a listing that was removed, or one that belongs to another account (a Universe folder copied from someone else), Studio creates a new listing and keeps its id instead.
Updating One Space
Publish Space updates one Space of a published Universe. It bakes only the open Space, swaps it into the world the listing already plays, keeps every other Space exactly as published, and refreshes the shared assets. The listing returns to pending review, because the content that people see has changed.
Publish Space reads the listing id (experience_id) from the
Universe's .eustress/sync.toml and stops with Publish the Universe first before publishing individual Spaces. when
it is missing. A listing published before chunks existed must be published as a
whole Universe once before its Spaces can be updated one at a time.
07What's Next
Removing a Listing
The API has no route to delete or unpublish a listing yet. Chunks a republish stops naming stay in storage until a cleanup job removes them; the Player only ever downloads the chunks the current manifest names.
Review Goes Live
Deploying the review gate comes next: setting its classifier key, reviewing older listings that predate the gate (25 per nightly run, oldest first, hidden until then), working the first held cases by hand, and calibrating the thresholds on real publishes. After that, the API will compute its own scene digest from the uploaded package, and perceptual hashes of the review views will block re-uploads of removed content.
Playing Published Worlds
The Player opens a published simulation today from its command line. Next: the
installer ships the Player and registers eustress://play/ links
to it, so Play Now opens the simulation; a browser build plays it on the listing
page; and a listing can have hosted sessions that players join together. The
multiplayer phases are listed on the Networking page.
Publish from Studio today. Play Now in the Player and in the browser comes next.