Cariosan

Workspaces

How Cariosan isolates one customer's data from another's.

A workspace is the top-level tenant in Cariosan. Every user, channel, message, and webhook belongs to exactly one workspace. Workspaces never share data with each other.

Why workspaces exist

Most consumers run a single Cariosan deployment but serve multiple apps — a marketplace buyer app and a seller dashboard, say, or a staging and a production environment that share a database cluster. A workspace is the isolation boundary: one set of API credentials, one set of users, one set of webhooks.

Good to know

If you only have one app, you'll still have exactly one workspace and you can mostly forget it exists. You can create as many workspaces as your hardware can handle — there's no per-workspace license fee.

Creating a workspace

Workspaces are created via the server CLI, not over the wire — they're the thing that holds API credentials, so bootstrapping them via the API would be a chicken-and-egg problem.

terminal
docker compose exec cariosan-server /cariosan workspace create \
  --name "My Company" --plan free

The CLI prints two values you must keep safe:

  • api_key (starts with pk_live_ or pk_test_) — safe to log.
  • api_secret (starts with sk_) — only shown on creation.

Warning

The api_secret is shown one time only. If you lose it, rotate the workspace via /cariosan workspace rotate-secret — there's no recovery flow.

Authenticating with a workspace

Every server-side call uses the API key pair as bearer auth. The key and secret are joined with a colon:

Authorization: Bearer pk_live_abc:sk_live_xyz

The SDKs wrap this for you so you don't construct headers manually:

server.ts
import { CariosanServer } from "@cariosan/server";
 
const server = new CariosanServer({
  apiKey: process.env.CARIOS_API_KEY!,
  apiSecret: process.env.CARIOS_API_SECRET!,
  baseUrl: "http://localhost:8080",
});

Per-user authentication

End users (browsers, mobile apps) never see the workspace secret. They authenticate with a JWT issued by your backend via the Go SDK's IssueUserToken, which the client SDK then passes on WebSocket connect.

api/token.ts
import { CariosanServer } from "@cariosan/server";
 
export async function POST(req: Request) {
  const session = await getSession(req);
  if (!session) return new Response("Unauthorized", { status: 401 });
 
  const server = new CariosanServer({
    apiKey: process.env.CARIOS_API_KEY!,
    apiSecret: process.env.CARIOS_API_SECRET!,
    baseUrl: process.env.CARIOS_API_URL!,
  });
 
  const token = await server.issueUserToken(session.userId, "24h");
  return Response.json({ token });
}

Tip

Issue tokens server-side only, never client-side. The token TTL should match your session expiry so a logged-out user can't keep chatting — 5–15 minutes is plenty for most apps, and the client SDK refreshes them automatically.

See users for how to provision the underlying user records before issuing a token.

Was this page helpful?

On this page