diff --git a/package.json b/package.json
index abdddefb0168b2966d228333fe3745f53dc07513..86fff61a0686eaecbb47c4ae3d6b9454ca51f5b8 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,10 @@
   "private": true,
   "type": "module",
   "scripts": {
-    "dev": "vite",
+    "kubo-browser": "cp ./src/kubo-browser.ts ./src/kubo.ts",
+    "kubo-node": "cp ./src/kubo-node.ts ./src/kubo.ts",
+    "dev": "pnpm kubo-browser && vite",
+    "start": "pnpm kubo-node && pnpm exec tsx ./src/indexer/start.ts",
     "build": "run-p type-check \"build-only {@}\" --",
     "preview": "vite preview",
     "build-only": "vite build",
diff --git a/scripts/kubo.ts b/scripts/kubo.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8b9f56f6cbe0241c330edd583e956883936d4285
--- /dev/null
+++ b/scripts/kubo.ts
@@ -0,0 +1,13 @@
+import { create } from 'kubo-rpc-client'
+
+// env
+export const KUBO_RPC = 'http://127.0.0.1:5001'
+export const KUBO_GATEWAY = 'http://127.0.0.1:8080'
+
+export function getKuboClientsBrower() {
+  return { kubo: create(), kubo2: null }
+}
+
+const getKuboClientsPlatform = getKuboClientsBrower
+
+export const {kubo, kubo2} = getKuboClientsPlatform()
\ No newline at end of file
diff --git a/src/cesium-plus.ts b/src/cesium-plus.ts
index 553e8143c3b89c190a16c57bcf535debbf2105be..9262ec25be5044a590da56748cb7e89a19777a5f 100644
--- a/src/cesium-plus.ts
+++ b/src/cesium-plus.ts
@@ -4,43 +4,7 @@ import { Buffer } from 'buffer'
 import { timestampToKey, arrayToVinode, mergeInodesSyncCID } from './processor'
 import { type IndexRequest } from './types'
 import { CESIUM_PLUS_PROFILE_IMPORT } from './consts'
-
-// ========================= types
-
-// for reference see
-// https://doc.e-is.pro/cesium-plus-pod/REST_API.html
-export interface CplusProfile {
-  version: number
-  title: string
-  description: string
-  time: number
-  issuer: string
-  hash: string
-  signature: string
-  city?: string
-  geoPoint?: Geoloc
-  socials?: Social[]
-  tags?: string[]
-  avatar?: Avatar | CID
-}
-
-// social
-interface Social {
-  type: string
-  url: string
-}
-
-// avatar field will be managed as an IPFS file
-interface Avatar {
-  _content_type: string // image/png for instance
-  _content: string // base64 encoded
-}
-
-// geoloc
-interface Geoloc {
-  lat: number
-  lon: number
-}
+import type{  CplusProfile, Avatar } from './types'
 
 // ========================= import functions
 
diff --git a/src/components/IndexNode.vue b/src/components/IndexNode.vue
index 41ce829e33a0ac243352193b20615535f7ec3960..d0ba1c6cca246036faf83fb947eb4c8e82da72cf 100644
--- a/src/components/IndexNode.vue
+++ b/src/components/IndexNode.vue
@@ -3,8 +3,8 @@ import { ref, type Ref, computed } from 'vue'
 import type { IndexInode, IndexLeaf } from '../types'
 import { formatDate } from '../utils'
 import { CID } from 'multiformats'
-import { kubo, exploreUrl } from '@/kubo'
-import { BASE } from '@/consts'
+import { kubo } from '@/kubo'
+import { exploreUrl, BASE } from '@/consts'
 
 const inode: Ref<IndexInode | null> = ref(null)
 const leaf: Ref<IndexLeaf | null> = ref(null)
diff --git a/src/consts.ts b/src/consts.ts
index 39de441af2f8e2c9433d7b9094163b5e63736d0d..68a23ae50d0b778cb0d22f46a1845fa44eb5460b 100644
--- a/src/consts.ts
+++ b/src/consts.ts
@@ -1,4 +1,5 @@
 import { CID } from 'multiformats'
+import { KUBO_RPC, KUBO_GATEWAY } from './kubo'
 
 // topic used for diffusion of index requests
 export const TOPIC = 'ddd'
@@ -20,3 +21,15 @@ export const EMPTY_NODE_CID = CID.parse('bafyreicvlp2p65agkxpzcboedba7zit55us4zv
 export const CESIUM_PLUS_PROFILE_IMPORT = CID.parse('bafkreiawtammeqc55cssr2zepfpaxbmp7kquhikkagipvtefeadsw4mqvq')
 export const CESIUM_PLUS_PROFILE_INSERT = CID.parse('bafkreigi5phtqpo6a2f3tx4obaja4fzevy3nyvnl4bnkcxylyqnfeowzbm')
 export const CESIUM_PLUS_PROFILE_DELETE = CID.parse('bafkreic5bv5ytl7zv5rh5j2bd5mw6nfrn33mxhiobgmpsiu65yjw3eeduu')
+
+// ==========
+
+// explorer resources
+const EXPLORER_CID = 'bafybeidf7cpkwsjkq6xs3r6fbbxghbugilx3jtezbza7gua3k5wjixpmba'
+export const EXPLORER_URL = KUBO_RPC + '/ipfs/' + EXPLORER_CID + '/#'
+export function exploreUrl(cid: CID): string {
+  return EXPLORER_URL + '/explore/ipfs/' + cid
+}
+export function gatewayUrl(cid: CID): string {
+  return KUBO_GATEWAY + '/ipfs/' + cid
+}
diff --git a/src/indexer/start.ts b/src/indexer/start.ts
index c489d4c89d0023a3548279e7ccc26b68083166e6..26b972fc35c9bddbaca70cd64d6e920a70db7792 100644
--- a/src/indexer/start.ts
+++ b/src/indexer/start.ts
@@ -1,7 +1,7 @@
 import { TOPIC } from '../consts'
 import { timestampToKey, arrayToVinode, publishHistory, mergeInodesSyncCID } from '../processor'
 import { getPubSubHandler } from '../collector'
-import { kubo, kubo2, KUBO_RPC } from '../kubo'
+import { kubo, kubo2 } from '../kubo'
 import type { IndexRequest } from '../types'
 import { CID } from 'multiformats'
 import { events, evtype, indexKnownDiff, indexStart } from './handlers'
@@ -134,6 +134,6 @@ events.emit(evtype.indexStart, rootCID)
 // publishHistory(rootCID)
 
 // process loop
-console.log('👂 listening on ' + KUBO_RPC + ', topic ' + TOPIC + '...')
+console.log('👂 listening on topic ' + TOPIC + '...')
 setInterval(() => {}, 1 << 30)
 ;['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => process.on(signal, process.exit))
diff --git a/src/kubo-browser.ts b/src/kubo-browser.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9928059d709495ee14d7d2fa03a73a44757c92d1
--- /dev/null
+++ b/src/kubo-browser.ts
@@ -0,0 +1,13 @@
+import { create } from 'kubo-rpc-client'
+
+// env
+export const KUBO_RPC = 'http://127.0.0.1:5001'
+export const KUBO_GATEWAY = 'http://127.0.0.1:8080'
+
+export function getKuboClientsBrower() {
+  return { kubo: create(KUBO_RPC), kubo2: null }
+}
+
+const getKuboClientsPlatform = getKuboClientsBrower
+
+export const {kubo, kubo2} = getKuboClientsPlatform()
\ No newline at end of file
diff --git a/src/kubo-node.ts b/src/kubo-node.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66508469f42df8eddab34582db9f2c60b86fd460
--- /dev/null
+++ b/src/kubo-node.ts
@@ -0,0 +1,33 @@
+import { create } from 'kubo-rpc-client'
+import type { KuboRPCClient } from 'kubo-rpc-client'
+import { Agent } from 'http'
+
+// env
+export const KUBO_RPC = process.env.KUBO_RPC || 'http://127.0.0.1:5001'
+export const KUBO_GATEWAY = process.env.KUBO_GATEWAY || 'http://127.0.0.1:8080'
+
+function getKuboClientsNode() {
+  // create a RPC HTTP client // TODO unix socket for optimization
+  const kubo: KuboRPCClient = create({
+    url: new URL(KUBO_RPC),
+    agent: new Agent({
+      maxSockets: 50000
+    })
+  })
+  // create an other RPC client only for pubsub
+  const kubo2: KuboRPCClient = create({
+    url: new URL(KUBO_RPC),
+    agent: new Agent({
+      keepAlive: true, // to prevent UND_ERR_BODY_TIMEOUT
+      keepAliveMsecs: 1000
+      // maxSockets: 1,
+      // timeout: 100
+    })
+  })
+
+  return { kubo, kubo2 }
+}
+
+const getKuboClientsPlatform = getKuboClientsNode
+
+export const { kubo, kubo2 } = getKuboClientsPlatform()
diff --git a/src/kubo.ts b/src/kubo.ts
index 2ca24a688b0641b5e2a48b0b9e4709e269606483..9928059d709495ee14d7d2fa03a73a44757c92d1 100644
--- a/src/kubo.ts
+++ b/src/kubo.ts
@@ -1,38 +1,13 @@
 import { create } from 'kubo-rpc-client'
-import type { KuboRPCClient } from 'kubo-rpc-client'
-import { CID } from 'multiformats'
-import { Agent } from 'http'
 
 // env
-// TODO clean way to declare KUBO_RPC for Vue App
-// var process : NodeJS.Process | undefined = typeof(process) == "undefined" ? undefined : process
-export const KUBO_RPC = process?.env.KUBO_RPC || 'http://127.0.0.1:5001'
-export const KUBO_GATEWAY = process?.env.KUBO_GATEWAY || 'http://127.0.0.1:8080'
+export const KUBO_RPC = 'http://127.0.0.1:5001'
+export const KUBO_GATEWAY = 'http://127.0.0.1:8080'
 
-// create a RPC HTTP client // TODO unix socket for optimization
-export const kubo: KuboRPCClient = create({
-  url: new URL(KUBO_RPC),
-  agent: new Agent({
-    maxSockets: 50000
-  })
-})
-// create an other RPC client only for pubsub
-export const kubo2: KuboRPCClient = create({
-  url: new URL(KUBO_RPC),
-  agent: new Agent({
-    keepAlive: true, // to prevent UND_ERR_BODY_TIMEOUT
-    keepAliveMsecs: 1000
-    // maxSockets: 1,
-    // timeout: 100
-  })
-})
-
-// explorer resources
-const EXPLORER_CID = 'bafybeidf7cpkwsjkq6xs3r6fbbxghbugilx3jtezbza7gua3k5wjixpmba'
-export const EXPLORER_URL = KUBO_RPC + '/ipfs/' + EXPLORER_CID + '/#'
-export function exploreUrl(cid: CID): string {
-  return EXPLORER_URL + '/explore/ipfs/' + cid
-}
-export function gatewayUrl(cid: CID): string {
-  return KUBO_GATEWAY + '/ipfs/' + cid
+export function getKuboClientsBrower() {
+  return { kubo: create(KUBO_RPC), kubo2: null }
 }
+
+const getKuboClientsPlatform = getKuboClientsBrower
+
+export const {kubo, kubo2} = getKuboClientsPlatform()
\ No newline at end of file
diff --git a/src/types.ts b/src/types.ts
index bd1e09d9fb82ccf59031748aefe4ad6d805891c4..628af2660ec9bdc9863aaefd1b4162ce93dfd6f5 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -85,3 +85,40 @@ export interface IndexHist {
   // timestamp
   timestamp: number
 }
+
+// =================== cplus
+
+// for reference see
+// https://doc.e-is.pro/cesium-plus-pod/REST_API.html
+export interface CplusProfile {
+  version: number
+  title: string
+  description: string
+  time: number
+  issuer: string
+  hash: string
+  signature: string
+  city?: string
+  geoPoint?: Geoloc
+  socials?: Social[]
+  tags?: string[]
+  avatar?: Avatar | CID
+}
+
+// social
+interface Social {
+  type: string
+  url: string
+}
+
+// avatar field will be managed as an IPFS file
+export interface Avatar {
+  _content_type: string // image/png for instance
+  _content: string // base64 encoded
+}
+
+// geoloc
+interface Geoloc {
+  lat: number
+  lon: number
+}
diff --git a/src/views/CplusView.vue b/src/views/CplusView.vue
index f6852266ce434b398a242dfd7e8202b3ccf2d469..838731be234998cd9d1a31aa72c95ac76bbe14eb 100644
--- a/src/views/CplusView.vue
+++ b/src/views/CplusView.vue
@@ -1,7 +1,8 @@
 <script setup lang="ts">
 import { ref, type Ref, onMounted } from 'vue'
-import { processCesiumPlusProfile, type CplusProfile } from '../cesium-plus'
+import type { CplusProfile, Avatar } from '../types'
 import { CID } from 'multiformats'
+import { kubo } from '../kubo'
 
 const file: Ref<File | null> = ref(null)
 const sample = ref('')
@@ -45,6 +46,25 @@ function importCplus() {
   }
 }
 
+// this is like processCesiumPlusProfile but does not use node-specific buffer
+async function processCesiumPlusProfile(obj: CplusProfile): Promise<CID> {
+  const { avatar, ...profileWithoutAvatar } = obj
+  if (avatar != undefined && (avatar as Avatar)._content != undefined) {
+    const base64String = (avatar as Avatar)._content
+    const byteCharacters = atob(base64String)
+    const byteNumbers = new Array(byteCharacters.length)
+    for (let i = 0; i < byteCharacters.length; i++) {
+      byteNumbers[i] = byteCharacters.charCodeAt(i)
+    }
+    const byteArray = new Uint8Array(byteNumbers)
+    const fileCandidate = { content: byteArray }
+    return kubo
+      .add(fileCandidate)
+      .then((result) => kubo.dag.put({ ...profileWithoutAvatar, avatar: result.cid as CID }) as Promise<CID>)
+  } else {
+    return kubo.dag.put(obj) as Promise<CID>
+  }
+}
 </script>
 
 <template>
diff --git a/src/views/IndexView.vue b/src/views/IndexView.vue
index 1f66536653dbefeb9afd027220b5b374e0fa54b4..345102abb33d2e16bf737076b593bee483a02b7d 100644
--- a/src/views/IndexView.vue
+++ b/src/views/IndexView.vue
@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { kubo } from '@/kubo'
-import { emptyInode, type IndexHist } from '../types'
 import { IPNS, IPNS_HIST } from '../consts'
 import { CID } from 'multiformats'
 import { ref, type Ref, computed, onMounted } from 'vue'
diff --git a/src/views/KuboView.vue b/src/views/KuboView.vue
index af480a4a1f8552deb0320f3d7e45e46ca233031b..8a5e3e3814f5fb4b2028497e42cd0bf850d8d60c 100644
--- a/src/views/KuboView.vue
+++ b/src/views/KuboView.vue
@@ -1,10 +1,11 @@
 <script setup lang="ts">
 import { ref, type Ref } from 'vue'
-import { kubo, EXPLORER_URL } from '@/kubo'
+import { kubo } from '@/kubo'
+import { EXPLORER_URL } from '../consts'
 import prettyBytes from 'pretty-bytes'
-import type { StatResult } from 'kubo-rpc-client'
+import type { RepoStatResult } from 'kubo-rpc-client'
 
-const stats: Ref<null | StatResult> = ref(null) // Ref<StatResult>
+const stats: Ref<null | RepoStatResult> = ref(null) // Ref<StatResult>
 
 async function refresh() {
   stats.value = await kubo.repo.stat()
@@ -19,13 +20,13 @@ refresh()
     <p>Kubo node status and control panel <button @click="refresh">refresh</button></p>
     <h2>Stats</h2>
     <p v-if="stats">
-      TODO update to latest StatResult<br />
       NumObjects: {{ stats.numObjects.toString() }}<br />
       RepoSize: {{ prettyBytes(Number(stats.repoSize)) }}<br />
       StorageMax: {{ prettyBytes(Number(stats.storageMax)) }}<br />
       RepoPath: {{ stats.repoPath }}<br />
       Version: {{ stats.version }}<br />
     </p>
+    <p v-else>loading stats...</p>
     <p>
       for more info see
       <a :href="EXPLORER_URL" target="_blank">explorer</a>