hc-persistent
v0.1.2
Published
Persistent single-conductor multi-agent Holochain dev tool — like hc-spin but data survives restarts. One conductor, N agents, one unified Playground.
Downloads
81
Maintainers
Readme
hc-persistent
Persistent single-conductor Holochain dev tool — like hc-spin but data survives restarts.
- 1 conductor (
ws://localhost:11111for Playground) — low RAM vshc-spinN conductors - N persistent agents on that one conductor (
ws://localhost:9001,9002,...for UIs) —profile/avatar/post/contractstay afterCtrl+C - 1 unified Playground (
http://localhost:8282) showing all agents in one graph - Holochain 0.6.3 —
danger_test_keystore+msgpackadmin (no TTYENODEV) - Local bootstrap by default (
127.0.0.1:12345/12346viahc run-local-services) — stays local, no publicdev-test-bootstrap2load (per Paul d'Aoust suggestion)
Install
Quick start (recommended) — via npm/npx:
# inside holochain nix develop (holochain/hc/hc-playground available), make sure bun is on PATH:
npx hc-persistent --happ ./workdir/my-app.happ --agents 4Or via bun:
bunx hc-persistent --happ ./workdir/my-app.happ --agents 4From source (for development/contributing):
# inside holochain nix develop (holochain/hc/hc-playground available):
git clone https://github.com/Nuruddindev/hc-persistent ~/hc-persistent
cd ~/hc-persistent
bun install # or npm install
chmod +x bin/hc-persistent
# optional: link globally
bun link # or npm linkRequires holochain, hc, hc-playground in PATH (via nix develop from holonix or flake.nix), plus Bun as the actual execution runtime — src/index.ts uses Bun-native APIs (Bun.spawn, Bun.$, Bun.sleep) internally.
You can install/invoke it via either package manager — both end up running the same Bun process:
bun:bunx hc-persistent --happ ...orbun run src/index.ts --happ ...npm:npx hc-persistent --happ ...(thebin/hc-persistentwrapper detects it's under plain Node and hands off tobun— make surebunis onPATH, e.g. vianpm i -g bunor the included Nix shell)
Usage
# inside your Holochain app repo (with .happ built):
nix develop
# 2 agents (default):
hc-persistent --happ ./workdir/my-app.happ
# 4 agents (multidevice test):
hc-persistent --happ ./workdir/my-app.happ --agents 4 --app-port 9001 --admin-port 11111
# with env:
HAPP_PATH=./workdir/my-app.happ AGENTS=4 hc-persistent
# clean restart:
hc-persistent --happ ./workdir/my-app.happ --agents 4 --clean
# add agents without wiping (reuse 2, add 2 more):
hc-persistent --happ ./workdir/my-app.happ --agents 4 # Existing apps: skip, new: installOptions:
--agents=<n> Number of agents on single conductor (default: 2)
--happ=<path> Path to .happ (default: ./workdir/my-app.happ)
--admin-port=<n> Single admin port for Playground (default: 11111)
--app-port=<n> First app port (default: 9001, +1 per agent)
--data-dir=<path> Persistent dir (default: ./.sandboxes)
--network-seed=<s> Network seed (default: my-app-seed)
--bootstrap-port=<n> Local bootstrap+signal ports (default: 12345/12346, 0=disable→public, not recommended)
--clean Wipe persistent data before start
--helpLocal bootstrap: default
127.0.0.1:12345(signal12346) viahc run-local-services. Gracefully falls back ifholonix 0.6.3lacks that command — single-conductor still works via loopback gossip. Disable with--bootstrap-port=0(useshttps://dev-test-bootstrap2.holochain.org/, not recommended per Holochain team).
After start, open the printed URLs (with token, not plain):
my-app → http://localhost:3000/?app_port=9001&app_api_token=BASE64...
my-app-agent-2 → http://localhost:3001/?app_port=9002&app_api_token=...
Playground: http://localhost:8282 (already connected to ws://localhost:11111)Vite UIs: UI_PORT=3000 VITE_HC_PORT=9001 vite --port $UI_PORT --strictPort
Web UI signing (browser dev only): Web UIs (Vite, not Tauri/Launcher) must authorize signing via
AdminWebsocketbefore firstcallZome, orcallZomefailsno signing credentials. Add to yourholochain-app.tsfirstUpdatedbeforeAppWebsocket.connect:import { authorizeSigningCredentialsInDev } from "hc-persistent/helpers/dev-signing"; // or copy `ui/src/services/dev-signing.ts` if not using npm const isDev = import.meta.env.DEV === true; if (isDev) { const adminPort = new URLSearchParams(location.search).get("admin_port") || "11111"; const appPort = new URLSearchParams(location.search).get("app_port") || "9001"; const basePort = 9001; const idx = parseInt(appPort,10) - basePort; const appId = idx <=0 ? "my-app" : `my-app-agent-${idx+1}`; await authorizeSigningCredentialsInDev(appId, adminPort); }
import.meta.env.DEVistrueinvite devandfalse(tree-shaken) invite build, so this never runs in production and the browser never triesws://localhost:11111in prod.
Data lives in .sandboxes/ (data_root_path + conductor-config.yaml + app-tokens.json). Ctrl+C keeps it. Only --clean or rm -rf .sandboxes wipes it.
How it works
- Writes
conductor-config.yamlwithdanger_test_keystore(no passphrase TTY)src/index.ts:230 - Spawns
holochain -c .sandboxes/conductor-config.yaml - Waits for admin
ws://localhost:11111vianet.Socketsrc/index.ts:85 - Connects admin via
ws+@msgpack/msgpackWireMessagesrc/index.ts:103 list_apps→ skip existing, elsegenerate_agent_pub_key→install_app {source:{type:"path",value:happ}}→enable_app→attach_app_interface→issue_app_authentication_token(30-day, non-single-use)- Spawns
hc-playground ws://localhost:11111src/index.ts:508at8282
Single conductor saves RAM vs hc-spin N conductors. For hc-spin parity, use multi-conductor mode (not default).
Comparison with hc-spin
| | hc-spin | hc-persistent |
|---|---|---|
| Conductors | N (one per agent) | 1 (N apps) |
| RAM | N × holochain | 1 × holochain |
| Playground | N ports | 1 port (unified graph) |
| Persistence | temp (wiped) | .sandboxes survives |
| Add agents | restart all | --agents=5 reuses |
License
MIT
