familyHub/readme.md
2026-04-03 20:43:00 -04:00

243 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 Googles 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 <MCP_API_TOKEN>`; 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 hubs **`…/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 Googles API. |
| **Monthly bill reminders** | `calendar_bill_days` | JSON array of `{ "dayOfMonth": 131, "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 credits 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 persons 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=<chore_id>&token=<chore_token>&person_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 URLs 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` 06 × 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)
```