> ## 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.

# Describe a world and look things up by plaintext

> Learn what a world holds and how each field is stored, then find a redacted row from the plaintext you know, with the same digest rule the runtime uses.

Two facts decide how you query a world: which entities and fields it has, and how each field is
stored. A world built from captured vendor data keeps identifying fields hashed, and secrets
dropped, per its `[redact]` policy. `describeWorld` reports that per field, and `storedForms`
turns a plaintext you know into the value the store actually holds.

## Describe

```ts theme={null}
import { describeWorld } from "@withgateway/sdk/worlds";

const world = await describeWorld("spycloud-world");

for (const entity of world.entities) {
  console.log(entity.name, entity.primaryKey, entity.rowCount);
  for (const field of entity.fields) {
    console.log("  ", field.name, field.type, field.redaction ?? "clear");
  }
}
```

`describeWorld(slug, options?)` reads the world's head version through `GET /api/public/worlds/{slug}`
and returns a `WorldDescription`. Pass the slug alone.

The fields that matter for querying:

| Field | What it holds |
| - | - |
| `entities[].name`, `primaryKey`, `unique`, `relationships` | The data model, as the world's contract declares it. |
| `entities[].rowCount` | Rows in the head version, or `null` when the platform does not count this kind of world. |
| `entities[].fields[].redaction` | `"hash"`, `"preserve"`, `"round:<n>"`, `"drop"`, or `null` for a field stored in the clear. |
| `entities[].fields[].enum` | The values the schema allows, when it lists them; absent otherwise. `gateway worlds describe` prints them after the type: `status: string  one of: requested \| approved \| rejected`. |
| `redact` | The world's `[redact]` policy: `slug`, `hash`, `drop`, `preserve`, `round`. `null` when the world has no vendor connection. |

The same document is what `gateway worlds describe <slug>` prints, and what it prints for a local
world directory through the runtime. See [the CLI page](/cli/worlds#put-rows-into-a-world).

## Look up a hashed field by its plaintext

```ts theme={null}
import { describeWorld, storedForms } from "@withgateway/sdk/worlds";

const world = await describeWorld("spycloud-world");

// The value the store holds for this email, under this world's policy.
const [stored] = storedForms(world, "email", "paul.wiggan@sky.com");
// "sha256:6ce08ee95b065f3a61afe81f81af55045500fcd9fae6fd420ebecbfaddd9f389"
```

`storedForms(world, field, plaintext)` returns every value the store may hold for that field given
that plaintext:

| Field is stored | Returns |
| - | - |
| in the clear | `[plaintext]` |
| `hash` | `["sha256:" + sha256("<salt>:<plaintext>")]` |
| `preserve` | `[shaped]`: same length, digits stay digits, letters stay letters of the same case |
| `round:n` | `[round(plaintext, n)]` for a number, `[plaintext]` otherwise |
| `drop` | `[]`: the field is not on disk. A lookup on it is a documented miss, not an error. |

The rule is the one the runtime hashed with, byte for byte, so a stored form you compute here
matches a row the world captured from the vendor. No lowercasing, no trimming: the plaintext is
hashed exactly as given.

<Info>
  The salt is the **connector's** slug, from `connector.toml`, not the world's platform slug. They
  can differ: the `spycloud-world` world hashes under `spycloud`. `storedForms` reads the salt from
  `world.redact.slug`, which `describeWorld` supplies, so pass the description through unchanged.
  Building the policy by hand and salting with the platform slug produces digests that never
  match a row.
</Info>

`redactionOf(world.redact, field)` returns which policy a field falls under, ranked the way the
runtime applies them.

[Keep real data out](/worlds/redaction) covers the author's side: how `hash`, `drop`, `preserve`
and `round` are declared in `connector.toml`, which fields each one matches, and how to redact
rows on the way in.

## Which fields answer a plaintext query

A field marked `hash` or `preserve` answers a plaintext lookup, through `storedForms`. A field in
the clear answers the plaintext as given. A field marked `drop` answers nothing, by policy: it was
never written. Plan your queries from `describeWorld` first, and never import plaintext into a
field the policy hashes.

You rarely call `storedForms` yourself. `queryRows(slug, entity, { where, resolve: true })` does
the resolution for every value in `where` and runs the query, and its `resolved` field reports
the form each value became, so a miss on a dropped field is explainable:

```ts theme={null}
import { queryRows } from "@withgateway/sdk/worlds";

const page = await queryRows("spycloud-world", "spycloud_breach_records", {
  where: { email: "paul.wiggan@sky.com" },
  resolve: true,
  limit: 50,
});
console.log(page.total, page.rows.length, page.resolved);
```

## Where to go next

* [Worlds](/sdk-ts/worlds) to open a world and dispatch a run.
* [World sessions](/sdk-ts/sessions) to drive one task against a live copy.
* [Put data in a world](/worlds/data) for the rows contract and the import path.
* [Keep real data out](/worlds/redaction) for declaring the policy these functions read.


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