anti-gambling-trader-tw
Free open-source investment anti-fraud, trading statistics and automated trading program tools: default PaperBroker, provides 14 broker/exchange options; trade record analysis runs locally.
AI translation
A private household budget: track expenses, income and every transaction in one calm ledger.
Translation pending
FinanceTypeScript · PythonGitHub @samjmarshallOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Personal finance tracking and management
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗This project is for tracking and managing personal/household finances. Allowing you to see where you are spending, what you have overspent on, identifying where you can save/reduce expenses on the "should haves" and "nice to haves".
Stretch goal for this project:
Validated in src/env.ts. Pull them locally with vercel env pull; set them per-environment in the Vercel project.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | always | Pooled Postgres connection. |
DATABASE_URL_UNPOOLED | optional | Direct connection for drizzle-kit DDL. |
BETTER_AUTH_SECRET | production | Signs session cookies. |
KV_REST_API_URL | production | Upstash Redis REST endpoint. |
KV_REST_API_TOKEN | production | Upstash Redis REST token. |
INNGEST_EVENT_KEY | production | Inngest Cloud key for sending events. |
INNGEST_SIGNING_KEY | production | Inngest Cloud key serve uses to verify inbound requests. |
BLOB_READ_WRITE_TOKEN | production | Vercel Blob token; mints client-upload tokens at /api/receipts/upload (private store) and signs reads for the /api/receipts/[id]/image proxy. One store serves every environment, so the Blob integration injects the same token into all of them. |
BLOB_PATH_PREFIX | preview | Blob pathname prefix naming the same Neon branch as DATABASE_URL: local/<branch>/ (written by scripts/neon-branch.sh) or preview/<branch>/ (set by deploy.yml). Writes go under it and deletes skip files outside it, so a branch never deletes production's files. Empty and forced empty on production. See adr028. |
AWS_REGION | optional | Bedrock region, default ap-southeast-2. Terraform sets it on Vercel; assume exports it locally. |
AWS_ROLE_ARN | Vercel only | IAM role the deployment's OIDC token is exchanged for (src/server/aws/credentials.ts). Set by Terraform per environment; leave unset locally. |
BEDROCK_EMBEDDING_MODEL | optional | Bedrock foundation-model id the bank reconcile embeds descriptions with, default amazon.titan-embed-text-v2:0 (adr029). A bare in-region id: Titan has no inference profile. Set by Terraform from infrastructure/locals.tf. |
BEDROCK_RECEIPT_MODEL | optional | Bedrock inference-profile id the process-receipt worker calls, default au.anthropic.claude-sonnet-4-6. Set by Terraform from infrastructure/locals.tf. |
REDBARK_API_KEY | optional | RedBark account key for the daily CDR bank sync (adr017). Unset: the sync is a logged no-op and a history import fails with a reason. Vercel dashboard secret; don't set it in production until RedBark v2 is GA. |
REDBARK_API_URL | optional | RedBark API origin, default https://api.redbark.com (the client adds /v2). |
RESEND_API_KEY | production | Resend key the inbound webhook uses to fetch the email body and attachment bytes, which the email.received payload omits. |
RESEND_WEBHOOK_SECRET | production | Svix signing secret verifying the raw body at /api/receipts/inbound. Without it the route refuses every request. |
RESEND_INBOUND_ADDRESS | optional | The address members forward receipts to. Display-only, shown on /settings; nothing routes on it. |
POSTHOG_KEY | optional | PostHog project token (phc_…) for the Rekurve project. Unset: OTel registration is skipped, the logger writes stdout only and exception capture is a no-op. A personal phx_ key fails validation on purpose. |
POSTHOG_HOST | optional | PostHog ingestion endpoint, default https://us.i.posthog.com. |
POSTHOG_UI_HOST | optional | PostHog UI host (distinct from the ingest endpoint above), default https://us.posthog.com. Sole source of the receipt "Open trace" deep-link; with POSTHOG_PROJECT_ID it builds …/project/<id>/tracing?trace=<traceId>. |
POSTHOG_PROJECT_ID | optional | PostHog project id for the "Open trace" deep-link. Unset: no link is built and the button never renders (the trace id is still shown). |
LOG_LEVEL | optional | Minimum severity the logger emits (debug/info/warn/error), default info. |
BEDROCK_INPUT_TOKEN_PRICE_USD | optional | USD price per input token (not per 1M), e.g. 0.0000033000. Stamped onto ai.* spans; PostHog uses it as the custom price for AI cost. Set by Terraform from the live AWS Price List for the model in infrastructure/locals.tf (infrastructure/pricing.tf). Unset locally: PostHog falls back to its own model matching. |
BEDROCK_OUTPUT_TOKEN_PRICE_USD | optional | USD price per output token, e.g. 0.0000165000, as above. |
next build runs with NODE_ENV=production, so make build and pnpm run preview fail locally unless the production-required variables are in .env.
Receipt extraction calls Claude through Amazon Bedrock (src/server/aws/bedrock.ts), never the Anthropic API. The bank reconcile embeds descriptions through the same provider (Titan Text Embeddings V2) and caches each vector in description_embedding. Without credentials it logs embedding unavailable; a pair with an uncached description then scores on words alone and never adopts. @ai-sdk/amazon-bedrock has no default credential chain: on Vercel the deployment's OIDC token is exchanged for AWS_ROLE_ARN; locally you must assume <profile> first (it exports AWS_ACCESS_KEY_ID/SECRET/SESSION_TOKEN) — AWS_PROFILE alone does nothing. Model switching and IAM live in Terraform; see infrastructure, adr014 and .claude/rules/bedrock-invocation.md.
Better Auth secondary storage — sessions and verification records dual-write to Redis and Postgres and read Redis-first, with Postgres as the durable copy. Rate-limit counters live here so they are shared across lambdas. See adr006 and adr007.
The KV_ names are fixed by Vercel's Upstash integration. Keys are namespaced ba:<VERCEL_ENV>:<db-fingerprint>:, so environments and Neon branches sharing one Upstash database cannot authenticate each other's sessions.
src/instrumentation.ts register() boots OpenTelemetry once per Node server instance via @vercel/otel, exporting spans and log records over OTLP to the PostHog project; onRequestError is the one place an unhandled route error is captured, logged and flushed before the serverless freeze. Tiers never touch the SDK — they take an injected Logger and the ambient @opentelemetry/api tracer.
Everything is gated on POSTHOG_KEY: unset, nothing registers, the logger writes stdout only and exception capture is a no-op, so tests and keyless local runs need no key. Outbound fetch carries no traceparent/baggage, so no correlation id reaches Bedrock or Resend. See adr015 and adr016.
The first server span is the trace root; the browser sends no traceparent (a browser-minted parent is never exported, which left every trace rootless and hidden from PostHog's trace lists). When telemetry is on (i.e. POSTHOG_KEY set, so a span is recording), the tRPC route and POST /api/receipts/upload echo the trace id back as an x-trace-id response header — read it from the Network tab to search PostHog; keyless local runs record no span, so no header is sent. One receipt upload is two traces: the Blob token mint (blob.token.mint) and the receipt.create ingest run; the receipt row stores the second. Inngest's own inngest.execution root is dropped by NameDropSampler so the function span sits under the producer. See adr020 and adr016.
An hourly PostHog alert emails the owner once a day's AI cost passes ai_daily_cost_alert_usd (infrastructure/locals.tf). It, its insight and the test-users cohort live in Terraform (infrastructure/posthog.tf); see infrastructure.
Side-effecting writes commit their canonical rows and an outbox row in one db.transaction(), then best-effort inngest.send; a cron re-delivers anything the send missed. At-least-once, idempotent on the outbox row id. See adr002; event names live in src/server/inngest/events.ts (EVENT_REGISTRY).
Inngest reaches the app at src/app/api/inngest/route.ts (/api/inngest), which serves three scheduled functions and two event-driven workers:
| Function | Trigger | Purpose |
|---|---|---|
outbox-sweep | 0 * * * * | Re-deliver unacked rows older than 30s; failures stay unacked for the next run. |
outbox-prune | 0 3 * * * | Delete rows acked over 7 days ago; never touches unacked or stuck rows. |
cdr-sync | 0 18 * * * (UTC, ~4–5 am Sydney) | Pull settled bank rows from RedBark per linked account, upsert them, then advance that account's cursor. One failed account doesn't block the others. Honours Retry-After up to 1 h; a deterministic failure doesn't retry. No-op when REDBARK_API_KEY is unset. See adr017. |
cdr-import | cdr/import.requested | Bring in older bank history for a member-picked range (/transactions/import, up to 7 years back). One step per account per 90-day window, so a retry redoes only the failed window; each reconciles like cdr-sync and adds its counts to the cdr_import row. Shares one concurrency slot with cdr-sync, so the two never overlap. Never deletes. Out of retries, or REDBARK_API_KEY unset → the row reads Failed with the reason. A row still queued/running after 24 h counts as not active, so the form opens again. |
process-receipt | receipt/uploaded, receipt/retry, receipt/email.received | Extract vendor/total/date + line items from a receipt via Claude on Bedrock, or short-circuit an identical re-upload as a duplicate on its content hash. All three triggers carry the same { receiptId }, so an emailed receipt rides the upload path unchanged. |
Local runs use the Inngest Dev Server (no keys needed). Run make setup once — it trusts the Portless CA so the Go Dev Server binary trusts the app's HTTPS cert — then make start boots the app plus the Dev Server as sibling Portless processes. The Dev Server syncs the app's /api/inngest, lists outbox-sweep + outbox-prune + process-receipt + cdr-sync + cdr-import (plus its cdr-import-failure handler), and can invoke them from its dashboard at https://inngest.personal-finance.localhost (worktree: https://<branch>.inngest.personal-finance.localhost).
Terraform in infrastructure/ owns infrastructure only: the Vercel project, the per-environment AWS IAM role, the Bedrock env vars, the production protection-bypass secret and PostHog alerting config (production only); Vercel's git auto-deploy is off (vercel.json). .github/workflows/deploy.yml runs the whole deploy on every push as one staged flow: terraform apply → vercel deploy (CLI, un-aliased) → resolve the Neon DB branch → migrate (unpooled). For main it then gates the staged deployment on /api/health behind the bypass header and only then vercel promotes it onto the domain. A failed migrate fails the job before promote; the previous deployment stays Current (no automated rollback — migrations are expand-only). Locally: assume <profile>, export VERCEL_API_TOKEN=… POSTHOG_API_KEY=… TF_VAR_posthog_key=… TF_VAR_posthog_project_id=…, then make plan env=preview. Bootstrap, cutover and model switching: infrastructure.md. Decisions: adr013, adr014.
Renovate (.github/renovate.json) auto-merges patches; minor and major PRs wait for a human. .github/workflows/claude-renovate.yml runs Claude on them: when a minor/major PR opens it reads the release notes and comments on breaking changes, deprecations, bug fixes and features worth adopting (no edits); when Quality Control or Deployment fails on a renovate/* push it fixes the cause, runs the checks and pushes to the PR branch, or comments why not. One fix attempt per Renovate push — if Claude's commit fails CI, a human takes over. Claude never starts, re-runs or waits on CI; its push does that. Both jobs log Claude's full output (show_full_output) for troubleshooting. Re-run either by hand with workflow_dispatch (pr, mode). Needs the CLAUDE_CODE_OAUTH_TOKEN secret (claude setup-token) and the Claude GitHub App on the repo; like any workflow_run / workflow_dispatch workflow it only runs from main.
MIT, see LICENSE. Self-host it, learn from it, contribute to it.
Open core, like PostHog: if paid hosted plans arrive, paid-only code will live in an ee/ directory under its own commercial license. Everything outside ee/ stays MIT.