You assess suppliers for a living, so this page is written the way you
would want one written: the actual parameters, the threat model, the things it does
not protect against, and how to verify every claim yourself without taking our word
for any of it. Veld can be run three ways, and the honest answer differs between them — so
each is set out separately rather than averaged into one comfortable sentence.
Two of the six sections on this page exist to tell you where the design stops.
The short version
Three ways to run it. None gives us your key.
You choose where the workspace lives. That choice changes our regulatory position, so it
is stated here first rather than buried.
No account, no server, nothing transmitted. The portal is a static page
served with connect-src 'none' — the browser itself refuses to let it open a
connection. On this setting we are not a processor of your clients' data, because we
receive none of it.
Role under UK GDPR
Neither processor nor controller. We receive none of it.
Article 28 contract
Not applicable — no processing on your behalf.
Data location
Your device, and nowhere else.
What we hold
Only the contact form and any email you send us.
Sign in and the encrypted workspace is stored by us so any browser on
any device can open it. We hold the ciphertext and cannot read it — the key is
derived from your password, which never leaves your machine. On this setting we are
a processor, with an Article 28 contract.
Role under UK GDPR
Processor, of encrypted data we cannot read.
Article 28 contract
Required. We provide one.
Data location
Your device, plus our UK/EU region.
What we hold
Your email, sign-in times and the encrypted envelope.
An AI assistant you switch on and pay for. Your records stay where they
are — the tools that read them run in your browser, and only a minimised,
pseudonymous answer leaves. A model provider will become a sub-processor, named in the DPA
before it is switched on. Leave it off and nothing changes.
Role under UK GDPR
Processor. Transmission and consultation are processing, even of minimised data.
Article 28 contract
Required, and covers the model provider as a sub-processor.
Data location
Assistant requests are processed in a UK/EU region and not retained.
What we hold
Token counts with timestamps, and which model answered. No content.
The encryption does not change between them. Same AES-GCM 256 envelope, same key
derivation, same slot design on all three. Syncing changes where the sealed envelope is kept
and the assistant changes what a question can reach — neither changes how anything is sealed,
and a workspace moves between the settings freely.
Which one are you on? Nothing is synced unless you sign in — a workspace opened at
/portal.html stays on this device and makes no requests at all. The assistant is
off until you turn it on, and it is not available at all without an account. If you already
have a workspace here and want it synced, the account page offers to move it, and leaves the
copy on this device exactly where it is. Moving a synced workspace back to device-only is not
built yet; export and re-import does it in the meantime.
01
How the encryption actually works
The workspace is encrypted with a random 256-bit data key. That data key is never
derived from your passcode directly — it is generated once, at random, and then wrapped
(encrypted) separately by each thing you use to unlock: your passcode, and each passkey you
add. Each of those is a slot.
This is why adding a passkey does not re-encrypt your workspace, why you can hold several
passkeys at once, and why removing one does not lock out the others.
Component
What is used
Workspace cipher
AES-GCM, 256-bit, fresh 96-bit IV per write
Data key
256 bits from the platform CSPRNG
Passcode slot
PBKDF2-HMAC-SHA-256, 250,000 iterations, 128-bit random salt
SHA-256 hash chain, each entry committing to the previous
Storage
Browser local storage, as an encrypted envelope — and, if you enable syncing, the identical envelope held by us
Cryptography
Web Crypto (crypto.subtle) — the browser's own implementation, no third-party crypto library
All of it runs through the browser's native Web Crypto implementation. We ship no
cryptographic code of our own, which is deliberate: hand-rolled crypto is how this goes wrong.
If you turn syncing on
Your password does two jobs, derived separately so that neither can be worked back into
the other. This is the part worth checking, because it is what makes "we cannot read it" a
structural claim rather than a promise.
Derived
How
Where it goes
Master key
PBKDF2-HMAC-SHA-256, 600,000 iterations
Never leaves your device
Auth verifier
HKDF-SHA-256 of the master key
Sent to us, stored only as a slow hash
Wrapping key
HKDF-SHA-256 of the master key, different context
Never transmitted, ever
Your password
PBKDF2-HMAC-SHA-256 · 600,000
Master keynever leaves your device
HKDF-SHA-256, two different contexts
Auth verifiersent to us, stored only as a slow hashWrapping keynever transmitted, ever
Because both are one-way derivations under different context strings, the verifier we
hold reveals nothing about the wrapping key. A total breach of our infrastructure would
expose verifier hashes and sealed envelopes, and neither decrypts anything.
What we can see on a synced account. Your email address, when you signed in, the
size of your encrypted workspace and how often it changes. Envelope size is a rough proxy
for how much work you are carrying. We would rather write that down than let you discover
it.
02
Passkeys without a login
A passkey normally proves who you are to a server. Veld never authenticates you to
anything — even on a synced account it is your password that does that, and the passkey is
doing something else entirely. It uses the other capability WebAuthn offers: the PRF extension, which asks the
authenticator to produce a stable high-entropy secret for a fixed salt.
That secret never leaves the authenticator, is not stored anywhere, and cannot be produced
without the device plus a user gesture — Face ID, Windows Hello, or a PIN. HKDF turns it into
a wrapping key for one more slot.
Add several — a laptop, a phone, a hardware key — each works independently.
A passkey is bound to the origin it was created on, so one made on
veldprivacy.co.uk will not open a copy of the portal hosted elsewhere.
The passcode slot cannot be removed. It is the recovery route if every device is
lost, which is also why a passkey does not let you forget it.
Older browsers and authenticators without PRF support simply do not get the option. The
portal works regardless.
03
The assistant, and what it is shown
Veld has an optional AI assistant. It is off unless you turn it on, it is part of a paid
plan, and it is the one feature where something leaves your device to a company that is not
us. So this section is longer than it would be if we were only selling it to you.
What the assistant does is a separate page; this one is about
what it is shown.
Not switched on yet, and this page is here first on purpose. The assistant is built,
no model provider is engaged, and nobody can use it today. We are publishing how it works
before it ships rather than after, so you can assess it while your answer can still change
ours. Where this section describes contractual terms, those are requirements we have
committed to meet before launch — six of them, each pass or fail — and not terms
already in force. When one is signed, the provider is named here and in the
privacy notice, and this box goes.
The workspace does not go to the model
The usual way to build this is to put your records in a database the model can query. We
cannot: your records are on your machine, sealed with a key we do not hold. That constraint
turned out to be the useful part of the design.
Instead the model is given a set of tools it may ask for by name — "what is
overdue", "what is the compliance score". Each tool runs in your browser, against
your open workspace, and returns a deliberately narrow answer. The model sees the answer. It
never sees the workspace, and there is no server-side copy for it to reach, because there is
no server-side copy.
Stays on your device
Goes to us, in transit only
Reaches the model provider
The workspace and every record in it
Your question
Your question
Client and data subject names
The minimised tool result
The minimised tool result
Notes, correspondence, documents
Token counts, for billing
—
Your encryption key
—
—
What a "minimised tool result" actually contains
This is the part to check rather than take on trust. Ask "what is overdue" and the tool
does not send the request — it sends this:
A. Okafor — Harborview Housing Association
Subject access · due 14 July 2026 · 12 days over
No subject name. No client name. No notes, no free text, nothing about what the request
is for. ref and clientRef are your workspace's own internal
identifiers — random strings that mean nothing outside your browser. The model reasons about
"request 7f3a91c4"; your screen shows "A. Okafor — Harborview Housing Association", because
the substitution happens back on your device after the answer arrives.
Each tool declares in code exactly which fields it may emit, and our gateway independently
rejects any result carrying a field outside that list. A mistake in that code fails your
request rather than quietly sending more than it should.
The model cannot change anything
Every tool is read-only. Not "we instruct it not to write" — there is no tool that writes,
so there is nothing to instruct. Anything the assistant suggests is a suggestion you act on
yourself.
This is also the honest answer to prompt injection. A document in a disclosure bundle
could contain text addressed to the model rather than to you, and no one has a complete
defence against that. What contains it here is that a successful injection can mislead a
human, and cannot act.
What we keep from an assistant session
Nothing, other than counts. Our gateway holds no conversation between one turn and the
next — the transcript travels with each request and lives in your browser. We write no
prompts, no answers and no tool results to any log, trace or error report; what we record is
an account id, token counts, and which model answered.
Content-free logging is a claim with a test behind it. Our logging function takes a
fixed list of permitted fields and silently drops everything else, including error objects
— which is where request content usually escapes. There is a test that hands it a
question, a transcript and a tool result and asserts none of them appear in the output. It
runs with every other test we have.
You set how much the scan flags
The document scan that finds personal data in a bundle runs entirely on your device and
involves no model at all. You choose how much guesswork it shows you, in Settings: everything
including weak guesses, or only the stronger matches. More candidates means more reviewing;
fewer means a greater chance something is missed. That is a judgement about your practice and
it is yours to make.
One part is not adjustable. An identifier that passes its own check digit — an NHS number
against modulus-11, a National Insurance number against the allocation rules, a card number
against Luhn — is not a guess, and no setting hides one. A control that let you configure
your way out of seeing an NHS number in a disclosure bundle would only ever do harm.
04
What this does not protect against
Any security page that only lists strengths is marketing. Here is the other half.
We cannot recover your data. Not on either setting. The key is derived from your
passcode and we never receive it. On a synced account you can reset your password
by email — that gets you back into the account, and it decrypts nothing. Only your
recovery key or a registered passkey can reopen the workspace itself. Lose the passcode,
the recovery key and every passkey, and the records are permanently unreadable by anyone
including us.
A compromised device compromises the workspace. Malware with access to your
browser profile, a malicious extension, or anyone who sits down at your unlocked machine is
inside the trust boundary. Local-first moves the risk to your endpoint; it does not remove it.
Clearing site data destroys an on-device workspace. "Clear browsing data", a
profile reset, or an over-enthusiastic cleanup tool will take it with no warning and no
undo. On a synced account it removes only the local cache — sign in again and the
workspace comes back.
Losing the machine loses an on-device workspace unless you exported a backup.
This is the single most likely way to lose your records, and it is the main reason
syncing exists.
Syncing means we hold a copy. Encrypted, unreadable by us, but held. If your
risk appetite says the records must never sit on a third party's infrastructure in any
form, stay on the on-device setting — it is not a lesser tier and never will be.
Concurrent editing on two machines is not supported. If the same workspace is
changed in two places between syncs, Veld detects it and asks you which version to keep.
It will not merge them, and it will not guess.
The evidence log is tamper-evident, not tamper-proof. The hash chain detects
casual editing and accidental corruption. It cannot stop someone with full control of the
device from recomputing the whole chain — genuine immutability needs a server-side vault,
which by definition this design does not have.
The document scan is not exhaustive. It is a reviewer's assistant. A clean scan
means nothing matched its rules, never that a bundle contains no personal data. Raising the
sensitivity setting in Settings makes that gap wider, which is why the safest setting is
the default.
The assistant can be confidently wrong. It is told to answer only from what the
tools return and to say so when it cannot, and every answer cites the records it used so
you can check them. None of that makes it reliable enough to act on unchecked. Treat it as
a colleague who is fast, well-read and occasionally mistaken — the citation is there to be
followed, not admired.
Using the assistant will mean a third party processes a request about your work.
Pseudonymous, minimised, and to be covered by a contract requiring zero retention — but a
request all the same. If your risk appetite says nothing about a client's matter may reach a model
provider under any conditions, leave the assistant off. Everything else in Veld works
without it, and always will.
Prompt injection has no complete defence. Text in a document you upload can be
written to address the model rather than you. We deliver document text as data rather than
as instructions, and the model has no tool that writes — so the realistic worst case is
that it tells you something wrong, not that it does something wrong. That is containment,
not immunity.
We can see the rhythm of your work. Assistant usage is metered for billing, so
we hold token counts with timestamps per account. That reveals when you are busy and
roughly how much, in the same way envelope size does. It reveals nothing about what you
asked or what came back.
Invite codes are not enforcement. They are signed and verified offline, which
stops casual sharing and lets us hand someone a workspace cleanly. The check runs in your
browser, so anyone willing to edit the JavaScript can bypass it. A lock on a door, not a vault.
Browser storage is not infinite. Very large disclosure bundles belong in your
own document management system, with Veld holding the record of the decisions about them.
If your risk appetite requires central custody, escrowed recovery and
server-enforced immutability, Veld's architecture is the wrong shape for you, and we would
rather say so on this page than three months into a deployment.
05
Verify it yourself, in about five minutes
Every claim above is checkable from your own browser. Please check it.
Watch the network, on-device. Open the portal without signing in, press F12, go
to the Network tab and use the product — add a client, log a request, generate a document.
After the initial page and assets load you will see no further requests at all. Nothing is
sent because nothing can be.
Watch the network, synced. Sign in and do the same. You will see requests, and
you should open one and read the body. It is base64 ciphertext. Search it for a
client name you just typed — it is not there, and cannot be.
Read the policy header. Inspect the response headers for portal.html.
On the on-device build the Content-Security-Policy sets connect-src 'none',
which instructs the browser to block every fetch, XHR and WebSocket the page might attempt.
With syncing available it reads connect-src 'self' — the page may talk to us
and to nobody else, still enforced by the browser rather than promised by us.
Look at what is stored. Application → Local Storage. The workspace is there as
an encrypted envelope. Lock the workspace and try to read it — it is ciphertext.
Read the source. The portal is unminified, unbundled JavaScript with the
reasoning written into the comments. assets/store.js holds the encryption,
assets/passkey.js the PRF unlock. No build step stands between what we wrote
and what you run.
Watch what the assistant sends. With the assistant on, open the Network tab and
ask it something about your caseload. Open the request to /api/ai/tool-result
and read the body — it is not encrypted, because it has to be readable by the model, which
is exactly why it is worth reading. Search it for a client name, a data subject's name, or
anything you typed into a notes field. None of it is there. What is there is a list of
record references, dates and day counts.
Pull the plug. Load the portal, go offline, and keep working. Everything
continues, because there was never anything on the other end of the connection. The
assistant is the one part that stops, and it says so rather than answering from memory.
06
For your supplier assessment
The answers you will need, in the form the form asks for them.
Question
On this device
Synced
Assistant on
Role under UK GDPR
Neither processor nor controller. We receive none of it.
Processor, of encrypted data we cannot read.
Processor. Transmission and consultation are processing, even of minimised data.
Article 28 contract
Not applicable — no processing on your behalf.
Required. We provide one.
Required, and covers the model provider as a sub-processor.
Data location
Your device, and nowhere else.
Your device, plus our UK/EU region.
The above. Assistant requests are processed in a UK/EU region and not retained.
International transfers
None.
None — storage stays in region.
None intended. Region pinning is a condition we require of a provider before launch, not a setting we can change later.
Sub-processors
Netlify, static hosting and the contact form only.
Netlify, plus our hosting and database provider. Named in the DPA.
The above, plus one model provider once engaged. To be named in the DPA before launch, with a change notified to you in advance.
Encryption at rest
AES-GCM 256, key derived on your device from your passcode, never transmitted. Identical on every setting — the assistant does not change how anything is stored.
Encryption in transit
Workspace data is never in transit.
HTTPS with HSTS. The payload is already ciphertext before it is sent.
HTTPS with HSTS. Assistant traffic is not ciphertext — it is minimised, pseudonymous data the model has to be able to read.
Access controls at supplier
No store to access.
Staff can reach ciphertext and account metadata. No one can decrypt it — we hold no key.
The above. Assistant content is not stored, so there is nothing for staff to reach after a request completes.
Breach notification
We hold no client personal data.
A breach exposes ciphertext and verifier hashes. We would tell you, and it would not expose readable client data.
A breach of the gateway exposes requests in flight, not a corpus — nothing is retained to breach. We would tell you.
Retention & deletion
Delete it and it is gone; we hold no copy.
Delete your account and the envelope is deleted with it.
Nothing to delete: no prompts, answers or tool results are stored. Usage counts go with the account.
Data portability
Full export in open JSON, plus PDF, Word and Excel, on every plan.
What we do hold
Only the contact form and any email you send us.
The above, plus your email, sign-in times and the encrypted envelope. See the privacy notice.
The above, plus token counts with timestamps, and which model answered. No content. See the privacy notice.
One caveat, stated plainly. The contact form on this site does post to our host, and
email you send us lands in an ordinary mailbox. That is us processing your data as
a prospect or customer — never your clients'. The portal remains entirely separate and
entirely silent.
Questions this page didn’t answer?
Send them. Technical questions get technical answers from the person who wrote the code.