Branch structure: See workflow/branching.md. Briefly: work on feature branches; commits directly on dev or master are blocked by hooks. Feature-branch commits run the quick suite. Release is scripts/promote-dev-to-master.sh.
Prerequisites: Node.js 24.x, Docker
npm install
verifier-run automated.build # or npm run buildSee build.md for more details.
After building, you can run:
./scripts/services.sh --start
./scripts/data.sh --seedThat's it. This uses Docker Compose to start a local Hardhat blockchain, deploys the smart contracts, starts IPFS, the Ponder indexer, and the platform API service, then publishes the selected UI domain SPA(s) to the local IPFS gateway.
services.sh --start first regenerates and activates the complete localhost
profile (chain 31337, local RPC/indexer, and local contracts), so a prior
testnet UI build cannot leak Base Sepolia settings into the local stack.
Generated snapshots live under gitignored .generated-env/<network>/.
Restart npm run causestarter:dev after that switch: Vite bakes VITE_* at
process start, so a leftover server will keep calling the previous network
(for example Base Sepolia indexer from a localhost profile).
Which UI bundles get built: local start currently publishes CauseStarter only. The eight legacy ui domains (commonality, lazyGiving, alignment, tally, content-funding, civility, common-sense-majority, conceptspace) each run a full Docker Vite build sequentially and were a major part of --start time. This is a temporary, reversible default — the compose services and source trees are still there.
- Default:
LOCAL_UI_DOMAINS=causestarter(implicit) - Restore every local IPFS SPA:
LOCAL_UI_DOMAINS=all ./scripts/services.sh --start - Subset:
LOCAL_UI_DOMAINS=causestarter,tally ./scripts/services.sh --start
The same env var is read by scripts/deploy-causestarter.sh. The allow-list lives in scripts/ui-domains.mjs (resolveLocalPublishDomains). CauseStarter's dedicated SPA on :8090 is always started and is independent of this list.
A local UI gateway then gives each published IPFS bundle a stable URL such as http://causestarter.localhost:8088/#/. Bookmark http://localhost:8088/admin for links to whatever was published. The latest CIDs, raw IPFS gateway URLs, and stable local URLs are written to ./data/ui-ipfs/<domain>/. You can re-print the stable URLs any time with ./scripts/services.sh --url. After that, run ./scripts/data.sh --seed to populate the chain.
Which seed: these flags are different jobs (plan: fake-data-generation/PLAN.md):
--seed/--seed=tiny(default) — small fake world for UI and basic tests (CauseStarter boards, personas, a handful of well-shaped statements). Not a real-user catalog. Not a load test.--seed=demo— formal seed-content statements plus replayed Alignment explorer/nudge fixtures (still fake activity).--seed=small/--seed=medium— more random fake users and actions (stress-ish).npm run gen:largeinfake-data-generationis 100 users; thousands of users is not a seed flag.
Curated real statements for Conceptspace live in fake-data-generation/seed-content/ and are authored separately; they are not “more fake users.”
For a clean local reset, use:
./scripts/data.sh --wipe
./scripts/services.sh --start
./scripts/data.sh --seedThe Anvil container (hardhat-node) persists blocks to data/hardhat/state.json via --state plus a 15s --state-interval. Docker stop is SIGTERM; a small entrypoint (scripts/anvil-docker-entrypoint.sh) forwards that as SIGINT so Anvil dumps instead of dying empty. Recreate the node after changing that compose service (docker compose up -d --force-recreate --no-deps hardhat-node) so the wrapper is mounted.
--wipe removes the saved local chain, IPFS repo, and Ponder indexer database. Do not delete only one of data/hardhat/ or data/ponder/: a reset chain with an old Ponder database can make the UI look empty because the indexer thinks old blocks were already processed. services.sh --start clears Ponder automatically when it sees Ponder data without a saved local chain. --start also records Hardhat-account TrustSets (CauseStarter’s starter network), so the indexer is not empty after a fresh start — that is bootstrap, not a seed. data.sh --seed only refuses if it already sees signatures / projects / published-data events. If you intentionally want to add another seed run on top of existing data, pass --allow-seed-on-existing-data. One-shot reset+seed: ./scripts/stop-wipe-restart.sh --seed.
For a richer first-run demo that uses the formal seed-content corpus (excluding proliferation variants) and publishes one-shot Explorer/nudge fixtures without live AI worker calls, run:
./scripts/data.sh --seed=demo--start runs cause-assist, christian-bridge-creator, and the attester
bundle service-host-attesters (implication-attester + content-attester on one
Express listener, :3006). Health: http://localhost:3006/health, and per
service at http://localhost:3006/implication-attester/health. CauseStarter
reaches it through /api/implication-attester — proxied by Vite on :5174 and
by nginx on :8090 — which is what the bridge-cluster editor's "submit pairs to
attester" step calls.
Two local-only wrinkles are worth knowing about:
- content-attester is off by default (
CONTENT_ATTESTER_ENABLED=falseindocker-compose.yml). It requiresALIGNMENT_TOPIC_STATEMENT_CID, which is a published statement CID rather than a deploy artifact, so a fresh chain has none. Because the bundle validates all its services at boot, leaving it on takes the implication-attester down with it. Set that CID andCONTENT_ATTESTER_ENABLED=trueto run it. - Service signer wallets need funding. Compose falls back to prefunded
Hardhat keys, but
docker composealso auto-loads the root.env, and oncescripts/generate-wallets.mjshas run that file holds generated keys with no balance on a local chain. Services then boot, reportdegraded, and fail every on-chain write.--startnow runsnode scripts/fund-local-service-wallets.mjs, which tops up any configured signer below 1 ETH from Hardhat account #0 (idempotent, and refuses to run off chain 31337). Run it by hand after a wipe if an attester reportsdegraded.
No API keys or secrets are needed for local development. The generated root .env and ui/.env are based on the local deployment defaults; use .env.example and ui/.env.example as the reference for the variables that the stack and UI understand. scripts/services.sh owns starting/stopping/status/URL printing for Docker services; scripts/data.sh owns wiping and seeding local chain/IPFS/indexer data.
See deployment.md for testnet/mainnet deployment (which does require secrets).
See:
- Verifier workspace — how to run the checks; DESIGN.md for the testing philosophy and validation-pass runbook