SubTrackr is a self-hosted subscription management application built with Go and HTMX. It helps users track subscriptions, visualize spending, and get renewal reminders.
- Backend: Go 1.21+ with Gin web framework
- Database: SQLite (GORM)
- Frontend: HTMX + Tailwind CSS
- Deployment: Docker & Docker Compose
subtrackr-xyz/
├── cmd/
│ ├── server/ # Main server entry point
│ └── migrate-dates/ # Date migration utility
├── internal/
│ ├── config/ # Configuration management
│ ├── database/ # Database initialization and migrations
│ ├── handlers/ # HTTP request handlers (Gin handlers)
│ ├── middleware/ # HTTP middleware (auth, etc.)
│ ├── models/ # Data models (GORM models)
│ ├── repository/ # Data access layer
│ ├── service/ # Business logic layer
│ └── version/ # Version information
├── templates/ # HTML templates (HTMX)
├── web/static/ # Static assets (JS, CSS, images)
├── tests/ # Playwright E2E tests
└── data/ # SQLite database (gitignored)
- Initializes database, repositories, services, and handlers
- Sets up Gin router with templates
- Configures routes (web and API)
- Starts HTTP server
- subscription.go: CRUD operations for subscriptions
- settings.go: SMTP config, Pushover config, notifications, API keys, currency, dark mode
- category.go: Category management
- Business logic layer
- subscription.go: Subscription operations
- settings.go: Settings management
- category.go: Category operations
- currency.go: Currency conversion (Fixer.io integration)
- email.go: Email notification service (SMTP)
- pushover.go: Pushover notification service
- GORM models:
Subscription: Main subscription entityCategory: Subscription categoriesSettings: Application settings (key-value store)SMTPConfig: Email configurationPushoverConfig: Pushover notification configurationAPIKey: API authentication keysExchangeRate: Currency exchange rates
- Data access layer using GORM
- Abstracts database operations
/- Dashboard/dashboard- Dashboard/subscriptions- Subscription list/analytics- Analytics view/settings- Settings page/form/subscription- Subscription form modal
/api/subscriptions- Subscription CRUD/api/stats- Statistics/api/export/*- Data export/api/settings/*- Settings management/api/categories- Category management
/api/v1/subscriptions- Subscription CRUD/api/v1/stats- Statistics/api/v1/export/*- Data export
- ID, Name, Cost, OriginalCurrency
- Schedule: Monthly, Annual, Weekly, Daily
- Status: Active, Cancelled, Paused, Trial
- CategoryID (foreign key)
- Dates: StartDate, RenewalDate, CancellationDate
- Additional: PaymentMethod, Account, URL, Notes, Usage
- ID, Name
- CreatedAt, UpdatedAt
- Key-value store for application settings
- Keys:
smtp_config,renewal_reminders,currency, etc.
-
Subscription Management
- CRUD operations
- Multiple schedules (Monthly, Annual, Weekly, Daily)
- Categories
- Multi-currency support
-
Email Notifications
- SMTP configuration with TLS/SSL support
- STARTTLS for ports 2525, 8025, 587, 25, 80
- Implicit TLS for ports 465, 8465, 443
- Renewal reminders
- High cost alerts
-
Pushover Notifications
- Pushover API integration for mobile push notifications
- User Key and Application Token configuration
- Renewal reminders (same settings as email)
- High cost alerts (same threshold as email)
- Works alongside email notifications
-
Currency Support
- USD, EUR, GBP, JPY, RUB, SEK, PLN, INR, CHF, BRL, COP, BDT
- Optional Fixer.io integration for real-time rates
- Automatic conversion display
- BDT (Bangladeshi Taka) with ৳ symbol
-
API Access
- API key authentication
- RESTful endpoints
- JSON responses
-
Data Management
- CSV/JSON export
- Backup functionality
- Clear all data option
- Follow Go standard formatting (
go fmt) - Use meaningful variable and function names
- Add comments for exported functions
- Keep functions focused and small
- Return errors from functions, don't panic
- Log errors appropriately
- Provide user-friendly error messages in handlers
- Unit tests in
*_test.gofiles - E2E tests in
tests/using Playwright - Test API endpoints with
test-api.sh
- Migrations in
internal/database/migrations.go - Use GORM AutoMigrate for schema changes
- Test migrations on sample data
- Use HTMX for dynamic updates
- Tailwind CSS for styling
- Dark mode support via class-based switching
- Mobile-responsive design
- Remember sorting preference (#85) - localStorage persistence
- Fix Tab and PWA icon missing (#84) - favicon, apple-touch-icon, manifest.json
- Input validation for sort parameters
- PWA meta tags on all HTML templates
- Enhanced currency support and conversion display
- Dark classic theme option
- Calendar view improvements
- Optional authentication system
- Beautiful theme options
This project uses versioned branches for releases. See CLAUDE.md for the complete workflow.
Quick Reference:
- Create versioned branch:
git checkout -b vX.Y.Z - Track work with beads:
bd create,bd update,bd close - Create draft release:
gh release create vX.Y.Z --draft - Run code review agent before committing
- Commit, push, create PR:
gh pr create - Comment on GitHub issues:
gh issue comment - Monitor CI:
gh run watch - Merge PR:
gh pr merge --merge --delete-branch - Publish release:
gh release edit vX.Y.Z --draft=false
- Create/update model in
internal/models/ - Add repository methods in
internal/repository/ - Add service logic in
internal/service/ - Create handler in
internal/handlers/ - Add routes in
cmd/server/main.go - Update templates if needed
- Add tests
- Update
Subscription.Schedulevalidation ininternal/models/subscription.go - Update
AnnualCost()andMonthlyCost()methods - Update frontend templates to include new option
- Update date calculation logic if needed
- Add currency code to
SupportedCurrenciesininternal/service/currency.go - Add currency symbol mapping in
GetCurrencySymbol()ininternal/service/settings.go - Add currency option to currency selection in
templates/settings.html - Update exchange rate handling if using Fixer.io
- Create notification config model in
internal/models/settings.go - Create notification service in
internal/service/(e.g.,pushover.go) - Add config save/get methods to
SettingsService - Add handlers in
internal/handlers/settings.go - Add UI in
templates/settings.html - Update subscription handler to send notifications
- Update renewal reminder scheduler in
cmd/server/main.go
PORT- Server port (default: 8080)DATABASE_PATH- SQLite database path (default: ./data/subtrackr.db)GIN_MODE- Gin mode: debug/release (default: debug)FIXER_API_KEY- Fixer.io API key for currency conversion (optional)
# Development
go run cmd/server/main.go
# Build
go build -o subtrackr cmd/server/main.go
# Docker
docker-compose up -d --build# Run Go tests
go test ./...
# Run E2E tests
npm test
# Test API
./test-api.shWhen ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
IMPORTANT: This project uses bd (beads) for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.
- Dependency-aware: Track blockers and relationships between issues
- Git-friendly: Auto-syncs to JSONL for version control
- Agent-optimized: JSON output, ready work detection, discovered-from links
- Prevents duplicate tracking systems and confusion
Check for ready work:
bd ready --jsonCreate new issues:
bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json
bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --jsonClaim and update:
bd update bd-42 --status in_progress --json
bd update bd-42 --priority 1 --jsonComplete work:
bd close bd-42 --reason "Completed" --jsonbug- Something brokenfeature- New functionalitytask- Work item (tests, docs, refactoring)epic- Large feature with subtaskschore- Maintenance (dependencies, tooling)
0- Critical (security, data loss, broken builds)1- High (major features, important bugs)2- Medium (default, nice-to-have)3- Low (polish, optimization)4- Backlog (future ideas)
- Check ready work:
bd readyshows unblocked issues - Claim your task:
bd update <id> --status in_progress - Work on it: Implement, test, document
- Discover new work? Create linked issue:
bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>
- Complete:
bd close <id> --reason "Done"
bd automatically syncs with git:
- Exports to
.beads/issues.jsonlafter changes (5s debounce) - Imports from JSONL when newer (e.g., after
git pull) - No manual export/import needed!
- ✅ Use bd for ALL task tracking
- ✅ Always use
--jsonflag for programmatic use - ✅ Link discovered work with
discovered-fromdependencies - ✅ Check
bd readybefore asking "what should I work on?" - ❌ Do NOT create markdown TODO lists
- ❌ Do NOT use external issue trackers
- ❌ Do NOT duplicate tracking systems
For more details, see README.md and docs/QUICKSTART.md.