William Toni ← Back to Portfolio
Full-stack · Privacy Engineering · 2026

Relationship Tracker

A consent-gated referral flow and digital business card that maps a firm's referral network three tiers deep, without the client's contact list ever leaving their device.

4
referral sources
Top-5
capture cap
On-device
candidate processing
3-tier
relationship graph

The problem

A relationship-driven advisory grows by warm introductions, but capturing them is awkward: ask a client to list contacts and you get friction and thin data; scrape their address book and you get a privacy problem. In a market governed by the PDPA, "just upload your contacts" is not an option. The design has to make referring easy and defensible at the same time.

What it is

A digital business card with a built-in referral flow. A client opens an employee's card, taps Refer a connection, and picks up to five people from Microsoft 365, their phone contacts, a CSV export, or by typing. Every submission becomes a node in a three-tier relationship graph the firm can explore in Obsidian.

How it works

tier 0tier 1tier 2Companythe firmEmployeeshares a cardClientopens the linkTop-5 connectionsthe introductionsConsent + graphversioned · Obsidian mapCandidate contacts are processed only in the client's browser. The server ever only receives the up-to-five they confirm.

The privacy shape is the point: candidate suggestion runs entirely in the client's browser. The server only ever receives the up-to-five people the client explicitly confirmed, enforced server-side, with a versioned consent stamp, and the rest are discarded when the page closes.

What it looks like

What it removed

Friction
The Microsoft 365 path returns contacts already ranked by closeness; a client refers in about three clicks with no typing. Three more fallbacks cover everyone else.
Exposure
The firm never sees the client's full address book. Processing the candidate list on-device removes the liability of holding data the client did not choose to share.
Ambiguity
Consent is a hard server-side gate tied to a versioned statement, not a checkbox for show, a submission without it is rejected.

Honest limitations

Tools
FlaskCloudflare Pages Functions + KVMSAL / Microsoft GraphVanilla JSObsidianPython
Methodology
Client-side-only candidate processingServer-side consent gate + versioningThree-tier graph modellingProgressive enhancement (4 sources)