Growloop autonomously runs conversion experiments on your product, ships each one via GitHub PR, watches what works, and kills what does not.
backend/- Express API, PostgreSQL repositories, experiment winner evaluation, GitHub/E2B adapter boundaries.dashboard/- React dashboard styled with a Geist-first system.sdk/- Drop-in browser SDK for traffic splitting and conversion tracking.shared/- Shared TypeScript contracts.
npm install
cp .env.example backend/.env
npm run migrate
npm run dev:backend
npm run dev:dashboardThe dashboard expects the backend at http://localhost:4100 unless VITE_API_URL is set.
Use these URLs for the hackathon domain:
- Homepage URL:
https://hackathon.shajinkp.com - Callback URL:
https://hackathon.shajinkp.com/api/github/callback - Webhook URL:
https://hackathon.shajinkp.com/api/github/webhook
Required permissions for the first PR workflow:
- Contents: read/write
- Pull requests: read/write
- Commit statuses: read
- Checks: read
- Metadata: read
Growloop uses the GitHub App to:
- read repository contents through an installation token
- open pull requests for generated variants
- read pull request state for variants
- merge the winning pull request
- close losing pull requests and delete their unmerged variant branches
E2B/Codex is still the right place to create the actual branch diff. The GitHub App owns repo access and PR lifecycle.
Set these on the backend:
GITHUB_APP_ID=
GITHUB_APP_PRIVATE_KEY=
GITHUB_APP_INSTALLATION_ID=
GITHUB_APP_WEBHOOK_SECRET=The included GitHub Action deploys the backend over SSH. Add repository secrets:
VPS_HOSTVPS_USERVPS_SSH_KEYVPS_APP_PATH
The backend expects Postgres and a persistent backend/.env on the VPS.
The deployed process is growloop-backend in pm2. Backend deploys are handled by .github/workflows/deploy-backend.yml.
<script
src="https://hackathon.shajinkp.com/sdk/growloop.js"
data-api-url="https://hackathon.shajinkp.com"
data-experiment-id="YOUR_EXPERIMENT_ID"
data-auto-pageview="true"
></script>Custom events:
window.Growloop.track("signup", { plan: "pro" });