Open Net Worth
Open Net Worth는 직접 운영하는 오픈소스 개인 AI 자산 비서로, 전체 재무를 하나의 원장에 기록하고 사용자가 제어하는 AI가 관리
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
SoleTax engineering showcase: HMRC MTD filing server, tax engine, CI/CD and security model
README 전체를 불러오는 중입니다.
README 전문 보기 ↗SoleTax is a Making Tax Digital (MTD) compliance app for UK sole traders: automatic mileage tracking, AI receipt capture, jobs and invoicing, a running tax estimate, and direct quarterly filing to HMRC. It is an iOS app (React Native / Expo, on the App Store since September 2026) with a web portal, both backed by Supabase (Postgres, Auth, Storage, Deno edge functions) on Cloudflare. It is built and run by one person. This repository is a scrubbed, read-only extract of the private production repo so that an engineer can read the parts that matter - the HMRC filing server, the tax engine and its test suites, the CI/CD workflows and the security model - without the production repo being made public. Everything here is real code from production; what was removed, and why, is listed at the end.
Jump to: Architecture · HMRC integration · CI/CD · Security model · Testing and validation · Observability · Known gaps · How this was built · What is in this repo
npm ci
npm run check # tsc + eslint + the tax engine and HMRC server rule suites (~1,000 assertions)
flowchart LR
subgraph Clients
APP[iOS app<br/>React Native + Expo<br/>SQLite/MMKV offline-first]
PORTAL[Web portal<br/>Vite + React<br/>Cloudflare Pages]
end
subgraph Supabase["Supabase (eu-west-1)"]
AUTH[Auth<br/>anon · OTP · Apple · Google]
PG[(Postgres<br/>RLS on every table)]
STORE[(Storage<br/>receipts, invoices)]
subgraph EF["Deno edge functions"]
EXTRACT[claude-extract]
SUBMIT[hmrc-submit]
CB[hmrc-oauth-callback]
MISC[support-agent · send-invoice<br/>delete-account · mapkit-token · …]
end
end
INGRESS[HMRC ingress<br/>Caddy on a single host<br/>captures client port]
HMRC[HMRC MTD APIs<br/>Business Details · Obligations<br/>SE Business · Calculations]
ANTH[Anthropic API]
EMAIL[Resend]
R2[(Cloudflare R2<br/>encrypted backups)]
GHA[GitHub Actions<br/>24 workflows]
CLOCK[Cloudflare Worker<br/>fleet clock]
APP -- own-row CRUD via anon key --> PG
APP --> AUTH
APP -- images --> STORE
PORTAL -- same --> PG
APP & PORTAL -- JWT --> EXTRACT & SUBMIT & MISC
APP & PORTAL -- filing actions --> INGRESS -- signed envelope --> SUBMIT
SUBMIT -- OAuth + FPH headers --> HMRC
HMRC -- redirect --> CB
EXTRACT --> ANTH
MISC --> EMAIL
GHA -- nightly pg_dump + rclone --> R2
CLOCK -- workflow_dispatch --> GHA
GHA -- RLS proof, health, invariants --> PG
How the pieces connect:
service_role key exists only inside those functions and a CI check fails if it ever
appears in a client bundle.hmrc-submit holds the OAuth client secrets,
the user's encrypted HMRC tokens and National Insurance number, assembles HMRC's
fraud-prevention headers and writes one audit row per attempt. Filing requests are
dialled through a small Caddy host rather than the Supabase hostname so the client's
public TCP port can be observed (HMRC requires it; see below).Source: supabase/functions/hmrc-submit/index.ts, supabase/functions/hmrc-oauth-callback/index.ts,
supabase/functions/_shared/hmrc*.ts. Rules pinned by scripts/test-hmrc-server.ts (120 checks).
app/portal ──connect──▶ hmrc-submit mints state = HMAC-SHA256(uid, env, surface, exp 10 min)
returns HMRC authorize URL
user ──authorises on HMRC──▶ HMRC redirects to hmrc-oauth-callback?code&state
hmrc-oauth-callback: verify HMAC → exchange code with client secret (same env the state was
minted for) → AES-256-GCM encrypt access+refresh tokens → upsert
hmrc_connections(user_id, environment, …) → 302 back to app deep link
or portal /tax
state is the whole security model: unforgeable
without HMRC_TOKEN_KEY, time-boxed, and it binds the resulting tokens to exactly one
user id, one environment and one return surface.hmrc-submit with an action field:
status, connect, disconnect, saveNino, businesses, selectBusiness,
obligations, submit, calculate, plus two sandbox-only actions (validate,
createTestBusiness) that the server refuses in production.여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