Master Contact Directory is the system of record for people and companies across your organization — resolved against duplicates, matched to a verified email pattern, and stamped with an audit trail that never gets edited quietly.
Each entry below is a real capability of the directory, indexed the way a registrar would file it — not a features list, a set of things the system actually does when a record is created, matched, or queried.
Every new company name is checked against the registry with Postgres pg_trgm before it's filed — a typo, an added “SRL,” or a shortened legal name resolves to the existing entry instead of forking it.
Once two or more verified addresses share a convention — first.last@, flast@ — the directory proposes the same pattern for a new contact, marked “guessed” until someone confirms it.
One contact can matter to several business units at once. Relevance is tagged per holding, so a search scoped to one entity never surfaces someone else's prospect list.
Field staff and partners submit cards through a revocable, expiring link — no dashboard credentials issued, no standing account to deprovision later.
Create, edit, delete, import, export — each is written once to an append-only ledger with a before/after snapshot. The log itself cannot be altered, even by an administrator.
Nothing disappears on the first click. Deleted records move to a recovery shelf with the reason and the person responsible, restorable until someone purges them on purpose.
Every control below exists because a directory of people and companies is, by nature, a directory of things worth protecting.
Required for every administrator, with a seven-day grace window on first sign-in and enforcement at the middleware layer, not just the login screen.
Postgres RLS restricts every table to the server's service role. There is no path from a browser directly to the data, even if a client key leaks.
UPDATE and DELETE are revoked on the audit table at the database level — not a convention, a grant that doesn't exist to break.
Login attempts, quick-add tokens, imports, and exports each carry their own sliding-window limit, enforced in Redis ahead of the database.
Quick-add links can expire, cap their use count, and restrict by IP — and revoking one takes effect on the next request, not the next deploy.
Administrators can see every active session across the team and force a sign-out on any of them, immediately.
Paste a signature, a business card photo's text, or a line from an email — the directory extracts a structured entry, checks it against every existing company and contact, and shows you the match before anything is saved. Nothing is written until a person confirms it.
A prospect at this domain is already filed. Register anyway, or open the existing entry?
Next.js Server Actions handle every write. Prisma keeps the schema honest. Supabase Postgres holds the data — and its own row-level policies, in case anything ever tries to reach it directly.
Next.js (App Router) — Server Actions & edge middleware, no separate API layer to drift out of sync
Prisma ORM — Typed schema, versioned migrations, one source of truth for the data model
Supabase Postgres — Session pooling plus pg_trgm for the fuzzy company matching above
Upstash Redis — Sliding-window rate limits for login, tokens, import, and export
// resolved by domain, then by trigram name match
model Company {
id String @id @default(uuid())
name String
normalizedName String @map("normalized_name")
domain String?
deletedAt DateTime? @map("deleted_at")
contacts Contact[]
@@index([normalizedName])
}
Sign in with your enterprise credentials, or ask an administrator to file you in.