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

> ## Agent Instructions
> These docs target Mesh SDK v0.27.0. Match the installed package and binding before generating code. Start at /getting-started/agents for task-specific reading paths.
> Call the company and product Offline Protocol, never Offline alone. Current packages: @offline-protocol/mesh-sdk 0.27.0 (React Native), @offline-protocol/id-react 0.2.0, @offline-protocol/id-react-native 0.3.3, @offline-protocol/pol 0.1.2 and @offline-protocol/cli 0.2.6. Canonical docs URLs start with https://www.offlineprotocol.com/docs.
> The Mesh SDK runs in a native app or gateway. A browser OfflineID SDK integration does not provide browser mesh transport. Local mesh operation does not require a portal API key.
> Service RPC is signed plaintext in v0.27.0. Message delivery, durable local acceptance and backend commit are distinct outcomes. Use the workflow guide for the required application logic.
> Offline Protocol CLI 0.2.6 is on npm (@offline-protocol/cli, command offline). Its local MCP server runs with offline mcp serve and requires no login or key. Hosted MCP is at https://mcp.offlineprotocol.com/mcp with an application API key in Authorization: Bearer and a matching x-app-id; organization keys are rejected. Follow /tools/overview for setup and do not invent commands beyond it. MCP provides integration context and planning, not mesh execution; file-writing tools are local only.
> Phone Wi-Fi Direct and MultipeerConnectivity carry no data in v0.27.0. Use BLE or a provisioned relay. The receiver core ACKs before application persistence; use application acceptance for durable workflows.
> Proof of Location is Sepolia testnet witness evidence, not zero-knowledge proof or proof of presence. The geohash is public onchain. Read /proof-of-location/security before integration.

# DataStore methods

> Exact document, map, list, text, counter and attachment signatures for local shared state.

Import `DataStore` from `@offline-protocol/mesh-sdk`. These signatures match **v0.27.0**. Read [the integration guide](/docs/guides/shared-state) for prerequisites and complete usage.

Start the protocol and establish the MLS space first. A local flush is not peer acknowledgment. See [replication semantics](/docs/mesh-sdk/data).

## Methods on this page

