docs: overhaul README for professional project presentation

Restructure README with compelling hero section, "Why Oikos?" philosophy
section, two-column feature grid, tablet screenshot gallery, and streamlined
quick start. Add GitHub Private Vulnerability Reporting link to SECURITY.md.
Include social preview HTML template for GitHub social card generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-03-31 17:34:23 +02:00
parent 9e8338c9b9
commit be8af0f154
3 changed files with 379 additions and 124 deletions
+185 -123
View File
@@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<!-- Replace with your logo: recommended 120160px, SVG or PNG, transparent background --> <!-- Replace with your logo when ready: recommended 120160px, SVG or PNG, transparent background -->
<!-- <img src="docs/logo.svg" alt="Oikos" width="140"> --> <!-- <img src="docs/logo.svg" alt="Oikos" width="140"> -->
<img src="https://img.shields.io/badge/%F0%9F%8F%A0-Oikos-007AFF?style=for-the-badge&labelColor=F5F5F7" alt="Oikos" height="48"> <img src="https://img.shields.io/badge/%F0%9F%8F%A0-Oikos-007AFF?style=for-the-badge&labelColor=F5F5F7" alt="Oikos" height="48">
</p> </p>
@@ -7,44 +7,57 @@
<h1 align="center">Oikos</h1> <h1 align="center">Oikos</h1>
<p align="center"> <p align="center">
<strong>Self-hosted family planner — tasks, calendars, shopping, meals, budget.</strong><br> <strong>The self-hosted family planner that respects your privacy.</strong><br>
Your data stays on your server. No subscriptions. No tracking. No cloud lock-in. Tasks, calendars, shopping, meals, budget, notes, contacts — <br>
all in one place, on your own server.
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/ulsklyc/oikos/releases"><img src="https://img.shields.io/github/v/release/ulsklyc/oikos?style=flat-square&color=007AFF&label=release" alt="Latest Release"></a>
<a href="https://github.com/ulsklyc/oikos/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT License"></a> <a href="https://github.com/ulsklyc/oikos/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT License"></a>
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D22-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node.js ≥22"></a> <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%E2%89%A522-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node.js ≥22"></a>
<a href="https://www.docker.com"><img src="https://img.shields.io/badge/docker-ready-2496ED?style=flat-square&logo=docker&logoColor=white" alt="Docker Ready"></a> <a href="https://www.docker.com"><img src="https://img.shields.io/badge/docker-ready-2496ED?style=flat-square&logo=docker&logoColor=white" alt="Docker Ready"></a>
<a href="https://www.zetetic.net/sqlcipher/"><img src="https://img.shields.io/badge/SQLCipher-AES--256-003B57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLCipher Encrypted"></a> <a href="https://www.zetetic.net/sqlcipher/"><img src="https://img.shields.io/badge/SQLCipher-AES--256-003B57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLCipher Encrypted"></a>
<a href="https://web.dev/progressive-web-apps/"><img src="https://img.shields.io/badge/PWA-offline--capable-5A0FC8?style=flat-square&logo=pwa&logoColor=white" alt="PWA Offline"></a> <a href="https://web.dev/progressive-web-apps/"><img src="https://img.shields.io/badge/PWA-offline--capable-5A0FC8?style=flat-square&logo=pwa&logoColor=white" alt="PWA Offline"></a>
<a href="https://github.com/ulsklyc/oikos/stargazers"><img src="https://img.shields.io/github/stars/ulsklyc/oikos?style=flat-square&color=f5c542" alt="GitHub Stars"></a>
<a href="https://github.com/ulsklyc/oikos/commits/main"><img src="https://img.shields.io/github/last-commit/ulsklyc/oikos?style=flat-square" alt="Last Commit"></a>
</p> </p>
<p align="center"> <p align="center">
<a href="#features">Features</a> · <a href="#quick-start">Quick Start</a> · <a href="#configuration">Configuration</a> · <a href="#calendar-sync">Calendar Sync</a> · <a href="#security">Security</a> <a href="#-screenshots">Screenshots</a>&ensp;·&ensp;
<a href="#-features">Features</a>&ensp;·&ensp;
<a href="#-quick-start">Quick Start</a>&ensp;·&ensp;
<a href="#-security">Security</a>&ensp;·&ensp;
<a href="#-contributing">Contributing</a>
</p> </p>
--- <br>
Oikos is a self-hosted family organizer for 26 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
<p align="center"> <p align="center">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-dashboard.png"> <source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-dashboard.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-dashboard.png"> <source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-dashboard.png">
<img src="docs/screenshots/tablet-light/tablet-light-dashboard.png" alt="Dashboard" width="720"> <img src="docs/screenshots/tablet-light/tablet-light-dashboard.png" alt="Oikos Dashboard" width="720">
</picture> </picture>
</p> </p>
---
## Why Oikos?
Most family organizers are cloud apps with monthly subscriptions, data mining, and vendor lock-in. Oikos takes a different approach:
- **Your server, your data** — runs in a single Docker container on your own hardware. Nothing leaves your network.
- **No subscriptions** — free and open source, forever. MIT licensed.
- **No tracking** — zero telemetry, zero analytics, zero third-party scripts.
- **Offline-first** — works as a PWA on phones and tablets, even without connectivity.
- **Encrypted at rest** — optional AES-256 database encryption via SQLCipher.
- **Lightweight** — vanilla JavaScript frontend with no framework and no build step. Express + SQLite backend. Minimal resource footprint.
Oikos is designed for **one family on one server** — not a SaaS product, not a team tool, not multi-tenant. It's a private, self-contained household organizer for 26 people.
---
## 📸 Screenshots
<table> <table>
<tr> <tr>
<td align="center" width="33%"> <td align="center" width="33%">
@@ -95,34 +108,113 @@ Oikos is **not** a SaaS product, not a team collaboration tool, and not designed
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/mobile-light/mobile-light-dashboard.png"> <source media="(prefers-color-scheme: light)" srcset="docs/screenshots/mobile-light/mobile-light-dashboard.png">
<img src="docs/screenshots/mobile-light/mobile-light-dashboard.png" alt="Dashboard Mobile" width="240"> <img src="docs/screenshots/mobile-light/mobile-light-dashboard.png" alt="Dashboard Mobile" width="240">
</picture> </picture>
<br><strong>Dashboard — Mobile</strong> <br><strong>Dashboard</strong>
</td> </td>
</tr> </tr>
</table> </table>
<details>
<summary>Tablet views</summary>
<br>
<table>
<tr>
<td align="center" width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-tasks.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-tasks.png">
<img src="docs/screenshots/tablet-light/tablet-light-tasks.png" alt="Tasks — Tablet" width="480">
</picture>
<br><strong>Tasks</strong>
</td>
<td align="center" width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-household.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-household.png">
<img src="docs/screenshots/tablet-light/tablet-light-household.png" alt="Shopping — Tablet" width="480">
</picture>
<br><strong>Shopping</strong>
</td>
</tr>
<tr>
<td align="center" width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-notes.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-notes.png">
<img src="docs/screenshots/tablet-light/tablet-light-notes.png" alt="Notes — Tablet" width="480">
</picture>
<br><strong>Notes</strong>
</td>
<td align="center" width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-budget.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-budget.png">
<img src="docs/screenshots/tablet-light/tablet-light-budget.png" alt="Budget — Tablet" width="480">
</picture>
<br><strong>Budget</strong>
</td>
</tr>
<tr>
<td align="center" width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/tablet-dark/tablet-dark-contacts.png">
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/tablet-light/tablet-light-contacts.png">
<img src="docs/screenshots/tablet-light/tablet-light-contacts.png" alt="Contacts — Tablet" width="480">
</picture>
<br><strong>Contacts</strong>
</td>
<td></td>
</tr>
</table>
</details>
<p align="center"> <p align="center">
<sub>Screenshots adapt to your GitHub theme — switch between light and dark mode to see both variants.</sub> <sub>Screenshots adapt to your GitHub theme — toggle light/dark mode to see both variants.</sub>
</p> </p>
--- ---
## Features ## Features
| | Module | What it does | Highlights | <table>
|---|---|---|---| <tr>
| 📋 | **Dashboard** | At-a-glance overview of your family's day | Weather widget · upcoming events · urgent tasks · today's meals · pinned notes | <td width="50%">
| ✅ | **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 | **📋 Dashboard**
| 🍽️ | **Meals** | Weekly meal planning with ingredients | Week view (MonSun) · ingredient management · one-click export to shopping list | At-a-glance family overview — weather, upcoming events, urgent tasks, today's meals, pinned notes.
| 📅 | **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) | **✅ Tasks**
| 👥 | **Contacts** | Important family contacts | Category filters · tap-to-call · tap-to-email · map links · vCard import/export | List + Kanban views, subtasks, recurring tasks (RRULE), swipe gestures, priority levels.
| 💰 | **Budget** | Income & expense tracking | Category breakdown · month-over-month comparison · CSV export |
| ⚙️ | **Settings** | User & sync management | Password changes · calendar sync config · family member admin | **🛒 Shopping**
Multiple lists, aisle-grouped categories, auto-import from meal plan, swipe to check off.
**🍽️ Meals**
Weekly planner (MonSun), drag & drop between slots, ingredients, one-click shopping list export.
</td>
<td width="50%">
**📅 Calendar**
Month / week / day / agenda views. Two-way sync with Google Calendar and Apple iCloud.
**📌 Notes**
Colored sticky notes, pinning, full-text search, Markdown formatting toolbar.
**👥 Contacts**
Category filters, tap-to-call/email, map links, vCard import & export.
**💰 Budget**
Income & expense tracking, recurring entries, month-over-month trends, CSV export.
</td>
</tr>
</table>
**And also:** dark mode with system detection · responsive design (mobile / tablet / desktop) · offline PWA with install prompt · per-module accent colors · accessibility (skip links, ARIA, reduced motion) · staggered animations · bottom sheet modals on mobile.
--- ---
## Tech Stack ## 🛠 Tech Stack
<p> <p>
<img src="https://img.shields.io/badge/Express-000000?style=flat-square&logo=express&logoColor=white" alt="Express"> <img src="https://img.shields.io/badge/Express-000000?style=flat-square&logo=express&logoColor=white" alt="Express">
@@ -133,78 +225,75 @@ Oikos is **not** a SaaS product, not a team collaboration tool, and not designed
</p> </p>
| Layer | Technology | | Layer | Technology |
|---|---| |:---|:---|
| **Server** | Node.js ≥ 22 · Express · better-sqlite3 · bcrypt · Helmet | | **Server** | Node.js ≥ 22 · Express · better-sqlite3 · bcrypt · Helmet |
| **Database** | SQLite with optional SQLCipher encryption (AES-256) | | **Database** | SQLite with optional [SQLCipher](https://www.zetetic.net/sqlcipher/) encryption (AES-256) |
| **Frontend** | Vanilla JavaScript ES modules — no framework, no build step. Web Components (`oikos-*`). Lucide Icons (self-hosted SVG sprite) | | **Frontend** | Vanilla JavaScript ES modules — no framework, no build step. [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) (`oikos-*`). [Lucide Icons](https://lucide.dev) (self-hosted) |
| **Auth** | Session-based · httpOnly cookies · CSRF double-submit · express-session | | **Auth** | Session-based · httpOnly cookies · CSRF double-submit · [express-session](https://github.com/expressjs/session) |
| **Deployment** | Docker + Docker Compose · Nginx reverse proxy · Let's Encrypt SSL | | **Deployment** | Docker + Docker Compose · Nginx reverse proxy · Let's Encrypt SSL |
| **Integrations** | Google Calendar API v3 (OAuth 2.0) · Apple iCloud CalDAV (tsdav) · OpenWeatherMap | | **Integrations** | [Google Calendar API v3](https://developers.google.com/calendar) (OAuth 2.0) · [Apple iCloud CalDAV](https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/) (tsdav) · [OpenWeatherMap](https://openweathermap.org/api) |
--- ---
## Quick Start ## 🚀 Quick Start
**Prerequisites:** Docker + Docker Compose on a Linux server. > **Prerequisites:** Docker and Docker Compose installed on a Linux server.
### 1. Clone
```bash ```bash
git clone https://github.com/ulsklyc/oikos.git # 1. Clone the repository
cd oikos git clone https://github.com/ulsklyc/oikos.git && cd oikos
```
### 2. Configure # 2. Configure environment
```bash
cp .env.example .env cp .env.example .env
``` # Edit .env — set SESSION_SECRET (≥32 chars) and optionally DB_ENCRYPTION_KEY
Edit `.env` and set the two required variables: # 3. Start the container
```env
SESSION_SECRET=your-random-string-at-least-32-chars
DB_ENCRYPTION_KEY=your-sqlcipher-aes256-key
```
### 3. Start
```bash
docker compose up -d docker compose up -d
``` # First build takes 23 minutes (compiles SQLCipher)
First build takes 23 minutes (compiles SQLCipher against better-sqlite3). # 4. Create your admin account
### 4. Create admin account
```bash
docker compose exec oikos node setup.js docker compose exec oikos node setup.js
# 5. Open http://localhost:3000
``` ```
Interactive script — sets up username, display name, and password. This admin can create additional family members from the settings page. The admin can create additional family members from **Settings → Family Members**.
### 5. Open > **Production:** See [`nginx.conf.example`](nginx.conf.example) for a reverse proxy config with SSL. If you use [Nginx Proxy Manager](https://nginxproxymanager.com), paste the contents into the Advanced tab. Ensure `X-Forwarded-Proto` is set for session cookies to work correctly.
Navigate to `http://localhost:3000` — or your configured domain after Nginx setup. ---
> **Nginx:** See [`nginx.conf.example`](nginx.conf.example) for a production-ready config. 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. ## 🔒 Security
Oikos is designed to run on a private server behind SSL. No public endpoints exist except the login page.
| Layer | Implementation |
|:---|:---|
| **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, 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 login) |
| **Registration** | Disabled — only admins can create user accounts |
--- ---
<details> <details>
<summary><h2>Configuration</h2></summary> <summary><strong>Configuration</strong></summary>
### Required ### Required
| Variable | Description | | Variable | Description |
|---|---| |:---|:---|
| `SESSION_SECRET` | Random string ≥ 32 characters for session signing | | `SESSION_SECRET` | Random string ≥ 32 characters for session signing |
| `DB_ENCRYPTION_KEY` | SQLCipher AES-256 key. Leave empty to disable encryption | | `DB_ENCRYPTION_KEY` | SQLCipher AES-256 key — leave empty to disable encryption |
### Optional ### Optional
| Variable | Default | Description | | Variable | Default | Description |
|---|---|---| |:---|:---|:---|
| `PORT` | `3000` | Server port | | `PORT` | `3000` | Server port |
| `NODE_ENV` | `development` | Set to `production` for deployment | | `NODE_ENV` | `development` | Set to `production` for deployment |
| `DB_PATH` | `./oikos.db` | Path to SQLite database file | | `DB_PATH` | `./oikos.db` | Path to SQLite database file |
@@ -216,7 +305,7 @@ Navigate to `http://localhost:3000` — or your configured domain after Nginx se
Register a free API key at [openweathermap.org](https://openweathermap.org/api): Register a free API key at [openweathermap.org](https://openweathermap.org/api):
| Variable | Default | Description | | Variable | Default | Description |
|---|---|---| |:---|:---|:---|
| `OPENWEATHER_API_KEY` | — | Your API key | | `OPENWEATHER_API_KEY` | — | Your API key |
| `OPENWEATHER_CITY` | `Berlin` | City name | | `OPENWEATHER_CITY` | `Berlin` | City name |
| `OPENWEATHER_UNITS` | `metric` | `metric` (°C) or `imperial` (°F) | | `OPENWEATHER_UNITS` | `metric` | `metric` (°C) or `imperial` (°F) |
@@ -225,7 +314,7 @@ Register a free API key at [openweathermap.org](https://openweathermap.org/api):
### Integrations ### Integrations
| Variable | Description | | Variable | Description |
|---|---| |:---|:---|
| `GOOGLE_CLIENT_ID` | Google OAuth 2.0 Client ID | | `GOOGLE_CLIENT_ID` | Google OAuth 2.0 Client ID |
| `GOOGLE_CLIENT_SECRET` | Google OAuth 2.0 Client Secret | | `GOOGLE_CLIENT_SECRET` | Google OAuth 2.0 Client Secret |
| `GOOGLE_REDIRECT_URI` | `https://your-domain/api/v1/calendar/google/callback` | | `GOOGLE_REDIRECT_URI` | `https://your-domain/api/v1/calendar/google/callback` |
@@ -237,10 +326,8 @@ Full template: [`.env.example`](.env.example)
</details> </details>
---
<details> <details>
<summary><h2>Calendar Sync</h2></summary> <summary><strong>Calendar Sync</strong></summary>
Oikos syncs bidirectionally with Google Calendar and Apple iCloud. External events are visually distinguished in the UI. On conflict, the external source wins. Oikos syncs bidirectionally with Google Calendar and Apple iCloud. External events are visually distinguished in the UI. On conflict, the external source wins.
@@ -266,13 +353,13 @@ Oikos syncs bidirectionally with Google Calendar and Apple iCloud. External even
### Apple Calendar (iCloud CalDAV) ### Apple Calendar (iCloud CalDAV)
**Option A — via Settings UI (recommended, no restart required):** **Option A — via Settings UI** (recommended, no restart required):
1. Go to [appleid.apple.com](https://appleid.apple.com) → Sign-In and Security → App-Specific Passwords 1. Go to [appleid.apple.com](https://appleid.apple.com) → Sign-In and Security → App-Specific Passwords
2. Generate a new password for "Oikos" 2. Generate a new password for "Oikos"
3. In Oikos: **Settings → Calendar Sync → Apple Calendar** → enter the CalDAV URL, Apple ID email and the app-specific password → click **Verbinden & testen** 3. In Oikos: **Settings → Calendar Sync → Apple Calendar** → enter CalDAV URL, Apple ID, and app-specific password → click **Verbinden & testen**
Credentials are stored in the database. No server restart required. Credentials are stored in the database. No server restart needed.
**Option B — via `.env`:** **Option B — via `.env`:**
@@ -282,29 +369,12 @@ APPLE_USERNAME=your@apple-id.com
APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx 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. Restart: `docker compose up -d`. The sync button appears automatically in Settings. `.env` credentials are used as fallback when no UI credentials are saved.
</details> </details>
---
## Security
Oikos is designed to run on a private server behind SSL. No public endpoints exist except the login page.
- **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, 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
---
<details> <details>
<summary><h2>Development</h2></summary> <summary><strong>Development</strong></summary>
### Local Setup ### Local Setup
@@ -318,7 +388,7 @@ npm run dev # Starts server with --watch (auto-reload)
### Tests ### Tests
```bash ```bash
npm test # 146 tests across 7 suites npm test # 146+ tests across 9 suites
``` ```
Tests use Node.js built-in test runner with `--experimental-sqlite` for in-memory SQLite. No running server required. Tests use Node.js built-in test runner with `--experimental-sqlite` for in-memory SQLite. No running server required.
@@ -330,6 +400,7 @@ server/
index.js # Express entry, middleware, static serving index.js # Express entry, middleware, static serving
db.js # SQLite connection, migration runner db.js # SQLite connection, migration runner
auth.js # Session auth + user management routes auth.js # Session auth + user management routes
middleware/ # CSRF, input validation
routes/ # One file per module routes/ # One file per module
services/ # Calendar sync, recurrence engine services/ # Calendar sync, recurrence engine
public/ public/
@@ -339,19 +410,17 @@ public/
styles/ # Design tokens, reset, layout, per-module CSS styles/ # Design tokens, reset, layout, per-module CSS
components/ # Web Components (oikos-* prefix) components/ # Web Components (oikos-* prefix)
pages/ # Page modules with render() export pages/ # Page modules with render() export
sw.js # Service worker sw.js # Service worker (app-shell caching)
``` ```
**Request flow:** Client → Express static or `/api/v1/*` → session auth middleware → route handler → better-sqlite3 (sync) → JSON response. **Request flow:** Browser → Express static (`public/`) 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. **Database migrations** run automatically on startup. Each migration is an idempotent SQL block in `server/db.js`. Append new migrations — never modify existing ones.
</details> </details>
---
<details> <details>
<summary><h2>Backup & Restore</h2></summary> <summary><strong>Backup & Restore</strong></summary>
### Backup ### Backup
@@ -373,16 +442,12 @@ docker run --rm \
docker compose up -d docker compose up -d
``` ```
Database migrations run automatically on startup. Data in the `oikos_data` volume is preserved across container rebuilds. Migrations run automatically on startup. Data in the `oikos_data` volume is preserved across container rebuilds.
</details> </details>
---
<details> <details>
<summary><h2>Updates & Family Members</h2></summary> <summary><strong>Updates</strong></summary>
### Updating Oikos
```bash ```bash
git pull git pull
@@ -393,19 +458,18 @@ Migrations run automatically. Your data volume stays intact.
### Adding Family Members ### Adding Family Members
Only admins can create new accounts — there is no public registration endpoint. Only admins can create accounts — there is no public registration.
**In the browser:** Settings → Family Members → Add Member - **In the browser:** Settings → Family Members → Add Member
- **Via CLI:** `docker compose exec oikos node setup.js`
**Via CLI:** `docker compose exec oikos node setup.js`
</details> </details>
--- ---
## Contributing ## 🤝 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). 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).
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup instructions, code conventions, commit format, and workflow. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup instructions, code conventions, commit format, and workflow.
@@ -413,10 +477,8 @@ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup instructions, code convention
## License ## License
[MIT](LICENSE) © 2025 ulsklyc [MIT](LICENSE) © 2026 ulsklyc
---
<p align="center"> <p align="center">
Made with ☕ by <a href="https://github.com/ulsklyc">ulsklyc</a> <sub>Made with care for families who value their privacy.</sub>
</p> </p>
+1 -1
View File
@@ -4,7 +4,7 @@
If you discover a security vulnerability in Oikos, please report it responsibly. **Do not open a public issue.** If you discover a security vulnerability in Oikos, please report it responsibly. **Do not open a public issue.**
Instead, use [GitHub Private Vulnerability Reporting](https://github.com/ulsklyc/oikos/security/advisories/new) to submit your report. This creates a private advisory visible only to you and the maintainers.
Include: Include:
+193
View File
@@ -0,0 +1,193 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Oikos — Social Preview</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 1280px;
height: 640px;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background: linear-gradient(135deg, #0a0f1a 0%, #141c2e 50%, #1a2540 100%);
display: flex;
color: #fff;
}
.left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: 64px 56px;
z-index: 2;
}
.badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: rgba(0, 122, 255, 0.15);
border: 1px solid rgba(0, 122, 255, 0.3);
border-radius: 20px;
padding: 6px 16px;
font-size: 13px;
font-weight: 600;
color: #5ac8fa;
letter-spacing: 0.5px;
text-transform: uppercase;
margin-bottom: 24px;
width: fit-content;
}
.title {
font-size: 72px;
font-weight: 800;
letter-spacing: -2px;
line-height: 1;
margin-bottom: 16px;
background: linear-gradient(135deg, #ffffff 0%, #c8d6e5 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.tagline {
font-size: 22px;
font-weight: 400;
color: #8899b0;
line-height: 1.5;
max-width: 440px;
margin-bottom: 32px;
}
.features {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.feature {
display: inline-flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
color: #a0b0c4;
}
.feature-icon {
font-size: 15px;
}
.right {
width: 560px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.screenshot-wrapper {
position: relative;
transform: perspective(1200px) rotateY(-8deg) rotateX(2deg);
border-radius: 12px;
overflow: hidden;
box-shadow:
0 24px 80px rgba(0, 0, 0, 0.5),
0 0 0 1px rgba(255, 255, 255, 0.08);
}
.screenshot-wrapper img {
display: block;
width: 500px;
height: auto;
border-radius: 12px;
}
/* Gradient overlay on the screenshot edges */
.screenshot-wrapper::after {
content: '';
position: absolute;
inset: 0;
border-radius: 12px;
background: linear-gradient(90deg, rgba(10, 15, 26, 0.4) 0%, transparent 30%, transparent 100%);
pointer-events: none;
}
/* Subtle glow behind screenshot */
.right::before {
content: '';
position: absolute;
width: 400px;
height: 400px;
background: radial-gradient(circle, rgba(0, 122, 255, 0.12) 0%, transparent 70%);
border-radius: 50%;
z-index: -1;
}
.footer {
position: absolute;
bottom: 28px;
left: 56px;
display: flex;
align-items: center;
gap: 16px;
font-size: 13px;
color: #556680;
}
.footer span {
display: inline-flex;
align-items: center;
gap: 5px;
}
.dot {
width: 3px;
height: 3px;
border-radius: 50%;
background: #556680;
}
</style>
</head>
<body>
<div class="left">
<div class="badge">Self-Hosted &middot; Open Source</div>
<div class="title">Oikos</div>
<p class="tagline">The family planner that respects your privacy. Tasks, calendars, shopping, meals, budget &mdash; on your own server.</p>
<div class="features">
<span class="feature"><span class="feature-icon">&#x2705;</span> Tasks</span>
<span class="feature"><span class="feature-icon">&#x1F4C5;</span> Calendar</span>
<span class="feature"><span class="feature-icon">&#x1F6D2;</span> Shopping</span>
<span class="feature"><span class="feature-icon">&#x1F35D;</span> Meals</span>
<span class="feature"><span class="feature-icon">&#x1F4B0;</span> Budget</span>
<span class="feature"><span class="feature-icon">&#x1F4CC;</span> Notes</span>
<span class="feature"><span class="feature-icon">&#x1F465;</span> Contacts</span>
<span class="feature"><span class="feature-icon">&#x1F512;</span> Encrypted</span>
</div>
</div>
<div class="right">
<!-- Replace with your actual tablet screenshot path -->
<div class="screenshot-wrapper">
<img src="../docs/screenshots/tablet-light/tablet-light-dashboard.png" alt="Dashboard">
</div>
</div>
<div class="footer">
<span>MIT License</span>
<div class="dot"></div>
<span>Docker &middot; Express &middot; SQLite &middot; Vanilla JS</span>
<div class="dot"></div>
<span>github.com/ulsklyc/oikos</span>
</div>
</body>
</html>