Skip to content
Merged
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 @@ -3,6 +3,7 @@ dist
*.tsbuildinfo
*.tgz
coverage
.pack-check

# Kubo repos (local dev + integration tests)
.ipfs
Expand Down
20 changes: 20 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
packages/
examples/
tests/
scripts/
docs/
.github/
coverage/
node_modules/

*.ts
!dist/**
tsconfig*.json
vitest.config.ts
vitest.setup*.ts
tsup.config.ts

.gitignore
.editorconfig
.prettierrc*
.eslintrc*
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

## 1.0.1 — 2026-07-06

### Fixed

- README and package metadata: correct repository links to [IPFS-Meshkit/meshkit0](https://github.com/IPFS-Meshkit/meshkit0)
- Expanded README with install, usage examples, API overview, and CommonJS support

## 1.0.0 — 2026-06-18

### Added

- **`@ipfs-meshkit/meshkit` v1.0.0** — single npm package with root `dist/` (ESM + CJS + TypeScript)
- Bundles core, node, and meshkit into one install — `npm install @ipfs-meshkit/meshkit`
- Portable `./.ipfs` repo for server migration
- Unit test suite with coverage; integration tests for persistence, IPNS, attach (local, requires Kubo)

### Not included in 1.0.0

- Separate `@ipfs-meshkit/core` / `@ipfs-meshkit/node` npm packages (code is bundled into meshkit)
- `@ipfs-meshkit/react-native` and `@ipfs-meshkit/capacitor` remain private in the monorepo
248 changes: 107 additions & 141 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,218 +1,184 @@
# IPFS Meshkit0

IPFS Meshkit0 is a universal SDK for decentralized storage on Node.js, React Native, Flutter, and Capacitor. Install **`@ipfs-meshkit/meshkit`** for Agentic and Web3 AI (ERC-8004) ecosystem with storage APIs, optional local Kubo startup, and failover, without wiring up Kubo RPC yourself.
**@ipfs-meshkit/meshkit** is a Node.js SDK for [Kubo](https://docs.ipfs.tech/) (IPFS). Upload, retrieve, and pin content over Kubo’s HTTP RPC, with optional local daemon startup, IPNS, and multi-node failover.

## Why Meshkit on Kubo?
[![test](https://github.com/IPFS-Meshkit/meshkit0/actions/workflows/test.yml/badge.svg)](https://github.com/IPFS-Meshkit/meshkit0/actions/workflows/test.yml)
[![npm version](https://img.shields.io/npm/v/@ipfs-meshkit/meshkit.svg)](https://www.npmjs.com/package/@ipfs-meshkit/meshkit)
[![license](https://img.shields.io/npm/l/@ipfs-meshkit/meshkit.svg)](https://github.com/IPFS-Meshkit/meshkit0/blob/main/LICENSE)
[![node](https://img.shields.io/node/v/@ipfs-meshkit/meshkit.svg)](https://www.npmjs.com/package/@ipfs-meshkit/meshkit)

[Helia](https://github.com/ipfs/helia) embeds IPFS inside your app (Node.js or browser) as a JavaScript library. **IPFS Meshkit** takes a different approach: it is a TypeScript SDK on top of **Kubo** — you get `upload`, `retrieve`, and `pin` over Kubo’s HTTP RPC, with optional local daemon startup, failover, and platform adapters.
[![NPM](https://nodei.co/npm/@ipfs-meshkit/meshkit.png?downloads=true&downloadRank=true)](https://www.npmjs.com/package/@ipfs-meshkit/meshkit)

We built Meshkit because apps like invoice backup need **Kubo’s persistence and pinning** without wiring up `kubo-rpc-client`, node lifecycle, repo migration, and mobile/browser integration by hand.
## Install

| | **Helia** | **IPFS Meshkit (Kubo)** |
|---|-----------|-------------------------|
| **Model** | IPFS runs in-process in your app | App talks to a Kubo daemon via RPC |
| **API** | Compose `@helia/unixfs`, blockstores, libp2p | `upload`, `retrieve`, `pin` out of the box |
| **Persistence** | You choose and wire the datastore | Kubo repo on disk (default `./.ipfs`) |
| **Networking** | App joins libp2p directly | Kubo handles swarm, DHT, bitswap |
| **Node lifecycle** | Start/stop Helia in-process | `init({ localNode: true })`, graceful shutdown |
| **Multi-node** | Roll your own retry logic | Built-in health checks and failover |
| **Host migration** | Re-export blocks or re-provide CIDs | Copy `./.ipfs` + `listPins()` manifest |
| **Web / mobile** | Large bundles, polyfills, in-browser P2P | Kubo on a server; app uses Meshkit HTTP API |

```
Your app ──► @ipfs-meshkit/meshkit ──► Kubo (./.ipfs)
upload · retrieve · pin API :5001 · GW :8080
```bash
npm install @ipfs-meshkit/meshkit
```

Meshkit keeps **Kubo as the engine** and gives applications a single SDK for storage, node management, and deployment across Node.js, Capacitor, and React Native.
**Requirements:** Node.js **20+**. For `localNode: true`, install [Kubo](https://docs.ipfs.tech/install/) and ensure `ipfs` is on your `PATH`.

## Typical use case
## Quick start

A developer building an invoice app can:

1. Store invoices in a local DB, **or**
2. Upload them to decentralized storage via Meshkit (`upload`, `retrieve`, `pin`)

On Node.js, Meshkit can start a local Kubo daemon for you. On mobile, point at a Kubo node on your PC or VPS.
```typescript
import { init, setupGracefulShutdown } from '@ipfs-meshkit/meshkit';

## Packages
const { meshkit, localNode } = await init({ localNode: true });
setupGracefulShutdown(localNode);

| Package | Description |
|---------|-------------|
| [`@ipfs-meshkit/meshkit`](./packages/meshkit) | **Primary entry** — storage + local Kubo lifecycle (Node.js) |
| [`@ipfs-meshkit/core`](./packages/core) | Kubo RPC client with multi-node failover |
| [`@ipfs-meshkit/node`](./packages/node) | Start/stop a local Kubo daemon (`startIPFSNode`) |
| [`@ipfs-meshkit/capacitor`](./packages/capacitor) | Capacitor adapter for Ionic / hybrid apps |
| [`@ipfs-meshkit/react-native`](./packages/react-native) | React Native adapter (re-exports core + polyfill entry) |
const bytes = new TextEncoder().encode('hello');
const cid = await meshkit.upload(bytes);
await meshkit.pin(cid);

## Prerequisites
const retrieved = await meshkit.retrieve(cid);
console.log(new TextDecoder().decode(retrieved)); // hello
```

Install [Kubo](https://docs.ipfs.tech/install/) (`ipfs` on your PATH). Meshkit can spawn the daemon on Node.js; mobile apps still need a reachable Kubo endpoint (LAN IP or VPS).
`localNode: true` starts or attaches to Kubo on `http://127.0.0.1:5001` and stores data in `./.ipfs`. Add `.ipfs` to `.gitignore`.

## Development
## How it works

```bash
npm install
npm run build
npm test # unit tests (no Kubo)
npm run test:integration # build + E2E (requires Kubo)
npm run test:all # full suite
```
Your app → init() → Kubo RPC (:5001) → ./.ipfs on disk
upload · retrieve · pin · IPNS
```

See [docs/Testing.md](./docs/Testing.md).
1. **`init()`** — connect to one or more Kubo nodes; optionally spawn a local daemon.
2. **`meshkit`** — storage API with failover (`upload`, `retrieve`, `pin`, IPNS).
3. **`setupGracefulShutdown()`** — stop a managed Kubo cleanly on Ctrl+C / SIGTERM.

## Usage

```bash
npm install @ipfs-meshkit/meshkit
```

### Node.js — automatic local Kubo

`localNode: true` stores pinned data in `./.ipfs` (relative to where you start the process). Add `.ipfs` to `.gitignore`.
### Local Kubo (recommended for Node.js)

```typescript
import { readFile, writeFile } from 'node:fs/promises';
import { readFile } from 'node:fs/promises';
import { init, listPins, setupGracefulShutdown } from '@ipfs-meshkit/meshkit';

const { meshkit, localNode } = await init({ localNode: true });
setupGracefulShutdown(localNode);

setupGracefulShutdown(localNode); // Ctrl+C flushes Kubo; ./.ipfs stays on disk

const pdf = await readFile('./invoice.pdf');
const cid = await meshkit.upload(pdf);
const file = await readFile('./document.pdf');
const cid = await meshkit.upload(file);
await meshkit.pin(cid);

console.log('repo:', localNode?.repo);
console.log('pins:', await listPins(meshkit.activeNodes[0]!));

const retrieved = await meshkit.retrieve(cid);
await writeFile('./invoice-copy.pdf', retrieved);
```

### Migrating servers (AWS → GCP)

1. Stop the server gracefully (`setupGracefulShutdown` or `stopIPFSNode`)
2. Copy the `./.ipfs` directory (tar, EBS snapshot, S3, etc.)
3. Restore on the new host and start with the same repo path:

```typescript
const { meshkit, localNode } = await init({
localNode: { repo: './.ipfs', init: false },
});
const pins = await listPins(meshkit.activeNodes[0]!);
console.log('pinned:', pins.length);
```

Use `listPins()` to export CIDs as a backup manifest for re-pinning.
### Remote Kubo only (no local spawn)

### Node.js — server bootstrap
Use when Kubo already runs on a server or LAN machine:

```typescript
import { init, setupGracefulShutdown } from '@ipfs-meshkit/meshkit';
import { init } from '@ipfs-meshkit/meshkit';

const { meshkit, localNode } = await init({ localNode: true });
setupGracefulShutdown(localNode, {
onShutdown: async () => { /* close HTTP server, DB, etc. */ },
const { meshkit } = await init({
nodes: ['https://kubo.example.com:5001'],
});

