Learn/Publishing

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.

Time13 min readLevelIntermediateUpdatedUpdated Sep 2026

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.

Publish UniverseStudiosave, bakeAPIlisting, diffR2new chunksReviewdossier, viewsGalleryif approvedA listing is served only when review approved it and you marked it Public
Studio bakes the world and uploads what changed; the API decides what the Gallery shows.

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.
Info
The age check reads your document

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.

Advanced
Signing in offline leaves you unable to publish

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:

CommandShortcutWhat it publishes
Publish UniverseCtrl+PEvery Space in the Universe, and its shared assets
Publish SpaceCtrl+Shift+POnly 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:

FieldWhat it does
Simulation NameThe listing name. It starts as the Universe folder's name, and Publish stays disabled while it is empty.
DescriptionThe listing description.
GenreAll, Adventure, Building, Comedy, Fighting, FPS, Horror, Medieval, Military, Naval, RPG, Sci-Fi, Sports, Town and City, or Western.
PublicOn by default. On: Anyone can play, once review approves. Off: Only you can access.
Share SourceAllow 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 .eustress and .git
  • The database files themselves: world.fjalldb and header.bin, whose content the chunks already carry
  • Operating system and temporary files: Thumbs.db, desktop.ini, and anything ending in .lock or .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.

Advanced
One file larger than 95 MB stops a publish

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

  1. Find or create the listing. The first publish of a Universe creates its listing and keeps the id in the Universe's .eustress/sync.toml at 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.
  2. Compare. Studio sends the manifest, and the API answers with the chunks it does not hold yet.
  3. Upload the new chunks. Only those, one request each. The API checks each is an .echk container and stores it in Cloudflare R2 at universes/<id>/chunks/<hash>.echk. A republish that changed one building uploads one chunk.
  4. 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.
  5. 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.
  6. Upload the thumbnail. The first of thumbnail.png, thumbnail.webp and thumbnail.jpg found in the Universe's .eustress folder, up to 5 MB.
  7. Submit for review. Covered in the next section. If submission does not go through, the listing stays pending while the world is already stored.
Advanced
Frame the shot before you press Publish

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:

FileLocationHolds
thumbnail.pngUniverse .eustress/The viewport capture, 512 by 288
publish/Universe .eustress/The baked chunks, kept so an unchanged Space is not rewritten
.last_publish_hashUniverse .eustress/The BLAKE3 hash of the last committed manifest
.last_publish_stateUniverse .eustress/What the last publish sent, so an unchanged one is skipped
moderation-dossier.jsonUniverse .eustress/The evidence review reads
capture-0.png to capture-3.pngUniverse .eustress/moderation/The review views
sync.tomlUniverse and open Space .eustress/The listing id (experience_id)
publish.toml, publish-journal.tomlUniverse .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:

LayerReadsCan decide
L0 DeterministicThe stored world and the dossier digestFlag empty or default-only scenes
L1 Text classifier (Jev, from TypeSafe)The dossier textQuarantine, hold, reject or ask for changes; it never approves
L2a Judge (xAI Grok)The views and a case summaryApprove, reject or hold under the Eustress AI Guardian Policy, version 1.2
L2b AgentThe case record and the moderation playbookSettle gray-band cases through guarded tools, in at most 4 rounds
L3 PeopleEverythingEvery 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.

Info
Deployment status

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:

StatusMeaningSummary in the log
approvedPassed review. Listed in the Gallery if Public; otherwise only you can open it.listed in the Gallery with the rating, or approved, private
rejectedNot listed, with a suggested edit.not listed and the edit
changes_requestedReads 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
heldWaiting for a person.held for human review
quarantinedUnder legal review. Nobody but an administrator can download it, and publishing from your account pauses until a person releases it.under legal review
pendingNo 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

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.

Info
The Player is not in the installer yet

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:

BadgeReview states
PublishedApproved and Public, so listed in the Gallery
Under ReviewPending, classifying, held, appealed, quarantined or changes requested
DraftRejected, 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.

Advanced
A new listing only when the old one is gone

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.

Loading Eustress Engine...