Customer-owned face search with Supabase pgvector
UsePOST /represent for embedding computation and keep your enrolled database
in your own Supabase project. deepface.dev does not expose hosted /register
or /search endpoints. Images and embeddings submitted for computation still
leave your backend; customer-owned storage does not mean local-only processing.
This worked example supports one authenticated Supabase user per tenant and
pseudonymous UUID subjects within that tenant. Organization/team membership is
a different authorization model and needs its own policies and isolation tests.
Face similarity alone is not legal identity, liveness or complete KYC.
Prepare an isolated customer sandbox
Use Node.js 24 and install@supabase/supabase-js in your backend project. The
transport fixture was tested with version 2.108.2. Save the files below with the
shown directory structure; complete code is available here without private
repository access. Run schema.sql once as the owner of a fresh sandbox.
It intentionally fails if these example tables already exist. It is not a
migration for deepface.dev or a script to apply blindly to an existing database.
This schema assumes pgvector is in extensions. If an existing installation
is in another schema, stop for owner review; do not relocate a shared extension
blindly.
The schema enables and forces RLS, revokes inherited anonymous/default grants,
and uses an invoker-rights search function. authenticated can access only rows
whose tenant is auth.uid(). Only the database owner provisions pipeline rows.
The application uses a publishable key plus the owner’s verified user session,
never a secret/service-role key. Service roles and database superusers bypass
RLS; RLS is not a boundary against a compromised administrator.
Supabase RLS guidance
and PostgreSQL function privileges
explain these boundaries.
Provision and preserve a pipeline
Before real enrollment, the owner must insert a row intoface_search_pipelines for the actual authenticated user. Set a new pipeline
UUID, allowed model, reviewed provider/runtime revision, detector, alignment,
normalization, reviewed detector fallback policy and measured embedding dimension. Do not copy the synthetic test
pipeline into production. Confirm the current model/detector allowlists through
capabilities and review
model rights.
The /represent response identifies model_name, but does not certify an exact
weight/runtime revision. provider_revision is the customer’s independently
verified record; a populated string is not proof. If revision continuity cannot
be established, stop production enrollment/search until the owner has a verified
pipeline or has evaluated and migrated to a new one. Do not invent a revision.
The model server can retry detection using a fallback detector (for example,
OpenCV to RetinaFace), even when detector_backend was explicit. The response
does not report the effective detector. Record the independently reviewed
fallback behavior in detector_policy, evaluate that complete preprocessing
policy, and bind it to provider_revision. An explicit detector parameter alone
is not proof of preprocessing identity. If your job requires one detector only,
production use requires an independently verified configuration without fallback.
See detector behavior.
Keep provisioned profiles immutable: add a new pipeline UUID when any model,
revision or preprocessing setting changes. Equal dimensions do not establish
compatibility. Each request uses the selected profile’s explicit settings.
Integrate with existing authorization
SetDEEPFACE_API_KEY, SUPABASE_URL and SUPABASE_PUBLISHABLE_KEY through your
server’s secret/configuration system. Obtain the user access token from the
existing authenticated backend request. Never solicit tokens in chat or put them
in source code, command arguments or browser bundles. Create one client per
request; do not share one user’s client with another.
authorize is a required trusted application function. It must check the
verified actor’s current enrollment, search or deletion permission and the
applicable permission/consent records for the subject and intended use. A
client-supplied authorized: true flag is not this function. No consent or
account interface is provided or changed by the example.
Outcomes, retries and deletion
An enrollment reserves(tenant_id, enrollment_id) atomically before compute.
Concurrent workers cannot both win the same reservation. A completed duplicate
returns already_enrolled without inference. A pending reservation blocks
another attempt; failures or lost responses never expire it automatically.
Preserve the enrollment and compute request IDs, investigate the result, and do
not change IDs or delete/recreate the subject to evade an uncertain computation.
A lost database acknowledgement can follow completed inference. Repair storage
from a securely retained known result when authorized; do not recompute blindly.
No raw embedding recovery store or automatic retry is included in this example.
One image must yield exactly one face with the provisioned model and dimensions.
Multiple faces or incompatible results leave enrollment pending for review.
The code omits paid positive priority and never automatically replays compute.
No-face outcomes can be billable; see errors,
limits and pricing.
Search requires an explicit evaluated cosine-distance threshold from 0 to 2.
Only complete rows from the same tenant and pipeline within that threshold are
returned. An empty result is no_acceptable_match; returned candidates still
require the application’s intended verification/decision process. The SQL uses
exact search for a bounded worked example. It makes no latency, capacity or ANN
recall claim. Set appropriate database timeouts and test the intended dataset.
Before adding HNSW/IVFFlat, measure filtered recall and no-match behavior against
this exact baseline. See the pgvector reference.
Deleting a subject cascades to its pending and complete enrollments. It removes
active rows; backups, replicas, application logs and external copies need the
customer’s separate retention/deletion process. This example stores no labels,
raw images or arbitrary metadata. Keep returned pseudonymous IDs private too.
Verify with synthetic fixtures
The included SQL suite uses synthetic 3-dimensional vectors, not face embeddings. Run fixture SQL only through the disposable runner, never against a shared or customer Supabase project. It checks non-owner RLS, cross-tenant read/search/write/delete denial, pipeline separation, invalid/zero/nonfinite inputs, explicit no-match, immutable completed enrollment, unique reservations and deletion. It does not validate recognition accuracy, Supabase JWT verification or a deployed Data API. With PostgreSQL tools and pgvector installed locally, run from the directory containingexamples/ and scripts/:
auth.uid();
Supabase’s verified JWT-to-role/claim boundary still needs integration acceptance.
Its optional source-build mode is for testing pgvector without a global extension
installation; that mode does not verify CREATE EXTENSION deployment.
For the real sandbox, also verify authenticated owner and cross-tenant requests
through Supabase’s Data API, configured grants/extension schema, pipeline
revision continuity, permitted fixture provenance, threshold calibration and a
bounded API cost cap. These are release inputs, not successes of local fixtures.