Skip to main content
Glama
Furqankhanzada

Hamara Hisaab

README.md
# Hamara Hisaab — ghar ka sara hisaab, ek jagah

Self-hosted household finance app: shared expense/income ledger, budgets, an investment
portfolio (with automatic PSX + Pakistani mutual fund prices), loans (qarz), recurring
bills and a zakat helper.
**API-first**: everything the web app can do, any AI agent can do too — via MCP or plain REST.

## Stack

One Node process (Hono) serving the REST API, an MCP endpoint and a React PWA, plus Postgres.
Two containers, one `docker compose up`.

## Quick start

```bash
cp .env.example .env      # set DB_PASSWORD, BETTER_AUTH_SECRET (openssl rand -base64 32), APP_URL
docker compose up -d --build
```

Open the app (default `http://localhost:3000`, change host port with `APP_PORT` in `.env`):

1. **Register** — creates your account.
2. **Create your household** — you get an invite code.
3. Your spouse registers and **joins with the invite code** → shared ledger, entries attributed per person.
4. **More → Agents & API keys** — create one API key per agent (one for you, one for your spouse's agent, etc.).

## Connecting agents

Every operation is exposed two ways; both authenticate with a per-user API key.
The key's owner is recorded as the payer on entries the agent makes.

### MCP (Claude, ChatGPT, and any MCP-capable agent)

Streamable-HTTP endpoint at `https://your-host/mcp` — 26 tools
(`add_transaction`, `get_budget_status`, `get_portfolio`, `get_monthly_report`, `add_loan`,
`get_zakat_summary`, …).

```bash
# Claude Code
claude mcp add finance --transport http https://your-host/mcp --header "x-api-key: YOUR_KEY"
```

`Authorization: Bearer YOUR_KEY` also works for clients that only support bearer auth.

**Hermes**: since Hermes' `mcp.json` only takes `command`/`args` (no HTTP transport, no headers), point it
at `mcp-remote` to bridge to the `/mcp` endpoint:

```json
{
  "mcpServers": {
    "finance": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://your-host/mcp",
        "--header",
        "x-api-key: YOUR_KEY"
      ]
    }
  }
}
```

**Daily WhatsApp summary**: schedule your agent (Hermes cron, Claude scheduled task, n8n…) to call
`get_daily_brief` (or `GET /api/v1/reports/brief`) every morning — it returns yesterday's spending,
month-to-date, budget-pace warnings, bills due within 7 days, open qarz and a zakat reminder,
plus a ready-to-post `text` rendering for the family group.

### REST (Hermes, OpenClaw, scripts, anything)

Same operations at `/api/v1/*` with the `x-api-key` header — e.g. your WhatsApp Hermes agent logs an expense with:

```bash
curl -X POST https://your-host/api/v1/transactions \
  -H "x-api-key: YOUR_KEY" -H "content-type: application/json" \
  -d '{"type":"expense","amount":2500,"category":"Groceries","note":"Imtiaz weekly run"}'
```

Resources: `transactions`, `categories`, `budgets` (+ `/budgets/status`), `accounts`,
`portfolio`, `holdings`, `instruments`, `prices` (+ `/prices/refresh`), `loans` (+ `/:id/payments`),
`recurring`, `reports/monthly`, `reports/overview?period=week|month|quarter|year&offset=-N`
(or `?from=YYYY-MM-DD&to=YYYY-MM-DD` for a custom range — totals, previous-period comparison,
trend buckets, category/member breakdowns; also the `get_report` MCP tool), `zakat`, `household`.
Amounts are PKR; dates are `YYYY-MM-DD` (Asia/Karachi).

## Price data (automatic)

A daily job (18:30 & 22:00 PKT) fetches:

- **PSX stocks** — closing price from `dps.psx.com.pk` (unofficial endpoint; fails soft).
- **Mutual funds** — daily NAVs scraped from the [MUFAP NAV table](https://www.mufap.com.pk/Industry/IndustryStatDaily?tab=3).
  When adding a fund, use the **exact fund name** as it appears there (e.g. `Al Meezan Mutual Fund`, `Mahaana Islamic Cash Fund`).
- **Other assets** (gold, property…) — manual valuation via *Set price*; manual prices always win over fetched ones.

Recurring bills materialize into the ledger on their due day (00:15 PKT, with catch-up after downtime).

## Going public with Cloudflare Tunnel

To reach the app (and its `/mcp` endpoint) from anywhere — phones, WhatsApp agents — without opening ports:

1. Add your domain to Cloudflare (free plan is fine).
2. [Zero Trust dashboard](https://one.dash.cloudflare.com) → **Networks → Tunnels → Create a tunnel** → *Cloudflared* connector → name it (e.g. `hisaab`).
3. On the connector page pick **Docker**, copy the token from the shown command, and put it in `.env` as `TUNNEL_TOKEN=...`.
4. Add a **Public hostname**: `hisaab.yourdomain.com` → service **HTTP** → `app:3000`.
5. Update `.env`:
   ```
   APP_URL=https://hisaab.yourdomain.com
   TRUSTED_ORIGINS=http://localhost:9700     # keep local login working (match your APP_PORT)
   ```
6. `docker compose --profile public up -d --build`
7. Register your household members at the public URL, then set `DISABLE_SIGNUPS=true` in `.env` and restart — no strangers can create accounts on your instance.

Agents then connect to `https://hisaab.yourdomain.com/mcp` (header `x-api-key`) from anywhere.

## Development

```bash
docker compose up -d db          # Postgres on localhost:5433
npm install
npm run dev                      # API + SPA-serving on :3000 (PORT=3001 to change)
npm run dev:web                  # optional: Vite dev server with HMR, proxies /api → :3001
npm run db:generate              # regenerate SQL migration after editing src/db/schema.ts
npm run typecheck && npm run build
npm test                         # integration + unit tests (vitest, real Postgres, no network)
npm run test:e2e                 # Playwright browser tests against a fresh e2e database
```

Auth tables (`src/db/auth-schema.ts`) are generated:
`npx @better-auth/cli generate --config scripts/auth-config.ts --output src/db/auth-schema.ts`.

Layout: `src/services/` holds all business logic; `src/routes.ts` (REST) and `src/mcp.ts` (MCP tools)
are thin wrappers over the same services, so the two surfaces can't drift.

Maintenance

ActivitySlowing
ResponsivenessWithin a week