π Oikos
Self-hosted family planner β private, open, no subscription.
Oikos is a self-hosted family organizer for 2β6 people. Tasks, calendars, shopping lists, meal plans, budget tracking, and more β all running on your own server. No cloud dependency. No data leaves your network. No tracking.
Features Β· Quick Start Β· Configuration Β· Calendar Sync Β· Security
---
## 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 | 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 sync (two-way) |
| π | **Notes** | Shared family pinboard | Colored sticky notes, pinning, lightweight Markdown (bold, italic, lists) |
| π₯ | **Contacts** | Important family contacts | Category filters, tap-to-call, tap-to-email, map links for addresses |
| π° | **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
**Backend:** Node.js, Express, SQLite via better-sqlite3, optional SQLCipher encryption (AES-256), bcrypt, express-session, Helmet
**Frontend:** Vanilla JavaScript ES modules β no framework, no build step, no bundler. Web Components for reusable UI. Lucide Icons (self-hosted SVG sprite).
**Deployment:** Docker + Docker Compose, Nginx reverse proxy with SSL, PWA with service worker for offline support
**Integrations:** Google Calendar API v3 (OAuth 2.0), Apple iCloud CalDAV via tsdav, OpenWeatherMap
## Quick Start
**Prerequisites:** Docker + Docker Compose on a Linux server.
**1. Clone**
```bash
git clone https://github.com/ulsklyc/oikos.git
cd oikos
```
**2. Configure**
```bash
cp .env.example .env
```
Edit `.env` and set the two required variables:
```env
SESSION_SECRET=your-random-string-at-least-32-chars
DB_ENCRYPTION_KEY=your-sqlcipher-aes256-key
```
**3. Start**
```bash
docker compose up -d
```
First build takes 2β3 minutes (compiles SQLCipher against better-sqlite3).
**4. Create admin account**
```bash
docker compose exec oikos node setup.js
```
Interactive script β sets up username, display name, and password. This admin can create additional family members.
**5. Open**
Navigate to `http://localhost:3000` β or your configured domain after Nginx setup.
> See [`nginx.conf.example`](nginx.conf.example) for a ready-to-use reverse proxy configuration. If you use [Nginx Proxy Manager](https://nginxproxymanager.com), paste the contents into the "Advanced" tab. Make sure `X-Forwarded-Proto` is set so session cookies work correctly in production.
π Configuration Reference
### 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](https://openweathermap.org/api):
| 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`](.env.example)
π
Calendar Sync
### Google Calendar
1. Create a project at [console.cloud.google.com](https://console.cloud.google.com)
2. Enable the **Google Calendar API**
3. Create an **OAuth 2.0 Client ID** (type: Web application)
4. Add your redirect URI:
```
https://your-domain.com/api/v1/calendar/google/callback
```
5. Add credentials to `.env`:
```env
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_REDIRECT_URI=https://your-domain.com/api/v1/calendar/google/callback
```
6. Restart: `docker compose up -d`
7. 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)
1. Go to [appleid.apple.com](https://appleid.apple.com) β Sign-In and Security β App-Specific Passwords
2. Generate a new password for "Oikos"
3. Add to `.env`:
```env
APPLE_CALDAV_URL=https://caldav.icloud.com
APPLE_USERNAME=your@apple-id.com
APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx
```
4. Restart: `docker compose up -d`
The sync button appears automatically in Settings.
## Security
- **Sessions:** `httpOnly`, `SameSite=Strict`, `Secure` in 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 per IP, 300 API requests/min per IP
- **Headers:** Content Security Policy via Helmet (`self`-only)
- **Encryption:** Optional SQLCipher AES-256 database encryption
- **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
```bash
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
```bash
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
```bash
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
```bash
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
```
## Updates
```bash
git pull
docker compose up -d --build
```
Database migrations run automatically on startup. Data in the `oikos_data` volume is preserved.
## 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](https://github.com/ulsklyc/oikos/issues). Pull requests are appreciated β please keep the vanilla JS constraint in mind (no frameworks, no build tools).
A `CONTRIBUTING.md` with detailed guidelines is coming soon.
## License
[MIT](LICENSE) Β© 2025 ulsklyc
---
Made with β by ulsklyc