Skip to content

Cloud workspaces and customer access

Sonaloop Cloud is the hosted, multi-user layer over sonaloop-research. Research records and runtime files are scoped to one workspace; a customer does not share a data root with another customer.

Identity and account creation

Cloud uses standards-compliant OpenID Connect (OIDC), using Authorization Code with PKCE. The configured identity provider owns account creation, password reset and multi-factor authentication. Sonaloop never stores customer passwords. A managed provider that offers hosted email/password registration therefore gives customers a normal email signup without requiring Google accounts.

Production callbacks accept only signed RS256 or ES256 ID tokens and check the issuer, audience, expiry, nonce and authorized party. UserInfo must return the same subject, a non-empty email address and the strict JSON Boolean email_verified: true. Membership is keyed by a hash of (issuer, subject), not by email, so changing providers or matching email addresses never silently links identities.

Required settings:

SONALOOP_OIDC_ISSUER=https://identity.example.com
SONALOOP_OIDC_CLIENT_ID=...
SONALOOP_OIDC_CLIENT_SECRET=...
SONALOOP_CLOUD_SECRET=...
SONALOOP_PROVISIONING_MODE=invite_only

Use invite_only for customer deployments. A successful login then creates no workspace, owner membership or billing trial. bootstrap is intended for the first account on a new self-hosted instance; self_serve creates a trial workspace for each new identity.

Provisioning a customer

An operator creates the workspace and grants their own verified, issuer-qualified subject the initial owner role:

sonaloop-cloud workspace-create "Customer research" <operator-subject>
sonaloop-cloud workspace-plan <workspace-id> comp

comp is an explicitly entitled, no-charge workspace. It does not require a Stripe checkout or subscription.

Owners can then create an invitation for one exact email address from Cloud → Workspace or the CLI:

sonaloop-cloud invite-mint <workspace-id> person@example.ch
sonaloop-cloud invites <workspace-id> --status active
sonaloop-cloud invite-revoke <invite-id>

The link expires after seven days, can be used once, and grants the non-admin workspace_user product role, persisted as workspace editor. The URL contains neither the address nor the role; its server-side record keeps the target email digest, creator, expiry, status and redemption audit. Redemption requires the same normalized, verified email returned by the identity provider. Domain-wide joining and email-based auto-linking are deliberately unsupported.

When Clerk delivery is configured, the Workspace form also creates a Clerk application invitation and sends the signup email. After the recipient verifies the exact address, Sonaloop redeems the one-time workspace grant, activates that workspace and opens its prepared Jobs view. No Stripe step is involved for a comp workspace; the copyable one-time link remains the owner fallback if email delivery fails or is ambiguous.

An owner who prepares several customer workspaces can activate one workspace and choose View as regular member. This creates a signed, browser-session role preview for that active workspace: research data and actions use the normal workspace_user permissions, owner-only controls disappear, and a persistent banner exits the preview. The membership row is never changed. Switching workspaces, signing out, losing owner membership, or presenting a stale preview cookie ends the preview fail-closed.

Customer-facing onboarding surface

Shared Postgres row tenancy disables the Core product tour and the browser loader for its bundled onboarding-showcase by default. A customer therefore opens the workspace data prepared for them rather than an onboarding demo that could add unrelated records. Local/single-user Core keeps the opt-in tour enabled by default; MCP and CLI example loading remain explicit host-controlled operations. Set SONALOOP_PRODUCT_TOUR_ENABLED=1 only when a shared deployment deliberately wants to expose that onboarding surface.

Workspace-owned design system and branding

Each workspace may publish one active, versioned workspace_design_system.v2. It is structured data, not arbitrary CSS: brand identity, light/dark color roles, approved font stacks/assets, spacing, imagery, charts and export/deck choices pass the Core validator and the Cloud publish gate. Drafts do not affect customer pages. Publishing switches the workspace pointer atomically; previous versions remain auditable and can be rolled back. Owner permission is required for design-system and asset writes.

The application resolves the published version per request from the authenticated active workspace. Its name and logo replace the Sonaloop shell lockup; its safe tokens flow into the shell, new prototypes and version-snapshotted HTML/PDF/PPTX exports. Missing or invalid branding falls back to the Sonaloop default rather than breaking research. A path parameter or workspace cookie alone cannot wear another tenant's theme: membership and the active row-tenancy scope are authoritative.

