Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ skills-lock.json

# Swift / SwiftPM build artifacts
.build/
.smoke-expo-moss/
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ sdks/
python/sdk/ — Python SDK (PyPI: moss), Python 3.10+
javascript/sdk/ — JS/TS SDK (npm: @moss-dev/moss), ESM-only
elixir/sdk/ — Elixir SDK (Hex: moss)
swift/ — Swift SDK for iOS (wraps Moss.xcframework)
react-native/ — Expo / React Native module (@moss-dev/moss-react-native); iOS native, Android stub
examples/
python/ — Standalone Python usage examples
javascript/ — Standalone TS usage examples
javascript-web/ — Browser/Vite examples (no Node runtime)
react-native/ — Expo usage sketch for @moss-dev/moss-react-native
c/ — C binding examples
go/ — Standalone Go SDK usage examples
bun/ — Bun runtime example
Expand Down Expand Up @@ -223,6 +226,17 @@ mix deps.get
mix test
```

### React Native / Expo module (`sdks/react-native/`)

```bash
cd sdks/react-native
npm install
npm run build # tsc → build/
```

iOS links `Moss.xcframework` (downloaded at `pod install` via `ios/scripts/download-moss-xcframework.sh`).
Android is a stub until [#411](https://github.com/usemoss/moss/issues/411). Requires Expo prebuild / a dev client — not Expo Go.

## Architecture: Two-Layer Design

Every SDK has the same two-layer structure:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

Moss is a sub-10 ms semantic search runtime built for Conversational AI agents. Hybrid retrieval (semantic + Keyword Search), built-in embeddings, metadata filtering, and a WebAssembly build that runs in the browser - all from a single SDK that embeds in your application.

No network hop on the hot path. No clusters to tune. Point the SDK at Moss Cloud, load your index, and query it in **under 10 ms**. Python, TypeScript, Elixir, and C.
No network hop on the hot path. No clusters to tune. Point the SDK at Moss Cloud, load your index, and query it in **under 10 ms**. Python, TypeScript, Elixir, C, Swift, and React Native / Expo.

![Moss Python walkthrough](https://github.com/user-attachments/assets/d826023d-92d6-49ac-8e5e-81cf04d409c5)

Expand Down Expand Up @@ -116,7 +116,7 @@ Moss isn't a database! It's a **search runtime**. You don't manage clusters, tun
- **Runs in the browser too** - separate WebAssembly SDK ([`@moss-dev/moss-web`](https://www.npmjs.com/package/@moss-dev/moss-web)) for client-side semantic search with no server
- **Database connectors** - ingest directly from SQLite, MongoDB, MySQL, and Supabase ([`packages/moss-data-connector/`](packages/moss-data-connector/))
- **CLI** - manage indexes and query from the terminal ([`packages/moss-cli/`](packages/moss-cli/))
- **SDKs** - Python (3.10+), TypeScript / Node.js (20+), Elixir, and C ([`libmoss`](https://github.com/usemoss/moss/releases))
- **SDKs** - Python (3.10+), TypeScript / Node.js (20+), Elixir, C ([`libmoss`](https://github.com/usemoss/moss/releases)), Swift (iOS), and React Native / Expo (`@moss-dev/moss-react-native`)
- **Framework integrations** - LangChain, DSPy, LlamaIndex, Pipecat, LiveKit, Vapi, ElevenLabs, Strands Agents

## Examples
Expand Down Expand Up @@ -274,7 +274,7 @@ Full Python SDK source code is available at [`sdks/python/`](sdks/python/).

Here's where the community can have the most impact:

- **New SDK bindings** — Swift, Go, Elixir,...
- **New SDK bindings** — Kotlin/Android, Rust,… React Native / Expo module shipped under `sdks/react-native/`
- **Framework integrations** — CrewAI, Haystack, AutoGen
- **Reranking support** — plug in cross-encoder rerankers
- **Doc-parsing connectors** — PDF, DOCX, HTML, Markdown ingestion
Expand Down
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ These are well-scoped and ready for contributors. Each one has (or will have) a
- [ ] **Swift bindings** — for iOS/macOS apps with on-device retrieval ([`good first issue`](https://github.com/usemoss/moss/labels/good%20first%20issue))
- [ ] **Rust bindings** — for performance-critical pipelines ([`good first issue`](https://github.com/usemoss/moss/labels/good%20first%20issue))
- [ ] **Kotlin bindings** — for Android apps and Spring Boot backend services ([`good first issue`](https://github.com/usemoss/moss/labels/good%20first%20issue))
- [x] **React Native / Expo module** — iOS via `Moss.xcframework` + Expo config plugin (`sdks/react-native/`, `@moss-dev/moss-react-native`); Android pending [#411](https://github.com/usemoss/moss/issues/411) ([#432](https://github.com/usemoss/moss/issues/432))

### Voice AI Ecosystem

Expand Down
93 changes: 93 additions & 0 deletions examples/react-native/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# React Native / Expo example (Moss)

Minimal usage sketch for [`@moss-dev/moss-react-native`](../../sdks/react-native/).

This is not a full Expo app (no `node_modules` committed). Scaffold your own with:

```bash
npx create-expo-app moss-rn-demo
cd moss-rn-demo
npx expo install @moss-dev/moss-react-native
```

Add the plugin in `app.json`:

```json
{
"expo": {
"plugins": ["@moss-dev/moss-react-native"]
}
}
```

Then:

```bash
npx expo prebuild
npx expo run:ios
```

## Example screen

```tsx
import { useEffect, useState } from 'react';
import { Button, ScrollView, Text, View } from 'react-native';
import { MossClient, type SearchResult } from '@moss-dev/moss-react-native';