* [`createDoc`](#createdoc)
* [`deleteDoc`](#deletedoc)
* [`removeDoc`](#removedoc)
* [`removeSpace`](#removespace)
* [`fetchAttachmentFrom`](#fetchattachmentfrom)
* [`setInterest`](#setinterest)
* [`listDocs`](#listdocs)
* [`listSpaces`](#listspaces)
* [`mapSet`](#mapset)
* [`mapDelete`](#mapdelete)
* [`mapGet`](#mapget)
* [`listPush`](#listpush)
* [`listDelete`](#listdelete)
* [`listLength`](#listlength)
* [`textInsert`](#textinsert)
* [`textDelete`](#textdelete)
* [`textValue`](#textvalue)
* [`counterIncrement`](#counterincrement)
* [`counterValue`](#countervalue)
* [`docJson`](#docjson)
* [`exportRaw`](#exportraw)
* [`flush`](#flush)
* [`flushAll`](#flushall)
* [`docSize`](#docsize)
* [`wipeAll`](#wipeall)
* [`attachmentHash`](#attachmenthash)
* [`fetchAttachment`](#fetchattachment)
* [`provideAttachment`](#provideattachment)
* [`declineAttachment`](#declineattachment)

## createDoc

Creates a document, or does nothing if it already exists.

```typescript theme={null}
createDoc(spaceId: string, docId: string): Promise<void>
```

## deleteDoc

Drops this device's copy, leaving the peers' copies alone.

Eviction rather than removal: it reclaims what the document occupies
here and records nothing about the name, so the next version exchange
with a replica that still holds it recreates and refills it. Use
`removeDoc` to remove content from the room.

```typescript theme={null}
deleteDoc(spaceId: string, docId: string): Promise<void>
```

## removeDoc

Removes a document from every replica of its space.

A replica holding an edit made concurrently with the removal keeps its
copy and hands the document back, contents and all, so an edit beats a
removal whichever happened first. That arrives as an ordinary
`data_changed` on a document you removed.

Every later call on the name rejects with `InvalidState` until
`createDoc` brings it back, on this device and on every device
that learns of the removal. `data_doc_removed` is the cue to close
whatever has the document open.

Removing a name this device does not hold does nothing. Re-using the
name afterwards is not fenced off: a replica that kept the old contents
past the removal merges them into the new document. Give new content a
fresh name.

```typescript theme={null}
removeDoc(spaceId: string, docId: string): Promise<void>
```

## removeSpace

Removes every document a space holds, from every replica of it.

```typescript theme={null}
removeSpace(spaceId: string): Promise<void>
```

## fetchAttachmentFrom

Asks one member of a group for the bytes behind a reference.

A reference replicates to everybody and says nothing about who holds the
bytes, so a group fetch names the member; the SDK does not try members
in turn, because each miss costs the whole silence timeout. The bytes
come back as frames under the group key, so no pairwise session with
that member is needed, and they are bounded at 1 MiB. Anything larger
travels over a 1:1 session, and the holder is told so at the call.

Use `fetchAttachment` for a 1:1 space.

```typescript theme={null}
fetchAttachmentFrom(spaceId: string, peerId: string, hash: string): Promise<void>
```

## setInterest

The documents this device wants from its peers in one space.

Each pattern is a document name, optionally ending in `*` to match a
prefix. `['*']` is everything and is the default; `[]` is nothing. At
most 32 patterns per space.

Toward a peer this is a request that saves the radio. Locally it is a
refusal, so a document outside it is never stored however it arrives,
which is what makes a narrowing mean something against a peer on an
older build.

Not persisted: declare it at launch, right after `protocol.start()` resolves. Narrowing does
not delete what is already held; widening asks the peers for what it
adds.

```typescript theme={null}
setInterest(spaceId: string, patterns: string[]): Promise<void>
```

## listDocs

The documents in a space.

```typescript theme={null}
listDocs(spaceId: string): Promise<string[]>
```

## listSpaces

Every space that holds at least one document.

```typescript theme={null}
listSpaces(): Promise<string[]>
```

## mapSet

Sets a key in a map collection.

```typescript theme={null}
mapSet(spaceId: string, docId: string, collection: string, key: string, value: DataValue): Promise<void>
```

## mapDelete

Removes a key from a map collection.

```typescript theme={null}
mapDelete(spaceId: string, docId: string, collection: string, key: string): Promise<void>
```

## mapGet

Reads a key from a map collection, or null when it is absent.

```typescript theme={null}
mapGet(spaceId: string, docId: string, collection: string, key: string): Promise<DataValue | null>
```

## listPush

Appends to a list collection.

```typescript theme={null}
listPush(spaceId: string, docId: string, collection: string, value: DataValue): Promise<void>
```

## listDelete

Deletes entries from a list collection.

```typescript theme={null}
listDelete(spaceId: string, docId: string, collection: string, index: number, count: number): Promise<void>
```

## listLength

The number of entries in a list collection.

```typescript theme={null}
listLength(spaceId: string, docId: string, collection: string): Promise<number>
```

## textInsert

Inserts into a text collection.

`position` is a character offset, not a byte offset.

```typescript theme={null}
textInsert(spaceId: string, docId: string, collection: string, position: number, text: string): Promise<void>
```

## textDelete

Deletes characters from a text collection.

```typescript theme={null}
textDelete(spaceId: string, docId: string, collection: string, position: number, count: number): Promise<void>
```

## textValue

The contents of a text collection.

```typescript theme={null}
textValue(spaceId: string, docId: string, collection: string): Promise<string>
```

## counterIncrement

Adds to a counter collection. Negative amounts subtract.

```typescript theme={null}
counterIncrement(spaceId: string, docId: string, collection: string, amount: number): Promise<void>
```

## counterValue

The value of a counter collection.

```typescript theme={null}
counterValue(spaceId: string, docId: string, collection: string): Promise<number>
```

## docJson

The document's current state as plain JSON.

Half of the escape hatch: an app can always take its data and leave,
and this half needs no knowledge of the SDK to read.

```typescript theme={null}
docJson(spaceId: string, docId: string): Promise<unknown>
```

## exportRaw

The document's full history, base64-encoded.

The other half of the escape hatch. Prefer `docJson` unless the
history itself is what you need.

```typescript theme={null}
exportRaw(spaceId: string, docId: string): Promise<string>
```

## flush

Persists pending edits to a document.

Edits batch before they reach a record, so call this when the app must
know a change survives a crash. The SDK also flushes on shutdown.

```typescript theme={null}
flush(spaceId: string, docId: string): Promise<void>
```

## flushAll

Persists pending edits to every open document.

```typescript theme={null}
flushAll(): Promise<void>
```

## docSize

The compacted size of a document, in bytes.

```typescript theme={null}
docSize(spaceId: string, docId: string): Promise<number>
```

## wipeAll

Deletes every record the data layer owns.

Only needed when documents were pointed at a storage backend the app
supplied: `wipePersistedState()` clears the default provider's account
directory, which a custom backend is not inside. Skipping it there
leaves documents behind after the account that made them is gone.

Only durable once replication has stopped. A wipe records no removals,
because a logout has to leave a custom backend empty, so a peer cannot
tell a wiped space from one this device has never seen, and with the
engine running and sessions live its next version offer recreates and
refills every document, with no error and no event. Logout tears the
engine down anyway; call `destroy()` first if you are wiping for any
other reason, and only for as long as it stays stopped: the peer still
holds the documents, so they return when replication resumes. This
clears the device; `removeSpace` is what clears the room.

```typescript theme={null}
wipeAll(): Promise<void>
```

## attachmentHash

The address of some bytes, in the spelling a reference uses.

Write the result into a document with `mapSet`, which takes the value
as an object and encodes it for you:

```ts theme={null}
const hash = await store.attachmentHash(bytesBase64);
await store.mapSet(space, 'notes', 'files', 'plan', {
  kind: 'attachment', hash, size: byteLength, name: 'plan.pdf',
});
```

Compute it here rather than anywhere else. Two spellings of one address
are two addresses: they fetch twice, store twice, and compare unequal
while naming identical bytes.

```typescript theme={null}
attachmentHash(bytesBase64: string): Promise<string>
```

## fetchAttachment

Asks the peer a 1:1 space is named after for the bytes behind a
reference.

Pull rather than push, because a space may reference more bytes than a
phone wants over Bluetooth: the decision to spend that is yours, per
blob, when somebody opens one. The answer arrives later as
`data_attachment_received` or `data_attachment_unavailable`, never from
this call.

Group spaces are refused in this version: a blob rides a transfer to a
confirmed 1:1 session, and two group members need not have one.

```typescript theme={null}
fetchAttachment(spaceId: string, hash: string): Promise<void>
```

## provideAttachment

Answers a peer's `data_attachment_requested` with the bytes.

Rejects bytes that do not hash to `hash`, so a mistake reaches you while
you still have the file in hand rather than travelling the whole media
path to be refused on the other side.

```typescript theme={null}
provideAttachment(spaceId: string, peerId: string, hash: string, bytesBase64: string): Promise<void>
```

## declineAttachment

Tells a peer their request will not be answered.

Answer this way when you no longer hold the bytes. It is a real answer
rather than silence: a reference outlives the bytes it names, and without
this the asking side cannot tell a peer that lost the file from one that
is merely slow, so it shows somebody a spinner that never resolves.

```typescript theme={null}
declineAttachment(spaceId: string, peerId: string, hash: string): Promise<void>
```

## Parameter and result types

Import these types from the same SDK package. Event payloads and configuration types are linked below.

### DataValue

```typescript theme={null}
export type DataValue =
  | { kind: 'null' }
  | { kind: 'bool'; value: boolean }
  | { kind: 'int'; value: number }
  | { kind: 'float'; value: number }
  | { kind: 'text'; value: string }
  | { kind: 'bytes'; value: number[] }
  | {
      kind: 'attachment';
      hash: string;
      size: number;
      name?: string;
      mime?: string;
    };
```

## Related references

* [`data_changed`](/docs/mesh-sdk/events#data_changed)
* [`data_doc_removed`](/docs/mesh-sdk/events#data_doc_removed)
* [Event payloads](/docs/mesh-sdk/events)
* [Configuration types](/docs/mesh-sdk/configuration-types)
* [All public types](/docs/mesh-sdk/public-types)
* [Error codes](/docs/mesh-sdk/errors)
* [All API tasks](/docs/mesh-sdk/reference)
