Skip to content
Centriu
Centriu Synapse

AI Agent and BYO LLM Key Cross-Tenant Selection Automation: The Only Active Key Belonged to Someone Else

Centriu Synapse lets an organization configure its own custom AI agents and connect its own bring-your-own LLM provider keys. Before this fix, the runtime functions that select which agent and which key to use for an actual incoming message — not an admin panel, the live message-answering path itself — read their entire underlying table with no organization filter at all: isolation depended entirely on the convention of running one process per organization, never on the query. Measured directly against a real deployment: the organization it served had exactly one active bring-your-own LLM key in the whole table, and that key belonged to a completely different organization. The very next customer message would have been answered — and billed — using someone else's connected key. The fix requires an explicit tenant scope on every single read, re-checks each returned row against that scope even after the database's own filter, keys the cache by scope so one tenant's cached results can never answer for another's, and fails closed — returning nothing rather than guessing — the moment the database's own scope columns cannot be confirmed present.
Scope required, every read
Fail-closed, not fail-open
Connected accounts and credentials screen
The only active key belonged to someone else.

A deployment habit is not an access control

Running one process per organization is a perfectly reasonable way to operate a system, and for as long as it holds, a query with no organization filter looks identical in its behavior to one that has been correctly scoped — every request the process ever handles genuinely does belong to the one organization it was configured for. The gap that convention hides only becomes visible the moment that assumption stops being universally true — a shared process, a migration in progress, an internal environment reused for more than its original purpose — at which point the query keeps confidently returning whatever the table's first-priority row happens to be, for anyone who asks.

How the underlying problem shows up before you fix it

A runtime function that selects a resource for the current request reads its source table with no organization or tenant filter in the query itself, relying entirely on deployment topology (one process per customer) to keep results correct.

The function's own log message describes an intention ("using the tenant's own default key") that the code does not actually verify — the phrase is honest about the design goal and silently wrong about what the query returns.

A resource selected by "first matching row" or "first by id" rather than by an explicit ownership check will, the moment more than one owner's rows exist in the same table, hand back whichever row happens to sort first — not necessarily the caller's own.

The defect produces no error and no crash: the request completes successfully, using a real resource, just not the resource that belongs to the organization actually being served.

A cache keyed by resource type alone, without the caller's own scope baked into the cache key, can serve one tenant's cached result to a completely different tenant's next request.

How a table-wide read became a scope-required one, with proof at every layer

Centriu Synapse's runtime, while actually answering an incoming customer message, calls two functions to decide how to respond: one selects which of the organization's configured custom AI agents applies, the other selects which bring-your-own LLM provider key to bill the call against. Before this fix, both functions issued the same shape of query: select every active row from the table, with no organization or tenant condition at all. The organization boundary that should have applied was never expressed in the query — it existed only as an operational convention, one deployment process per organization, which the query itself had no way to enforce or even know about.

A direct, dated measurement against a real deployment made the gap concrete rather than theoretical: querying the bring-your-own key table for the organization one specific deployment was configured to serve found exactly one active key in the ENTIRE table — and that key belonged to a different organization altogether, one used for internal purposes. The function's own log line at the time read "using the tenant's own default key" — an accurate description of the code's intention, and a false description of what it was about to do. The very next real customer message routed through that deployment would have been answered, and billed, using a key that organization never connected and never authorized.

The fix threads a `TenantScope` — organization id, and optionally a more specific sub-tenant id — through both functions as a required parameter, never an optional afterthought and never read from request configuration. Every query now applies that scope as a real database condition, layered with a second, independent check: after the database returns rows, each one is re-verified in application code against the same scope before being used at all — defense in depth against the theoretical case of a filter condition built incorrectly and silently returning too much. The in-memory cache that avoids re-querying on every message is keyed by the scope itself, not just by resource type, so a cached list computed for one tenant can structurally never be handed to a different tenant's request. Looking up a specific key by its own database id — the path a custom agent's own configuration uses to reference "its" key — resolves only inside the map that has ALREADY been filtered to the caller's scope, so a key id belonging to a different organization is indistinguishable from an id that simply does not exist, which is the correct answer for a resource that isn't the caller's. And when the database cannot confirm the scope columns exist at all — the specific migration state where scoping information might genuinely be unavailable — every one of these functions fails CLOSED, returning nothing, rather than falling back to an unscoped read that could hand back anyone's data.

What is actually built today

Both the custom-agent selection and the default BYO-key selection require an explicit tenant scope as a parameter — there is no code path that reads either table without one.

Every returned row is independently re-verified against the caller's scope in application code, even after the database's own query condition — a second, structurally independent check rather than trusting one filter alone.

The result cache is keyed by the caller's own scope, so a cached answer computed for one organization or tenant cannot be served to a different one.

A key referenced by its own database id resolves only within the map already restricted to the caller's scope — a foreign-organization id returns exactly the same "not found" a genuinely nonexistent id would.

When the database cannot confirm the scoping columns exist, agent and key lookups return nothing rather than falling back to an unscoped query — the conversation continues without a custom agent or a configured key, which is an acceptable degradation; serving someone else's is not.

