Cariosan

Users

How Cariosan represents end users and issues JWTs for client SDKs.

3 min readUpdated Mar 14, 2026

A user is the identity an end customer sees. Cariosan doesn't authenticate users itself — your app already does that. Instead, you tell Cariosan about your users and it mints short-lived JWTs the client SDK can use to authenticate WebSocket connections and REST reads.

The external_id

Every user has two identifiers:

  • An internal UUID, assigned by Cariosan, used as the foreign key target for memberships and messages.
  • An external_id you supply ("user_alice", "auth0|123", etc.). This is the stable handle you use in every SDK call.

Tip

Pick an external_id that won't change if the user renames themselves. The account row's primary key in your own database is a natural choice.

Upsert semantics

There is no "create" endpoint — just upsert. Call UpsertUser every time the user's name or avatar changes in your app:

users.go
client.UpsertUser(ctx, cariosan.UpsertUserRequest{
    ExternalID: "user_alice",
    Name:       "Alice Setiawan",
    AvatarURL:  "https://cdn.example.com/users/alice.jpg",
    Metadata:   map[string]any{"plan": "pro"},
})
  • First call → row inserted, user.created webhook fires.
  • Subsequent calls → row updated in place, no webhook.

Issuing a client JWT

Your backend mints a per-user JWT and returns it to the browser (or mobile client). The client SDK stores it in memory — never persist it to localStorage if you have alternatives, since anything in localStorage is exposed to XSS.

token.go
token, _ := client.IssueUserToken(ctx, "user_alice", 24*time.Hour)
// Return token.Token to the browser.

Tip

The default TTL follows CARIOS_JWT_TTL_HOURS (24h). Issue longer tokens for desktop clients, shorter for embedded public browsers. Tokens are stateless — Cariosan doesn't store them server-side, so rotate the workspace's signing key to invalidate every issued token at once.

Deleting a user

DeleteUser hard-deletes the user row and cascades to their channel memberships.

Hard delete

Deleting a user does not delete their messages. Those stay attributed to the deleted user's UUID — this is intentional: removing message history leaves confusing gaps in conversations other people still see. Prefer soft delete (flip a deleted_at column in your own DB) when history matters; once Cariosan's row is gone, the user can't be re-linked to past messages.

Was this page helpful?

On this page