Skip to main content

Use workspaces and clean rooms

A workspace is the universal ownership boundary, every asset, table, message, model, and job belongs to one. A clean room is a mutual-distrust governance overlay laid on a workspace, so parties can compute jointly without trusting each other with raw inputs. This guide is the practical path through both: create, populate, invite, govern, and promote a result.

Operations run against the node admin surface (/api/public/v1/admin/…, authenticated session, the console drives the same endpoints). Workspaces also have a s9-admin CLI.

The flow at a glance​

Figure: the workspace-and-clean-room flow. A workspace (green) is created and given members; a clean room (green) is laid on a host workspace, collaborators are invited, assets contributed (seen only as aliases), a notebook approved under M-of-N review (cyan), a run executed through the fail-first egress gate (amber), and the result promoted to a data product (filled). Ceiling: a clean room is policy-based isolation with a signed, hash-chained run audit, not a hardware trusted execution environment.

Table equivalent

StepOperationKey inputs
Create workspacePOST /workspaces / s9-admin ws createname
Add memberPOST /workspaces/{id}/members / s9-admin ws members addentity_id, role
Create clean roomPOST /cleanrooms/name, owner_entity_id, governance
Invite collaboratorPOST /cleanrooms/{id}/collaboratorsentity_id, role
Contribute assetPOST /cleanrooms/{id}/assetscontributor_entity_id, source_fqn, alias
Approve notebookPOST /cleanrooms/{id}/notebooks/{id}/approvereviewer authority
RunPOST /cleanrooms/{id}/runsproposed_epsilon_burn
PromotePOST /data-products/from-clean-roomproduct fields + governance

Part 1: Workspaces​

Create a workspace​

Every entity is auto-provisioned one workspace at creation, but you create additional ones for teams, projects, and environments. name is the only required field:

# CLI
s9-admin ws create acme-analytics --description "Analytics team workspace"

# API equivalent, type. user | agent | shared | system; environment: prod | staging | dev | sandbox
curl -X POST https://CELL_HOST:8443/api/public/v1/admin/workspaces \
-H "Content-Type: application/json" \
-d '{"name":"acme-analytics","type":"shared","environment":"prod","description":"Analytics team workspace"}'

Creating a workspace mints the whole environment in one step, a dedicated storage namespace, a messaging inbox, a feed, default streams, and an owner membership row for you. Isolation is structural, not a name prefix.

Add members and roles​

Access is by membership role (owner, editor, contributor, viewer) or a group grant, enforced at the gateway:

s9-admin ws members add <workspace_id> --entity <entity_id> --role editor
s9-admin ws members list <workspace_id>

# API equivalent
curl -X POST https://CELL_HOST:8443/api/public/v1/admin/workspaces/<workspace_id>/members \
-d '{"entity_id":"entity:bob","role":"editor"}'

Move a workspace across cells​

A workspace is a whole, portable environment. Clone, migrate, snapshot, archive, and fail it over across cells with the Workspace Mobility operations (clone, cross-org-clone, archive/restore, DR failover/failback), also on the workspace admin router. See Migrating a workspace for clone-vs-migrate semantics.

Part 2: Clean rooms​

A clean room is the workspace console, scoped to the room, with collaboration governance layered on. Everything you can do in a workspace you can do in a clean room, with the guarantees built in.

Create a clean room​

Create it on a host workspace. name and owner_entity_id are required; the rest are the governance dials, each with a safe default:

curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/ \
-H "Content-Type: application/json" \
-d '{
"name":"acme-partner-overlap",
"owner_entity_id":"entity:acme",
"workspace_id":"<host_workspace_id>",
"min_aggregation_threshold":50,
"dp_budget_per_collaborator":1.0,
"output_review_required":true,
"approval_threshold":2,
"region_constraint":"eu",
"purpose":"audience overlap for a joint campaign"
}'

Key dials: min_aggregation_threshold (default 50) sets the k-anonymity floor; dp_epsilon_max / dp_budget_per_collaborator set the differential-privacy budget; output_review_required forces human output review; approval_threshold (default 1) is the M in M-of-N notebook approval; region_constraint pins where the room may run. A new room is deny-all until grants are made.

The governing permission scopes are catalog.cleanroom.create, .admin, .contribute, .review, and .run, separation of duties is built into the model.

Invite collaborators​

curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/<room_id>/collaborators \
-d '{"entity_id":"entity:partner","role":"analyst"}'

Contribute assets: seen only as aliases​

Each party contributes its own data; a collaborator only ever sees a per-room alias, never the source table name (notebook bodies are alias-rewritten before compute, so the kernel never sees a source name):

curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/<room_id>/assets \
-d '{"contributor_entity_id":"entity:acme","source_fqn":"analytics.sales.orders","alias":"party_a_orders"}'

Approve a computation and run it​

Notebooks are attached, submitted, and then approved under M-of-N review, an edit resets approval. Only an approved notebook runs, and every output passes the egress gate:

# Attach + submit a notebook, then approve (needs approval_threshold reviewers)
curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/<room_id>/notebooks -d '{...}'
curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/<room_id>/notebooks/<nb_id>/approve

# Run, declare the DP budget the run may burn
curl -X POST https://CELL_HOST:8443/api/public/v1/admin/cleanrooms/<room_id>/runs \
-d '{"notebook_id":"<nb_id>","proposed_epsilon_burn":0.1}'

The egress gate: an ordered, fail-first cascade​

Every output passes through an ordered cascade, and the first failure terminates the run. No output leaves except through it:

OrderCheckOn failure
1k-anonymityTerminate
2Max-output-rows capTerminate
3Per-collaborator differential-privacy budgetTerminate
4Per-run epsilon ceilingTerminate
5Destination allow / deny (default deny)Terminate
6DLP inspectionTerminate

Adjust the destination allow/deny list with PUT /cleanrooms/{id}/egress-policy.

What a clean room is, precisely

A clean room is policy-based isolation with a signed, hash-chained run audit, not a hardware trusted execution environment; the tamper-evident run chain is the documented compensation for not having an enclave. A workspace is fully mobile across cells, but a clean room is single-cell for cross-cell recovery today, the room-clone primitives and fail-closed cutover admission gate are real, while carrying an in-flight run's DP budget and output-review posture across cells is in progress.

Promote a clean-room result​

The commercially interesting end of an engagement: a joint result computed under mutual distrust becomes a first-class, owned, contract-bearing asset:

curl -X POST https://CELL_HOST:8443/api/public/v1/admin/data-products/from-clean-room \
-d '{"room_id":"<room_id>","name":"overlap_summary","version":"1.0.0","domain":"marketing","owner_entity_id":"entity:acme","team_id":"team:growth","governance":{"classification":"CONFIDENTIAL"}}'

See Set up data-platform resources → data products.

Verify it worked​

For a workspace:

#Check
1Members hold the roles you set, and the resolved capability set matches
2Bound catalogs appear with the right privilege
3The activity record shows your actions on a hash-chained timeline

For a clean room, four more that matter:

#CheckWhy
4Every contributed asset shows its alias, never a source nameThe alias is the contract surface
5The egress policy defaults to denyDenial always wins over the allow list
6A notebook must be approved before it runsAnd editing it resets that approval
7The privacy budget moves when a run completesIt is a ledger that gates, not a report

Check 7 is the one to test deliberately. Run something, then confirm the budget went down and stayed down.

When it does not work​

SymptomMost likely causeWhat to do
A collaborator sees nothingThey are not an active collaborator, or nothing is contributed yetReads are deny-closed on membership
A notebook will not runIt is not approved, or an edit reset its approvalApprovals are keyed to a version hash. Re-review the new version
A query returns fewer rows than expectedThe anonymity threshold refused the under-sized groupAggregates flow, raw and under-threshold rows do not. Working as intended
A result will not leave the roomThe destination is not on the allow listDefault deny. Add the destination or change the default deliberately
The privacy budget is exhaustedConsumption reached the ceilingIt does not come back. Deleting a run does not restore it, because the disclosure already happened
A tool is missing that the docs describeYou are in the narrower catalog view of the roomSee clean-room governance for the two entry points

Across the deployment matrix​

  • Single-cell: a workspace or clean room is a local boundary the engine enforces on every read; a clean room runs on one cell.
  • Multi-cell: a workspace boundary spans cells and the whole environment is mobile (clone, migrate, snapshot, archive, failover); clean-room cross-cell in-flight recovery is in progress.
  • Sovereign / air-gapped and cloud-enabled: boundary enforcement is a property of the local engine, so it holds identically with or without connectivity; a clean room needs no vendor cloud to run.