Selection between multiple eligible keys or agents is deterministic — the caller's own specific-tenant resource wins over an organization-wide one, and ties resolve by id — so the same request never nondeterministically picks a different resource on a different call.

A key that was never yours, chosen with total confidence (illustrative framing of the actual measured finding)

A deployment serving one organization's customers receives a real inbound message and needs a bring-your-own LLM key to answer it. Before the fix, the selection function asks the database for "the active key," gets back a single row — because, in this specific measured case, only one active key existed in the entire table — and uses it without ever confirming which organization it belongs to. The message is answered, successfully, using and billing a completely different organization's connected credential. After the fix, the same selection first narrows the query to the caller's own organization and tenant; finding no key that belongs to it, the correct response is to report that no key is configured — not to reach across the table for whichever key happens to be there.

What changes operationally

Centriu Synapse's runtime can no longer select a custom AI agent or a bring-your-own LLM key belonging to an organization other than the one an incoming message is actually being answered for — every selection requires and verifies an explicit tenant scope, at the query, the cache, and the row level.

When this is not the right fit

This automation governs how Centriu Synapse's own runtime selects which internal resource (agent, key) applies to a message it is already processing — it does not change how a customer connects or manages their own keys through the product's own interface, and it is distinct from Centriu's separate internal admin panel for API-key administration, which is its own, separately secured surface.

Trusting deployment topology vs. checking scope in the query itself

Relying on "one process per organization" to keep data separated is not unreasonable as a starting architecture — it is simple, and for a long time it is even correct in practice. The cost only shows up later, and it shows up as a query that has no idea it is supposed to be scoped, which means it also has no way to fail safely when the topology assumption stops holding. Making the scope an explicit, required parameter at every read — rather than an emergent property of how the system happens to be deployed — means the correctness of the boundary no longer depends on an operational fact nobody wrote down.

Related systems

Main system: Centriu Synapse.

What it does NOT do

  • Does not change how a customer connects, views, or manages their own bring-your-own LLM key through Centriu Synapse's own product interface — this fix governs the internal runtime selection logic only.
  • Does not overlap with Centriu's separate internal admin panel for API-key administration (list, create, revoke) — that is a distinct code path with its own, separately documented organization-resolution fix.
  • Does not attempt to retroactively identify which specific past messages, if any, were answered using a cross-organization key before this fix — a team with that concern should review the specific deployment's own message logs for the affected window.
  • Does not add a general person-to-organization permission model — this fix is about which organization's DATA a runtime selection may return, not about which individual person is allowed to ask.
  • Does not require any change to how an organization configures its own agents or keys — the fix is entirely in how the runtime reads and scopes that already-existing configuration.

Security and governance

Centriu Synapse's runtime requires an explicit, verified tenant scope for every custom-agent and bring-your-own LLM key selection — re-checked at the row level after the database query, cached per scope, and fail-closed whenever scope cannot be confirmed. Any personal data referenced remains subject to Brazil's LGPD (Law No. 13,709/2018). Full detail on access control lives at /governanca and /iso.

Pricing and contracting

Available by monthly subscription, with tiered plans. Values and terms come from the official pricing table at /precos (Centriu's central source — never restated here).

Frequently asked questions

Was a real cross-organization key selection actually measured, or is this theoretical?

Measured directly against a real deployment: the organization it served had exactly one active bring-your-own key in the whole table, and that key belonged to a different organization — the next real message would have used it.

Does the fix depend on a specific database migration being applied first?

The scoping logic requires the organization/tenant columns to exist; where the database cannot confirm they do, every affected lookup fails closed — returning nothing — rather than falling back to an unscoped read.

How is a key or agent belonging to another organization handled now?

It is never returned. A lookup by its own id resolves only within a map already restricted to the caller's scope, so a foreign-organization id produces the identical result a nonexistent id would.

Is this the same fix as Centriu's admin API-key panel organization resolution?

No — that is a separate, administrative CRUD surface (listing, creating, and revoking keys). This fix is the runtime path used while actually answering a real customer message.

How is the fix proven, not just described?

19 dedicated tests in the module's own organization-scoping test suite naming the exact scenarios (legacy unscoped rows, org-wide callers, cache isolation, revoked keys, foreign-organization ids), plus 7 independently applied mutations to the scoping check itself, all caught.

What does Centriu Synapse cost?

It is sold by subscription with a published starting price — exact current values are on the central pricing page.

See how Centriu Synapse scopes agent and key selection by organization

Reach our commercial team directly, or leave your details below — we'll follow up with guidance for your case.

Sources

  1. Centriu Synapse — public product page — Centriu, 2026-07-20 · link(primária)
  2. Centriu Synapse — public factsheet (API, JSON) — Centriu, 2026-07-21 · link
  3. Law No. 13,709/2018 — Brazil’s General Data Protection Law (LGPD) — Presidência da República (Brazil), 2018-08-14 · link

Last material update on .

By · AI-assisted production, with human review