Where Sonela runs
One rung is the page your users are already on. The other two are the choice you make
about your database, and they carry different trust boundaries — both are described
here rather than only the flattering one.
In the page
One script tag and a widget key — the whole change to your application. The page
holds nothing secret: the widget key is public by design, and the tenant identity
is bound server-side from a signed token the browser cannot mint. Editing the
page's JavaScript reaches no other tenant's rows, and no key of yours. An Origin
header is a courtesy, never identity: sessions held to signed-in users open only
on a signed voucher outsiders cannot forge — ours do, and yours can. The whole
mechanism is set out under tenant identity verification below. The person on the
page sees the guarantees too: a shield in the assistant's header opens them in
plain words, and when page capture is on, the person being read is told.
In your network — the Sonela Gateway
One small service, on one server of yours, next to the data it reads — never on a
staff machine. It dials out to us over port 443, so you open no inbound port and
expose nothing to the internet. What it needs to read your data is configured on
your side and stays there — run this way there is nothing for us to store, and a
row holding null cannot leak. We keep a hash of the gateway key,
and both validation and execution run on your side of the firewall.
On our infrastructure — a direct connection
The setup with nothing to install, and it costs you a trust decision the gateway
does not. You give us what your database expects at sign-in, and our servers open
the connection themselves. Those details sit on our infrastructure. We hold them
encrypted with AES-256-GCM, bound to the one workspace and data source they belong
to, and no API we expose reads them back — but encryption is what we do about that
exposure, not a reason there isn't one, and the exposure is the thing to weigh. We
refuse a target that points at our own database, and we test the connection before
accepting it.
- The three layers below all still apply — on this path they run on our infrastructure rather than yours.
- The hop to your database is secured by what you configure on it; we do not impose that setting, so set it if your review needs it.
- There is no fixed outbound address to put in your firewall today. We do not offer one and will not name one. If your review needs an allowlisted source, choose the gateway.
Read-only, enforced three times
Three independent layers: all of them have to fail before the assistant can change
anything, and each is stated per engine below rather than borrowed across engines.
Behind the gateway they run inside your network; on a direct connection they run on
ours. Which machine they run on changes; that they run does not.
1
An allowlist grammar, before the query runs
Model-written SQL is refused unless every part of it is explicitly permitted —
syntax nobody anticipated is denied by default.
- Must begin with
SELECT. Any semicolon is refused, so the input stays one statement.
- Every keyword and function must be on the allowlist, per engine, and each engine's system catalogue is unreachable —
pg_*, information_schema and dblink on PostgreSQL; xp_*/sp_* procedures, OPENROWSET, hints, WAITFOR and the sys* views on SQL Server, whose lexer refuses @ and [ outright; LOAD_FILE, INTO OUTFILE and the query-modifier keywords on MySQL, whose lexer refuses @, backticks and # comments outright; packages such as DBMS_* and UTL_*, sequences and the V$ views on Oracle, where a name that is no column of a table the query reads is refused too, because Oracle would run a function of that name.
- Schema-qualified names, comma-separated
FROM lists and parenthesised join groups are refused — each one evades the rewrite.
2
The engine's own guard, while it runs
Every question runs under a statement timeout of three seconds and a cap of 500
rows, so the timeout bounds what any one question can cost you. On PostgreSQL and
MySQL it also runs inside a READ ONLY transaction: the database
session itself refuses a write, whatever reached it. SQL Server offers no such
session mode, so there the guarantee is the two layers beside this one — the only
statement of the model's that can reach the server is a single validated
SELECT, and every session ends in a rollback. Oracle runs it inside a
READ ONLY transaction too, which refuses a write the statement itself
makes; it does not bind a function that commits on its own, so the rung before this
one refuses every call but an allowlisted built-in, and every name that is not a column the query reads.
3
A discard, not a commit
The transaction is thrown away rather than committed: the success path rolls it
back and every failure path unwinds it, and there is no commit in the query code
for either to reach. A write that somehow executed would not survive the request.
Tenant isolation by rewrite, not by prompt
Every table reference is replaced with a subquery projecting only the columns you
approved and filtering on the tenant column you declared. Isolation never depends on
the model behaving, and
a table absent from your manifest is rejected. The tenant value is a bound parameter,
server-bound from a signed session: the browser cannot assert it, the model never
sees it. If the database holds one organisation's data and you told us so, there is no
tenant column and no such filter — you are asked which it is, and nothing is read from
your schema until you answer.
What the model wrote
SELECT "Status", count(*) FROM "Orders" GROUP BY "Status"
What actually ran (PostgreSQL shown; on SQL Server and MySQL an integer flag compares with = 0)
SELECT "Status", count(*) FROM (SELECT "Id", "PlacedOn", "Status", "Total" FROM "Orders" WHERE "TenantId" = @__ai_tenant AND "IsDeleted" = false) AS "Orders" GROUP BY "Status"
Tenant identity verification — signed embeds
A widget key is public by design, so it can open a session but never prove who is
asking. Proof is a voucher your own server signs with a secret the page never holds,
and you climb to it in three states: with no secret the widget is open to the page it
is on; generating the secret arms the workspace and changes nothing a
visitor sees; enforcement is a separate switch you flip yourself,
after which an unsigned page is refused a new session. Turning enforcement on with no secret is
refused too — there would be nothing to verify against.
Two shapes your server can sign
Both are HMAC-SHA256 under your workspace's own signing secret. There is
deliberately no issuer check: the secret is the trust root, and a signature that
verifies is itself the workspace binding.
- A JWT. HMAC-SHA256 is the only algorithm accepted, the audience is pinned, and an expiry is required: a token whose expiry sits more than eight hours out is refused whole, and clocks are allowed thirty seconds of skew.
- A raw triple — user id, timestamp, signature — for a backend without a JWT library. The signature is HMAC-SHA256 over the timestamp and the user id joined by a colon; the timestamp is bare unix seconds, digits only; the signature is hex that must decode to exactly 32 bytes. Freshness is eight hours behind and thirty seconds ahead, and the comparison is fixed-time.
Verification always runs
A signature that arrives is verified whether or not the workspace demands one, and
a failure is refused — a bad voucher is evidence of spoofing, never noise: it is
written to your workspace's security feed, and five in 24 hours raise a probing
warning in your dashboard. A valid
one earns the vouched session even before you enforce; only the total absence of a
signature consults your enforcement switch.
- A partial triple — one or two of its three fields — is refused outright.
- If both shapes arrive, both must verify.
- A voucher sent to a workspace that has no signing secret is refused, not waved through.
- The refusal never says which check failed.
The secret, and rotating it
sk_ plus 256 bits from a cryptographic generator, shown exactly once,
at generation; at rest we hold it encrypted with AES-256-GCM, bound to its one
workspace. Rotating keeps the old secret verifying for 24 hours,
so your servers can switch to the new one without an outage. If the old one
leaked, end that window early from your dashboard.
What a voucher proves — and what it does not
It proves who your application's server says is asking. It is identity
attestation, not transport secrecy: the voucher travels in the page, any script in
that page can read it, and there is no replay cache — so until it expires it can
be replayed by whoever captured it. The window is the expiry your signer chose,
up to the eight-hour ceiling, and it is the whole blast radius, so sign short.
- An allowed-origins list is a browser courtesy: it stops other websites, not other programs. The voucher is the one check a script cannot forge, because the secret never reaches the page.
- Until you enforce, an unvouched session still answers — rate-limited to a per-visitor-address slice (IPv6 grouped by /64 subnet) of the workspace's own request rate, so anonymous traffic divides your capacity and never widens it. A vouched session is served at the workspace's full rate, with no per-address slice.
What the assistant sees of the page someone is on
A picture of the screen, taken only while someone keeps the camera pressed, and the data
the page already fetched for the person looking at it. Both travel only with a question
that person sends, on the signed-in assistant and never the public one.
A screenshot, only while someone keeps the camera pressed
One press turns the camera on, and each question then carries a picture of what the
window shows at that moment, at the screen's own sharpness; the whole page only when
chosen for one question. A second press turns it off. Nothing automatic, scheduled or
on open, and our own widget is cut out of the picture.
- Beside it goes the text drawn on that screen — headings, labels, table rows, the selected tab — never what was typed into a field, and masked as the question is where masking is on. The picture passes as taken.
- Each picture rides its own question and never joins the history; its text returns with later questions only sealed in the person's own page, for four hours at most. Never logged, written to our disks or cached, and our metering has no field either could ride in.
- Checked before use: PNG, JPEG or WebP, two megabytes at most before and after decoding; the text is bounded, and refused without its picture.
What the page's own data reader does — and where it stops
The bundle holds, in memory for the life of the page and never in browser storage,
what your application already delivered to its user's browser: at most eight
responses, half a megabyte. It leaves the browser only with that person's question, to
answer it. A count over it is "of the records on this page", and a list with no total
of its own is never read as the whole system.
- What it can see, both ways. Through the page, every field that person’s browser received, including fields nobody reviewed, with no approval step and only the stripping below. Through the Gateway or a direct connection, only the tables and columns you approved, confined to the asking organisation.
- Before anything is held, obvious secrets are stripped: values under names like password, token, secret, api_key, card or ssn, and values shaped like a signed token or a random key; depth, string and array length are capped, and only query parameter names are kept. Masked as a lookup's values where masking is on; your switch off holds on our side too.
- That stripping is a heuristic and we call it one. It matches names and shapes, so it will not recognise personal data sitting under an ordinary business name — a notes field, a delivery address, a date of birth. It is deliberately biased towards removing too much. It is not a guarantee, and a review should not treat it as one.
Two switches over what the assistant sees, and what off actually does
Both start on, because the assistant is more useful with them; both are yours to turn
off, for a whole workspace rather than one embed.
| Switch |
Default |
What turning it off does |
| Reading the page's own data |
On |
On a production embed the reader is never installed, so nothing is captured at
all. The setting is held by us and read from us, so a page cannot quietly
restore it while the assistant runs normally.
|
| Page screenshots |
On |
Refused at the API, whatever the page does: a question carrying an image to a
workspace with the camera off is answered with 400 and the picture reaches no
model. On the page itself the composer offers no camera once the setting
arrives, and the code that takes a picture is never loaded. One limit worth a
reviewer's time: a load whose setting never arrived still shows the button, so
a visitor there can attach a picture and be refused rather than see it greyed
out.
|
Personal data masked before the model reads it
Before the AI model that writes an answer — yours under your key, or ours — reads
anything, Sonela swaps the personal values it recognises for numbered placeholders
such as [PERSON_1]: in the question and the conversation so far, the rows a query
returns, your own systems' replies and, on every assistant but a public one, your
manuals and documents. The model answers with them, and Sonela puts the real values
back before the person asking reads the answer.
Always masked, for every workspace: card numbers, IBANs, passwords,
keys and tokens, kept out of an answer's words, which say one was removed; and ID,
tax, passport and bank account numbers. Your switches: names,
contact details (an email before the @, phones), addresses and company names, and
whether people asking see real values. New workspaces mask names and contact
details; those made before 6 October 2026 leave names in clear until switched on.
To look a record up, Sonela puts the real value in the lookup, or in a query only
where a condition compares it with an approved column. The pairings live in memory
for one answer; a follow-up is masked again from a sealed note the conversation
carries for a day, which only Sonela can open. Masking keeps counts, never values.
Where it stops
It reads format, checksum, column, title or label: a heuristic, not a guarantee. A
name or ID number in prose with none of those reaches the model unchanged, as
does a screenshot; a recording reaches our speech-to-text provider as recorded. Your
business context, schema notes and templates go as written, as do a public
assistant's pages, and so does a value typed that one of them already shows; your
key's embedding provider, yours or ours, indexes your manuals as written. With real
values shown, tables are your rows as returned. Sonela can
pause masking for everyone in an emergency, and our hosting platform's monitoring
can record a lookup's address, value included.
What Sonela can see — and what it cannot
Sonela is a data processor: your content transits our service to an AI provider —
yours under your key and DPA, or one of ours under our agreement and model choice.
| Data |
What actually happens |
| The question, the rows that answer it, and the page it is asked on |
In transit Reach Sonela and one AI
provider — yours under your DPA, or ours — with the personal values masking
recognises as placeholders, and a screenshot, if one is sent, as it was taken.
Held in memory for one request, never logged, written to our disks or cached;
only the screen's text returns with later questions, sealed in the person's page.
|
| Stored questions, answers or SQL |
Never A metering row has counts, latency,
tokens, provider and model — and no column that can hold a question, an answer
or a query. It records which of your tenants asked, for billing. A question's
words reach our storage in three cases only: a test question you save yourself
to check your assistant, kept in your workspace with your written material,
and the next two rows.
|
| An answer someone reports |
Stored Someone who reports an answer
with the thumbs-down beside it is offered a ticked box that sends that one
question and that one answer to you, so you can put it right; untick it and
the report arrives with no words at all. Only your workspace reads them — we
count reports and never see the text. Deleted 100 days after the report, and
you can switch reporting off.
|
| A conversation handed to a person |
Stored Where you switch it on, a
visitor who asks for a person can carry on in the same chat with your people,
and is told what is kept before they choose to. The conversation so far, each
answer in it checked to be one the assistant gave, and what either side writes
after it are kept for your workspace alone — we never see the text — and
deleted 30 days after the conversation ends. Your people are emailed that
someone is waiting, with none of its words.
|
| Written material you upload |
Stored Procedures and notes you give
the assistant are kept in your workspace and sent to that same provider with
the question. Workspace-scoped; deleting them removes them. Google Docs, Word
and Excel files you sync from Google Drive are kept the same way, read with a
service-account key that is
encrypted at rest and never returned; a file you delete or stop sharing in
Google Drive is removed at the next hourly sync that reads everything you
shared, and any file Google has not confirmed for seven days is removed even
if no sync could reach it. Word and Excel files you sync from SharePoint or
OneDrive for work or school are kept the same way, read through your own
Microsoft app with a certificate whose private key is encrypted at rest and
never returned; a file you delete in SharePoint, or that your app may no
longer read, is removed at the next hourly sync that reads everything you
added, and any file SharePoint has not confirmed for seven days is removed
even if no sync could reach it. Posts from public Facebook pages and Instagram
profiles you add are kept the same way, the latest ten from each: a scraping
provider reads the page for us and is given its address and nothing else of
yours. A post you delete from the page is removed within a week, at the
page's next full reading, and a page no full reading has confirmed for
fourteen days loses its posts.
Products from a product feed you add are kept the same way: each product’s
name, description, price, availability, link, picture address, brand,
product codes, categories and specifications, replaced whole at each
reading and deleted with the feed or the workspace. Pictures load from
your shop’s own address and are never copied, and the address of the page
a visitor has open is used for that one question to find its product and to tell the assistant which page it is asked from, by its path and title and never its query string, then dropped.
|
| Transcripts |
Never In the assistant's panel, the
transcript lives in the visitor's browser. We keep no conversation unless a
visitor hands it to your people; a reported answer is one exchange out of
one, and nothing either side of it. A visitor may email themselves the
conversation from the panel; it leaves through our mail provider once,
carrying only lines the assistant actually said, and we keep no copy.
|
| Remembered answers |
Stored An answer may be kept for up to
seven days and shown again for the same question, keyed by a hash of the
question, never its words. Any change to your manuals, context or rules
retires it, and you can switch it off or empty it from the dashboard. An
answer grounded in query results or a lookup is never cached.
|
| How your data is reached |
Your side The gateway you run reaches
it from inside your network, with a value configured and held there.
Or ours On a direct connection we hold
your database sign-in details, encrypted, and reach it ourselves.
|
| Your AI provider key |
Encrypted Yours: AES-256-GCM,
row-bound, never returned.
Never On our key: none of yours held.
|
| Write access to your data |
Never Three layers refuse it, and the
last of them discards the transaction rather than committing it.
|
| Your data used to train a model |
Never We train nothing, and your rows
reach the provider that answers and no further.
|
What is written down instead: two append-only feeds in your dashboard that nothing
can edit or delete. One records every refusal at your workspace's doors — a forged
signature, a blocked origin, a query outside the allowlist — as a kind and a time,
never the text; the other, every switch an admin flips, by whom and when.
Ask us for the threat model
We will send the real one: what we assume, what we deliberately do not defend
against, and where the boundaries sit. We hold no security certifications today and
would rather say so — what we offer instead is specificity you can check against
the code.