Examples
Real-world applications and projects built with the Meshkit SDK.
For a minimal two-peer walkthrough — Alice encrypts a file, publishes the ciphertext, and Bob decrypts it after fetching the CID — see Encryption.
Each entry below is a working implementation of Meshkit in a real application. Jump to a specific example using the table of contents on the right.
| Example | Stack | Features used |
|---|---|---|
| Invoice-Pro | React · Ionic · Capacitor (Android / iOS) | createMeshkitClient, upload, pin, retrieve, healthCheck |
| SocialCalc-Sugarizer | Sugarizer · RequireJS · Electron / Web | init, upload, pin, publishName, resolveName, retrieve, generateKey, listKeys |
| Aspiring Apps | Ionic portal · Python apps-service · Node meshkit-service | init, upload, pin, retrieve |
Invoice-Pro
Repository: github.com/anurag-p6/invoice-mvp-android-101 — branch meshkit-vps
Description: An offline-first mobile invoicing app for Android and iOS. Users create and manage professional invoices on-device without requiring any cloud account. Meshkit connects the app to a self-hosted Kubo node — running on a personal VPS, a cloud server, or a local machine — so invoices can be backed up as PDFs to your own IPFS node and pinned there permanently.
Tech stack: React 19 · Ionic 8 · Capacitor 8 · SQLite (@capacitor-community/sqlite) · SocialCalc spreadsheet engine · Vite 7
SDK features used
| API | How it's used |
|---|---|
createMeshkitClient({ apiUrl }) | Creates an HTTP RPC client pointed at the user's Kubo node URL (e.g. http://your-vps:5001). The client is lazily instantiated and cached; the cache is busted whenever the user saves a new node URL in Settings. |
client.upload(Uint8Array) | The active invoice is rendered to a PDF Blob via html2canvas + jsPDF, converted to Uint8Array, and uploaded to the Kubo node. Returns a CID shown to the user as a short toast. |
client.pin(cid) | Called immediately after every successful upload to pin the PDF on the node so it is not garbage-collected. |
client.retrieve(cid) | Exported and available for restoring a backed-up invoice by CID. |
client.healthCheck() | Runs a connectivity check when the user taps "Test" in Settings, showing a green or red status indicator. |
Architecture
All SDK calls are isolated in a single service module (src/services/meshkitService.ts). UI components only import named wrapper functions — the meshkit dependency is never touched directly outside the service.
@ipfs-meshkit/meshkit
│
▼
src/services/meshkitService.ts ← single integration point
│
├── InvoicePage/Menu.tsx uploadBlobToIPFS() → "Backup to IPFS" action sheet item
└── SettingsPage.tsx setKuboUrl() + checkConnection() → IPFS Backup settings panelNode URL configuration
The Kubo node URL is set by the user at runtime in Settings → IPFS Backup:
- Input field accepts any URL —
http://127.0.0.1:5001for a local daemon,http://your-vps-ip:5001for a self-hosted VPS, or an HTTPS address for a cloud-hosted node. - Saved to
localStorage["meshkit_kubo_url"]; defaults tohttp://127.0.0.1:5001if unset. - The IPFS gateway URL for the backed-up PDF is derived automatically by swapping port
5001→8080(getGatewayUrl), following the standard Kubo convention.
Key implementation details
- PDF backup, not raw data: The app uploads the rendered invoice as a PDF blob — not raw JSON. The invoice is first rendered to HTML by SocialCalc, then converted to a PDF
Blob, then uploaded asUint8Array. - Browser compatibility:
@ipfs-meshkit/meshkitships Node.js-only internals (child_process,fs/promises,path) that are never invoked at browser/Capacitor runtime.vite.config.tsaliases them to lightweight stubs so the bundle resolves cleanly. - Android cleartext HTTP:
network_security_config.xmlwhitelists cleartext HTTP for127.0.0.1,localhost, and entire192.168.x.x/10.x.x.xsubnets, so the app can reach a LAN or local Kubo node without requiring TLS. - Upload + pin as one operation:
uploadToIPFS()always callspin()immediately afterupload()— a single wrapper ensures content is never uploaded without being pinned.
SocialCalc-Sugarizer
Repository: github.com/AspiringDevelopers/socialcalc-sugarizer
Description: A collaborative spreadsheet activity inside Sugarizer — an open-source learning platform for children. The integration enables serverless peer-to-peer spreadsheet sharing: a teacher or student publishes a spreadsheet to their local Kubo node and shares a stable IPNS link. Anyone with that link can open the latest version of the sheet directly in their Sugarizer instance, with no Sugarizer server involved. IPNS ensures the link stays valid across edits — each "Publish / Update" action pushes a new CID behind the same IPNS name.
Tech stack: Sugarizer 2.0 · Vue 3 · Electron · RequireJS (AMD) · SocialCalc spreadsheet engine · Vendored Meshkit browser AMD module
SDK features used
| API | How it's used |
|---|---|
Meshkit.init({ nodes }) | Initialises the singleton with all configured node URLs. Health-checks every node concurrently at startup and keeps only reachable ones. Called lazily on first use via getMeshkit(). |
upload(Uint8Array) | Uploads the full SocialCalc save format (plain-text, UTF-8 encoded) to the Kubo node. Returns a CID. |
pin(cid) | Pins the CID immediately after every upload to prevent garbage collection. |
publishName(cid, { key, ttl: "1m" }) | Publishes the new CID to IPNS under the sheet's ed25519 key. TTL is set to 1 minute (IPNS_TTL_FAST) for fast propagation across the network. |
resolveName(name) | Resolves an IPNS name to its latest CID (/api/v0/name/resolve). Called with nocache: true on explicit "Fetch latest" to bypass local DHT cache. |
resolveAndRetrieve(name) | Convenience combo used in the fetch flow: resolves the IPNS name, extracts the CID, and retrieves the bytes in one call. Falls back to the HTTP gateway if the RPC API is unreachable. |
retrieve(cid) | Fetches spreadsheet bytes by CID and decodes them back to the SocialCalc plain-text format. |
generateKey(label) | Creates a per-spreadsheet ed25519 IPNS key (socialcalc-<sheetId>). Called once on first publish. |
listKeys() | Fallback if generateKey() fails because the key already exists — finds and reuses the existing key by label. |
IPNS persistence flow
PUBLISH (user clicks "Publish / Update"):
SocialCalc.CreateSpreadsheetSave()
→ TextEncoder → Uint8Array
→ upload(bytes) → CID
→ pin(cid)
→ publishName(cid, { key: "socialcalc-<id>", ttl: "1m" })
→ stable IPNS link: /ipns/<k51...> (same link on every update)
FETCH (user opens shared link or clicks "Fetch latest"):
parseShareTarget(input) ← accepts ?ipns=, /ipns/, ipns://, ?cid=, bare CID/key
→ resolveAndRetrieve(name, { nocache: true })
→ /api/v0/name/resolve → CID
→ /api/v0/cat → bytes
→ [fallback] GET http://127.0.0.1:8080/ipns/<name>
→ TextDecoder → SocialCalc format → loadSpreadsheet()Node URL configuration
Node URLs are read from localStorage at runtime — no build config or env vars:
| Key | Purpose | Default |
|---|---|---|
socialcalc_meshkit_nodes | JSON array of Kubo API URLs | ["http://127.0.0.1:5001"] |
socialcalc_meshkit_gateway | HTTP gateway URL for fallback fetches | http://127.0.0.1:8080 |
Multi-node is supported: pass multiple URLs in the JSON array. Write operations (upload, pin, publish) always use the primary node; read operations (retrieve, resolve) use failover across all healthy nodes.
Sugar journal integration
After every publish or fetch, IPFS metadata is written to the Sugar datastore so the share state persists across sessions:
| Journal key | Value stored |
|---|---|
ipfs_ipns | The IPNS name (k51…) |
ipfs_ipns_key | The local key label (socialcalc-<id>) |
ipfs_cid | The latest content CID |
ipfs_gateway | The full gateway URL |
On activity resume, the stored key and IPNS name are restored from journal metadata and reused — no new key is generated for an existing sheet.
Key implementation details
- Vendored AMD SDK: The Meshkit SDK is not installed via npm. It is shipped as a self-contained RequireJS AMD module at
lib/meshkit/meshkit.js— a browser port of@ipfs-meshkit/corewith no Node.js dependencies and no build step required. - URL-based auto-load: If the activity is launched with
?ipns=<name>or?cid=<hash>in the URL, it automatically fetches and loads the shared spreadsheet on startup, before falling back to the Sugar journal. - Share link formats:
parseShareTarget()accepts all common IPFS/IPNS link formats —?ipns=,/ipns/,ipns://,?cid=,/ipfs/,ipfs://, bare CID (Qm…,baf…), and bare IPNS key (k51…,12D3…). - Graceful gateway fallback: If the local Kubo RPC API is unreachable during a fetch, the app retries automatically via the HTTP gateway (
http://127.0.0.1:8080/ipns/<name>), so read access works even when the API port is firewalled.
Aspiring Apps
Pull request: its-me-ani/aspiring-apps #1 — Meshkit IPFS app service
Description: Agentic invoice applications in Aspiring Apps. Users pick a storage backend in Settings. With IPFS selected, invoice workbook bodies are uploaded to a Kubo node and fetched back by CID. The default node is Aspiring Apps' Kubo on Render (https://kubo-render.onrender.com). A user can point the same setting at their own Kubo RPC URL.
Tech stack: Ionic portal (React) · Python apps-service · Node meshkit-service (@ipfs-meshkit/meshkit ^1.2.1) · Kubo
SDK features used
| API | How it's used |
|---|---|
init({ nodes }) | meshkit-service connects to the local Kubo URL (MESHKIT_NODES_LOCAL, default http://localhost:5001) and the server URL (MESHKIT_NODES_SERVER, default https://kubo-render.onrender.com). |
upload + pin | uploadAndPin uploads the invoice JSON (or a file) and pins the CID on that Kubo target. |
retrieve | Loads the workbook bytes back by CID. JSON responses are decoded in the service; file bytes are returned as an octet stream. |
The portal and the Python service do not import the SDK. They call meshkit-service over HTTP (MESHKIT_SERVICE_URL, default http://127.0.0.1:4000).
Storage choice
Settings → Storage offers two backends:
| Backend | What happens to the invoice body |
|---|---|
Aspiring Storage (s3) | Stored in S3 as before. |
IPFS (ipfs) | JSON is sent to meshkit-service, which uploads and pins it on Kubo. The apps-service record keeps the CID. |
The Kubo URL field accepts any http(s) RPC address. Leaving the default uses the Render node.
Request path
Settings (storageBackend: "ipfs", Kubo URL)
│
▼
apps-service storage_manager.py
│ MeshkitClient.upload_json / retrieve_json
▼
meshkit-service (Node, @ipfs-meshkit/meshkit)
│ init({ nodes }) → upload → pin → retrieve
▼
Kubo (local :5001 or https://kubo-render.onrender.com)meshkit-service exposes POST /ipfs/upload, POST /ipfs/retrieve, and POST /ipfs/pin, plus the invoice aliases POST /putJSON and GET /getJSON/:cid. Each call carries a target of local or server.
Agent uploads
An agent using IPFS Meshkit MCP can upload the workbook to the user's own Kubo node and record the CID with ipfsNodeUrl. On read, storage_manager fetches that CID from the recorded node (POST /api/v0/cat) instead of the shared Render node. Deleting the app record does not unpin the IPFS object.