PocketKid is a mobile-first Python PWA to manage a virtual wallet for your children. It helps parents organize pocket money through configurable challenges and a complete request approval flow.
Public repository:
Parents and children access the same app with role-based features:
- Children can submit reward requests linked to challenges, plus withdrawal and generic deposit requests.
- Parents can approve/reject requests, perform manual wallet movements, configure recurring movements, manage users, and review full history.
The app is installable as a Progressive Web App (PWA) and supports real Web Push notifications (VAPID) for system-level alerts.
PocketKid uses a simple, production-friendly stack:
- Backend: Python + Flask
- Server-side rendered pages
- Session-based authentication
- Role-based access (parent/child)
- Production container runtime via Gunicorn
- Database: SQLite (local file under
data/) - ORM: Flask-SQLAlchemy
- Frontend:
- Jinja templates
- Custom CSS (mobile-first layout)
- Vanilla JavaScript for dynamic UI, polling, auto-refresh, and push registration
- PWA Layer:
- Web App Manifest
- Service Worker (offline static caching + push event handling)
- Notifications:
- In-app notifications
- Real Web Push (VAPID) via
pywebpush
- Containerization:
- Docker + Docker Compose
- Internationalization (i18n):
- English + Italian locale files
- Per-user preferred language setting
- Python 3.12+
pip
cp .env.example .env
# edit .env with VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY (or VAPID_PRIVATE_KEY_B64)
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.pyOpen:
http://localhost:8000
- No demo users are auto-created.
- The app opens the setup screen to create the first parent account.
- You can optionally create the first child during setup.
docker build -t pocketkid .Before running, provide VAPID keys via environment variables:
cp .env.example .env
# edit .env with real VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY (or VAPID_PRIVATE_KEY_B64)docker run --rm \
--env-file .env \
-p 8000:8000 \
-v $(pwd)/data:/app/data \
pocketkidOpen:
http://localhost:8000
Prepare env file first:
cp .env.example .env
# edit .env with real VAPID keys
# optional: set host user/group for container process
# PUID=1000
# PGID=1000docker compose up -d --buildThe default compose file:
- maps port
8000 - persists app data in
./data - restarts container automatically (
unless-stopped) - supports optional
PUID/PGIDto run the container as a specific host UID/GID (default1000:1000)
- Manage children (create/delete with double confirmation)
- Manage other parents (create/delete with safety constraints)
- Configure challenges with predefined reward amounts
- Review and approve/reject pending child requests
- Execute manual wallet movements (deposit/withdrawal)
- Create recurring movements with configurable frequency:
- daily
- weekly
- biweekly
- monthly
- Reset child passwords
- Configure personal language and password
- View wallet balance and operation history
- Submit reward request (linked to configured challenge)
- Submit withdrawal request
- Submit generic deposit request
- Change personal language and password
- Child submits a request (reward / withdrawal / deposit).
- Parent receives notification and sees pending request.
- Parent approves or rejects:
- Approve reward/deposit → wallet increases.
- Approve withdrawal → wallet decreases (if sufficient balance).
- Reject → request state updates to rejected.
- Transaction is saved in history and notifications are generated.
- In-app notifications are available in the top-right mail icon.
- System notifications are sent via Web Push when supported.
- Pages auto-refresh on incoming events to keep dashboards synchronized without manual actions.
- Static assets are cached by the Service Worker.
- Navigation is network-first to avoid stale pages after submit/approval actions.
HTTPS is required for full PWA + Push behavior in real devices/environments (except localhost during development).
- Service Worker reliability in production
- Web Push delivery on iOS/Android/browser
- Proper installability and secure context
Use a reverse proxy with TLS termination (Nginx/Caddy/Traefik) in front of the Flask app/container.
Example architecture:
https://your-domain→ reverse proxy (TLS certificate)- proxy forwards to PocketKid (Gunicorn) on internal port
8000
PocketKid reads VAPID keys from environment variables:
VAPID_PUBLIC_KEY(required)VAPID_PRIVATE_KEY(required ifVAPID_PRIVATE_KEY_B64is not set)VAPID_PRIVATE_KEY_B64(optional alternative toVAPID_PRIVATE_KEY)VAPID_SUBJECT(optional, default:mailto:pocketkid@example.com)
For Docker, set these in .env and load them via --env-file or Docker Compose.
Run from the project root after creating the virtual environment:
source .venv/bin/activate
python - <<'PY'
import base64
from py_vapid import Vapid
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
v = Vapid()
v.generate_keys()
public_bytes = v.public_key.public_bytes(
encoding=Encoding.X962,
format=PublicFormat.UncompressedPoint,
)
public_key = base64.urlsafe_b64encode(public_bytes).decode().rstrip('=')
private_key = v.private_pem().decode('utf-8').replace('\n', '\\n')
print(f"VAPID_PUBLIC_KEY={public_key}")
print(f"VAPID_PRIVATE_KEY={private_key}")
print("VAPID_SUBJECT=mailto:pocketkid@example.com")
PYCopy the output lines into your .env file.
source .venv/bin/activate
python -m py_vapid --gen
python -m py_vapid --applicationServerKey -k private_key.pem- Use the
Application Server Keyvalue asVAPID_PUBLIC_KEY. - Put the full PEM private key into
VAPID_PRIVATE_KEYusing escaped newlines (\n), or encode it to base64 and useVAPID_PRIVATE_KEY_B64.
You can generate keys inside the same Docker image used by the app.
docker compose run --rm pocketkid python - <<'PY'
import base64
from py_vapid import Vapid
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
v = Vapid()
v.generate_keys()
public_bytes = v.public_key.public_bytes(
encoding=Encoding.X962,
format=PublicFormat.UncompressedPoint,
)
public_key = base64.urlsafe_b64encode(public_bytes).decode().rstrip('=')
private_key = v.private_pem().decode('utf-8').replace('\n', '\\n')
print(f"VAPID_PUBLIC_KEY={public_key}")
print(f"VAPID_PRIVATE_KEY={private_key}")
print("VAPID_SUBJECT=mailto:pocketkid@example.com")
PYThen paste these lines into .env and restart:
docker compose up -d --buildPocketKid includes a helper script to generate .env-ready VAPID values:
source .venv/bin/activate
python scripts/generate_vapid.pyOptional subject:
python scripts/generate_vapid.py --subject mailto:admin@example.comWrite directly to .env:
python scripts/generate_vapid.py --write-envCustom env file:
python scripts/generate_vapid.py --write-env --env-file .env.production- Open the app URL in Safari.
- Tap Share.
- Tap Add to Home Screen.
- Confirm installation.
Notes:
- Push notifications on iOS require HTTPS and supported iOS versions.
- User must allow notification permissions.
- Open the app URL.
- Tap browser menu.
- Tap Install app / Add to Home screen.
- Confirm installation.
Notes:
- Push notifications require HTTPS in production.
- User must grant notification permission.
app.py→ minimal application entrypointpocketkid/config.py→ configuration and pathspocketkid/extensions.py→ shared extensions (SQLAlchemy)pocketkid/models.py→ database modelspocketkid/services.py→ support/business utilities (auth helpers, i18n, push, recurring)pocketkid/routes.py→ HTTP routespocketkid/__init__.py→ app factory and bootstraptemplates/→ UI viewsstatic/→ CSS, JS, Service Worker, manifest, iconslocales/→ i18n files (en.json,it.json)data/→ SQLite DB.env.example→ template for VAPID environment variablesDockerfile,docker-compose.yml→ container setup
Stefano Perna
This project is licensed under the MIT License. See the LICENSE file for details.





