# Family Hub A centralized family organization system with tabs for chores, groceries, meal planning, family currency, and settings. ## Features - Tabbed interface for chores, groceries, meals, combined Calendar (Family Hub agenda plus optional Google embed), currency, and family settings (profiles, stores, preferences) - Optional banking mode in the Currency tab: per-person checking, savings, and charity accounts with transfer rules and HoH controls - Banking safeguards and reporting: irreversible transfers into charity, immutable transaction reversals, and monthly statements (JSON/CSV) - JSON-based data storage under `data/` - Daily automated exports (`scripts/daily_export.php` and `export.php?type=…`) - Mobile-friendly interface ## Setup 1. Clone this repository to your web server. 2. **Configure the environment** - With a browser, open the app URL (for example `http://your-server/familyHub/`). If `.env` is missing, you are redirected to **`install.php`**, which builds `.env` from **`env.example`**. - Alternatively, copy `env.example` to `.env` and edit `.env` by hand. - See [Environment variables](#environment-variables) below for where to get each value. 3. Ensure proper permissions on the `data` directory (and that the web server can create or write JSON files there). 4. Set up the daily cron job for exports (see `scripts/daily_export.php`). It writes one JSON file per type into `exports/` (for example `chores`, `groceries`, `meals`, **`meal_plans`**, `expenses`, `stores`, `grocery_lists`, `grocery_catalog`, `bank_transactions`). From the command line, the script exits with an error if `.env` is missing; complete browser setup first or copy `env.example` manually. 5. Access the hub at `http://your-local-ip/familyHub/` (adjust path and host to match your deployment). ### Google Cloud prerequisites (OAuth + APIs) Most Google-related values come from **[Google Cloud Console](https://console.cloud.google.com/)**. 1. Create or select a **project**. 2. Open **APIs & Services → Library**, then enable any APIs you rely on (for example **Google Calendar API** and **Google Drive API** if you use those integrations). 3. Open **APIs & Services → OAuth consent screen** and complete it for your use case (Internal for Workspace-only, External for broader testing; test users may be required until published). 4. Open **APIs & Services → Credentials → Create credentials → OAuth client ID**. Choose **Web application** if your app uses a browser redirect. 5. Under **Authorized redirect URIs**, add the exact URL you will put in **`GOOGLE_REDIRECT_URI`** in `.env` (scheme, host, path, no trailing slash unless your app uses one). Use the official [Google Cloud OAuth documentation](https://developers.google.com/identity/protocols/oauth2) for details if anything in the console changes. ## Environment variables Values are defined in **`env.example`**. The install wizard reads that file only; use this section to learn **where to obtain** each value. ### Google API credentials | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`GOOGLE_CLIENT_ID`** | Public OAuth 2.0 client ID for your app | **Google Cloud Console → APIs & Services → Credentials**. Open your **OAuth 2.0 Client ID** (Web application). Copy **Client ID**. | | **`GOOGLE_CLIENT_SECRET`** | Secret for the same OAuth client | Same credential row. Copy **Client secret** (treat it like a password; never commit it). | | **`GOOGLE_REDIRECT_URI`** | URL Google redirects to after OAuth | Must match **exactly** one of **Authorized redirect URIs** on that OAuth client. Set it to your real callback URL (e.g. `https://yourdomain.com/family-hub/auth/google/callback`). Use the same scheme and path your server exposes. | ### Google Calendar The **Calendar** tab embeds your family calendar in the hub using these values (HTTPS Google Calendar URLs only; optional iframe `src` is extracted from pasted HTML). | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`GOOGLE_CALENDAR_ID`** | ID of the calendar to target (APIs, sharing, embeds) | **[Google Calendar](https://calendar.google.com/)** → hover the calendar in the sidebar → **⋮** → **Settings and sharing** → scroll to **Integrate calendar** → **Calendar ID** (often an email or `...@group.calendar.google.com`). If **`GOOGLE_CALENDAR_EMBED_CODE`** is unset, the tab builds a standard embed URL from this ID. | | **`GOOGLE_CALENDAR_EMBED_CODE`** | Embed URL or full iframe HTML from Google | Same **Settings and sharing** page → **Integrate calendar** → copy the **embed code** (iframe) or the `https://calendar.google.com/...` URL from the `src` attribute. Prefer this when you want Google’s chosen view options. Leave the placeholder `your_embed_code_here` unchanged only if you use **`GOOGLE_CALENDAR_ID`** instead. If a long iframe line misbehaves in `.env`, wrap the whole value in **double quotes**. | ### Google Drive | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`GOOGLE_DRIVE_FOLDER_ID`** | Folder used as export or attachment root | **[Google Drive](https://drive.google.com/)** → open the folder in the browser. The URL looks like `https://drive.google.com/drive/folders/THIS_PART_IS_THE_ID` — copy **`THIS_PART_IS_THE_ID`**. | ### Application settings | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`APP_ENV`** | Logical environment name | Your choice (e.g. `development`, `staging`, `production`). Used for app behavior toggles if the code checks it. | | **`APP_DEBUG`** | Verbose errors / debug mode | `true` or `false` (strings as in `env.example`). Only use **`true`** on trusted local or staging servers. | | **`APP_URL`** | Base URL of this app | Same origin users use in the browser (e.g. `https://example.com/family-hub`), no trailing path beyond the app root unless your deployment requires it. Should align with **`GOOGLE_REDIRECT_URI`** host/path where applicable. | ### Export settings | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`EXPORT_FREQUENCY`** | How often exports are expected to run | Your choice (e.g. `daily`). Match how you schedule **`scripts/daily_export.php`** in cron. | | **`EXPORT_RETENTION_DAYS`** | How long to keep export files (interpreted by app logic) | Integer number of days (e.g. `30`). | ### MCP / AI automation (optional) These enable **machine-to-machine** access to the same JSON APIs the browser uses. When `MCP_API_TOKEN` is non-empty, API requests may send `Authorization: Bearer `; the app then treats the request as the Head of household identified by `MCP_ACTOR_PERSON_ID` (PIN-verified semantics), without a browser session. | Variable | What it is | Where to find it | | -------- | ----------- | ---------------- | | **`MCP_API_TOKEN`** | Shared secret for automation (Cursor MCP, scripts, etc.) | Generate locally (treat like a password). Example: `openssl rand -hex 32`. Paste into `.env`. Never commit the real value. If left **empty**, Bearer impersonation is disabled and only normal browser sessions work. | | **`MCP_ACTOR_PERSON_ID`** | Which person the token impersonates | Open **`data/people.json`** (after setup) and copy the **`id`** field for your **Head of household** profile. Must match a person whose **`role`** is `head_of_household`. Wrong or non-head IDs are ignored and the token will not authenticate. | **Security:** The token grants the same power as a logged-in, HoH-verified session (create chores, manage people, grocery data, etc.). Use **HTTPS** in production. Rotate the token by changing `.env` and restarting anything that caches env. **Cursor MCP:** The MCP server is plain PHP (no Node): **`mcp/familyhub/server.php`**. Register it in Cursor **Settings → MCP** with the absolute path to PHP and the script, and set environment variables `FAMILYHUB_BASE_URL` (your hub’s **`…/api`** URL, e.g. `http://localhost/family-hub/api`) and `FAMILYHUB_MCP_TOKEN` (same value as **`MCP_API_TOKEN`**). Tools exposed: `familyhub_health`, `familyhub_get_snapshot`, `familyhub_calendar_events`, `familyhub_post_api` (allowlisted POST endpoints only). Example `mcp.json` server entry (adjust paths and env): ```json { "mcpServers": { "familyhub": { "command": "php", "args": ["/full/path/to/familyHub/mcp/familyhub/server.php"], "env": { "FAMILYHUB_BASE_URL": "http://localhost/family-hub/api", "FAMILYHUB_MCP_TOKEN": "paste-the-same-secret-as-MCP_API_TOKEN" } } } } ``` ## Calendar preferences (family settings) These are **not** in `.env`; they live in `data/family_settings.json` and are edited under **Family settings → Calendar and dates**. | Key / UI | Purpose | Notes | | -------- | -------- | ----- | | **Family timezone** | US IANA zone (e.g. `America/Denver`) | Dropdown of US zones only. Drives **today** and date ranges on the **Calendar** tab agenda. Options are the standard PHP US timezone identifiers (e.g. **America/***, **Pacific/Honolulu**). | | **Notify when two-way Google Calendar sync is available** (`calendar_two_way_google`) | Preference for a future OAuth feature | Default off. **Does not enable sync today.** The Calendar tab still uses only the Google **iframe embed** from `.env` (if set). The Family Hub agenda is built from chores, meals, groceries, expenses, bills, and birthdays in this app—not from Google’s API. | | **Monthly bill reminders** | `calendar_bill_days` | JSON array of `{ "dayOfMonth": 1–31, "title": "Rent" }`. Shown on the agenda on that calendar day each month. | **API:** `GET api/calendar_events.php?from=YYYY-MM-DD&to=YYYY-MM-DD` returns `{ success, events, rangeStart, rangeEnd, today }` for the signed-in profile (same session rules as other APIs). Omit `from`/`to` to use the default two-week window from **today** in the family timezone. ## Banking mode (optional) Enable this in **Family settings → Chore economy → Banking system**. - **Accounts per person** - `checking_balance`: primary spendable chore income balance. - `savings_balance`: earns the configured monthly rate, compounded daily via lazy apply when balances are read/written. - `charity_pending_balance`: funds committed to charity and not transferable back out. - `charity_donated_total`: historical amount already logged as donated by HoH. - **Income routing** - Approved chore payouts and HoH manual credits flow through checking. - Optional auto split sends a percent of each incoming credit to savings and charity. - Optional round-up rule moves each income credit’s fractional remainder to either savings or charity. - **Permissions** - Any active person can move their own funds between checking/savings/charity, but cannot transfer out of charity. - Verified HoH can add checking credits/debits, log charity outflows, and reverse transactions with immutable compensating entries. - **Donation goals** - Per-person monthly donation goals with progress based on current-month charity outflow totals. - **Statements** - `GET api/bank_statement.php?month=YYYY-MM&format=json|csv[&person_id=...]` - Non-HoH users can fetch only their own statement; verified HoH can request any person. ## NFC chore submission (HoH) NFC support uses per-chore tokens plus per-person submitter tokens so a scan can submit as a specific person and credit that person after HoH approval. ### Family settings for NFC In **Family settings → Chore economy**: - **NFC base URL override** (`nfc_base_url`): optional app base URL used when generating NFC links. Leave blank to auto-detect from the current request. - **NFC scan cooldown seconds** (`nfc_scan_cooldown_seconds`): optional soft replay dampener (0-600 seconds). - **Show NFC confirmation page after scan** (`nfc_show_confirmation`): when off, NFC scans redirect directly to chores. - **Emergency NFC actions**: - Disable all chore NFC links - Rotate all person NFC submitter tokens ### HoH setup checklist (per chore) 1. Open **Chores** as a verified Head of household. 2. For a chore, optionally enable **Allow anyone to complete this chore**. 3. Click **Generate NFC links**. 4. Copy each person-specific URL and write it to that person’s NFC card/tag (NDEF URI). 5. Label tags physically (name + chore). 6. Test scan once per person. ### NFC URL format Generated links use: `/api/chore_submit_nfc.php?id=&token=&person_token=` - `token` identifies the chore NFC token. - `person_token` identifies who is submitting (credit recipient on approval). ### Completion and credit rules - **Assignee-only chore** (`anyone_can_complete = false`): only assignees may submit, even with valid person tokens. - **Anyone-can-complete chore** (`anyone_can_complete = true`): any valid person token holder may submit. - Credit is always applied only on HoH approval (not at scan time), to the `submitted_by` person on the pending submission. ### Troubleshooting - **Invalid chore token**: regenerate chore NFC links and rewrite tags. - **Invalid submitter token**: rotate that person token and regenerate links. - **Already pending review**: wait for HoH approve/reject. - **Chore inactive**: reactivate/update chore before scanning again. - **Lost/shared card**: rotate token(s) or disable chore NFC immediately. ### Security notes - NFC links are bearer-style secrets. Anyone with a valid URL can submit as that URL’s person token. - Use HTTPS in production and rotate tokens on loss/suspected leakage. - Keep HoH approval enabled as the payout gate. ## Directory Structure ``` familyHub/ ├── api/ # JSON endpoints (chores, groceries, meals, people, settings, expenses, banking, calendar_events, NFC helpers, …) ├── assets/ # Static assets │ ├── css/ # CSS files │ │ └── style.css │ ├── js/ # JavaScript files │ │ └── main.js │ └── img/ # Images ├── config/ # Configuration files │ ├── config.php │ └── env.php # Loads .env ├── data/ # JSON data storage (not tracked in git) │ ├── chores.json # Chores, assignments, submissions, reviews │ ├── people.json # Profiles, roles, PIN hashes, currency balances │ ├── family_settings.json # Currency labels, US timezone, week start, calendar merge flag, bill reminders, etc. │ ├── expenses.json # Head-of-household expense log │ ├── bank_transactions.json # Banking ledger (credits, debits, transfers, outflows, reversals) │ ├── stores.json # Grocery store definitions │ ├── grocery_lists.json # Per-store shopping lines │ ├── grocery_catalog.json # Deduped catalog / recurring metadata │ ├── groceries.json # Legacy single-file groceries; migrated when detected │ ├── meals.json # Meal library (titles, tags, ingredients, shopping items) │ └── meal_plans.json # Active weekly plan: `weekStart` (Monday YYYY-MM-DD) and `slots` 0–6 × breakfast/lunch/dinner → meal id or empty ├── includes/ # PHP includes (db, persona, tab helpers, `api_bootstrap.php`, …) │ ├── header.php │ ├── footer.php │ ├── db.php # JSON file I/O (flock) │ └── utils.php # Shared helpers ├── exports/ # Export output (not tracked in git) ├── scripts/ # Cron / CLI helpers │ └── daily_export.php ├── mcp/familyhub/ # Optional MCP stdio server (PHP only): `server.php` ├── tabs/ # Tab views included by `index.php` │ ├── chores.php │ ├── groceries.php │ ├── meals.php │ ├── calendar.php │ ├── currency.php │ └── settings.php ├── env.example # Environment template (parsed by install.php) ├── install.php # Web wizard to create .env when missing ├── .gitignore ├── .cursor/ # Cursor editor configuration and rules ├── readme.md ├── index.php # Main entry point ├── AGENTS.md # Agent / contributor notes └── export.php # `?type=` JSON export (same types as daily script) ```