ProjectsCase Study

Apna Contacts

A security-first multi-tenant contact vault for families and small businesses—owner CRUD, view-only members, and passworded guest share links.

Apna Contacts landing page
Industry
Consumer SaaS · Family & small business tooling
Category
Multi-tenant SaaS · Contact vault
Role
Full Stack Product Engineer — product, architecture, security, and UI end to end
Timeline
2025 — 2026
Status
Production-ready product build
Stack
Next.js · React · TypeScript · Tailwind · Supabase · Postgres RLS · Server Actions · Zod

Executive Overview

Apna Contacts solves a simple, painful problem: important numbers live in one person’s phone, while family and staff keep asking for the electrician, the doctor, or the shop supplier in group chats.

The product is a private vault—not a public social directory. Owners create named workspaces (home, shop, family), invite members as view-only, and issue guest share links with optional passwords and expiry.

The engineering story is defense in depth: Server Actions enforce roles, Postgres RLS isolates tenants, and guests never touch tables through the Data API—only SECURITY DEFINER RPCs after a signed session cookie.

The build evolved from a single-owner family book into a multi-tenant SaaS with Google OAuth, CSV import (including Google Contacts), CSP nonces, and privacy-aware SEO that keeps private routes out of indexes.

Business Problem

Existing workflow

Households and shops store numbers in personal phones or shared Google accounts, then forward screenshots, CSVs, or full account access whenever someone needs a lookup.

Pain points

  • Family chats constantly ask “who has the electrician’s number?”
  • Home and shop contacts mix in one phone with no clean separation.
  • Sharing a Google account or full CSV exposes edit rights and private data.
  • Relatives who only need read access should not need full logins or owner privileges.
  • Guest access without proper session design either leaks data or becomes unusable.

Why it matters

Contact books hold trusted personal and business relationships. The product only works if sharing is easy for guests and impossible to abuse—view-only by default, tenant isolation by design.

Solution

Apna Contacts keeps one source of truth for numbers and three clear access modes:

  • Owner vaults

    Full CRUD on contacts, categories, favorites, CSV import, invites, guest links, and workspace settings across multiple named vaults.

  • Invited members

    Authenticated view-only access to a single vault—search and open contacts without create, edit, or delete rights.

  • Guest share links

    Password-optional, expiring links at /g/{token}. Guests get a signed cookie session and a mobile-first read-only contact UI—no signup.

  • Search-first mobile UX

    All / Favorites / Recent tabs, multi-phone contacts, call / WhatsApp / copy actions, and category badges inspired by familiar phone contact apps.

  • Security as product

    Dual authorization (app roles + RLS), HMAC guest cookies, rate-limited share attempts, CSP with nonces, and noindex on private routes.

Families and shops keep one trusted book, share lookup access without edit rights, and separate home vs work vaults—while guests never become members and never gain table-level API access.

Core Features

Contact vault

Contacts CRUD

Create, edit, delete, favorite, and browse contacts with multi-phone JSON fields.

NeedOwners need one reliable book, not scattered notes.

ValueSingle source of truth for important numbers.

Search & tabs

Search by name, phone, or category; filter All / Favorites / Recent.

NeedLookups happen on mobile, under time pressure.

ValueFaster find → call / WhatsApp.

CSV import

Import standard and Google Contacts CSV with preview and validation.

NeedExisting phone books should not be retyped.

ValueLow-friction migration into the vault.

Multi-tenant workspaces

Named vaults

Multiple workspaces per account (e.g. Home, Shop) with active-workspace context.

NeedHome and business contacts should not mix.

ValueClean separation without multiple apps.

Invitations

Email invite tokens, join flow, revoke, and member remove—members stay view-only.

NeedTrusted family needs access without owner powers.

ValueShared access without shared passwords for members.

Owner dashboard

Totals, favorites, weekly adds, category breakdown, and recent updates.

NeedOwners need a quick health check of the vault.

ValueClear overview before diving into contacts.

Guest sharing

Share links

Create, list, and revoke guest links with optional password and expiry.

NeedRelatives should not need accounts for a quick lookup.

ValueFrictionless read access under owner control.

Signed guest sessions

HMAC-signed HTTP-only cookie after RPC session create; middleware validates signature then DB session.

NeedAnonymous access must still be authenticatable and short-lived.

ValueGuest UX without opening the Data API to anon.

Guest contact UI

Read-only list and detail under /{slug}/guest/contacts with call / WhatsApp / copy.

NeedGuests need the same lookup actions as members.

ValueUseful sharing, not a dead view-only dump.

Auth & account

Email + Google OAuth

Supabase Auth with signup bootstrap that creates a personal workspace via DB trigger.

NeedOwners need a fast, familiar sign-in path.

ValueLower setup friction for the person who owns the vault.

Account controls

Profile, password change, and delete-account confirmation.

NeedPersonal data products need clear exit and security hygiene.

ValueOwner trust and compliance-minded UX.

User Journey

Owner → Vault ready

  1. 01

    Signs up or signs in (email / Google)

  2. 02

    Personal workspace bootstrapped automatically

  3. 03

    Adds contacts or imports CSV

  4. 04

    Optionally creates a second vault (e.g. Shop)

  5. 05

    Invites family members as view-only

  6. 06

    Creates a guest share link for relatives

Member → Lookup

  1. 01

    Accepts invite and signs in

  2. 02

    Opens the assigned vault contacts

  3. 03

    Searches or browses Favorites / Recent

  4. 04

    Calls, WhatsApps, or copies a number

  5. 05

    Cannot create, edit, or delete contacts

