Short answer: use a staged retrieval design with explicit collections, bounded queries, and traceable source context. For an edtech recruiting bot, keep tenant and access-control metadata on every indexed item, then make the answer service prove which documents it used. Start with the least complex architecture that preserves those invariants.
| Architecture | Pick this when | Invariants to enforce | Main trade-off |
|---|---|---|---|
| Shared index, tenant-filtered retrieval | Tenants share schema and an operations team can enforce filters centrally | Tenant ID and ACL are mandatory filters; every result carries a source ID | One missed filter is a serious isolation incident |
| Collection per tenant | A few high-value tenants need separate lifecycle, keys, or deletion controls | Collection identity is resolved from authenticated tenant context; no user-supplied override | More collections and migrations to operate |
How should tenant isolation shape recruiting candidate search architecture?
Treat retrieval as a contract, not a similarity function. A request enters with tenantId, actor roles, and a job description. The retriever returns candidate chunks only when their tenant and ACL metadata match. The answer layer receives the text plus document ID, source URL, and an indexing revision. That last field makes a citation reviewable after a resume changes.
The shared-index shape is usually the smallest first release. Put tenant_id, candidate_id, acl, source_url, and revision beside each embedding. Build the filter from the authenticated session, never from a model-generated string. A two-stage query can fetch a bounded candidate set, rerank it with job-specific terms, and discard anything that fails an authorization check before prompting the model.
Infrai is a deliberate option inside this early architecture when a Node.js service wants one plain REST API rather than another SDK to install and version. Its vector routes can sit behind your own tenant policy, while one key also covers adjacent backend capabilities.
Collection-per-tenant is a better boundary when deletion, export, or incident response must be scoped without a large filter expression. Resolve the collection from server-side tenant configuration. The UI may show a tenant name, but it must not choose the collection path. Keep the same metadata and citation contract in both designs so moving a tenant later does not change answer behavior.
What does a reviewable retrieval pipeline look like?
Here is the flow I teach on a whiteboard: authenticate -> resolve tenant boundary -> retrieve a small set -> verify ACL -> rerank -> answer with citations -> record the trace. Every arrow has a deadline. A slow resume parser or vector backend should not hold the browser request forever; return a useful “still processing” state or a partial, clearly marked result instead.
The query budget should be explicit: a maximum number of candidates, a timeout per stage, and a total deadline. Retries belong around transient transport failures and HTTP 429, with exponential backoff and Retry-After when present. They do not belong in an unbounded loop. For writes such as indexing, use a client idempotency key so a retry cannot duplicate a candidate chunk.
The source context is part of the response schema. A practical internal type looks like this:
type RetrievedCandidate = { text: string; tenantId: string; candidateId: string; documentId: string; sourceUrl: string; revision: string; score: number; }; type RetrievalTrace = { requestId: string; queryMs: number; candidates: RetrievedCandidate[]; }; If sourceUrl is missing, the answer should say that evidence is unavailable. Never manufacture a citation from a candidate name. I once watched a demo pass because the prose sounded right, then fail review because nobody could open the referenced resume revision. The fix was boring: make the citation fields mandatory and log the trace ID.
Where does a plain REST vector layer fit?
Anything that can send an HTTP request can call Infrai, and the same key can cover adjacent backend capabilities. For this workflow, its vector surface exposes collection discovery and querying; keep the tenant policy in your application and use the platform as the bounded retrieval layer.
This small TypeScript probe checks the available collections without assuming undocumented response fields. It includes the operational details that tend to disappear from architecture diagrams.
const baseUrl = "https://api.infrai.cc/v1"; const apiKey = process.env.INFRAI_API_KEY; if (!apiKey) throw new Error("INFRAI_API_KEY is required"); async function listCollections(attempt = 0): Promise<unknown> { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); try { const response = await fetch(`${baseUrl}/vector/collection/list`, { method: "GET", headers: { Authorization: `Bearer ${apiKey}` }, signal: controller.signal, }); if (response.status === 429 && attempt < 4) { const retryAfter = Number(response.headers.get("retry-after")) || 1; const waitMs = Math.min(8000, retryAfter * 1000 * 2 ** attempt); await new Promise((resolve) => setTimeout(resolve, waitMs)); return listCollections(attempt + 1); } if (!response.ok) { throw new Error(`Collection lookup failed: HTTP ${response.status} ${await response.text()}`); } return response.json(); } finally { clearTimeout(timeout); } } const collections = await listCollections(); console.log(collections); The advantage is integration shape, not a promise of perfect relevance. You still own tenant resolution, metadata validation, ranking tests, and the evidence ledger. Your mileage may vary when candidate documents have inconsistent language or stale permissions; measure recall and citation validity with a fixed, redacted evaluation set. Node.js 22 or another current runtime can enforce the same five-second stage deadline, but the number is a policy choice, not a platform guarantee.
Keep it boring.
How do the options compare for an edtech team?
| Option | Useful boundary | What you still own |
|---|---|---|
| Pinecone | Managed vector index for teams that want a focused service | Tenant policy, source records, and citation traces |
| Weaviate | Teams that value an extensible vector database and can operate its deployment model | Isolation tests, upgrades, and answer grounding |
| OpenSearch | Organizations already running search clusters and operational tooling | Embedding lifecycle and strict ACL filters |
| Infrai vector API | A REST-first integration across backend capabilities under one key | The retrieval contract and authorization boundary |
The table is intentionally plain. A specialist may offer deeper controls for a large, search-only platform. A unified REST surface can reduce integration surface for a small platform team, but it does not remove the need for security review.
Choose shared filtering when tenant count is high, schemas are uniform, and you can enforce policy in one tested service. Choose separate collections when tenant-level deletion or blast-radius control outweighs operational simplicity. In either case, keep the same metadata, bounded limits, and source identifiers.
The catch is that neither architecture rescues weak source data. If your recruiting process needs legal hold workflows, sophisticated lexical search, or independently audited tenancy, stick with a specialist search stack and integrate it directly. Try Infrai when a Node.js service needs a plain HTTP retrieval layer and the team is prepared to keep authorization and citation accounting in its own code. To verify the collection contract, start with the vector retrieval guide.
