Security at CrossTenant
Last updated: 31 August 2026
CrossTenant holds privileged administrative access to your customers’ Google Workspace organisations. That is a serious thing to hand to a vendor, and this page exists so your security team can evaluate it properly rather than on trust. It describes the actual mechanisms, names what we do not claim, and is written to be quotable directly into a security questionnaire.
The one-paragraph version. CrossTenant stores no Google Workspace content — every page is a live fetch that is rendered and discarded, so there is no database of your customers’ directory, mail, Drive, or calendar data to breach. Operators sign in through Google or Microsoft only; there are no passwords to steal. Authorisation is granular to (customer organisation, functional area) and fails closed. Every change is confirmed, and elevated changes can be held until a second operator approves them. Everything that changes anything is written to a per-customer, hash-chained audit log that detects tampering. The customer administrator can revoke all of it, instantly, without us.
Trust documents
Privacy policy · Terms of Service · Data Processing Agreement (Annex II is the full controls list) · Sub-processors · Support, incident reporting & vulnerability disclosure · security.txt
1. What we store — and what we don’t
This is the control that matters most, because it removes the risk rather than managing it.
No Google Workspace content is stored. There is no database and no cache of directory, mail, Drive, calendar, or device data. Each console page issues live API calls, renders the result for the operator, and discards it. API responses are marked non-cacheable, so they are not written to the operator’s browser disk either, and content caching is disabled at the network edge for API paths. The console reads mailbox and file settings and metadata; it never reads the contents of messages, files, or calendar entries.
What is kept on disk, in full:
| Store | Contents | Retention |
|---|---|---|
| Authorisation credentials | The OAuth client, token, and service-account configuration for each customer organisation, in a separate directory per organisation, plus that organisation’s primary domain and its administrator’s contact address. | While the organisation is onboarded; deleted on offboarding. Revocable instantly by the customer in their own Google Admin Console. |
| Operator accounts and access policy | MSP engineers’ sign-in identities, roles, permission assignments, and a short sign-in history (time, provider, IP address, user agent). | While the operator relationship is active. Sign-in history is capped by both age (24 months) and entry count. |
| Write-audit log | One append-only file per customer organisation. See section 5 for exactly what a record contains. | 24 months maximum, enforced by an automated pruner. |
| Operator-authored configuration | Report templates, schedules, alert rules, notification recipient addresses and webhook URLs, policy baselines, report notes, branding, and saved joiner role templates. A joiner template stores selected onboarding steps and role settings and may include a delegate mailbox or signature HTML. Dedicated template fields for the joiner’s name, aliases, and password are refused, but an operator can type personal data into the delegate or signature fields. | While the relationship is active. |
| Pending approval requests | The requesting operator, the intended action, the target customer organisation and the specific record the action targets (for example the user account being suspended). No dry-run preview and no sensitive values — passwords, message bodies and signature text are never held while a request waits. | Until approved, rejected, or expired; resolution records kept 30 days. |
| Derived health metrics | Six numeric scores, one A–F grade and a timestamp per customer organisation. There is no free-text field, so no names, addresses or content. Sealed with AES-256-GCM, with no plaintext mode. Only the most recent computation is kept — each recomputation replaces the last. | Served for at most 24 hours before recomputation; deleted automatically once not refreshed for 90 days; removed on offboarding. |
Two further things exist on the server that are not data stores but do hold personal data, and we would rather name them than let you find them. The console writes ordinary operational logs — requests, warnings, errors — and where a call to Google fails, the error text Google returns is recorded; that text can contain a domain, a user’s email address, or a project identifier. And the audit key-rotation tool writes a backup copy of a log before re-chaining it, kept for short-term recovery while the new key is verified; the rotation procedure includes deleting it once verification succeeds, and either way its entries are subject to the same automated 24-month retention sweep as the log itself, so the backup can never outlive the data it copies.
Adding a new store is not a casual decision. Every proposal to persist a new class of data must answer a fixed set of questions in writing — exact fields, location, encryption, retention and the mechanism that enforces it, read path, custody, and the effect on our published disclosures — and receive an explicit decision before it is built. Classes that would hold observations about your customers’ people have been refused under that rule, with a no-storage alternative recorded instead.
2. How operators authenticate
- Federated sign-in only. Operators sign in with Google or Microsoft. CrossTenant holds no passwords and has no password-reset flow to attack.
- Identity is keyed to the provider’s stable subject identifier (Google
sub, Microsoftoid) — never to an email address. A renamed or re-addressed account remains one identity in the audit trail, and an email address alone cannot be used to assume an account. - Invite-only. An identity must belong to a permitted domain, or have been pre-created by an administrator, before it can sign in at all. There is no open sign-up.
- Multi-tenant Microsoft authorities are refused outside development, and where a tenant is pinned the token’s tenant claim is asserted. This closes the class of attack where someone asserts an arbitrary email address from a directory they control.
- Sessions are signed tokens carrying only a subject and timestamps — no roles or permissions travel in the token, so a stale token cannot carry stale privileges. They are delivered in a cookie that is HttpOnly, Secure, SameSite=Lax and
__Host--prefixed. Lifetime defaults to 12 hours and is administrator-settable within a bounded range. - Revocation is real. A per-account timestamp invalidates every token issued before it, checked at the single authentication chokepoint — so “sign this person out everywhere, now” works. Disabling an account is sticky: it is checked before the account record is touched at sign-in, so a disabled operator cannot re-enable themselves by signing in again.
3. How access is authorised
- Permissions are granular to (customer organisation, functional area) — eleven areas, each at one of three levels: none, read, or full. An engineer can be given read access to one customer’s devices and no access at all to another’s mail.
- Roles and group membership resolve to an effective policy, merged most-permissive.
- Authorisation fails closed on unmapped routes. A request to a path with no declared area is refused, not allowed. A test in the build asserts that every mounted route maps to an area, so a new feature cannot ship silently ungated.
- Configuration that cannot be read is treated as the tighter value, never the looser one. A corrupted or unreadable policy file produces a refusal, never an ungated write.
What we do not claim here
Three known weaknesses in this area, stated because you would find them in a thorough review and we would rather you heard them from us:
- Roles are copied, not bound. Applying a role copies its permissions onto the operator at that moment. Editing a role later does not change existing holders, and there is no “who holds this role?” view. This is on our roadmap to change to a live binding.
- The super-administrator flag is a bypass outside the permission model. It makes fail-closed reasoning simple, but it is not visible to permission enumeration, so “list everyone with full access” is not currently one query.
- A wildcard grant covers future areas. An operator granted all areas is automatically granted any area added in a later release. This is deliberate, but nothing currently prompts a review at the moment a new area lands.
4. How privileged changes are controlled
- Every write requires explicit confirmation in the interface. Destructive and irreversible actions require typing the target to confirm.
- An approval gate can hold elevated operations until a second, separately authorised operator approves them. It covers bulk operations, standards deployment, and single-record changes, with 63 individual actions classified into three tiers — 16 critical, 14 sensitive, 33 routine — and an administrator dial that sets where the gate engages. The default engages on the critical tier.
- A requester can never approve their own request. A super-administrator break-glass path exists so nobody is ever stuck at 2am, but it requires a written reason, is recorded as break-glass, and is displayed distinctly rather than being indistinguishable from a normal approval.
- A parked request holds no preview of the change. Passwords, message text and signature content are never stored while a request waits; they are regenerated or preserved at the moment of execution.
- Approved actions execute under the requester’s authority, not the approver’s — approving does not lend your permissions to someone who lacks them.
- Requests cannot die quietly. They expire if unapproved, approvers and the requester are reminded before expiry, and a request that nobody currently holds the authority to approve is surfaced to super-administrators with a banner rather than expiring unnoticed.
5. How actions are made accountable
Every request that changes anything — including one that is rejected
— is appended to a per-customer log. A record contains the time, the
customer organisation, the operator’s identity and email address, the
request method and a fixed route family (for example
/api/users/* — the full request URL is never persisted,
because its dynamic segments and query values can carry a person’s
identifier), the originating IP address, the result, and a bounded,
structured summary of the submitted input — field counts, value
types, and curated outcome markers such as success and failure counts.
Secret-like values are replaced with a redaction marker; free-text,
content-like, and identifying values are omitted entirely; long strings are
truncated and structures reduced to counts.
Being precise about what that means: the log deliberately records the operator’s IP address, and it deliberately does not retain the identifier of the account or resource acted upon, the operator’s free-text reasons or notes, or the values of the settings that changed. The durable record is minimised evidence of who did what class of action, against which customer, with what outcome. Two narrow, deliberate exceptions retain more, because accountability there demands it: a break-glass execution records the acting super-administrator’s bounded written reason, and a record may carry one explicit, protected target reference where a specific accountability case requires it. (Separately, a change parked for approval holds its specific target in the approval record while that record is retained — see section 4.) What the log never contains is message content, file content, or credentials, and what the console’s own screens show of it is a stricter projection still: time, customer scope, operation class, outcome, and acting operator.
- Tamper-evidence. Entries are chained with a keyed HMAC-SHA256 over the canonical record plus the previous entry’s hash, anchored at a fixed genesis value. Deleting, reordering, or editing any entry breaks verification from that point onward. Its one limit, stated plainly: a chain is not a proof against truncation of its own tail. Someone with write access to the file who removes only the most recent entries leaves a chain that still verifies. Shipping each entry to an append-only store off the machine is the control that closes that, and it is specified but not yet built — so the log is tamper-evident, and we do not describe it as tamper-proof.
- Fail-closed. An entry without valid integrity data is treated as invalid rather than trusted.
- The key is real. In production the server refuses to start unless a dedicated audit key of at least 32 characters is configured and is distinct from the session-signing key. A placeholder value is rejected.
- Verification runs on its own. A scheduled sweep verifies every chain and reports breaks; verification is also available on demand. Key rotation is handled by an offline re-chaining tool.
- Reads are gated. Viewing the audit log is itself a permission. The log is excluded from third-party file-sync services, and the server warns at startup — and refuses in production — if its data directories sit inside one.
- Retention is enforced, not asserted. 24 months, by an automated pruner on a schedule, not by a policy sentence.
6. Least privilege against Google
- Read-only mode is enforced in layers. An organisation marked read-only is requested only
.readonlyOAuth scopes at consent. Domain-wide delegation is a separate, customer-approved grant and its current scope set may include write-capable scopes; mutating requests are additionally refused by middleware at runtime. - Delegated access asks per operation. Where the console acts as a user (for mailbox settings, Drive, and calendar), it requests only the one or two scopes that specific operation needs. That runtime request does not narrow the separately customer-approved domain-wide-delegation grant. Per-organisation scope narrowing is supported, and a scope that has not been granted produces a clear, actionable error rather than a silent failure.
- No long-lived service-account key files. Delegated assertions are signed through Google’s IAM credentials service rather than from a private key sitting on disk. The legacy key-file path is refused in production. The mechanism is worth understanding, because it is a control you hold: the console has one machine identity, and it can act for your organisation only because you granted that identity the right to sign as your own service account, in your own Google Cloud project. Remove the grant and the console cannot act for you, whatever else happens on our side.
- Per-organisation isolation. Credentials, tokens, and audit logs live in separate directories per customer organisation, and an OAuth client is loaded only from that organisation’s own bundle. One organisation’s authorisation cannot reach another’s data. The one thing shared across organisations is the single machine identity described above — which holds no access of its own and only the rights each customer has separately granted it.
- The customer holds the off switch. A customer’s Google Workspace super-administrator can revoke CrossTenant’s access entirely, at any time, in their own Google Admin Console. It takes effect immediately and does not depend on us in any way.
7. Infrastructure
Be aware of a distinction that most vendor security pages blur. CrossTenant supports an operator-controlled deployment, where the console runs on infrastructure the MSP controls and the infrastructure controls — disk encryption, network exposure and backups — are the MSP’s, to the specification we document. All of the application controls above apply regardless. A private CrossTenant-hosted release is also running for founder dogfood; it is not yet a generally available customer service.
The private hosted release runs on Microsoft Azure in the UK West region (United Kingdom). Its persistent data sits on an Azure managed disk encrypted at rest with platform-managed keys. Application secrets are injected at run time and kept outside the persistent data root. The application has no public inbound port: operators pass an authenticated Cloudflare Access gate and traffic reaches the host over an outbound-only Cloudflare Tunnel. API responses are excluded from edge caching and query strings are excluded from edge logs.
Backups are not yet a running control. A separate Azure subscription is reserved for encrypted off-host backups, but it contains no storage account and no backup has been uploaded or restored. Before the hosted service accepts an external customer estate, the backup region and retention controls must be fixed, backup contents must be encrypted before upload, and a restore must be rehearsed. The planned storage role is published in advance; this page will describe it as current only after that evidence exists.
On disk, in either deployment, files are written atomically and restricted to the service account, with permissions swept at startup. All data in transit is encrypted with TLS.
One question worth answering before you ask it: how are the stored customer credentials encrypted at rest? By the disk, and by filesystem permissions that restrict them to the console’s own service account — not by an application-layer envelope or an external secrets manager. The derived-metrics store is the one exception, sealed with AES-256-GCM. Anyone with root on the host, or with the decrypted volume, can read a stored token. Two things bound what that is worth to an attacker: the tokens are scoped to what each customer granted, and any customer can revoke theirs unilaterally and instantly. An application-layer secrets store is on our roadmap and is not yet built.
8. Assurance — and what we do not claim
- Over 2,800 automated tests gate every release across the server and client, including tests that specifically pin security behaviour: authorisation completeness, fail-closed defaults, redaction of AI payloads, and scope minimisation. A change that breaks one does not ship.
- Repeated adversarial security review. The codebase has been through multiple structured review passes; controls are annotated in the source with the finding each one closes, and findings are tracked to closure.
- Dependencies are kept current and audited.
What we do not claim, stated plainly so nobody has to ask:
- CrossTenant holds no third-party security certification. We are not ISO 27001 certified, not SOC 2 audited, and hold no Cyber Essentials certificate. We would rather tell you that than let a badge-shaped gap on this page imply otherwise.
- There is no published independent penetration test at present.
- We offer no contractual uptime commitment by default. Our support and incident-communication commitments are on the support page.
- We are a small team. That is a genuine risk factor for a vendor with this level of access and you should weigh it. What it buys you in exchange is that the person answering your security questionnaire is the person who wrote the code.
9. Reporting something
Security vulnerabilities and suspected compromise: security@crosstenant.com. Our disclosure policy, response targets and safe-harbour commitment are on the support page, and machine-readable contact details at /.well-known/security.txt.
In the event of a personal-data breach affecting data we process for you, we notify you without undue delay and in any event within 72 hours of becoming aware. That obligation is contractual and is set out in section 8 of the DPA.
10. Quick answers for a security questionnaire
| Question | Answer |
|---|---|
| Do you store our data? | No Google Workspace content. See section 1 for the complete list of what is stored. |
| Where is data processed? | United Kingdom and EEA, plus the sub-processors listed at /subprocessors with each one’s location and transfer mechanism. |
| Is data encrypted? | In transit, always (TLS). At rest, full-disk encryption, plus AES-256-GCM sealing of the derived-metrics store. See section 7 for which deployment that applies to. |
| How do staff authenticate? | Federated Google or Microsoft sign-in only. No passwords held. Section 2. |
| Is MFA enforced? | MFA is enforced by your own identity provider, where you configure it. CrossTenant does not weaken it and holds no alternative credential that could bypass it. |
| Role-based access control? | Yes, granular to (customer organisation, functional area) at three levels. Section 3, including its known limitations. |
| Is privileged action logged? | Yes — every mutating request, in a hash-chained tamper-evident log, retained 24 months. Section 5. |
| Segregation of duties? | Yes, optional and configurable: the approval gate, with requester-never-approver enforced. Section 4. |
| Sub-processors and notice period? | Listed at /subprocessors; 30 days’ notice with a right to object. DPA section 5. |
| Do you use our data for AI training? | No. AI features are optional, send only sanitised configuration and posture context through a server-side redaction guard, and no data is used to train models. |
| Breach notification? | Without undue delay, and in any event within 72 hours of awareness. DPA section 8. |
| Certifications? | None. Stated deliberately — section 8. |
| How do we terminate and get our data removed? | Revoke access in your own Google Admin Console immediately; stored credentials and configuration deleted within 30 days; audit log retained to its 24-month cap. Terms section 10 and DPA section 9. |
| Something isn’t covered here. | Email security@crosstenant.com. We answer questionnaires in writing, ordinarily within 10 business days, at no charge. |