// ... app.listen(3000) ...
const cid = await meshkit.upload(new Uint8Array([1, 2, 3]));
```

`startIPFSNode` reuses an existing daemon on `127.0.0.1:5001` when one is already healthy.

### Remote nodes + failover
### Local + backup nodes (failover)

```typescript
import { init } from '@ipfs-meshkit/meshkit';

const { meshkit } = await init({
localNode: true,
nodes: [
'https://node2.yourinfra.com:5001',
'https://node3.yourinfra.com:5001',
],
nodes: ['https://backup.example.com:5001'],
});

console.log('Active nodes:', meshkit.activeNodes);
const backup = new TextEncoder().encode('failover example');
await meshkit.upload(backup);
```

Each `upload`, `retrieve`, and `pin` call tries nodes in priority order. If every node fails, a `MeshkitError` is thrown.

### IPNS (mutable names)

Use a **CID** when you want a permanent link to one exact version of content. Use **IPNS** when you need a **stable name** that can be updated to point at a newer CID (e.g. latest invoice bundle).

| | **CID** | **IPNS** |
|---|---------|----------|
| **Address** | `/ipfs/Qm...` | `/ipns/Qm...` |
| **Mutability** | Immutable — new content = new CID | Mutable pointer — same name, new CID via `publishName` |
| **Analogy** | Direct file hash | DNS record pointing at current content |
| **Read access** | Public | Public (no key required to resolve) |
| **Write access** | Anyone can add content; pinning is separate | Only the node holding the **private key** can publish updates |
### IPNS (mutable pointer)

```typescript
import { init, IPNS_TTL_FAST } from '@ipfs-meshkit/meshkit';

