Pulsecheck started because I needed it. I was building multiple apps and had no centralized way to collect user feedback. Bug reports came through email, feature requests through App Store reviews, and suggestions through DMs. None of it was organized. None of it was actionable.
So I built the tool I wanted: a feedback platform where users submit through an embedded widget, and I manage everything from one dashboard.
Then I decided to make it a product other indie developers could use too. That's where the scope got interesting.
What Shipped
Pulsecheck launched with more features than I originally planned. That's the polite way of saying scope crept. But each addition solved a real problem, so I'm not apologizing.
Core loop: Users submit feedback through an embeddable widget or public board. Feedback gets categorized (bug, feature, idea, question), voted on by other users, and managed by the project admin.
Multi-tenant: Self-service registration. Each developer gets their own project with a public slug (pulsecheckapp.com/your-project). Free tier: 1 project. Pro: unlimited.
Admin tools: Status workflow (Under Review, Planned, In Progress, Shipped), admin comments, internal notes (visible only to admin), pin/lock individual items, Kanban board view.
Notifications: Email alerts for new feedback, status changes, and admin comments. Slack webhook integration. All non-blocking so the API stays fast.
Public roadmap: Optional Kanban view at /your-project/roadmap. Four columns, configurable labels. Available to all tiers because transparency shouldn't be a paid feature.
Billing: Stripe integration with Pro tier at $19/month. Feature gating enforced server-side. The whole billing layer gracefully degrades if Stripe secrets are missing (returns 503 instead of crashing).
Analytics: CSS-based charts. No d3, no recharts, no charting library at all. Stat cards, status breakdowns, SVG sparkline trends. Free tier gets overview stats, Pro gets 30/90/180-day trend analysis.
The Build Phases
I built Pulsecheck in 18 phases. Not sprints, phases. Some took a day, some took a week. The order was deliberate:
- Core feedback CRUD
- Admin dashboard
- Registration + multi-tenant
- Public board + voting
- Status workflow
- Email notifications
- Pro billing
- Public roadmap
- Admin comments
- Embeddable widget
- Security hardening
- Pin/lock/internal notes
- Search
- Analytics + Kanban 15-18. Blog, comparison pages, CSV export, markdown (some pending)
The sequence follows a pattern: get the core loop working first, then layer on business features, then polish. The widget didn't ship until Phase 10, which is late for a "feedback collection" product. But without the admin tools to actually manage feedback, a widget is just a complaint box with no one checking it.
Security Hardening as a Phase
Phase 11 was entirely security work. No features, no UI changes. Just hardening:
- Constant-time password comparison (prevents timing attacks)
- IDOR validation on all public endpoints (every request verified against the authenticated user)
- Rate limiting tiers: 10 login attempts per 15 minutes, 5 registrations per hour
- Email addresses stripped from all public API responses
- Atomic vote operations via batched database writes
- Request body size limits
I treated security as its own phase instead of sprinkling it across feature work. Controversial take: security added incrementally during feature development gets forgotten. Security as a dedicated phase gets done.
What's Live
Pulsecheck is deployed at pulsecheckapp.com. Real users. Real projects. Real feedback flowing through the system.
The hosting bill is $0/month (Cloudflare's free tier). The Stripe integration works. The widget embeds on external sites. The email notifications fire. The Kanban board drags and drops.
It's a real product that solves a real problem, built by one person in about two months of focused work. That's the part I'm most proud of. Not the feature count. Not the tech stack. The fact that it works, it's live, and people are using it.
// 18 phases. 1 developer. 0 servers.
// Sometimes the best infrastructure is the kind
// you don't have to think about.
const deployment = {
command: "npm run preview && npm run promote",
hosting: "$0/month",
servers: 0,
uptime: "Cloudflare's problem",
};