# Operator and customer, as a design idea

Connect runs one implementation for two audiences. The operator's workspace runs the platform; a customer workspace uses it. They share the code, the data model and the isolation, and differ only in which routes each may call and what a plan allows. The design decision is that a capability has one body and two doors — never two implementations that drift apart.

- **Status:** Available
- **Audience:** both
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/compare/owner-vs-tenant-concept/

## The two audiences, precisely

|  | Operator workspace | Customer workspace |
|---|---|---|
| What it is for | Running the platform itself | Running a business with Connect |
| Plan and allowance | None — it is not a customer of itself | The plan decides the allowance |
| Feature set | The same features, plus the ones that exist to run the platform | The same features |
| Isolation | A workspace like any other, under the same rules | A workspace like any other, under the same rules |
| Route access | May call operator routes | May call the routes the tenant facade allows, and no others |

That last row is the whole mechanism. A customer session is not trusted to call anything an operator session can; it reaches a facade that permits the routes it is meant to have and refuses the rest. The refusal is the default state — a route nobody has deliberately opened is closed, which is the right way round for a boundary that separates businesses.

## One body, two doors

The rule the codebase is held to is that new behaviour lives in a module both routers call, and each router adds only a route. A capability therefore has one implementation, one set of rules and one place to fix a defect. What differs between the audiences is which door is open, not what is behind it.

1. **A shared module holds the behaviour**, whatever it is — mailbox management, voice configuration, the approvals queue.
2. **Each router exposes it** under its own path, for its own audience.
3. **The tenant facade decides** whether a customer session may reach that path at all.
4. **A parity check fails** when a new capability exists for one audience and not the other without a written reason.

The alternative — a customer version of a screen that renders a subset, written separately — is what the rule exists to prevent. Two implementations of the same idea do not stay the same. One gets the bug fix, the other keeps the bug, and nobody notices until a customer reports behaviour that the operator cannot reproduce.

## What the pattern costs

It is not free, and pretending otherwise would misrepresent the trade. Every change to a screen or a route costs more than it would in a single-audience product.

**Two verifications per change** — A change is checked as the operator and as a customer, in a browser, because a difference in what each sees is invisible in the source.
**A rewrite rule for every new path** — An operator path that a customer should also reach needs its customer equivalent declared, or an explicit written reason why not.
**A slower first implementation** — Putting behaviour in a shared module takes longer than putting it in the screen that needs it today.
**Discipline about subsets** — A view that shows a customer less has to be a deliberate, recorded decision rather than a shortcut somebody took once.

> **Note** The failure this prevents is specific and expensive: a capability that works for one audience and returns a refusal for the other. Because the code looks correct in both cases, that class of defect is found by looking, not by reading — which is why the check is a browser check.

## What is deliberately operator-only

A small set of capabilities exist to run the platform rather than to run a business on it, and they carry the status `available_owner` rather than `available`. Verifying a payment, setting commercial terms and handling enquiries that arrive from the public website are the examples. They are not missing from a customer workspace; they would be meaningless in one.

- **`available`** — running in production and reachable by both audiences.
- **`available_owner`** — running in production, deliberately for the operator only.
- **Neither is a promise about the other.** A capability being operator-only says nothing about whether a customer version is intended.

The distinction matters when you are reading this documentation and deciding what you can use. A page marked operator-only is describing how the platform is run, and reading it tells you something true about Connect that you cannot do yourself.

## Questions

### Does the operator's workspace see customer data?

It is a workspace like any other and sits under the same isolation: the tenant facade in the middleware, the workspace kernel in the data layer, and row-level security in PostgreSQL. Being the operator is about which routes a session may call, not about a wider view of other people's records.

### If a feature works for me and not for a colleague, is that this?

Usually it is permissions inside one workspace rather than the audience split. The audience split explains a difference between an operator session and a customer session; a difference between two people in the same workspace is about what each of them is allowed to do there.

### Why not simply build two products?

Because they would be two products, with two sets of defects and two sets of behaviour to document. The operator running its own business on the same implementation is also the strongest daily test of it — a capability that is awkward for a customer is awkward for the operator first.

## Related

- [Comparisons and concepts](https://connectbyjbrh.com/docs/compare/)
- [Account and access](https://connectbyjbrh.com/docs/account/)
- [Security and isolation](https://connectbyjbrh.com/docs/security/)
- [One implementation, two audiences](https://connectbyjbrh.com/research/two-audiences-one-implementation/)
- [A tenant facade that fails closed](https://connectbyjbrh.com/research/closed-by-default-facade/)
- [Workspace and account](https://connectbyjbrh.com/docs/compare/workspace-vs-account/)

## What this page is based on

- Connect capability registry (docs-source/facts.py) — `owner_tenant_routing`, `rls_isolation`, `available_owner` capabilities
- docs-source/facts.py — PRODUCTION.isolation_layers
- `CLAUDE.md` — the two-audiences rule this repository is held to
