| name | helia |
| description | Helia IPFS browser node, @helia/http gateway-only client, unixfs DAG ops, blockstore/datastore/routing, Bitswap/TrustlessGateway block brokers, CID generation with custom hashers, browser constraints. Triggers: "Helia", "@helia/unixfs", "IPFS browser", "createHelia", "createHeliaHTTP", "Bitswap", "TrustlessGateway", "ipfs-loader" |
Helia — IPFS for JavaScript Environments
Helia is a JS/IPFS implementation that works in both Node.js and browsers. Unlike ipfs-http-client which proxies to a remote daemon, Helia runs a full IPFS node in-process.
Two Implementations
Full Node: createHelia (Node.js / browser)
Runs a complete IPFS node with libp2p, Bitswap, Kademlia DHT, and pubsub.
import { createHelia } from "helia";
import { unixfs } from "@helia/unixfs";
const helia = await createHelia();
const fs = unixfs(helia);
await fs.addFileBytes(Uint8Array.of(1, 2, 3), "/hello.txt");
Gateway-Only: createHeliaHTTP (@helia/http)
Lightweight HTTP gateway-only client. No libp2p, no Bitswap. Falls back to public/gateway ipfs.io and cloudflare-ipfs.com gateways. Use this in browsers when you don't need P2P upload.
import { createHeliaHTTP } from "@helia/http";
import { unixfs } from "@helia/unixfs";
const helia = await createHeliaHTTP();
const fs = unixfs(helia);
const bytes = await fs.readFile("/ipfs/Qm...");
Helia Interface
interface Helia {
blockstore: Blockstore;
datastore: Datastore;
pins: PinManager;
libp2p: Libp2pInstance;
routing: Routing;
gc(): AsyncGenerator<{ cid: CID }>;
start(): Promise<void>;
stop(): Promise<void>;
events: EventEmitter<HeliaEvents>;
}
Key Properties
| Property | Purpose |
|---|
blockstore | Raw block storage. blockstore.put(cid, bytes) / blockstore.get(cid) |
datastore | Higher-level persistent store for metadata (pins, IPNS records) |
pins | pins.add(cid), pins.rm(cid), pins.isPinned(cid) |
libp2p | libp2p node — add peers manually: libp2p.dial(multiaddr) |
routing | Find providers: routing.findProviders(cid) → yields { id: peerId, addrs: multiaddr[] } |
Supplemental Modules
All imported separately and composed with helia:
import { unixfs } } from '@helia/unixfs'
import { strings } from '@helia/strings'
import { json } from '@helia/json'
import { dagCbor } from '@helia/dag-cbor'
import { dagJson } from '@helia/dag-json'
import { ipns } from '@helia/ipns'
import { car } from '@helia/car'
import { mfs } from '@helia/mfs'
Common Usage Patterns
import { createHelia } from "helia";
import { unixfs } from "@helia/unixfs";
import { strings } from "@helia/strings";
import { json } from "@helia/json";
import { dagCbor } from "@helia/dag-cbor";
const helia = await createHelia();
const fs = unixfs(helia);
const str = strings(helia);
const j = json(helia);
const cbor = dagCbor(helia);
const cid = await fs.addFileBytes(Uint8Array.of(1, 2, 3), "/myfile.txt");
const bytes = await fs.readFile(cid);
const cid2 = await str.addFile("hello world", "/msg.txt");
const text = await str.cat(cid2);
const cid3 = await j.add({ foo: "bar" });
const obj = await j.get(cid3);
const cid4 = await cbor.add({ myData: 42 });
System Architecture
Application
│
▼
┌─────────────────────────┐
│ Helia (Blockstore API) │
│ put(cid, bytes) │
│ get(cid) → bytes │
└──────────┬──────────────┘
│
┌──────┴──────┐
│ │
▼ ▼
Bitswap TrustlessGateway
(blockstore (HTTP fallback
from libp2p when Bitswap
peers) fails)
│ │
▼ ▼
┌───────────────────────┐
│ libp2p │
│ DHT / PubSub / IPNI │
│ / Reframe │
└───────────────────────┘
│
▼
Network Peers
BlockBrokers handle where blocks come from. Helia ships with:
BitswapBlockBroker — asks libp2p peers for blocks (P2P)
TrustlessGatewayBlockBroker — fetches blocks from HTTP gateways (fallback)
The routing layer (helia.routing) delegates to:
libp2p services in full Helia (Kademlia DHT, PubSub, IPNI, Reframe)
- HTTP delegation in
@helia/http
BlockBrokers in ManaMesh
See skill:manamesh-assets — ManaMesh uses a HTTPFallbackBlockBroker strategy: tries Bitswap first (P2P), falls back to HTTP gateways. This is configured in packages/frontend/src/assets/ipfs-loader.ts.
Custom Hashers
Helia supports custom CID hashers (default: SHA-256). Create a hasher once, reuse across operations:
import { createHelia } from "helia";
import { sha256 } from "multiformats/hashes/sha";
import { identity } from "multiformats/hashes/identity";
import { CID } from "multiformats/cid";
const customHasher = sha256;
const cid = CID.create(1, "dag-pb", sha256.code, multihash);
Helia Events
type HeliaEvents =
| "start"
| "stop"
| "error"
| "peer"
| "block"
| "bitswap:wantlist"
| "bitswap:peers";
HeliaHTTP (Gateway-Only)
@helia/http (createHeliaHTTP) is a lightweight alternative for browser environments:
import { createHeliaHTTP } from '@helia/http'
const helia = await createHeliaHTTP({
gateways: [
'https://ipfs.io/ipfs/',
'https://cloudflare-ipfs.com/ipfs/',
'https://dweb.link/ipfs/'
],
routers: [
{
findProviders: async (cid, options) => [
{ id: 'Qm...', addrs: [/ip4/.../tcp/4001/p2p/Qm.../'] }
]
}
]
})
Key differences from full Helia:
| Feature | createHelia | createHeliaHTTP |
|---|
| libp2p | ✅ Full node | ❌ None |
| Bitswap | ✅ P2P block exchange | ❌ None |
| DHT | ✅ Kademlia | ❌ None (HTTP delegated) |
| PubSub | ✅ | ❌ |
| GC | ✅ | ❌ |
| HTTP Gateway fallback | ❌ (uses Bitswap) | ✅ |
Browser Considerations
What Works in Browsers
createHelia in browsers via js-libp2p (WebSocket, WebTransport, WebRTC)
createHeliaHTTP — gateway-only, no native networking
- Wanted: Bitswap over WebRTC (via
libp2p/webrtc-star)
What Doesn't Work / Caveats
- Node.js-only transports (e.g., circuit relay v1) don't work in browsers
- Kademlia DHT in browsers is limited — use
@helia/http with HTTP routers for provider lookup
- Helia in web workers requires
postMessage serialization of blocks
- IndexedDB used for blockstore in browsers (see
blockstore-ipns adapter)
helia.gc() requires pinning to protect blocks from garbage collection
ManaMesh Usage
ManaMesh uses gateway-only (@helia/http) in the browser, with a custom HTTPFallbackBlockBroker strategy for block retrieval. See:
skill:manamesh-assets — asset loading with IPFS, three-store IndexedDB cache
packages/frontend/src/assets/ipfs-loader.ts — actual Helia/HeliaHTTP initialization
packages/frontend/src/assets/gateway-fallback.ts — gateway fallback strategy
CID Manipulation
import { CID } from "multiformats/cid";
const cid = CID.parse("Qm...");
const cidV1 = CID.create(1, "dag-pb", sha256.code, digest);
cid.toV0();
cid.toV1();
cid.version;
cid.codec;
cid.multihash;
cid.toString();
cid.toBaseEncodedString();
Constraints / Gotchas
- Helia v3+ is ESM-only — use
import, not require(). Node.js package.json must have "type": "module" or use .mjs extension.
- Browser blockstore defaults to in-memory — persisted blockstore requires
blockstore-ipns or blockstore-fs adapter. In browsers, blocks may be fetched repeatedly if not cached.
- GC requires pinning — without
helia.pins.add(cid), helia.gc() will sweep blocks you want to keep.
@helia/http has no Bitswap — it cannot serve blocks to other peers, only fetch from gateways.
- CID timing — CID creation is deterministic (content-addressed), no timestamps in the CID itself.
- Multihash table — Helia supports multiple hash algorithms via
multiformats/hashes/. Default is SHA-256 (code 0x12).
- libp2p version mismatch — Helia bundles its own libp2p. Don't double-bundle libp2p in the same app.
Key Files
| File | Purpose |
|---|
packages/frontend/src/assets/ipfs-loader.ts | Helia/HTTP initialization, block broker config |
packages/frontend/src/assets/gateway-fallback.ts | Gateway fallback strategy |
packages/frontend/src/assets/asset-cache.ts | IndexedDB asset cache |
packages/frontend/src/assets/types.ts | Asset loader types |
packages/frontend/package.json | @helia/unixfs, @helia/http dependencies |
Cross-References
skill:manamesh-assets — how ManaMesh uses Helia for card images and asset packs
skill:libp2p — libp2p is bundled inside full Helia; for NAT traversal, DHT, discovery