"Rent it. Lend it. Share it. Teach it."
Free as in freedom. Open source. No platform fees. Forever.
Live demo: https://46.62.138.218 (test user: angel / helix_pass)
First visit? Your browser will show a certificate warning ("Your connection is not private") because the server uses a self-signed certificate. This is expected. Click Advanced then Proceed to site. It only happens once.
Video walkthroughs: YouTube Playlist -- BorrowHood Feature Demos
| Home | Browse | Item Detail |
|---|---|---|
![]() |
![]() |
![]() |
| Workshop Profile | Dashboard | Members |
|---|---|---|
![]() |
![]() |
![]() |
A community rental, services, and skills platform. People list their underused tools, kitchen gear, equipment, and skills -- and neighbors borrow, rent, or trade. No middlemen. No fees. No algorithm deciding who sees what.
Every user is a workshop. Every garage becomes a rental shop. Every kitchen becomes a bakery.
My father Albert was a hand-shovel landscaper in Switzerland from 1960 to 2020. His garage had 500 tools. When he passed, they sat there collecting dust. His neighbors still needed shovels, rakes, and chainsaws -- they just didn't know where to look.
Craigslist is a wasteland. Facebook Marketplace is a data farm. None of them care about your community.
BorrowHood fixes that. Built from a camper van in Trapani, Sicily.
This project was built for the DEV Weekend Challenge -- Anthropic Claude Code edition.
Built with: Claude Code (Claude Opus 4.6) as co-pilot.
- List anything -- tools, kitchen gear, digital goods, services, spaces, made-to-order items
- Eight listing types -- Rent, Sell, Give Away, Commission, Offer, Service, Training, Auction
- Bilingual -- English + Italian (476 translatable strings, 3-tier language detection)
- Workshop profiles -- Every user is a shop with skills, languages (CEFR levels), and social links
- Reputation system -- Reviews weighted by reviewer badge tier (Newcomer 1x to Legend 10x)
- Rental state machine -- Request > Approve > Pickup > Return > Complete (with dispute handling)
- Auction system -- Timed bidding with auto-outbid notifications, reserve prices, bid increments
- Giveaway flow -- Free items with simplified claim, no return dates, earns Generous Neighbor badge
- Dispute resolution -- 3-step flow: file, respond, resolve (8 reasons, 7 resolution types)
- Security deposits -- Hold at pickup, release on return, forfeit on damage
- Lockbox codes -- One-time 8-character codes for contactless pickup and return
- PayPal payments -- Create order, buyer approves, capture, refund
- Notification bell -- In-app notifications with 15 event types + optional Telegram forwarding
- Community helpboard -- Post requests, get replies, track status
- AI-assisted listings -- Generate descriptions, images, and skill bios via Pollinations
- Onboarding wizard -- 3-step profile setup for new users
- 109 REST API endpoints with OpenAPI docs (
/docs) - 250 automated tests across 23 test files (201 pass without DB)
- 32 SQLAlchemy models with UUID PKs, soft deletes, audit timestamps
- 37 typed enums -- no magic strings anywhere
- Keycloak OIDC -- enterprise SSO with 6 realm roles and RBAC
- Idempotency keys on rental requests (no double-submit)
- Optimistic locking on listings (version field)
- PayPal REST API v2 integration (sandbox + live)
- Telegram bot for notification forwarding
- QA dashboard -- bug tracking, test phases, activity trails
- Backlog board -- project management with status tracking
| Layer | Technology |
|---|---|
| Backend | FastAPI + SQLAlchemy 2.0 async + asyncpg |
| Auth | Keycloak OIDC (RS256 JWT, 6 roles) |
| Database | PostgreSQL 17 |
| Cache | Redis 7 |
| Queue | RabbitMQ 3.13 |
| Object Storage | MinIO |
| Frontend | Jinja2 SSR + Tailwind CSS (CDN) + Alpine.js (CDN) |
| Fonts | Google Fonts (Inter) via CDN |
| Payments | PayPal REST API v2 (sandbox + live) |
| AI | Pollinations API (image + text generation) |
| Bot | Telegram Bot API |
| Tests | pytest + pytest-asyncio (250 tests, 201 without DB) |
| Container | Docker |
| Reverse Proxy | Caddy 2 (automatic TLS) |
| Hosting | Hetzner CX32 (4 vCPU, 8 GB RAM, EUR 7.59/mo) |
No bundled subsets. Full CDN libraries only.
Browser
|
v
Caddy (TLS termination)
|
v
FastAPI (uvicorn)
|
+-- Page Routes (Jinja2 SSR) -----> 18 templates (base + pages)
| |
| +-- i18n engine (EN/IT) ----> 476 translatable strings
|
+-- API Routes (/api/v1/) --------> 109 endpoints, JSON
| |
| +-- Items CRUD + search
| +-- Listings CRUD + filters
| +-- Rentals (state machine)
| +-- Reviews (weighted scoring)
| +-- Bids (auction system)
| +-- Deposits (hold/release/forfeit)
| +-- Disputes (3-step resolution)
| +-- Payments (PayPal checkout)
| +-- Lockbox (pickup/return codes)
| +-- Notifications (bell + Telegram)
| +-- Badges (reputation tiers)
| +-- Helpboard (community posts)
| +-- AI (image + listing generation)
| +-- QA (bug tracking, test phases)
| +-- Backlog (project board)
| +-- Onboarding, Reports, Health
|
+-- Auth Routes ------------------> Keycloak OIDC
|
+-- SQLAlchemy async -------------> PostgreSQL
| |
| +-- 32 models (UUID PKs, soft deletes)
| +-- 37 typed enums
| +-- Seed data (12 workshops, 119 items)
|
+-- Redis (caching) + RabbitMQ (task queue) + MinIO (file storage)
BHUser (every user IS a workshop)
|-- BHUserLanguage (CEFR levels: A1-C2, Native)
|-- BHUserSkill (self-declared, community-verified)
|-- BHUserPoints (reputation tracking)
|-- BHUserSocialLink (YouTube, Instagram, etc.)
|-- BHUserFavorite (saved items)
|-- BHNotification (in-app + Telegram, 15 event types)
|-- BHTelegramLink (account linking)
|
+-- BHItem (tools, equipment, services, spaces -- 21 categories)
| |-- BHItemMedia (photos, video embeds)
| +-- BHContentTranslation (multi-language content)
|
+-- BHListing (rent, sell, giveaway, commission, offer, service, training, auction)
| |-- BHBid (auction bids with auto-outbid)
| +-- BHRental (state machine: pending -> completed)
| |-- BHReview (weighted by reviewer badge tier)
| |-- BHDeposit (hold -> release/forfeit)
| |-- BHPayment (PayPal checkout flow)
| |-- BHLockBoxAccess (pickup/return codes)
| +-- BHDispute (3-step: file -> respond -> resolve)
|
+-- BHHelpPost + BHHelpReply (community helpboard)
+-- BHReport (content moderation)
+-- BHWorkshopMember (team management)
+-- BHBadge (achievement system)
+-- BHAuditLog (system audit trail)
QA Module:
BHTestResult, BHBugReport, BHBugActivity, BHBugCommit
BHBacklogItem, BHBacklogActivity
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/health |
Health check with DB status |
| GET | /api/v1/items |
List items (search, filter, sort, paginate) |
| GET | /api/v1/items/{id} |
Item detail |
| GET | /api/v1/listings |
List listings (filter by item, status, type) |
| GET | /api/v1/listings/{id} |
Listing detail |
| GET | /api/v1/reviews |
List reviews (filter by user) |
| GET | /api/v1/reviews/summary/{user_id} |
Review summary (avg, weighted avg, count) |
| GET | /api/v1/bids/summary |
Auction summary (current price, winner) |
| GET | /api/v1/users |
List community members |
| GET | /api/v1/users/{id} |
User profile |
| GET | /api/v1/badges/catalog |
Available badges |
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/items |
Create item |
| PATCH | /api/v1/items/{id} |
Update item (owner only) |
| DELETE | /api/v1/items/{id} |
Soft-delete item (owner only) |
| POST | /api/v1/items/{id}/media |
Add media to item |
| POST | /api/v1/items/{id}/upload |
Upload image |
| POST | /api/v1/listings |
Create listing |
| PATCH | /api/v1/listings/{id} |
Update listing (owner only) |
| DELETE | /api/v1/listings/{id} |
Soft-delete listing (owner only) |
| GET | /api/v1/rentals |
List your rentals |
| GET | /api/v1/rentals/{id} |
Rental details |
| POST | /api/v1/rentals |
Request a rental (idempotency key) |
| PATCH | /api/v1/rentals/{id}/status |
Transition rental status |
| POST | /api/v1/reviews |
Submit review (completed rentals only) |
| POST | /api/v1/bids |
Place bid on auction |
| GET | /api/v1/bids |
List bids |
| POST | /api/v1/bids/{id}/end |
End auction (owner only) |
| GET | /api/v1/notifications |
List notifications |
| GET | /api/v1/notifications/summary |
Unread counts |
| PATCH | /api/v1/notifications/{id}/read |
Mark as read |
| POST | /api/v1/notifications/read-all |
Mark all as read |
| POST | /api/v1/disputes |
File a dispute |
| GET | /api/v1/disputes |
List disputes |
| GET | /api/v1/disputes/summary |
Dispute counts |
| PATCH | /api/v1/disputes/{id}/respond |
Respond to dispute |
| PATCH | /api/v1/disputes/{id}/resolve |
Resolve dispute |
| POST | /api/v1/deposits |
Hold deposit |
| GET | /api/v1/deposits |
List deposits |
| PATCH | /api/v1/deposits/{id}/release |
Release deposit |
| PATCH | /api/v1/deposits/{id}/forfeit |
Forfeit deposit |
| POST | /api/v1/payments/create-order |
Create PayPal order |
| POST | /api/v1/payments/capture |
Capture payment |
| POST | /api/v1/payments/{id}/refund |
Refund payment |
| GET | /api/v1/payments |
List payments |
| POST | /api/v1/lockbox/{id}/generate |
Generate pickup/return code |
| POST | /api/v1/lockbox/{id}/verify |
Verify code |
| GET | /api/v1/lockbox/{id} |
Lockbox status |
| POST | /api/v1/onboarding/profile |
Setup/update profile |
| GET | /api/v1/users/me/favorites |
List favorites |
| GET | /api/v1/users/me/favorite-ids |
Favorite IDs |
| POST | /api/v1/users/{id}/favorite |
Toggle favorite |
| GET | /api/v1/badges |
User badges |
| POST | /api/v1/badges/check |
Check badge eligibility |
| GET | /api/v1/badges/user/{id} |
Badges for user |
| POST | /api/v1/helpboard/posts |
Create help post |
| GET | /api/v1/helpboard/posts |
List posts |
| GET | /api/v1/helpboard/posts/{id} |
Post detail |
| POST | /api/v1/helpboard/posts/{id}/replies |
Reply to post |
| PATCH | /api/v1/helpboard/posts/{id}/status |
Update post status |
| GET | /api/v1/helpboard/summary |
Post counts |
| POST | /api/v1/reports |
Report content |
| POST | /api/v1/ai/generate-listing |
AI-generate listing text |
| POST | /api/v1/ai/generate-image |
AI-generate item image |
| POST | /api/v1/ai/generate-skill-bio |
AI-generate skill bio |
| POST | /api/v1/telegram/link |
Link Telegram account |
| GET | /api/v1/telegram/status |
Link status |
| POST | /api/v1/telegram/toggle |
Toggle notifications |
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/testing/bugs |
List bugs |
| POST | /api/v1/testing/bugs |
File bug |
| GET | /api/v1/testing/bugs/{id} |
Bug detail |
| GET | /api/v1/testing/bugs/{id}/activities |
Bug activity trail |
| POST | /api/v1/testing/bugs/{id}/commits |
Link commit to bug |
| GET | /api/v1/testing/tests |
List test cases |
| POST | /api/v1/testing/tests |
Create test case |
| PATCH | /api/v1/testing/tests/{id} |
Update test result |
| POST | /api/v1/testing/tests/reset |
Reset test phase |
| GET | /api/v1/testing/phases |
Test phases |
| GET | /api/v1/testing/summary |
QA summary |
| GET | /api/v1/backlog/items |
List backlog items |
| POST | /api/v1/backlog/items |
Create backlog item |
| PATCH | /api/v1/backlog/items/{id} |
Update backlog item |
| GET | /api/v1/backlog/items/{id}/activities |
Activity trail |
| GET | /api/v1/backlog/summary |
Backlog summary |
| Path | Description |
|---|---|
/ |
Home (featured items, stats, origin story) |
/browse |
Search + filter marketplace |
/items/{slug} |
Item detail with rental request |
/workshop/{slug} |
Workshop profile (exportable) |
/members |
Community directory |
/helpboard |
Community help posts |
/list |
List a new item (multi-step form) |
/dashboard |
Personal hub (items, rentals, requests) |
/profile |
Edit profile |
/onboarding |
3-step new user setup |
/testing |
QA dashboard |
/backlog |
Project board |
/terms |
Terms of Service + Code of Conduct |
/demo-login |
Demo user selector |
/login |
Keycloak OIDC login |
/logout |
Clear session + Keycloak logout |
- Open https://46.62.138.218
- Your browser will warn you about the certificate. Click Advanced > Proceed. (Self-signed cert, expected.)
- Browse around. No login needed for the public pages.
- To log in: click Demo in the nav bar, or go to
/demo-loginand pick a user. All passwords arehelix_pass.
Prerequisites:
- Python 3.11+
- PostgreSQL 15+
- Docker (for Keycloak)
- mkcert (for local HTTPS -- Keycloak OIDC requires it)
# 1. Clone
git clone https://github.com/akenel/borrowhood.git
cd borrowhood
# 2. Python environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 3. Local HTTPS certificates (required for Keycloak OIDC)
# Without this, login will fail with redirect_uri errors.
mkcert -install # Trust the local CA (one-time)
mkcert localhost 127.0.0.1 ::1 # Creates localhost+2.pem and localhost+2-key.pem
# 4. Start Keycloak (Docker)
# Import the realm file on first run.
docker run -d --name keycloak \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:24.0.4 start-dev
# Then go to http://localhost:8080, log in as admin/admin,
# and import keycloak/borrowhood-realm-dev.json
# 5. Configure
cp .env.example .env
# Edit .env -- set your PostgreSQL URL and Keycloak URL (http://localhost:8080)
# 6. Run
uvicorn src.main:app --reload --host 0.0.0.0 --port 8000
# 7. Seed test data (auto on startup in debug mode, or manually):
curl -X POST http://localhost:8000/api/v1/seed
# 8. Open in browser
open http://localhost:8000| Problem | Cause | Fix |
|---|---|---|
| Login redirects to blank page or errors | Keycloak needs HTTPS for redirect URIs | Install mkcert and generate local certs (step 3) |
| "redirect_uri mismatch" on login | App URL doesn't match Keycloak client config | Check BH_APP_URL in .env matches what's in Keycloak client settings |
| Browser says "connection not private" | Self-signed certificate | Click Advanced > Proceed (expected for local dev and the live demo) |
| Seed data missing after restart | DEBUG=false in .env |
Set DEBUG=true for auto-seed on startup |
source .venv/bin/activate
# Unit tests only (no database required -- runs anywhere):
python -m pytest tests/ -v
# Result: 201 passed (49 skipped -- need DB)
# Full suite (requires PostgreSQL with current schema):
python -m pytest tests/ -v
# Result: 250 passedNo database? 201 tests pass without any external services -- models, enums, schemas, i18n, API edge cases, state machine logic. The remaining 49 are integration tests that hit the database.
250 tests across 23 files
test_models_and_services.py - 41 tests (model creation, enums, service logic)
test_pages.py - 34 tests (all pages EN/IT, auth redirects, 404s)
test_business_logic.py - 31 tests (rental flows, deposits, disputes, auctions)
test_api_edge_cases.py - 27 tests (bad input, missing fields, boundaries)
test_rental_state_machine.py - 26 tests (all state transitions, invalid transitions)
test_i18n.py - 20 tests (locale loading, detection, completeness)
test_api_items.py - 10 tests (CRUD, search, filter, auth gates)
test_auth.py - 6 tests (login redirect, logout, cookie)
test_api_listings.py - 6 tests (CRUD, filters, auth gates)
test_api_ai.py - 5 tests (image gen, listing gen, skill bio)
test_api_disputes.py - 5 tests (file, list, respond, resolve, summary)
test_api_lockbox.py - 5 tests (generate, verify, status)
test_api_bids.py - 4 tests (place, list, summary, end auction)
test_api_deposits.py - 4 tests (hold, list, release, forfeit)
test_api_notifications.py - 4 tests (summary, list, mark read, read-all)
test_api_payments.py - 4 tests (create order, capture, refund, list)
test_api_rentals.py - 4 tests (auth gates on all endpoints)
test_api_reviews.py - 4 tests (public reads, auth writes)
test_api_badges.py - 4 tests (catalog, check, user badges)
test_health.py - 3 tests (status, timestamp, uptime)
test_api_onboarding.py - 3 tests (page, steps, auth gate)
Full bilingual support: English + Italian. 476 translatable strings across 32 sections.
Language detection chain:
?lang=itquery parameter (highest priority)bh_langcookie (persistent across sessions)Accept-Languageheader (browser default)- English (fallback)
The locale completeness test ensures every English key has an Italian translation. Adding a key to en.json without adding it to it.json fails the test suite.
Import keycloak/borrowhood-realm-dev.json into your Keycloak instance.
Roles: bh-member, bh-lender, bh-moderator, bh-admin, bh-operator, bh-qa-tester
Test users (all password: helix_pass):
| User | Roles | Notes |
|---|---|---|
angel |
All roles | Admin |
sally |
lender, member | Lister |
nino |
operator | Operations |
luna |
moderator | Content moderation |
anne |
qa-tester | QA testing |
mike |
member | Basic user |
maria |
member, lender | Community member |
marco |
member, lender | Community member |
jake |
member | International user |
rosa |
member, lender | Community member |
dave |
member | International user |
| Metric | Count |
|---|---|
| Python source lines | 10,625 |
| HTML template lines | 6,244 |
| Test lines | 2,220 |
| Total lines | ~19,100 |
| Python source files | 75 |
| Test files | 23 |
| Total files | 250+ |
| SQLAlchemy models | 32 |
| Typed enums | 37 |
| API endpoints | 109 |
| Router modules | 23 |
| Service modules | 12 |
| Automated tests | 250 (201 without DB) |
| Puppeteer screen tests | 52 edge-case checks |
| i18n strings | 476 (EN + IT) |
| Seed items | 119 (across 21 categories) |
| Custom SVG illustrations | 20 |
Full stats breakdown: docs/BORROWHOOD-STATS.md
MIT
Built from a camper van in Trapani, Sicily. 2026. "Every neighborhood has a garage like his."