const { meshkit } = await init({ localNode: true });

// One-time: create a stable signing identity
await meshkit.generateKey('invoice-latest');
await meshkit.generateKey('latest');
const contentBytes = new TextEncoder().encode('v1');
const cid = await meshkit.upload(contentBytes);
await meshkit.pin(cid); // publishName does not pin — pin content you care about

// Publish v1
const cid1 = await meshkit.upload(invoiceV1);
await meshkit.pin(cid1);
const { name } = await meshkit.publishName(cid1, {
key: 'invoice-latest',
const { name } = await meshkit.publishName(cid, {
key: 'latest',
ttl: IPNS_TTL_FAST,
});

// Readers use the stable IPNS name (no key required)
const bytes = await meshkit.resolveAndRetrieve(`/ipns/${name}`);

// Update to v2 — same IPNS name, new CID
const cid2 = await meshkit.upload(invoiceV2);
await meshkit.pin(cid2);
await meshkit.publishName(cid2, { key: 'invoice-latest', ttl: IPNS_TTL_FAST });

// Verify latest (nocache bypasses local cache after publish)
const path = await meshkit.resolveName(`/ipns/${name}`, { nocache: true });
const latest = await meshkit.resolveAndRetrieve(`/ipns/${name}`);
```

Gateway URL for browsers: `http://127.0.0.1:8080/ipns/${name}`.
### Server shutdown hook

**Operator notes:**
```typescript
import { init, setupGracefulShutdown } from '@ipfs-meshkit/meshkit';

- `publishName` does **not** pin — always `pin(cid)` for content you publish.
- IPNS keys live in `./.ipfs/keystore` — copy the repo to migrate names (same as pins).
- **`ttl`** is a cache hint (upper bound on stale reads for cached resolvers), not a fixed global propagation delay. After updating a CID, expect eventual consistency (seconds to a few minutes).
- **Multi-node:** `publishName`, `generateKey`, and `listKeys` use the **primary node** (`activeNodes[0]`). `resolveName` and `resolveAndRetrieve` failover across healthy nodes.
const { meshkit, localNode } = await init({ localNode: true });

### Low-level client
setupGracefulShutdown(localNode, {
onShutdown: async () => {
// close HTTP server, DB, etc.
},
});
```

For a single node without failover, use `@ipfs-meshkit/core` directly:
### Low-level client (single node, no failover)

```typescript
import { createMeshkitClient } from '@ipfs-meshkit/core';
import { createMeshkitClient } from '@ipfs-meshkit/meshkit';

const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' });
const cid = await client.upload(await readFile('./invoice.pdf'));
const cid = await client.upload(new Uint8Array([1, 2, 3]));
```

### React Native
### CommonJS

```bash
npm install @ipfs-meshkit/react-native react-native-get-random-values \
react-native-url-polyfill react-native-fetch-api web-streams-polyfill fast-text-encoding
```javascript
const { init } = require('@ipfs-meshkit/meshkit');

(async () => {
const { meshkit } = await init({ nodes: ['http://127.0.0.1:5001'] });
console.log(await meshkit.upload(Buffer.from('hello')));
})();
```

```typescript
import '@ipfs-meshkit/react-native/polyfills';
import { Meshkit } from '@ipfs-meshkit/react-native';
## API overview

const mk = await Meshkit.init({
nodes: ['http://192.168.1.42:5001'],
});
| Export | Purpose |
|--------|---------|
| `init()` | Main entry — connect (+ optional `localNode`) |
| `meshkit.upload` / `retrieve` / `pin` | Storage with failover |
| `meshkit.publishName` / `resolveName` / `resolveAndRetrieve` | IPNS |
| `meshkit.generateKey` / `listKeys` | IPNS keystore |
| `setupGracefulShutdown` | Graceful Kubo shutdown |
| `listPins` | List pinned CIDs (migration / backups) |
| `createMeshkitClient` | Single-node RPC client |
| `startIPFSNode` / `stopIPFSNode` | Low-level daemon control |

const cid = await mk.upload(new Uint8Array([1, 2, 3]));
```
TypeScript types are included (`import type { Meshkit, IPFSNodeHandle } from '@ipfs-meshkit/meshkit'`).

## Documentation

- [GitHub repository](https://github.com/IPFS-Meshkit/meshkit0)
- [Kubo install guide](https://docs.ipfs.tech/install/)
- [Security policy](https://github.com/IPFS-Meshkit/meshkit0/blob/main/SECURITY.md)

## Support

- [GitHub Issues](https://github.com/IPFS-Meshkit/meshkit0/issues) — bugs and feature requests

## Credits

Open source project by **IPFS Meshkit Contributors**:

- [Anurag Pandey](https://github.com/anurag-p6) — primary development
- [Manu Sheel Gupta](https://github.com/seetadev) — project lead

## License

On Android, allow cleartext HTTP for local dev (`android:usesCleartextTraffic="true"`). On iOS, enable local networking in `Info.plist` if using `http://` on LAN.
[MIT](https://github.com/IPFS-Meshkit/meshkit0/blob/main/LICENSE)
Loading
Loading