Orkestia
Blog
Chat

Theme and customization

Customize a chat space live. The theme document carries brand, colors, layout, feature flags, attachment limits and copy, and moves through validate, draft, publish and rollback

Every chat space has one published theme: a closed JSON document that decides how the chat page looks, which features it offers, how large attachments may be, and what the interface says. A theme is data only. There is no CSS, HTML or script in it, and the validator rejects any key it does not know.

The lifecycle

StepWorkflowInputsWhat happens
Checkbuzz.theme.validatedocumentRead-only. Returns the normalized document, field errors, and contrast warnings
Save a draftbuzz.theme.save-draftspace_uuid, documentValidates and stores the draft. One draft per space; a new save replaces it
Publishbuzz.theme.publishspace_uuidThe draft becomes the published theme. Open chats pick it up on their next theme read
Roll backbuzz.theme.rollbackspace_uuid, versionRepublishes an earlier version as a new version. History is never rewritten
Readdata.buzz.theme.getspace_uuid or identity_app_uuid, which, versionThe published theme by default. Org members may also read the draft or a version. End users read the published theme only

In the console, the identity app's Chat tab has a live editor: every change is validated as you type, the preview beside it renders the real chat page with demo channels and a demo actor (it never touches your relay), and a pause saves the draft. Publish and history are buttons on the same screen.

buzz.space.enable publishes the default Orkestia theme, so a new space always has one.

A theme document

A document may be partial. Anything you leave out keeps the default.

{
  "brand": {"display_name": "Acme Support", "logo_url": "https://example.com/logo.png"},
  "appearance": {"color_scheme": "system"},
  "tokens": {
    "color": {"primary": "#0f766e", "bubble_self": "#0f766e"},
    "radius": {"bubble": 12},
    "density": "comfortable"
  },
  "layout": {"mode": "full"},
  "features": {"member_channel_create": true, "read_receipts": true, "missed_message_emails": true},
  "media": {
    "max_bytes": 10485760,
    "max_per_message": 4,
    "allowed_types": ["image/png", "image/jpeg", "application/pdf"],
    "allow_in_private_channels": false
  },
  "copy": {"locale": "en", "strings": {"composer.placeholder": "Ask the team or mention an assistant"}}
}
start_workflow("buzz.theme.save-draft", {"space_uuid": "<space uuid>", "document": { ... }})
start_workflow("buzz.theme.publish", {"space_uuid": "<space uuid>"})

Sections

SectionKeys and rules
branddisplay_name (1 to 64 characters), logo_url, favicon_url (https only)
appearancecolor_scheme: light, dark or system
tokens.color, tokens.color_darkHex colors only: primary, primary_contrast, background, surface, surface_alt, text, text_muted, border, danger, success, bubble_self, bubble_self_text, bubble_other, bubble_other_text, actor_accent
tokens.fontfamily_body, family_mono (letters, digits, spaces, commas, quotes and hyphens), size_base_px 12 to 20
tokens.radiussm, md, lg, bubble, each 0 to 32
tokens.density, tokens.shadowcompact, comfortable, spacious; none, subtle, strong
layoutmode (full, drawer, widget), channel_list, header, position (left, right), width_px 320 to 720
featuresBoolean flags, below
mediaAttachment limits, below
copylocale (pt-BR, en, es; default pt-BR) and strings, overrides for allowlisted interface keys such as composer.placeholder, login.title or search.placeholder, each up to 200 characters

Contrast below WCAG AA (4.5 to 1) on the text and bubble pairs is a warning in the editor, not a rejection.

Feature flags

FlagDefaultTurns on
threadsonReply counts and the thread panel
reactionsonReaction chips and the picker
message_actionsonEdit and delete of your own messages
pinsonPinned messages: pin and unpin, the pinned bar and list
mentionsonThe @ picker and plain-text @Name mentions
direct_messagesonDMs
attachmentsonUpload, drag and drop, paste, and rendering files on messages
searchonMessage search and the member search box
read_receiptsoffRead markers and unread counts. Markers are private to each person
typing_indicatoron"Ana is typing" and publishing your own typing
markdownonMarkdown in messages (raw HTML is never rendered)
actor_badgesonThe badge that marks an actor
actor_railonChips above the composer for the actors that answer in this channel
member_panelonThe member panel with people and actors
message_filtersonFilters such as "mentions of me" and "actors"
member_channel_createoffMembers may create, rename and invite to channels
member_channel_joinonMembers may join and leave open channels
missed_message_emailsonThe missed-message email for this space
structured_uionCards from actors with a DGI responder: forms, confirm cards and views. Off, a card shows as its text

The chat page also shows an actor's progress line, its tool trace and its quick replies. Those are on by default and are not theme flags today.

Three flags are enforced by the platform

member_channel_create, member_channel_join and missed_message_emails are read on the server from the published theme. With the flag off, the matching workflows refuse with buzz_feature_disabled: <flag>, so the page can hide the control and the console shows why.

These flags turn off the platform path. A member holds their own chat key, so a person using a separate relay client could still send a channel command straight to the relay. Every other flag is presentation only.

Attachment limits (media)

KeyDefaultRange
max_bytes25 MiB16 KiB to 25 MiB
max_per_message41 to 10
allowed_typesapplication/pdf, image/gif, image/jpeg, image/png, image/webp, text/csv, text/plainA non-empty subset of that list
allow_in_private_channelstruefalse hides the attach control in private channels and DMs

A space may only make a limit smaller. The type list is deliberately short and has no wildcards: SVG, archives and executables are not allowed (see Limits).

These limits are what the chat page enforces before it uploads, and what buzz.session.issue reports back to the client. The relay itself only applies its own, much larger, global ceilings.

Beyond the theme

You wantDo
Your own components inside the Orkestia chatThe chat component behind the hosted page exposes slots (header, channel list item, message bubble, empty state, actor badge, actor rail, member panel) that you override in your own bundle. Access to the component package is by request today
A completely different chat pageDeploy your own build to the site with the App Host release workflows. The chat entry points on your identity app stay the same

Ask your AI assistant

prompts
Read the published theme of my chat space with data.buzz.theme.get and summarize the brand, colors and which feature flags are off.

Validate this theme with buzz.theme.validate and list every error and contrast warning: <paste JSON>.

Change my chat's primary color to #0f766e and turn on member_channel_create. Save it as a draft, show me the diff, and publish only after I confirm.

Roll my chat theme back to version 3 with buzz.theme.rollback.

For AI agents

RuleDetail
Validate before savingbuzz.theme.validate is read-only and safe. Unknown keys are errors, not ignored
Draft then publishbuzz.theme.save-draft does not change what people see. buzz.theme.publish does. Confirm before publishing
Human callersSave, publish and rollback refuse agents
Partial documentsSend only the keys you change. The validator merges them over the defaults
Policy flagsOnly member_channel_create, member_channel_join and missed_message_emails gate workflows. Do not describe other flags as security controls