// Development build only. `EXPO_PUBLIC_*` values are inlined into the shipped JS
// bundle, so a project key set this way is readable by anyone with the app and
// grants mutating access (createIndex / addDocs / deleteIndex). Do not ship it
// in a production app — see the package README's "Credentials" section.
const PROJECT_ID = process.env.EXPO_PUBLIC_MOSS_PROJECT_ID!;
const PROJECT_KEY = process.env.EXPO_PUBLIC_MOSS_PROJECT_KEY!;

export default function App() {
const [result, setResult] = useState<SearchResult | null>(null);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const client = new MossClient(PROJECT_ID, PROJECT_KEY);
let cancelled = false;

(async () => {
try {
await client.createIndex('rn-demo', [
{ id: '1', text: 'Refunds are processed within 3-5 business days.' },
{ id: '2', text: 'Shipping usually takes 2 business days.' },
]);
await client.loadIndex('rn-demo');
const search = await client.query('rn-demo', 'how long do refunds take?');
if (!cancelled) setResult(search);
} catch (e) {
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
} finally {
client.close();
}
})();

return () => {
cancelled = true;
};
}, []);

return (
<ScrollView contentContainerStyle={{ padding: 24, gap: 12 }}>
<Text style={{ fontSize: 22, fontWeight: '600' }}>Moss RN demo</Text>
{error ? <Text style={{ color: 'crimson' }}>{error}</Text> : null}
{result?.docs.map((doc) => (
<View key={doc.id}>
<Text>
[{doc.score.toFixed(3)}] {doc.text}
</Text>
</View>
))}
<Button title={`SDK ${MossClient.sdkVersion}`} onPress={() => {}} />
</ScrollView>
);
}
```

## Notes

- iOS only for on-device query today. Android throws until [#411](https://github.com/usemoss/moss/issues/411) lands.
- Requires a development build — Expo Go cannot load custom native modules.
9 changes: 9 additions & 0 deletions sdks/react-native/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/
build/
.DS_Store
*.tgz
ios/Frameworks/
ios/Moss.xcframework/
*.xcframework/
.expo/
package-lock.json
7 changes: 7 additions & 0 deletions sdks/react-native/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
ios/Frameworks/
ios/Moss.xcframework/
*.tgz
.DS_Store
tsconfig.json
.gitignore
25 changes: 25 additions & 0 deletions sdks/react-native/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BSD 2-Clause License

Copyright (c) 2026, Moss Team
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
182 changes: 182 additions & 0 deletions sdks/react-native/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# @moss-dev/moss-react-native

React Native / Expo module for [Moss](https://github.com/usemoss/moss) — on-device semantic search.

Closes the gap described in [usemoss/moss#432](https://github.com/usemoss/moss/issues/432).

## Status

| Platform | Support |
|----------|---------|
| **iOS** | Native via `Moss.xcframework` (same binary as the Swift SDK, release `v0.6.2`) |
| **Android** | Stub — throws until Android native builds land ([#411](https://github.com/usemoss/moss/issues/411)) |
| **Expo Go** | Not supported (custom native code; use a [dev client](https://docs.expo.dev/develop/development-builds/introduction/) / `expo prebuild`) |

## Install

```bash
npx expo install @moss-dev/moss-react-native
```

Add the config plugin in `app.json` / `app.config.js`:

```json
{
"expo": {
"plugins": ["@moss-dev/moss-react-native"]
}
}
```

Then regenerate native projects:

```bash
npx expo prebuild
npx pod-install
```

CocoaPods downloads `Moss.xcframework` during `pod install` (checksum-verified against the Swift SDK release).

## Quick start

> [!WARNING]
> The snippet below reads the project key from `EXPO_PUBLIC_*`, which is fine
> for a local dev build but **must not ship in a production app**. See
> [Credentials](#credentials) before you release.

```tsx
import { MossClient } from '@moss-dev/moss-react-native';

