Your customers’ calls
never share a lock
with anyone else’s.
Most platforms keep their customers apart with a filter in application code. One missing clause and the wall is gone. We spent ten months making that class of mistake impossible to make — by moving the boundary out of our code and into the infrastructure underneath it.
Six gates, six different systems.
Follow one ordinary API call — a receptionist opening today's bookings. Each gate below is enforced by a separate part of the stack. Passing one earns you nothing at the next.
Device binding
Is this session still on the hardware it was issued to?
The browser never holds a readable session token — only an opaque handle, kept in a cookie script cannot reach. On top of that, where the platform supports it, the session is additionally bound to a key generated inside the device and never exported: the Secure Enclave on Apple platforms, and Device Bound Session Credentials on Chrome. A bound session cannot be renewed off the device that created it. Browsers that do not implement the standard yet keep the opaque-handle protection without the hardware binding.
A readable token scraped out of storage by script or an extension — and, on platforms with hardware binding, a session cookie lifted from one machine and replayed from another.
Clearing this check proves the caller is the device we issued to. It proves nothing about what that device is allowed to ask for — that is the next gate's job.
Edge authorization
Is this identity allowed to perform this action on this resource?
Before a request reaches any service, it is evaluated against a policy engine — AWS Verified Permissions, policies written in Cedar. The decision is made on the route, the action, the target workspace and the caller's role, and it is deny-by-default: an action with no policy granting it is refused rather than passed through.
A valid session reaching an endpoint its role was never granted, including one nobody remembered to guard.
Now we know the action is permitted in principle. Nothing yet constrains which workspace's data it will touch.
Scoped credential mint
What credential should this specific request run under?
The service does not hold a standing database key. It mints one per request: a short-lived role session, scoped to a single workspace, carrying the workspace, business and user identity as session tags that cannot be edited downstream. The session expires in minutes, and there is no long-lived administrative credential in the application path to fall back on.
One compromised service handing an attacker permanent, platform-wide database access.
The credential now names exactly one workspace. But a credential that merely names a workspace can still be pointed at the wrong one — unless the storage layer itself checks.
Storage-level workspace fence
Does the data being asked for belong to the workspace in the credential?
This is the part most platforms do in application code. We do it in the credential. The permissions attached to that per-request session restrict it to the requesting workspace's own partition of the database — so the check runs inside the storage service, on every read and every write, before our code is consulted. A query aimed at another workspace is refused by the database, not by an if-statement.
The classic shared-platform breach: a missing filter, a mistyped identifier, or an injected parameter returning someone else's rows.
Live data is now fenced. Recordings, transcripts and archives are a separate estate, and get their own boundary.
Per-business encryption boundary
Where does this call's recording physically live, and who can decrypt it?
Each business gets its own storage vault — a separate, isolated bucket, not a folder inside a shared one — and its own encryption key, created when the business is provisioned and used for nothing else. Media is encrypted under that key by default at the bucket, the bucket blocks all public access, the key's policy denies any principal carrying a different business's identity tag, and every use of the key is recorded in the cloud audit trail. Object versioning is on, so an overwrite or delete cannot quietly destroy evidence.
A path-traversal or misapplied prefix rule exposing a neighbouring business's audio, and a compromised service in one business decrypting another business's recordings.
Data is fenced at rest and in transit. One surface is left: the language model, which by design is told things.
Model and log redaction
How does the AI come to know anything about the caller?
By asking, never by being handed it. The prompt is assembled by one code path and carries the business's own configuration — the caller's identity is not in it, not in the call's token, and not broadcast over live dashboard sockets. When the agent needs caller context to actually do the job, it requests it through an authenticated tool call scoped to that workspace, and the caller is resolved from the live call record rather than from anything the model said. Logs record identifiers, durations and outcomes — not what was said.
Caller personal data leaking through the one component whose whole job is to repeat back what it was given.
Six independent checks, each enforced by a different system. Defeating one gets you to the next one — not to the data.
One workspace asking for another workspace's data.
This is the failure that ends shared platforms. Here it has to defeat three unrelated systems in sequence — and each one refuses for its own reason.
Nothing crosses. Not a partial read, not an empty result set that confirms a record exists — the request is refused before it resolves to data.
- 1
The policy engine never issues the decision
The caller's identity resolves to one workspace. A request naming a different one is not a permission the policy engine can grant, so no service is ever invoked.
- 2
The credential cannot express the request
Suppose it got through anyway. The credential minted for that request is tagged with the caller's own workspace. There is no parameter, header or body field that changes those tags — they are fixed at mint time and travel with the session.
- 3
The database refuses the read
Suppose the query is issued regardless. The storage service compares the requested partition against the credential's own scope and rejects it. Our application code is not consulted, so no bug in it can approve the read.
A separate vault per business.
Not a folder in a shared one.
Shared buckets with per-customer prefixes are the industry norm, and they fail the same way every time: one wrong prefix, one over-broad rule, and two customers are in the same directory. Every business on EarlyBird is provisioned its own isolated storage vault instead, with a name derived so that it discloses nothing about the business it belongs to.
- Own vault
- Recordings, transcripts and voicemail for one business only.
- Own key
- A key created for that business alone, whose policy denies any other business's identity.
- Own door
- All public access blocked, and versioning on so nothing can be quietly overwritten.
The agent knows your name. It had to ask for it.
Obviously the agent learns who is calling — it cannot book Sarah at 1042 Maple Drive without knowing it is Sarah at 1042 Maple Drive. What is controlled is where that comes from. None of it is planted in the agent's context when the call begins; it has to request it, through an authenticated tool bound to that one call.
Caller: Hi, this is Sarah Jenkins at 1042 Maple Drive, you can reach me on 555-0148 — I need someone out today.
- The business's own instructions, hours and services
- Which capabilities it may use on this call
- The current time in the business's timezone
None of it rides in the prompt, the call’s token or the live dashboard feed. When the agent needs any of it, it calls an authenticated tool — and that request is bound to this specific call, this workspace and this business. The caller it resolves comes from the live call record, not from anything the model said, so the agent cannot ask about someone who is not on the phone.
Three services, on purpose.
It would be cheaper to run the voice stack as one process. We run it as three, split along the line that matters: what is exposed to the internet, and what can reach your data. Only one component is both — and it is the one we hardened hardest.
Tool plane
The only component that is both reachable from outside and able to touch customer data. It sits behind a web application firewall whose default action is block, accepts traffic from our voice provider's published ranges, requires a secret on every single request before any tool runs, and resolves which business a call belongs to from the call itself — never from anything the AI said.
Media plane
Bridges the phone network to the voice model for the duration of a call. It builds no data credentials and can reach no customer records — by construction, not by policy. Compromising it gets an attacker audio for calls in flight and nothing that is stored.
Worker plane
Scoring, summarisation and follow-up work run here, off the call path, on an internal queue with no inbound route from the internet at all.
The defaults we refuse to inherit.
The request path is the interesting part. These are the boring parts — the settings that are easy to leave on their defaults, and that show up in the post-mortem when somebody does.
Deny by default, everywhere
New routes are unreachable until a policy grants them. An endpoint someone forgot to protect fails closed rather than open — the failure mode is an outage, never a leak.
No standing keys
Application code holds no long-lived database or storage credentials. Every data touch runs on a short-lived, single-workspace session that expires on its own.
Keys we create and govern
We create and police the keys that protect your database tables and your recordings, rather than relying on shared defaults. Every database table holding your data is encrypted under a key we created, with automatic rotation, and each business's recordings and transcripts sit under a key made for that business alone, whose own policy denies every principal carrying a different business's identity, so that key refuses the request even if something upstream let it through. Key usage is recorded in the cloud audit trail.
Pinned cryptography
Every signature and token check names the algorithm it will accept. None of them ask the incoming token which algorithm to use — the class of bug that has broken so many token implementations is simply not reachable.
Immutable, pre-built machine images
Production servers boot from images built ahead of time and never patched in place. The running fleet matches a build we can reproduce, not the accumulated history of what was installed on it.
Hardened instance identity
Instance metadata requires session-authenticated access, so the request-forgery trick that has turned many a web bug into stolen cloud credentials returns nothing.
A narrow front door
The only internet-facing component that touches customer data sits behind a web application firewall whose default action is block — it admits our voice provider's published address ranges and refuses everything else. The rest of the platform has no inbound route from the internet at all.
Contracts enforced by machines
Isolation rules are configuration, distributed to services at runtime rather than compiled into them, and automated checks in our build pipeline reject code that reaches around them. A reviewer having a bad day is not the last line of defence.
Same words. Very different guarantee.
Nearly every platform will tell you your data is isolated. The question worth asking is which system enforces it — and what happens on the day someone writes the query wrong.
Bring your security review.
Architecture detail, threat model, data-handling specifics and subprocessor information are available to teams evaluating EarlyBird. Ask hard questions — this page is the short version.
Security inquiries: security@oneearlybird.ai · Report a vulnerability: report@oneearlybird.ai. We read every report and respond to valid findings.