RX Data Store
name: creationix-rx-data-store
by adisinghstudent · published 2026-04-01
$ claw add gh:adisinghstudent/adisinghstudent-creationix-rx-data-store---
name: creationix-rx-data-store
description: Expert skill for using RX, an embedded data store for JSON-shaped data with random-access reads, no-parse lookups, and a text-safe binary encoding format.
triggers:
- encode data with RX
- use RX data store
- parse RX format
- random access JSON data
- RX encoder decoder TypeScript
- convert JSON to RX
- query RX encoded buffer
- RX format serialization
---
# RX Data Store
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
RX is an embedded data store for JSON-shaped data. Encode once, then query the encoded document in place — no parsing, no object graph, no GC pressure. Think of it as *no-SQL SQLite*: unstructured data with database-style random access.
**Key benefits:**
---
Installation
npm install @creationix/rx # library
npm install -g @creationix/rx # CLI (global)
npx @creationix/rx data.rx # CLI (one-off)---
Core API
String API (most common)
import { stringify, parse } from "@creationix/rx";
// Encode
const rx = stringify({ users: ["alice", "bob"], version: 3 });
// Returns a string — store it anywhere you'd store JSON text
// Decode (returns a read-only Proxy)
const data = parse(rx) as any;
data.users[0] // "alice"
data.version // 3
Object.keys(data) // ["users", "version"]
JSON.stringify(data) // works — full JS interopUint8Array API (performance-critical paths)
import { encode, open } from "@creationix/rx";
const buf = encode({ path: "/api/users", status: 200 });
const data = open(buf) as any;
data.path // "/api/users"
data.status // 200Inspect API (lazy AST)
import { encode, inspect } from "@creationix/rx";
const buf = encode({ name: "alice", scores: [10, 20, 30] });
const root = inspect(buf);
root.tag // ":"
root[0].tag // "," (a string key)
root[0].value // "name"
root.length // 4 (key, value, key, value)
// Iterate children
for (const child of root) {
console.log(child.tag, child.b64);
}
// Object helpers
for (const [key, val] of root.entries()) { /* ... */ }
const node = root.index("name"); // key lookup → node
const elem = root.index(2); // array index → node
// Filtered key search (O(log n + m) on indexed objects)
for (const [key, val] of root.filteredKeys("/api/")) { /* ... */ }Escape hatch to underlying buffer
import { handle } from "@creationix/rx";
const h = handle(data.nested);
// h.data: Uint8Array
// h.right: byte offset---
Encoding Options
stringify(data, {
// Add sorted indexes to containers with >= N entries (enables O(log n) lookup)
// Use 0 for all containers, false to disable entirely
indexes: 10,
// External refs — shared dictionary of known values for cross-document dedup
refs: { R: ["/api/users", "/api/teams"] },
// Streaming — receive chunks as they're produced
onChunk: (chunk: string, offset: number) => process.stdout.write(chunk),
});
encode(data, {
indexes: 10,
refs: { R: ["/api/users", "/api/teams"] },
onChunk: (chunk: Uint8Array, offset: number) => { /* ... */ },
});**If the encoder used external refs, pass the same dictionary to the decoder:**
const data = parse(payload, { refs: { R: ["/api/users", "/api/teams"] } });
const data = open(buf, { refs: { R: ["/api/users", "/api/teams"] } });---
CLI
rx data.rx # pretty-print as tree (default on TTY)
rx data.rx -j # convert to JSON
rx data.json -r # convert to RX
cat data.rx | rx # read from stdin (auto-detect format)
rx data.rx -s key 0 sub # select a sub-value: data["key"][0]["sub"]
rx data.rx -o out.json # write to file
rx data.rx --ast # output encoding structure as JSON
rx data.rx -w # write converted file (.json↔.rx)**Full CLI flags:**
| Flag | Description |
|------|-------------|
| `<file>` | Input file (format auto-detected) |
| `-` | Read from stdin explicitly |
| `-j`, `--json` | Output as JSON |
| `-r`, `--rexc` | Output as RX |
| `-t`, `--tree` | Output as tree |
| `-a`, `--ast` | Output encoding structure |
| `-s`, `--select <seg>...` | Select a sub-value by path segments |
| `-w`, `--write` | Write converted file |
| `-o`, `--out <path>` | Write to file instead of stdout |
| `-c`, `--color` / `--no-color` | Force or disable ANSI color |
| `--index-threshold <n>` | Index containers above n values (default: 16) |
| `--string-chain-threshold <n>` | Split strings longer than n (default: 64) |
| `--string-chain-delimiter <s>` | Delimiter for string chains (default: `/`) |
| `--key-complexity-threshold <n>` | Max object complexity for dedupe keys (default: 100) |
**Shell completions:**
rx --completions setup zsh # or bash**Tip — paged, colorized viewing:**
p() { rx "$1" -t -c | less -RFX; }---
Proxy Behavior
The value returned by `parse`/`open` is **read-only**:
obj.newKey = 1; // throws TypeError
delete obj.key; // throws TypeError
"key" in obj; // works (zero-alloc key search)
obj.nested === obj.nested // true (container Proxies are memoized)**Supported operations on the Proxy:**
---
Common Patterns
Build-step: convert JSON artifact to RX
import { readFileSync, writeFileSync } from "fs";
import { stringify } from "@creationix/rx";
const json = JSON.parse(readFileSync("manifest.json", "utf-8"));
const rx = stringify(json, { indexes: 10 });
writeFileSync("manifest.rx", rx, "utf-8");Runtime: sparse read from large RX file
import { readFileSync } from "fs";
import { parse } from "@creationix/rx";
const manifest = parse(readFileSync("manifest.rx", "utf-8")) as any;
// Only the accessed values are decoded — everything else is skipped
const route = manifest.routes["/dashboard/projects"];
console.log(route.title, route.component, route.auth);Streaming encode to stdout
import { stringify } from "@creationix/rx";
stringify(largeData, {
onChunk: (chunk, offset) => process.stdout.write(chunk),
});Using external refs for cross-document deduplication
import { stringify, parse } from "@creationix/rx";
const sharedRefs = { R: ["/api/users", "/api/teams", "/api/projects"] };
// Encode multiple documents sharing the same ref dictionary
const doc1 = stringify(data1, { refs: sharedRefs });
const doc2 = stringify(data2, { refs: sharedRefs });
// Decode with the same dictionary
const val1 = parse(doc1, { refs: sharedRefs }) as any;
const val2 = parse(doc2, { refs: sharedRefs }) as any;Low-level inspect traversal (zero allocation)
import { encode, inspect } from "@creationix/rx";
const buf = encode(routes);
const root = inspect(buf);
// Walk object entries without creating JS objects
if (root.tag === ":") {
for (const [key, val] of root.entries()) {
if (key.value === "/dashboard") {
console.log("Found:", val.index("title").value);
break;
}
}
}Type-safe usage pattern
import { parse } from "@creationix/rx";
interface Route {
title: string;
component: string;
auth: boolean;
}
interface Manifest {
routes: Record<string, Route>;
version: number;
}
// Cast after parse — RX Proxy supports all read operations
const manifest = parse(rxString) as unknown as Manifest;
const dashboard = manifest.routes["/dashboard"];---
Format Reference
RX is a **text encoding** — not human-readable like JSON, but safe to copy-paste.
Every value is read **right-to-left**. The parser scans left past base64 digits to find a **tag** character:
[body][tag][b64 varint]
◄── read this way ──| JSON | RX | Description |
|------|----|-------------|
| `42` | `+1k` | tag `+` (integer), b64 `1k` = 84, zigzag → 42 |
| `"hi"` | `hi,2` | tag `,` (string), length = 2 |
| `true` | `'t` | tag `'` (ref), built-in literal |
| `[1,2,3]` | `+6+4+2;6` | tag `;` (array), b64 `6` = content size |
| `{"a":1}` | `+2a,1:a` | tag `:` (object), interleaved keys/values |
**Tags:** `+` integer · `*` decimal · `,` string · `'` ref/literal · `:` object · `;` array · `^` pointer · `.` chain · `#` index
---
When to Use RX vs Alternatives
| Scenario | Use |
|----------|-----|
| Large artifact, sparse reads | **RX** |
| Build manifests, route tables | **RX** |
| Small config files | JSON |
| Human-authored config | JSON/YAML |
| Write-heavy / mutable data | Real database |
| Fixed schema, minimize wire size | Protobuf |
| Relational/tabular data | SQLite |
| Minimizing compressed transfer | gzip/zstd + JSON |
---
Troubleshooting
**`TypeError: Cannot set property` on decoded value**
The Proxy returned by `parse`/`open` is read-only by design. To mutate, spread into a plain object first:
const mutable = { ...parse(rx) };
mutable.newKey = "value"; // works**Decoded value looks correct but `instanceof Array` fails**
Use `Array.isArray(val)` instead — this is correctly intercepted by the Proxy.
**External refs mismatch / wrong values decoded**
Ensure the exact same `refs` dictionary (same keys, same arrays, same order) is passed to both `stringify`/`encode` and `parse`/`open`.
**CLI not found after global install**
npm install -g @creationix/rx
# If still not found, check your npm global bin path:
npm bin -g**Inspecting encoded bytes**
rx data.rx --ast # shows encoding structure with tags and offsetsOr use the live viewer at **[rx.run](https://rx.run/)** — paste RX or JSON directly.
**Performance: lookups slower than expected**
Ensure `indexes` threshold is set appropriately when encoding. For large objects (e.g., 35k keys), use `indexes: 0` to index all containers:
stringify(data, { indexes: 0 });---
Resources
More tools from the same signal band
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
Sign plugins, rotate agent credentials without losing identity, and publicly attest to plugin behavior with verifiable claims and authenticated transfers.
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your...