> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfacearea.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish a world

> Push a world's source as a content-hashed version, pull one back down, propose a change to a world you do not own, and create branches and tags, all from Node.

The hub half of `@withgateway/sdk/worlds` publishes and retrieves world source. A world version is content-addressed, so pushing an unchanged tree files nothing new and every run can name exactly what it ran.

The `gateway` CLI runs on the same code.

## Push a checkout as a version

`push(dir, opts?)` hashes the directory, uploads the bundle, and files a version on a branch.

```typescript theme={null}
import { push } from "@withgateway/sdk/worlds";

const version = await push("./worlds/acme-billing", {
  slug: "acme-billing",
  branch: "main",
  message: "add the late-fee scenario",
});

console.log(version.versionId, version.contentHash, version.alreadyUploaded);
```

| Option | Type | Meaning |
| - | - | - |
| `slug` | `string` | The world to publish as; defaults to the `pyproject` name, then the directory name |
| `branch` | `string` | Branch to file the version on; defaults to the local metadata's branch, then `main` |
| `message` | `string` | The version's message |
| `author` | `string` | Who pushed |
| `baseVersionId` | `string \| null` | Push on top of this version instead of the local metadata's base |
| `force` | `boolean` | Push despite a conflicting base |
| `name`, `description`, `tags`, `visibility` | | World metadata recorded on resolve |
| `imageRef` | `string` | A pre-built image reference, for image and agentic worlds |
| `options` | `WorldsClientOptions` | Host and keys |

The return value is `{ containerId, slug, versionId, contentHash, branch, state, alreadyUploaded }`. `alreadyUploaded` is true when the content hash already existed, so re-pushing an unchanged tree is free.

<Info>
  A continuous integration checkout carries no local metadata, so a re-run would conflict with the version its own previous run created. Resolve the branch tip first and pass it as `baseVersionId`. [`validateWorld`](/sdk-ts/ci) already does exactly that.
</Info>

## Pull a world down

`pull(slugRef, target?, options?)` resolves a slug or `slug@ref`, downloads the bundle, verifies its checksum, and extracts it.

```typescript theme={null}
import { pull } from "@withgateway/sdk/worlds";

const result = await pull("acme-billing@main", "./worlds/acme-billing");
console.log(result.dir, result.versionId, result.files);
```

Without a `target`, the directory is the slug, with a numeric suffix added when that name is taken. The ref defaults to `latest`.

`pull` writes local metadata under `.gateway/` recording the slug, branch, version and a hash of every file. The metadata is what lets `propose` know which files you changed.

<Info>
  Pulling is for authoring a world. To run against one, resolve it with [`openWorld`](/sdk-ts/worlds) instead; running needs no bundle on disk.
</Info>

## Propose a change to a world you do not own

`propose(dir, { title, reason, targetBranch?, options? })` sends the edits in a pulled directory as a proposal for the world's owner to review.

```typescript theme={null}
import { propose } from "@withgateway/sdk/worlds";

const { proposalId } = await propose("./worlds/acme-billing", {
  title: "Late fees should compound monthly",
  reason: "Matches the vendor's published schedule as of March.",
});
```

The directory must have been pulled, so there is a base to compare against, and it must actually differ from that base. Both cases throw with a message saying which one happened.

## Create a branch or a tag

`createRef(slug, name, kind, versionId?, options?)` points a named ref at a version. `kind` is `"branch"` or `"tag"`.

```typescript theme={null}
import { createRef } from "@withgateway/sdk/worlds";

await createRef("acme-billing", "v1.4.0", "tag", version.versionId);
```

Without a `versionId` the ref points at the world's current head.

## Inspect a tree before pushing it

Four helpers expose the content-hash rules.

```typescript theme={null}
import {
  computeContentHash,
  collectArchiveRelPaths,
  localTreeState,
  readLocalMeta,
} from "@withgateway/sdk/worlds";

console.log(computeContentHash("./worlds/acme-billing"));
console.log(collectArchiveRelPaths("./worlds/acme-billing")); // exactly what is hashed

const state = localTreeState("./worlds/acme-billing");
console.log(state.clean, state.dirty);   // files changed since the pull
console.log(readLocalMeta("./worlds/acme-billing"));
```

`collectArchiveRelPaths` returns the sorted list of files that go into the hash. Dot-files, `__pycache__` and the excluded root directories are left out, which is why a stray build directory does not change a version.

## Where to go next

* [Gate a pull request](/sdk-ts/ci) for push, dispatch and gate as one call.
* [Worlds hub](/sdk/benchmark-hub) for the same operations in Python.
* [The gateway CLI](/cli/bench) for `gateway bench push` and the version commands.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.