// Development builds only — see the Credentials section below.
const client = new MossClient(process.env.EXPO_PUBLIC_MOSS_PROJECT_ID!, process.env.EXPO_PUBLIC_MOSS_PROJECT_KEY!);
Comment thread
samanyugoyal2010 marked this conversation as resolved.

await client.createIndex('support-docs', [
{ id: '1', text: 'Refunds are processed within 3-5 business days.' },
{ id: '2', text: 'You can track your order on the dashboard.' },
]);

await client.loadIndex('support-docs');
const result = await client.query('support-docs', 'how long do refunds take?');
for (const doc of result.docs) {
console.log(`[${doc.score.toFixed(3)}] ${doc.text}`);
}

client.close();
```

## Credentials

Anything in an `EXPO_PUBLIC_*` variable is **inlined into the JS bundle at build
time**. It is shipped to every user and can be read straight out of the app —
it is not a secret. A project key exposed that way is usable by anyone who
extracts it, and `MossClient` also exposes mutating calls (`createIndex`,
`addDocs`, `deleteIndex`), so a leaked key is not merely read access to your
project.

So:

- **Development / internal builds** — `EXPO_PUBLIC_MOSS_PROJECT_KEY` is fine.
- **Production apps** — do not embed a project key. Use the token form below.

### Short-lived tokens (production)

Pass `getAuthToken` instead of a project key. It is called whenever the native
runtime needs a bearer token, so your backend mints a short-lived, scoped one
and nothing long-lived is ever in the bundle:

```ts
const client = new MossClient({
projectId: process.env.EXPO_PUBLIC_MOSS_PROJECT_ID!,
getAuthToken: async () => {
const res = await fetch('https://api.example.com/moss-token', {
headers: { Authorization: `Bearer ${await mySessionToken()}` },
});
const { token } = await res.json();
return token; // raw token — do NOT prefix with "Bearer "
},
});
```

Notes:

- Return the **raw token**. The native side builds the
`Authorization: Bearer <token>` header itself.
- `getAuthToken` may be called from a background thread and more than once —
cache until expiry if the round trip is expensive.
- Throwing (or returning a non-string / empty string) fails the in-flight
request with your error rather than hanging it.
- `projectId` is not a secret; only the key is.

This is the same mechanism as the Swift SDK's `Authenticator`, wired through
`moss_client_new_with_authenticator` in the native ABI.

## API

Mirrors the Node `@moss-dev/moss` client for the core cloud + local query loop:

- `new MossClient(projectId, projectKey)` (development builds)
- `new MossClient({ projectId, getAuthToken, baseUrl? })` (short-lived tokens; see [Credentials](#credentials))
- `createIndex(name, docs, options?)`
- `addDocs(name, docs, options?)`
- `loadIndex(name, options?)` / `unloadIndex(name)`
- `query(name, query, options?)`
- `listIndexes()` / `getIndex(name)` / `deleteIndex(name)`
- `close()`
- `MossClient.sdkVersion`
- `MossClient.setModelCacheDir(path)` (optional; iOS defaults to `Library/Caches/moss-models`)

The Swift SDK's `Authenticator` is bridged (see [Credentials](#credentials)).
Session APIs remain out of scope for this first release.

### Metadata filters

`query` takes the same `filter` shape as `@moss-dev/moss`:

```ts
await client.query('support-docs', 'refund timing', {
filter: { field: 'locale', condition: { $eq: 'en-US' } },
});
```

Pass `filterJson` instead if you already hold the engine's serialized form.
Supplying both is an error rather than a silent precedence rule.

### Custom embeddings

Passing documents with an `embedding` makes `createIndex` select `modelId: 'custom'`,
which means there is no on-device model to embed query text with. Supply the query
vector yourself:

```ts
await client.createIndex('vectors', [
{ id: '1', text: 'Refunds take 3-5 business days.', embedding: myVector },
]);
await client.loadIndex('vectors');

const result = await client.query('vectors', 'refund timing', {
embedding: myQueryVector, // must match the index dimensionality
});
```

## Requirements

- Expo SDK 54+ (or a React Native app with Expo Modules)
- iOS 16.4+ (Expo SDK 54+ baseline)
- Xcode 15+
- Apple Silicon Mac for the iOS Simulator (the Moss.xcframework simulator slice is arm64-only)
- A development build / `expo prebuild` — Expo Go is not supported
- Until this package is published to npm, install from a local path or git checkout (`file:…` / `github:…`)

## Development (this monorepo)

```bash
cd sdks/react-native
npm install
npm run build
```

See [`examples/react-native/`](../../examples/react-native/) for a minimal usage sketch.

## License

[BSD 2-Clause](./LICENSE)
Loading
Loading