Security

How we keep your apps private and separate

The specific version, for whoever has to sign off. TeamOnly is not open yet, so this describes how the platform is built. We will keep this page exact as it ships, and we don’t claim certifications we don’t have.

Every request is signed in first

All app traffic on *.teamonly.app goes to one dispatch Worker before anything else. It resolves the organization and app from the hostname, checks for a valid session, and only then forwards the request to the app. Without a session, the visitor gets the sign-in screen. Customer code never runs for them.

Sign-in uses Google and Microsoft OpenID Connect. The ID token is verified on our server, including signature, audience, expiry and the email domain. Google’s hd value is checked from the verified token, not trusted from the request.

Identity your app can trust

The dispatch Worker strips any identity headers on the incoming request, then sets the verified email and name itself. The SDK exposes them to your app as ctx.user.

At the door
incoming request to expenses.acme.teamonly.appx-teamonly-user: ceo@acme.com    removed, not from usx-teamonly-user: dana@acme.com   added from verified sign-in
Illustrative header names. Your app reads the user from the SDK, not from headers.

Separate database and file storage per app

Each app gets its own D1 database, created on first deploy, and its own R2 storage prefix for files. Those are the only data bindings attached to the app. Apps run as separate scripts in a Workers for Platforms dispatch namespace and cannot reach each other’s bindings.

We test this on purpose: an app trying to read another app’s data, cookies or secrets is on our launch checklist, and we will keep running those tests after launch.

A floor plan with three rooms: the expenses, inventory and lookup apps. Each room holds its own data, files and secrets. The expenses app tries to reach into the inventory room and hits the wall. The inventory app tries to reach into the lookup room and hits the wall too. Data, cookies and secrets stay in their own room.

Each app sits in its own room. Data, files, cookies and secrets stay inside.

Secrets stored outside your code

Secrets are set with npx teamonly secrets set and stored as encrypted Worker secrets attached to that one app. They are not in your repository, not in the bundle, and not visible to other apps. The skill file tells your AI never to put keys in code.

Cookies scoped to one app’s address

Session cookies are set for the exact app hostname, such as expenses.acme.teamonly.app, with Secure, HttpOnly and SameSite. They are never set on teamonly.app or the organization’s subdomain, so one app cannot read another’s session.

We also plan to add teamonly.app to the Public Suffix List, so browsers treat every app address as its own site. That is not done yet; this page will say so when it is accepted.

Customer code is treated as untrusted

We assume any app may contain mistakes or hostile code, because AI wrote some of it and anyone can make an error. That is why authentication lives outside the app, why each app gets only its own resources, and why nothing an app sends can change who is signed in.

Where it runs

Everything runs on Cloudflare: Workers and Workers for Platforms for code, D1 for databases, R2 for files, and KV for sessions. We don’t run our own servers. Choosing a data region is planned for the Business plan.

What we don’t do yet

  • Coming later
    SAML SSO (Business plan)
  • Coming later
    Audit logs (Business plan)
  • Coming later
    Choice of data region (Business plan)
  • Not planned
    Public, unauthenticated apps

We have no SOC 2 or ISO certification today.

Reporting a security issue

Email hello@teamonly.dev with “Security” in the subject. A person reads every message, and we will reply to confirm we have it. Please give us a reasonable chance to fix an issue before you share it.

FAQ

Questions people ask

All questions and answers

Can one app read another app’s database?

No. Each app is given access only to its own database, file storage and secrets. It has no credentials or bindings for any other app’s resources, so there is nothing to connect with.

Can an app pretend to be a different user?

Not to itself or to us. Identity headers on incoming requests are removed at the door and replaced with the verified ones, so a visitor cannot fake being someone else. The identity an app sees always comes from a real sign-in.

Are apps indexed by search engines?

No. Every app is behind login, so crawlers only ever reach the sign-in screen, and every response, including that screen, sends noindex.

Early access

Get on the list.

We are new and not open yet. Leave your work email and you will be one of the first teams to deploy.

Use your work email. We will write when your spot opens.

Want to tell us what you are building? Use the longer waitlist form. Questions go to hello@teamonly.dev, and a person answers.