Guest → Share link

  1. 01

    Opens /g/{token}

  2. 02

    Enters optional share password

  3. 03

    Receives signed guest session cookie

  4. 04

    Browses read-only contacts for that vault

  5. 05

    Calls or WhatsApps without creating an account

Technical Highlights

Dual-layer authorization

Server Actions check owner / member / guest roles; Postgres RLS isolates every row by workspace and blocks member writes.

Business valueA UI bug cannot casually cross tenants or grant edit rights.

Guests never touch tables

Anonymous clients have no table grants. Contact reads go through SECURITY DEFINER RPCs that return only the columns a share link needs.

Business valueGuest links stay useful without exposing the vault schema.

Signed guest sessions

Share access uses HMAC cookies (GUEST_SESSION_SECRET), middleware validation, and a DB check that the session is still valid.

Business valuePassworded, time-bounded lookup without forcing guests to create accounts.

Tenancy shipped in waves

Migrations move from a single-owner share password to named workspaces, invites, view-only members, and repeated authz hardening.

Business valueProduction path: ship the vault, then harden sharing as real households use it.

Real phone-book import

CSV import maps Google Contacts headers, multi-phone rows, system labels, and starred → favorite—with a preview before insert.

Business valueOwners migrate existing books without cleaning spreadsheets first.

Challenges & Solutions

ChallengeSolution
Guests needed one-tap lookup, but giving anon clients direct table access would leak private numbers across workspaces.No anon table grants—HMAC guest cookies, rate-limited share passwords, and narrow SECURITY DEFINER RPCs for list/get only.
App-layer role checks alone are not enough when Server Actions and the Data API can disagree on who may write.Same rules in two places: action-level roles plus Postgres RLS that isolates tenants and restricts writes to owners.
A single shared book mixed home and shop numbers, and a one-password share link could not support invites or view-only staff.Named workspaces with RLS, member invites as view-only, and guest share links as a separate path.
Early share-password design broke as soon as households needed multiple vaults and harder session rules.Incremental migrations—workspaces, invitations, guest_share_links—then authz and input hardening passes against real misuse cases.

Tech Stack

Frontend
Next.js App Router, React 19, TypeScript, Tailwind CSS v4, shadcn/ui, Lucide, Sonner
Forms & validation
React Hook Form, Zod, Server Actions
Backend
Next.js Server Actions — no separate API server
Database
Supabase Postgres with Row Level Security
Authentication
Supabase Auth — email/password + Google OAuth; HMAC guest sessions
Guest security
SECURITY DEFINER RPCs, pgcrypto password hashes, signed HTTP-only cookies
Theming
next-themes, light/dark product UI
Ops
Supabase Edge Function keep-alive + pg_cron for free-tier pause prevention
Deployment
Vercel-oriented Next.js deploy with security headers

Project Gallery

Recommended capture order for the case-study gallery.

  1. 01Landing — private vault positioning
  2. 02Owner dashboard — metrics & categories
  3. 03Contacts list — search & tabs
  4. 04Contact detail — call / WhatsApp / copy
  5. 05New contact / multi-phone form
  6. 06CSV import preview
  7. 07Workspace switcher — Home vs Shop
  8. 08Settings — members & invites
  9. 09Share — guest link management
  10. 10Guest entry — password gate
  11. 11Guest contacts — read-only mobile UI
  12. 12Dark mode — dashboard & contacts

Key Learnings

Product

On private contact data, the access model is the product. Owners, members, and guests need different paths—one shared login is the wrong default.

Engineering

UI checks and RLS must agree. If only one layer is correct, multi-tenant contact data will eventually leak through the other.

Architecture

Guest safety improves when anonymous users never receive table grants and only call RPCs that return a minimal column set.

Future Improvements

PriorityImprovementBusiness value
HighInvite email delivery (Resend) so owners do not rely only on copyable linksFaster member onboarding with less manual sharing.
HighFine-grained roles and selective contact sharing beyond owner vs view-onlyBetter fit for shops where some staff need limited edits.
MediumWorkspace switcher and settings UX polish for multi-vault ownersClearer day-to-day vault management.
MediumContact activity / recent-call hints for guests and membersFaster repeat lookups for frequently used numbers.
LowOptional WhatsApp deep-link templates and label presets per categorySlightly faster outreach from the detail screen.

Need a multi-tenant product with real access control?

Apna Contacts is a full-stack case study in private data sharing—workspaces, RLS, guest sessions, and a mobile-first vault UX. If you are building something where permissions are the product, let’s talk.

Start a conversation
Optional technical notes

Notes on how guest access, tenancy, and authorization stay aligned across the Next.js app and Supabase.

Access modes, not one role

Owner, member, and guest are different products in one codebase. Write paths require owner; members and guests only read. That rule is enforced in role helpers and again in RLS policies.

Guest share passwords protect the link—not the workspace membership model. Guests never become members through a share URL.

Tenant isolation

Every contact, category, invite, and share link is scoped by workspace_id. Signup bootstraps a personal workspace via a database trigger so owners land in a usable vault immediately.

The workspace slug in guest URLs is branding only—the active guest workspace always comes from the validated session, never from a client-trusted path segment alone.

Security migrations as narrative

The SQL history is part of the product story: initial contacts schema, production owner RLS, guest sessions, multi-tenant workspaces, member view-only, then waves that hide share tokens, bind invites to auth emails, and harden rate limiting.

That arc is deliberate—ship a useful vault, then harden the surfaces that appear once real sharing begins.

Role

Full Stack Product Engineer

Client

Personal product / SaaS build

Date

2026

Collaborators

Solo build

Next Project

GitSearch AI

View next