Quiet Habits is a small self-hosted habit tracker. It runs as a local Node application, stores data in SQLite, and does not require an accou
JavaScript 96%
Dockerfile 2%
1%
HTML <1%

README.md

Quiet Habits #

Quiet Habits is a small self-hosted habit tracker. It runs as a local Node application, stores data in SQLite, and does not require an account, browser extension, or hosted service.

Requirements #

  • Node.js 20 or newer
  • npm

Local development #

Install dependencies:

npm install

Start the frontend and API together:

npm run dev

Open http://localhost:5173 during development. Vite serves the React UI and proxies /api requests to the Node server on port 3001.

Production-style local run #

Build the frontend and start the server:

npm run build
npm start

Open http://localhost:3001. The server serves the built frontend and the API from the same origin.

You can change the API port with PORT:

$env:PORT = 3100
npm start

PORT and API_RATE_LIMIT_MAX must be positive integers. The process closes its HTTP listener and SQLite connection on SIGINT and SIGTERM.

Data storage #

The SQLite database is created at data/quiet-habits.db. The entire data/ runtime directory is ignored by Git so personal habit data stays local.

The database contains:

  • habits: habit identity and creation time
  • habit_checkins: one completion date per habit

Back up the app by copying the database while the server is stopped. To reset all data, stop the server and remove data/quiet-habits.db plus any data/quiet-habits.db-wal and data/quiet-habits.db-shm files. The next start recreates the schema.

The database uses SQLite user_version migrations. The current schema version is 1; future schema changes should be added as numbered migrations rather than editing existing tables manually.

Habit check-ins use the browser's local calendar date. This avoids UTC-midnight surprises for users whose local timezone differs from the host or container timezone.

API smoke check #

With the server running:

Invoke-RestMethod http://localhost:3001/api/health
Invoke-RestMethod http://localhost:3001/api/habits

API requests are limited to 120 requests per client IP per minute by default. For local testing only, API_RATE_LIMIT_MAX can lower that threshold; do not disable throttling in a deployed instance. JSON request bodies are limited to 8 KB. The shared habit-name limit is defined in src/config.js and is enforced by both the UI and API.

Tests and build #

npm test
npm run build

Optional container deployment #

A Containerfile is included for Podman or Docker-compatible runtimes. The SQLite database should be mounted at /app/data so it survives container replacement.

podman build -t quiet-habits .
podman run --rm -p 3001:3001 -v quiet-habits-data:/app/data quiet-habits

For a rootless Podman systemd deployment, copy quiet-habits.container to ~/.config/containers/systemd/, then run:

systemctl --user daemon-reload
systemctl --user enable --now quiet-habits.service

The quadlet uses a named volume called quiet-habits-data and publishes port 3001.

Before enabling the quadlet, build the image with the exact tag it references:

podman build -t localhost/quiet-habits:latest .
systemctl --user daemon-reload
systemctl --user enable --now quiet-habits.service

The UI uses local system font stacks and has no runtime font-provider dependency.

Project structure #

  • src/: React UI and browser-safe shared domain/configuration modules
  • server/app.js: Express app, middleware, API routes, and static serving
  • server/index.js: production startup entry point
  • server/db.js: SQLite connection, schema, and persistence operations
  • server/config.js: validated server configuration
  • server/lifecycle.js: graceful shutdown behavior
  • server/api.test.js: HTTP-level API contract tests
  • server/config.test.js: configuration validation tests
  • server/lifecycle.test.js: shutdown lifecycle tests
  • data/: local SQLite runtime data, ignored by Git