Getting Started
What this helps you do
Use this page to prepare a local Green Goods checkout, choose the right development profile, start the services you need, and verify that the web stack responds before you begin package work.
Before you start
Before cloning, install the tools the setup script cannot reliably provide:
| Tool | Minimum | Required for | Install |
|---|---|---|---|
| Node.js | 22+ | Running npm run setup and repo tooling | nodejs.org |
| Git | any | Cloning and contributing | git-scm.com |
| Docker Desktop | current stable | Full-stack and indexer development | docker.com |
Node includes npm, which is enough to run the first setup command. npm run setup installs Bun automatically if it is missing, installs workspace dependencies, and prints the env-bootstrap commands to run next.
Additional tools are workflow-specific:
| Tool | Required for | Install |
|---|---|---|
| Foundry | Contract development and tests | curl -L https://foundry.paradigm.xyz | bash && foundryup |
| cloudflared | HTTPS tunnel for mobile-device testing | brew install cloudflared |
Docker, Foundry, and cloudflared warnings do not block setup unless you are working in the matching workflow.
Operating system support
| OS | Support level | Notes |
|---|---|---|
| macOS | Native | Standard local path. Full stack uses the Docker-backed indexer to avoid native Envio/Rust issues. |
| Linux | Native | Good path for native indexer work, provided Docker and generated indexer dependencies are ready. |
| Windows | WSL2 or dev container | Native PowerShell is not a supported target yet because repo scripts use POSIX shell tools such as sh, bash, lsof, xargs, pkill, and shell env syntax. |
Use WSL2 for Windows contributors until a dedicated native Windows script pass lands.
Running Development Setup
git clone https://github.com/greenpill-dev-guild/green-goods.git
cd green-goods
npm run setup # first-clone bridge: deps and workspace install
bun run env:sync # materializes .env from .env.template via 1Password (op inject)
bun run dev:doctor -- --profile web # non-mutating web readiness check
bun run dev:web # starts client, admin, docs, and Storybook
bun run dev:smoke:web # confirms browser surfaces respond locally
bun run env:sync requires the 1Password CLI signed in with desktop app integration on. If you are not on the team and do not have vault access, copy .env.template to .env and fill local values directly (see Required Secrets & Variables).
npm run setup is the only npm entrypoint in the normal workflow. It exists because a fresh machine may not have Bun installed yet. After setup, use bun run ... for package operations and repo scripts.
What npm run setup does
The setup script (scripts/dev/setup.js) runs through three steps:
- Dependency check -- verifies Node.js 22+ and Git, installs Bun if needed, and warns about missing workflow-specific tools.
- Install packages -- runs
bun installacross all workspaces ifnode_modulesdoes not exist yet. - Next steps -- prints the env-sync, doctor, frontend stack, full stack, and test commands.
Environment files are materialized separately by bun run env:sync (which runs op inject against .env.template). See Required Secrets & Variables below.
Role profiles
| Role | Required local state | First command |
|---|---|---|
| Frontend QA | Node.js, Git, Bun from setup, root .env, ports 3001/3002/3003/6006 available | bun run dev:doctor -- --profile web |
| Full-stack / indexer | Frontend QA plus Docker, packages/indexer/generated, and generated dependencies | bun run dev:doctor -- --profile full |
| Contracts | Frontend QA plus Foundry | bun run dev:doctor -- --profile contracts |
| Upload-capable QA | Frontend QA plus VITE_API_BASE_URL and PINATA_JWT_OP_REF=op://... or PINATA_JWT in root .env | bun run dev:doctor -- --profile upload |
Run bun run dev:doctor -- --profile web|full|contracts|upload any time you need a non-mutating check of the current machine. It reports missing tools, Docker state, Pinata readiness, passkey RP ID posture, indexer generated state, stale environment keys, and port conflicts without printing secret values. Add --json for machine-readable output in CI, containers, or agent workflows.
Required Secrets & Variables
Green Goods uses one root .env plus the committed .env.schema (full key contract) and .env.template (team-shared file with 1Password refs for shared secrets and plain values for non-secrets). Do not create package-level env files.
The standard flow:
bun run env:sync # materialize .env from .env.template via op inject (Touch ID once)
bun run env:check # verify .env satisfies .env.schema
For details on regenerating the template (e.g. after a new key lands in .env.schema), and for first-time setup without 1Password access, see Environment management.
| Variable | Role |
|---|---|
APP_ENV | Environment selector (development, test, staging, production) |
VITE_CHAIN_ID | Sets the target chain at build time (42161 for Arbitrum, 11155111 for Sepolia) |
VITE_PASSKEY_RP_ID | Optional passkey relying-party override. Leave blank for localhost development fallback; set localhost for explicit local passkey QA. |
VITE_API_BASE_URL | Browser API origin. Upload-capable flows call the agent for short-lived Pinata signed upload URLs. |
PINATA_JWT | Server/script Pinata JWT used by the agent upload signer and maintenance scripts. Not embedded in browser bundles. |
VITE_PINATA_GATEWAY_URL | Public Pinata gateway URL for reading IPFS media. |
SEPOLIA_RPC_URL | RPC endpoint for Sepolia fork tests |
Variables prefixed with VITE_ are embedded into frontend bundles at build time.
For personal local-only credentials, put direct values in root .env. For shared Green Goods credentials (upload-capable QA, deploys, CI), edit .env.template to use 1Password URIs in the op://Vault/Item/field form, sign in to the op CLI, and run bun run env:sync.
Running services
Frontend stack
bun run dev:web launches the frontend developer stack through PM2:
| PM2 process | Package | Default port |
|---|---|---|
client | packages/client | 3001 |
admin | packages/admin | 3002 |
docs | docs | 3003 |
storybook | packages/shared | 6006 |
This is the recommended first target for onboarding and frontend QA because it avoids Docker, indexer generated-code setup, and agent credentials.
After the processes are up, run bun run dev:smoke:web in another terminal. It reruns the web doctor, then checks that the client, admin, docs, and Storybook respond on their local ports.
Full stack
bun run dev launches every service through PM2:
| PM2 process | Package | Default port |
|---|---|---|
client | packages/client | 3001 |
admin | packages/admin | 3002 |
docs | docs | 3003 |
indexer | packages/indexer (Docker / GraphQL) | 8080 |
agent | packages/agent | -- |
tunnel | cloudflared HTTPS tunnel | -- |
browser | local URL opener | -- |
storybook | packages/shared | 6006 |
Stop everything with bun run dev:stop.
Individual packages
You can also run packages directly without PM2:
bun run dev:client # Gardener PWA
bun run dev:admin # Operator dashboard
bun run dev:docs # Docusaurus site
bun run dev:indexer # Envio indexer (Docker Compose)
bun run dev:contracts # Anvil local chain
bun run dev:agent # Agent bot
Mobile device testing
Green Goods is a mobile-first PWA, so testing on a real phone is essential. When you run bun run dev, a cloudflared tunnel starts automatically alongside the other services. It creates a temporary public HTTPS URL (e.g. https://random-words.trycloudflare.com) that points to your local client on port 3001.
Once the tunnel is ready, the landing page QR code automatically switches to the tunnel URL. Open https://localhost:3001 on your laptop and scan the QR code with your phone — you'll get the full PWA experience including service worker, install prompts, and passkey auth.
# Tunnel starts automatically with bun run dev.
# To run it standalone (e.g. for the admin dashboard):
bun run dev:tunnel # default: port 3001
bun run dev:tunnel -- --port 3002 # admin on port 3002
If cloudflared is not installed, the tunnel service exits silently and the QR code falls back to window.location.origin. Install it with brew install cloudflared.
If you prefer a wired connection, plug in your Android device with USB debugging enabled and run:
adb reverse tcp:3001 tcp:3001
Then navigate to https://localhost:3001 on Android Chrome. Since localhost is a secure context, the service worker registers without a trusted CA certificate.
How to know it worked
- 1
Read project rules
Start with `AGENTS.md`, `CLAUDE.md`, and `.claude/context/*` for conventions.
- 2
Configure env bootstrap
Keep the generated root `.env` for baseline web dev. Add direct personal values only when needed; use 1Password refs for shared team, deploy, upload, or CI secrets. Never add package-level env files.
- 3
Check role readiness
Run `bun run dev:doctor -- --profile web` before debugging setup. It does not mutate files or start services.
- 4
Start services
Run `bun run dev:web` for frontend QA or `bun run dev` for the full Docker-backed stack.
- 5
Smoke the web stack
Run `bun run dev:smoke:web` once client/admin/docs/Storybook are starting. It verifies local HTTP/HTTPS reachability.
- 6
Run the quality gate
Run `bun run format:check`, `bun run lint`, `bun run test`, and `bun run build` before pushing changes.
Development workflow
The standard edit-test-build-lint cycle:
# 1. Run tests for the package you changed (always `bun run test`, never `bun test`)
cd packages/shared && bun run test
# 2. Check formatting and lint the workspace
bun run format:check
bun run lint
# 3. Build (respects dependency order: contracts -> shared -> indexer -> client/admin)
bun run build
# 4. Format before committing
bun run format
bun test vs bun run testbun test invokes Bun's built-in test runner, which ignores your Vitest config. Always use bun run test to run the package.json script with proper environment setup.
If something goes wrong
| Symptom | Cause | Fix |
|---|---|---|
forge: command not found | Foundry not installed | curl -L https://foundry.paradigm.xyz | bash && foundryup |
| Indexer fails to start | Docker not running | Start Docker Desktop, then retry bun run dev:indexer |
VITE_CHAIN_ID is undefined | Missing .env | Run bun run env:sync, or copy .env.template to .env and fill values manually |
Pinata forbidden or upload fails | Missing signer or Pinata server credential | Confirm VITE_API_BASE_URL points at the agent and add PINATA_JWT to root .env (or set it via 1Password ref in .env.template and re-run bun run env:sync), then rerun bun run dev:doctor -- --profile upload |
Client PM2 process is online but localhost:3001 does not respond | Vite failed to read env | Run bun run dev:doctor -- --profile web and bun run env:check. For 1Password-resolved env, ensure op CLI is signed in via desktop integration, then bun run env:sync. |
bun run dev:smoke:web fails before checking ports | Web doctor failed | Fix the doctor failures first; smoke only probes services after the web profile is ready. |
| Passkey registration fails on localhost | RP ID mismatch | Leave VITE_PASSKEY_RP_ID blank for fallback behavior, or set VITE_PASSKEY_RP_ID=localhost for local passkey QA |
| Port 3001/3002 in use | Another dev server running | Kill the process or change the port in the package's Vite config |
bun install hangs | Stale dependency state | Delete node_modules, then bun install |
| No QR code on landing page | cloudflared not installed | brew install cloudflared, then restart bun run dev |
| Tunnel URL not appearing | Client not ready yet | The tunnel waits up to 60s for port 3001 — check PM2 logs with bunx pm2 logs tunnel |
Non-negotiables
- All React hooks live in
packages/shared-- client and admin only have components and views. - The target chain derives from
VITE_CHAIN_IDat build time. There is no runtime chain switching. - Deployment changes go through
bun deploy:*wrappers, never rawforgecommands. - User-facing strings must be added to all three translation files (
en.json,es.json,pt.json).
Next page
Next best action
Keep momentum by moving to the next high-value step in this journey.
Architecture