Oikos
Self-hosted family planner — tasks, calendars, shopping, meals, budget.
Your data stays on your server. No subscriptions. No tracking. No cloud lock-in.
Features · Quick Start · Configuration · Calendar Sync · Security
Oikos is a self-hosted family organizer for 2–6 people. Tasks, calendars, shopping lists, meal plans, budget tracking, notes, and contacts — all running on your own server inside a single Docker container. No cloud dependency, no telemetry, no data leaves your network.
Built with Express.js, SQLite (optionally encrypted via SQLCipher), and vanilla JavaScript — no frontend framework, no build step. Works offline as a PWA on phones and tablets.
Oikos is not a SaaS product, not a team collaboration tool, and not designed for public multi-tenant use. It is a private tool for one family on one server.
Screenshots
Tasks |
Shopping |
Budget |
Notes |
Contacts |
Dashboard — Mobile |
Screenshots adapt to your GitHub theme — switch between light and dark mode to see both variants.
Features
| Module | What it does | Highlights | |
|---|---|---|---|
| 📋 | Dashboard | At-a-glance overview of your family's day | Weather widget · upcoming events · urgent tasks · today's meals · pinned notes |
| ✅ | Tasks | Shared to-do lists with accountability | List + Kanban views · subtasks · recurring tasks (RRULE) · swipe gestures · priority levels |
| 🛒 | Shopping | Collaborative grocery lists | Multiple lists · aisle-grouped categories · auto-import from meal plan |
| 🍽️ | Meals | Weekly meal planning with ingredients | Week view (Mon–Sun) · ingredient management · one-click export to shopping list |
| 📅 | Calendar | Family calendar with external sync | Month/week/day/agenda views · Google Calendar & Apple iCloud two-way sync |
| 📌 | Notes | Shared family pinboard | Colored sticky notes · pinning · full-text search · lightweight Markdown (bold, italic, lists) |
| 👥 | Contacts | Important family contacts | Category filters · tap-to-call · tap-to-email · map links · vCard import/export |
| 💰 | Budget | Income & expense tracking | Category breakdown · month-over-month comparison · CSV export |
| ⚙️ | Settings | User & sync management | Password changes · calendar sync config · family member admin |
Tech Stack
| Layer | Technology |
|---|---|
| Server | Node.js ≥ 22 · Express · better-sqlite3 · bcrypt · Helmet |
| Database | SQLite with optional SQLCipher encryption (AES-256) |
| Frontend | Vanilla JavaScript ES modules — no framework, no build step. Web Components (oikos-*). Lucide Icons (self-hosted SVG sprite) |
| Auth | Session-based · httpOnly cookies · CSRF double-submit · express-session |
| Deployment | Docker + Docker Compose · Nginx reverse proxy · Let's Encrypt SSL |
| Integrations | Google Calendar API v3 (OAuth 2.0) · Apple iCloud CalDAV (tsdav) · OpenWeatherMap |
Quick Start
Prerequisites: Docker + Docker Compose on a Linux server.
1. Clone
git clone https://github.com/ulsklyc/oikos.git
cd oikos
2. Configure
cp .env.example .env
Edit .env and set the two required variables:
SESSION_SECRET=your-random-string-at-least-32-chars
DB_ENCRYPTION_KEY=your-sqlcipher-aes256-key
3. Start
docker compose up -d
First build takes 2–3 minutes (compiles SQLCipher against better-sqlite3).
4. Create admin account
docker compose exec oikos node setup.js
Interactive script — sets up username, display name, and password. This admin can create additional family members from the settings page.
5. Open
Navigate to http://localhost:3000 — or your configured domain after Nginx setup.
Nginx: See
nginx.conf.examplefor a production-ready config. If you use Nginx Proxy Manager, paste the contents into the "Advanced" tab. Make sureX-Forwarded-Protois set so session cookies work correctly in production.
Configuration
Required
| Variable | Description |
|---|---|
SESSION_SECRET |
Random string ≥ 32 characters for session signing |
DB_ENCRYPTION_KEY |
SQLCipher AES-256 key. Leave empty to disable encryption |
Optional
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
NODE_ENV |
development |
Set to production for deployment |
DB_PATH |
./oikos.db |
Path to SQLite database file |
SYNC_INTERVAL_MINUTES |
15 |
Automatic calendar sync interval |
RATE_LIMIT_MAX_ATTEMPTS |
5 |
Max login attempts per minute per IP |
Weather Widget
Register a free API key at openweathermap.org:
| Variable | Default | Description |
|---|---|---|
OPENWEATHER_API_KEY |
— | Your API key |
OPENWEATHER_CITY |
Berlin |
City name |
OPENWEATHER_UNITS |
metric |
metric (°C) or imperial (°F) |
OPENWEATHER_LANG |
de |
Language code |
Integrations
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
Google OAuth 2.0 Client ID |
GOOGLE_CLIENT_SECRET |
Google OAuth 2.0 Client Secret |
GOOGLE_REDIRECT_URI |
https://your-domain/api/v1/calendar/google/callback |
APPLE_CALDAV_URL |
https://caldav.icloud.com |
APPLE_USERNAME |
Your Apple ID email |
APPLE_APP_SPECIFIC_PASSWORD |
App-specific password from Apple ID settings |
Full template: .env.example
Calendar Sync
Oikos syncs bidirectionally with Google Calendar and Apple iCloud. External events are visually distinguished in the UI. On conflict, the external source wins.
Google Calendar
- Create a project at console.cloud.google.com
- Enable the Google Calendar API
- Create an OAuth 2.0 Client ID (type: Web application)
- Add your redirect URI:
https://your-domain.com/api/v1/calendar/google/callback - Add credentials to
.env:GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_REDIRECT_URI=https://your-domain.com/api/v1/calendar/google/callback - Restart:
docker compose up -d - In Oikos: Settings → Calendar Sync → Connect Google
Sync behavior: Initial sync pulls events from 3 months ago to 12 months ahead. Subsequent syncs use Google's syncToken for incremental updates. Local events push to Google automatically. Conflicts: Google wins on simultaneous edits.
Apple Calendar (iCloud CalDAV)
Option A — via Settings UI (recommended, no restart required):
- Go to appleid.apple.com → Sign-In and Security → App-Specific Passwords
- Generate a new password for "Oikos"
- In Oikos: Settings → Calendar Sync → Apple Calendar → enter the CalDAV URL, Apple ID email and the app-specific password → click Verbinden & testen
Credentials are stored in the database. No server restart required.
Option B — via .env:
APPLE_CALDAV_URL=https://caldav.icloud.com
APPLE_USERNAME=your@apple-id.com
APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx
Restart: docker compose up -d. The sync button appears automatically in Settings. .env-credentials are used as fallback when no UI-credentials are saved.
Security
Oikos is designed to run on a private server behind SSL. No public endpoints exist except the login page.
- Sessions —
httpOnly,SameSite=Strict,Securein production, 7-day TTL - CSRF — Double-submit cookie pattern on all state-changing requests
- Passwords — bcrypt with cost factor 12
- Rate limiting — 5 login attempts/min, 300 API requests/min per IP
- Headers — Strict Content Security Policy via Helmet (
self-only) - Encryption — Optional SQLCipher AES-256 database encryption at rest
- Access control — No API endpoint accessible without session auth (except
/api/v1/auth/login) - No public registration — Only admins can create user accounts
Development
Local Setup
npm install
cp .env.example .env
# Set SESSION_SECRET — skip DB_ENCRYPTION_KEY (no SQLCipher needed locally)
npm run dev # Starts server with --watch (auto-reload)
Tests
npm test # 146 tests across 7 suites
Tests use Node.js built-in test runner with --experimental-sqlite for in-memory SQLite. No running server required.
Architecture
server/
index.js # Express entry, middleware, static serving
db.js # SQLite connection, migration runner
auth.js # Session auth + user management routes
routes/ # One file per module
services/ # Calendar sync, recurrence engine
public/
index.html # SPA shell
router.js # History API router (~50 lines)
api.js # Fetch wrapper with auth + CSRF
styles/ # Design tokens, reset, layout, per-module CSS
components/ # Web Components (oikos-* prefix)
pages/ # Page modules with render() export
sw.js # Service worker
Request flow: Client → Express static or /api/v1/* → session auth middleware → route handler → better-sqlite3 (sync) → JSON response.
Database migrations run automatically on startup. Each migration is an idempotent SQL block in server/db.js. Append new migrations — never modify existing ones.
Backup & Restore
Backup
docker run --rm \
-v oikos_oikos_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/oikos-backup-$(date +%Y%m%d).tar.gz /data
Restore
docker compose down
docker run --rm \
-v oikos_oikos_data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/oikos-backup-YYYYMMDD.tar.gz -C /
docker compose up -d
Database migrations run automatically on startup. Data in the oikos_data volume is preserved across container rebuilds.
Updates & Family Members
Updating Oikos
git pull
docker compose up -d --build
Migrations run automatically. Your data volume stays intact.
Adding Family Members
Only admins can create new accounts — there is no public registration endpoint.
In the browser: Settings → Family Members → Add Member
Via CLI: docker compose exec oikos node setup.js
Contributing
Contributions are welcome. If you find a bug or have a feature idea, open an issue. Pull requests are appreciated — please keep the vanilla JS constraint in mind (no frameworks, no build tools).
See CONTRIBUTING.md for setup instructions, code conventions, commit format, and workflow.
License
MIT © 2025 ulsklyc
Made with ☕ by ulsklyc