The Deck section may also hold an approved 16:9 PowerPoint master. Sonaloop retains the uploaded theme, canvas, masters, layouts and placeholders, discards its sample slide instances, and maps cover, agenda, section, content, comparison, image and closing slides to the closest native layout. Opaque layout names are classified from placeholder structure. A customer master owns its fixed artwork, theme fonts and presentation palette, so no duplicate Sonaloop overlay or separate web palette is added. Upload inspection reports simple compatibility; every export records a fidelity check for unchanged master parts, bounds, layout/placeholder use, font overrides and off-theme colors. Without an uploaded master, the polished Sonaloop deck remains the deterministic default. The report page also offers a concise stakeholder PDF; both exports resolve the published workspace brand at export time.

Workspace → Templates can additionally hold a PowerPoint style reference made from real example slides. Sonaloop analyzes semantic color usage, explicit type sizes and visual density, then removes every concrete slide before storage. The resulting rules complement the master: the master supplies geometry and fixed brand artwork; the reference can establish usage rules such as keeping a bright accent on markers/fills while using readable ink for generated text.

Brand artwork is never recolored by CSS. Supply the official positive/dark-on-light lockup as brand.logo_variants.lockup (or the configured logo_preferred) and, where the brand provides one, the official negative/light-on-dark artwork as brand.logo_variants.lockup_dark; reversed is the fallback dark-scheme role. The shell swaps these assets for explicit light/dark mode and for the browser's preferred scheme. If no negative variant exists, it keeps the positive asset rather than inventing one.

Design assets are a separate workspace-owned library. Uploads are size- and MIME-bounded; logos accept sanitized SVG or PNG, images accept PNG/JPEG/WebP, fonts accept WOFF/WOFF2, and deck masters accept inert .pptx packages. PowerPoint uploads reject macros, embedded/ActiveX/OLE content, external relationships, archive bombs and malformed packages. Payloads refer to immutable workspace-asset:<id>@<hash> values, never arbitrary filesystem paths or remote runtime URLs. Publish validation resolves every reference in the same workspace and checks its expected kind. At render/export time Cloud resolves bytes again under that workspace id and inlines or serves only the authorized rendition. Archiving a library record does not delete bytes needed by an already published historical version.

Tenant and file boundary

Postgres row-level security and the request workspace scope protect structured records. Every authenticated browser request binds exactly the active workspace as its only read and write scope; other memberships remain switcher choices and their projects, personas and evidence are not unioned into the active UI. Runtime files live under:

<SONALOOP_DATA_DIR>/workspaces/<workspace-id>/

This includes persona SOUL and memory files, avatars, assets, snapshots, simulations, exports, prototypes and browser-session logs. In tenant mode, external filesystem paths are rejected and unscoped /data, /proto-files and /sessions-files routes do not expose runtime files. Images remain available to authorized research agents through view_asset; browser previews and downloads resolve an opaque asset id through an authenticated, active-workspace-only route without reopening raw filesystem mounts. Persona portraits use the same boundary at GET /personas/<persona-id>/avatar: the persona is resolved under the active RLS scope, its PNG is contained inside that workspace's avatar directory, and the response is private, no-store. Stable catalog persona ids therefore cannot leak a portrait or a locally revised avatar across a workspace switch. Fixed avatar and asset thumbnail routes repeat that authorization before deriving bounded WebP pixels. Their caches live below the active workspace partition; Cloud skips theme compilation for these exact binary paths, while the outer session and RLS middleware still executes normally.

Static prototype previews use a short-lived signed route at GET /prototype-files/<capability>/<prototype-id>/<path>. The authenticated detail page mints the capability for exactly its active workspace and prototype tree. It is not a session token and authorizes no app/API route. Each file request revalidates the signature, expiry, live prototype row and registered directory inside that workspace's prototype partition. The iframe is opaque and credentialless (no allow-same-origin): relative CSS/JS/images/fetches work, while prototype code gets no Sonaloop session/workspace cookies or parent/API access. Entry HTML is revalidated; unchanged nested files get a bounded private cache. Missing, foreign, expired, forged, dynamic-runner and escaping paths all return the same 404. The legacy unscoped /proto-files mount remains unavailable in Cloud.

For externally hosted mockups, Remote MCP exposes the narrower register_remote_prototype admission path. It stores only validated HTTP(S) metadata and an optional concept-note pairing; the Cloud server does not fetch or execute the URL. Local-path registration, runners and browser-driving tools remain outside the remote workspace-user profile.

For a first customer rollout, keep command lifecycle hooks and the hosted-agent worker disabled until their workflows and outbound actions have been reviewed:

SONALOOP_DISABLE_HOOKS=1
SONALOOP_CLOUD_AGENT_WORKER=0