Customisation Research · OpenChamber 2.5.2 · 27 October 2026
Short answer: yes, you can change every line — it is MIT and the entire React app ships in the repo. But the smarter question is which changes keep working after an upstream update, and which ones turn your fork into a permanent maintenance job.
Three sentences:
If the change is "add something next to what exists", do it as an extension. If the change is "make what already exists look or behave differently", it is a React edit. There is no third option, because extensions live in a sandboxed iframe and never touch the host DOM.
Each layer is a different contract with different maintenance cost. Choose the lowest layer that can express your change.
Legend: SDK no React edit · PART partially, or needs a small core patch · CODE React/server source required.
| Change | How | Reality |
|---|---|---|
| Colours, light + dark | SDK | Theme JSON in ~/.config/openchamber/themes/, reload in Settings. No rebuild. |
| Import an existing VS Code theme | SDK | Built-in importer from Open VSX or a local .json. |
| Typography scale, radius, spacing | PART | Theme covers colours and fonts; sizes/spacing live in design-system.css and Tailwind classes. |
| Logo, wordmark, app icon | CODE | Assets and manifest icons; also PWA icons and Electron packaging. |
| Make your theme the default for everyone | CODE | Add it to packages/ui/src/lib/theme/themes/ and the preset index. |
| Change | How | Reality |
|---|---|---|
| Add a panel on the right rail | SDK | contributes.panel + sandboxed iframe. |
| Add a full-screen page (board, dashboard) | SDK | contributes.page; user opens it from the Extension pages menu. |
| Add an entry to the + menu / message menu / session menu | SDK | contributes.attach and contributes.actions. |
| Add a widget inside the Work Status panel | SDK | contributes.statusSection, height capped at 320 px. |
| Dock your own controls beside a live surface | SDK | contributes.service.surface + panel.dock. |
| Move the sidebar, change rail order, resize panes | CODE | Shell components under packages/ui/src/components/layout. |
| Add or remove built-in nav sections | CODE | Same layout tree; no contribution slot exists for core nav. |
| Mobile / VS Code layout | CODE | Separate shells; extensions do not even load there. |
| Change | How | Reality |
|---|---|---|
| Slash commands in the composer | SDK | contributes.commands + onResolve; up to 8. Names already used by the app are ignored. |
| How a tool call renders (icon, title, table/markdown/code) | SDK | contributes.tools, declarative manifest, no code, up to 16. |
| Send prompts, start sessions, create worktrees | SDK | Permission-gated: prompt, sessions. |
| Read/write project files, or paths outside it | SDK | files and filesystem capabilities with explicit paths. |
| One-off text generation with the Small Model | SDK | model capability → host.generate(). |
| Change how message bubbles, reasoning, or markdown look | CODE | Timeline components and streamdown configuration. |
| Change composer behaviour, attachments, voice | CODE | Composer components; the SDK can prefill text, not restyle the box. |
| Add a permission/approval rule | PART | Rules live in OpenCode config; the UI to manage them is core. |
| Change | How | Reality |
|---|---|---|
| Connect an external service with a token/OAuth | SDK | contributes.integration; token never reaches the iframe. |
| Persist your own data | SDK | host.storage on the server, or device-scoped storage. |
| Run a local process / CLI | SDK | contributes.service, unsandboxed, user-approved. |
| Act as the agent's browser | SDK | provides: ["browser"]. |
| Add a git host (issues/PRs/worktrees) | SDK | provides: ["source-control"]. |
| Custom file editor for a file type | SDK | contributes.fileEditors, replaces the built-in editor for matched files. |
| New settings page or new core setting | CODE | Settings registry is internal; extensions only get declared integration fields. |
| New Express API route | CODE | packages/web/server/index.js and server/lib/*. |
| Change | How | Reality |
|---|---|---|
| Tunnels, reverse proxy, remote access | SDK | Already built: Cloudflare/ngrok/Tailscale, private relay, proxy docs. |
| Auth method (WebAuthn / password / headers) | PART | Configure it; changing the flow is server/lib/ui-auth. |
| Branded desktop app + installer | CODE | packages/electron, icon and packaging config. |
| Branded mobile app | CODE | packages/mobile, Capacitor project, store signing. |
| Host on Cloudflare / any Node host | SDK | It is an Express server serving a static bundle. |
These are the only ways an extension can enter the app. Each is declared in package.json under openchamber.contributes and validated on disk at install time.
| Contribution | What it gives you | Notes |
|---|---|---|
panel | Icon on the right rail + an iframe panel | kebab-case id, Remixicon name or package SVG |
page | Full-screen page from the Extension pages menu | The extension cannot open it itself |
attach | Dialog or panel opened from the + menu | "dialog" / "panel" / custom entry |
background | Hidden sandbox for actions and slash commands | 20 s limit per invocation |
actions | Entries on messages and sessions | Up to 8, optionally mode: "background" |
commands | Slash commands in the chat box | Up to 8, name collisions ignored |
tools | Custom rendering for tool calls | Declarative only: icon, title template, text/json/markdown/code/table |
statusSection | Widget in Work Status | Frame or host-drawn from a service endpoint |
fileEditors | Custom editor for matching files | Cmd/Ctrl+S routed through host |
integration | Settings card, token/OAuth/Linear account | Secrets stay on the server |
service | Local process, browser or source-control provider, live surface | Runs with the user's full access |
filesystem | Allow-listed paths outside the project | Explicit globs, user-approved |
connectHost() exposes a permission-checked RPC, not a DOM API: onReady, onDirectory, onSession, onSessionLifecycle, onConnection, onSettings, onItem, onAction, onResolve, onFileOpen, onFileSnapshot, onFileSaved, onStatusControl, onPopoverClosed, plus methods such as toast, compose, attach, prompt, startSession, openSession, listProjects, listWorktrees, listSessions, storage.get/set/delete/keys, generate, setBadge, setHeight, openCommit, openUrl, openPopover, request, oauthStart, readFile, writeFile, listDir, stat.
Theme tokens are pushed into the iframe on every onReady, so your extension can look native — including syntax colours — without importing app CSS.
@openchamber/sdk is published to npm, versioned with the app (apiVersion: 1, wire envelope v: 1), ships hosts/guest types and a UI kit, and has six official examples. It is not a private hook.
This list matters more than the previous one, because these are the requests that look possible and are not:
integration.settings, rendered in the integration card.packages/sdk and packages/ui/src/lib/guests — a core patch, not an extension.Everything here is a normal component edit, but it is your fork now. The app is TypeScript + React 19 + Tailwind 4, built with Vite 7.
| Area | Where it lives | Cost to change |
|---|---|---|
| App shell, sidebar, rail, header | packages/ui/src/components/layout/ | Medium — affects every screen |
| Chat timeline, bubbles, reasoning, markdown | packages/ui/src/components/chat/ | Medium–high — most surface area |
| Composer, attachments, voice | packages/ui/src/components/chat/ composer files | Medium |
| Settings pages and registry | packages/ui/src/lib/settings/registry.ts + components/sections/ | Low per page, grows with pages |
| Theme engine, presets, export | packages/ui/src/lib/theme/ | Low — well isolated |
| Extension host (frames, grants, protocol) | packages/ui/src/lib/guests/ + components/layout/PluginPane.tsx | High — security-sensitive |
| Server, proxy, auth, tunnels | packages/web/server/index.js (≈137 KB) + server/lib/* | Medium — plain ESM JavaScript, not TS |
| Desktop packaging | packages/electron | Medium |
| Mobile shell | packages/mobile (Capacitor) | High — native build + store signing |
| VS Code webview | packages/vscode | Medium |
The backend is JavaScript, not TypeScript. packages/web/server holds hand-written ESM .js files, including a 137 KB index.js. TypeScript lives in packages/ui, packages/sdk and packages/electron.
One UI build feeds five surfaces. packages/ui is consumed by web, Electron, mobile and the VS Code webview — so a component change propagates everywhere, and must be tested everywhere you ship.
There is no generated UI code you are forbidden to touch, no closed binary in the frontend path, and no obfuscation. The practical ceiling is not permission, it is:
zustand), the streaming bridge and the settings registry mean a "small" change can ripple.Ranges assume you know React and TypeScript; they are estimates, not measurements.
| Goal | Layer | First version | Upstream merge pain |
|---|---|---|---|
| Full recolour, light + dark | Theme JSON | 1–4 h | None |
| Rail panel from your API | Extension | 1–2 days | None |
| Custom tool rendering in chat | Extension (manifest only) | 1–2 h | None |
| Slash command that attaches a ticket | Extension | Half a day | None |
| Status widget with a local service | Extension | 2–3 days | None |
| Git-host or browser provider | Extension service | 1–2 weeks | Low |
| New settings page | React source | 1–2 days | Medium |
| Reskin the chat timeline | React source | 1–2 weeks | High |
| New app shell / navigation model | React source | 2–4 weeks | High |
| Branded desktop installer | Electron source | 3–5 days | Medium |
| Branded iOS/Android app | Capacitor source | 2–4 weeks | High |
"Add a Deploy panel that shows CI status and can start a deploy." → extension. "Make the sidebar 40 px wider and move sessions to the left." → React edit. The sentence selects the layer.
Themes, settings, project actions, magic prompts, OpenCode config, skills and MCP servers cover a surprising amount of "customisation" with zero maintenance.
my-ext/
├── package.json # openchamber.contributes block
├── panel/index.html # sandboxed page
├── panel/main.js # built IIFE — the app never compiles TS
└── icon.svg # optional, package-local
bunx openchamber-guest-bundle panel/main.ts panel/main.js
# then: Settings → Extensions → add folder / zip / git URL
Install from a folder while developing, so edit → rebuild → reload is instant. Switch to a pinned git URL for distribution.
custom/theme, custom/shell — never mixed.| You want | Ship |
|---|---|
| Only config + extensions | Your themes/ folder, extension zips, a settings note — no fork at all |
| A branded web workspace | Forked packages/ui + packages/web, built bundle, Node/Cloudflare host |
| A desktop product | Forked Electron package with your icon, signing and updater |
| A mobile product | Forked Capacitor project, native builds, store accounts |
| Limit | Consequence for you |
|---|---|
| Extensions are sandboxed iframes | No host styling, no DOM access, no shared React state. "Make the app look like X" is never an extension. |
| No runtime compilation | You must build IIFE JS yourself; TypeScript sources in the package fail install. |
| Extensions load on web + desktop only | A customisation built as an extension is absent on mobile and VS Code. |
| Directory permissions are coarse | Filesystem access is all-or-nothing per declared path, not per file per call. |
| Services run unsandboxed | A service extension is a real local process with the user's access — a trust decision, and a security review point. |
| Background actions are short-lived | Long jobs need a service or an external API, not a background frame. |
| SDK version tracks the app | @openchamber/sdk@2.5.2 is the contract of 2.5.2. Preview builds exist but are not compatibility promises. |
| Upstream is active | Core patches will conflict. Theme JSON and extensions will not. |
With zero React edits you can build a genuinely different product surface: your own panels, pages, dashboards, menus, commands, integrations, tool renderers and even a git host — all themed natively. You cannot change the shell you live in: the sidebar, the chat, the composer, the settings pages. That is the line. Cross it only for changes you are willing to maintain.
main, app version 2.5.2, MIT.packages/sdk/README.md — manifest fields, capabilities, install rules, actions, pages, status sections, storage, services, UI kit.packages/sdk/API.md — full host/guest protocol, subscription and method list, limits, error codes.packages/sdk/DOCUMENTATION.md — architecture of the guest contract, service providers, surfaces, file editors.packages/docs/content/docs/extensions.mdx, sdk.mdx, sdk/host.mdx, sdk/ui.mdx — user-facing documentation.docs/CUSTOM_THEMES.md and packages/ui/src/lib/theme/DOCUMENTATION.md — theme authoring and validation.packages/extensions/DOCUMENTATION.md and registry.json — built-in allowlist, empty today.packages/ui/src tree (4,388 files, 40.6 MB), packages/web/server (hand-written ESM JS), read 27 October 2026.