For development commands, see AGENTS.md and the repository contribution guide.
The main window keeps its content visible while resizing or maximizing. Continuous resizing briefly batches expensive transcript and terminal measurements, with regular updates during the drag. Single adjustments use normal live layout; macOS releases the batch as soon as the native drag ends. Conversations, draft text and editor views remain mounted, with no resize cover, logo or reveal animation. On macOS the main WebView follows its native container immediately, including when embedded browsers are open. Browser children retain their own bounds. The opaque scene background is also painted underneath the WebView by AppKit, so newly exposed edges retain the page color while WebKit catches up. The sidebar keeps its native material. Theme changes and sidebar geometry update this paint; translucent or image-backed scene surfaces retain their original composition.
Release availability: smart mode is not available in this release. The GUI keeps its entry disabled, and the shared runtime rejects activation, including from older clients. Saved smart-mode configuration and history are retained; ordinary conversation execution does not invoke the Router or its summaries. Configuration files and legacy Router environment variables cannot enable it. The implementation details below document the retained experimental path.
Smart mode chooses between the primary model and an independently configured economy model for each main conversation step. Switching smart mode on or off changes the current conversation or draft, without changing the strategy default for future conversations. There is no remember-selection or scope control. Before a conversation or draft exists, the host strategy default owns the selection. Manual model and pool choices retain their existing session and future-default saving rules. Primary/economy role bindings remain shared model configuration.
Subagents keep their independent explicit model or pool selection, agent/default
settings, inheritance and Fork rules. Ordinary fresh subagents retain the fast
model fallback; enabling smart mode does not replace it with the primary or economy
model. Existing subagents retain their own binding when resumed. They do not
participate in Router classification or Router summaries. The legacy
ai.model_routing.subagent_model field remains readable for compatibility but
does not control subagent selection, and the GUI exposes no smart-subagent setting.
Choices made in a new conversation draft are applied on its execution host
before the first message is sent.
In Settings → AI → Models, add the Router as an API-key model, then open Router service to select it. No tokenizer file or remote tokenization endpoint is required. OpenBitFun estimates the input locally and uses the service's full prompt usage to tighten the budget independently for each execution. The Router must support the classifier protocol described in the deployment guide. Configure the primary and economy models, then save and test the connections before enabling smart mode in the composer. The dialog shows independent connection results for the primary model, economy model and Router service; the Router check also validates the classifier protocol. Saving configuration alone does not prove connectivity. Router request failures fall back to the primary model; each response records its actual model and fallback status. Smart mode automatically maintains Router context summaries with the economy model before input fills the budget. If the complete context still fits, pending or failed summaries do not interrupt routing; otherwise the current round uses the primary model until complete context is available. When an older conversation first enters smart mode, the execution host reads its visible persisted turns incrementally and builds a separate Router summary. Each accepted chunk saves its source cursor, so interrupted preparation resumes rather than losing the prefix. Preparation can continue between turns; the next routing boundary adopts the completed history together with new observations. Summary requests use the economy model's own lowest supported effort, independently of its chat default, and are bounded by that model's input window. Billing hints do not establish actual charges.
Older execution hosts without estimatedTokenBudgetSupported can continue saving
Router settings when a tokenizer path is already configured; the GUI preserves
that path. Without an existing path, the GUI asks you to upgrade the execution
host and blocks saving those settings. Upgrading only the controller does not
add this capability to the host.
Persisted conversations carry their independent Router summary into the next smart turn. Recovery validates the visible conversation history; reverting or forking never imports an abandoned or another conversation's summary. Legacy checkpoints without history proof or following a manual turn rebuild from the execution host's visible original turn records instead of the main context compression summary.
The execution host bounds Router requests per service (two running and up to 32 waiting by default). Root conversation groups take turns receiving capacity, with FIFO ordering within each group; one group can have at most eight waiting requests at the default limits. Idle slots can be borrowed. Queue time counts toward the routing timeout. Cancellation removes the waiter; overload and timeout retain the primary-model fallback. Optional background summaries use a separate fair queue, merge pending observations per conversation, and do not block model selection. These queues serve eligible main conversations; subagents and model-pool conversations do not enter the Router. Traces retain root, parent, session, turn and round IDs for isolation. A route that would discard images readable by the primary model falls back to primary; retries retain the model chosen for that logical round. Host startup overrides and persistence details are in the deployment guide linked above.
Peer Device Mode applies configuration on the execution host and requires a host advertising smart-routing support. SSH/Docker workspaces also use their owning OpenBitFun runtime's Router configuration, not configuration or local files on the controller. Mobile and bot model editors explicitly reject changes to smart sessions; use the GUI to change the strategy. External agents and approved immutable dispatch jobs retain their existing model bindings. Local fixture tests do not establish live remote execution or classifier quality. Before production evaluation, compare fixed-primary and routed main conversations for completion quality, total latency, model/cache usage and auxiliary request cost.
Sign in with the same OpenBitFun account on each device, using the same GitHub or email-code login method. The official Relay needs no user deployment. The official Relay and Same network (the embedded LAN Relay) can run at the same time. Each tab shows its own connection and connected browsers; Disconnect affects that tab only. Closing a connection code keeps an established connection running.
My devices, Peer Device Mode, account synchronization and account-device detached dispatch use the official Relay. Starting or stopping the LAN Relay does not switch their account or endpoint. LAN invitations advertise the selected network interface; select it again and create a new invitation after changing networks.
The official device session is restored on restart. The embedded LAN connection is temporary and starts when requested. Builds that previously saved a temporary LAN session in the account slot attempt to restore the official Relay from the shared identity; failed sign-in leaves the previous credential on disk. Signing out closes both connections.
Both modes require the online identity service for sign-in. Same-network mode does not provide offline login. See the Relay FAQ for privacy, service availability, and private-deployment requirements.
Choose Other agents → Paper Writing in the session composer and ask the
agent to draft or revise files in your workspace. Open the main .tex file
from the file tree or its chat link. The editor shows source on the left and the
typeset PDF on the right. Use Save & compile or turn on Auto compile
to save and recompile the main source after you pause. A failed build keeps the
last successful preview and shows the compiler log. The source and PDF panes can
be closed independently, restored from the header, and resized by dragging their
divider. Hidden source views retain the document and its undo history.
The source toolbar includes undo/redo, find/replace, a section outline, and
snippets for equations, figures, tables, and references. Command, environment,
and in-document label completions are available in Monaco. Select the main TeX
entry when editing an included chapter, or add a % !TeX root = ../main.tex
comment. Compiler diagnostics for the current file link back to their source line.
PDF review supports text highlights, sticky notes, comment threads, freehand ink,
annotation colors, resolution, deletion, and undo/redo. Reviews are automatically
saved beside the source as <source>.openbitfun-review.json; include that file
when moving or sharing the project. Save PDF writes an editable
<paper>.annotated.pdf to the workspace. Export annotated PDF saves a copy
on the viewing device. Saved PDFs embed their review state so notes and threads
can be edited again when reopened in OpenBitFun; other PDF applications see
standard PDF annotations. Replacing an existing output checks its current
version and asks before overwriting an externally changed file.
Annotations retain PDF page coordinates across compilation. Check their positions when a revision changes the layout; the preview shows a reminder after recompiling. LaTeX source remains the place to change manuscript text and figures.
The image uses a sample paper in an isolated UI fixture.
Install latexmk and the selected pdfLaTeX or XeLaTeX engine on the machine that
owns the workspace. For SSH, Docker, and WSL workspaces, compile in that
environment. In Peer Device Mode the selected Desktop host must advertise
LaTeX preview support. Mobile and bot surfaces can continue an existing writing
session and receive PDF files saved to the workspace by the agent.
Preview compilation uses temporary files and accepts PDFs up to 8 MiB.
It disables shell escape and skips latexmk configuration scripts.
Enable automatic compilation for projects you trust.
Put personal instructions for local OpenBitFun sessions in
~/.openbitfun/AGENTS.md (%USERPROFILE%\.openbitfun\AGENTS.md on Windows).
If OPENBITFUN_HOME is set, the file is read from that directory instead.
A non-empty file replaces Codex's global instructions, including
AGENTS.override.md. If the OpenBitFun file is missing, empty, or whitespace-only,
OpenBitFun keeps loading Codex's global instructions as before. OpenCode,
Claude Code, and project instruction files continue to load independently.
After editing the file, run /reload instructions in the session. The next
message rebuilds its instruction context. Remote SSH/Docker workspaces use
their workspace instruction files and do not load the controller's global files.
OpenBitFun selects one instruction family at the workspace root, in this order:
- AGENTS: the first non-empty file among
AGENTS.override.mdandAGENTS.md. - Claude: the first non-empty file among
CLAUDE.mdand.claude/CLAUDE.md, followed byCLAUDE.local.mdand.claude/rules/**/*.md. - OpenCode: local instruction files referenced by the project OpenCode configs.
Missing, empty, and whitespace-only files do not block a fallback. Once a
family supplies instructions, lower-priority families are skipped. A Claude
family containing only local notes or path-scoped rules still counts as selected.
Path-scoped rules load only after a matching file is read, and only when the
Claude family is selected. Use /reload instructions after changing the files
to rebuild startup context on the next message.
MiniApps use a customizable titlebar in independent windows, with native macOS traffic lights and the main application's right-hand Windows caption controls. The return icon brings the app back to OpenBitFun. Inside OpenBitFun, MiniApp actions live on the main top bar, alongside an icon to open an independent window. System app installation actions are available from More app actions. Customization uses the Creative mode icon; the default action group has no reload icon.
Each app can customize chrome.titlebar in its meta.json: title and icon
visibility, alignment, density, app button shape, semantic colors,
and app action buttons. Draft previews include the same titlebar renderer used by
the native shell. The titlebar logo is hidden by default; set showIcon: true to
display it. The settings travel with imports, marketplace packages, saved
versions and rollbacks. App actions emit chrome:action in the current view;
minimize, maximize, drag, resize, close and return remain host operations.
System button placement and appearance follow the host platform. Legacy
controlsPosition values are preserved but do not move system controls. macOS
keeps the main window's toolbar height and native control inset for both densities;
draft previews reserve that space without drawing imitation traffic lights.
Missing configuration uses the default custom titlebar. Existing exported apps
need Update system app and a restart to use the new Shell. Older hosts and
Shells remain compatible; custom chrome support is advertised as
miniapp_custom_chrome_v1 without changing the v1 launch-record format.
Installed MiniApp cards offer Open in app and Open in a window before launch, including when an update is available. Running state appears on the same card. Selecting the current destination focuses it; selecting the other destination transfers it after confirmation. App details provide Stop and Add system app without requiring a launch first. A tab also provides Open in a window and Add system app. Every generated icon carries the OpenBitFun mark in its bottom-right corner. System launchers are placed in the user's Applications folder on macOS, Start Menu on Windows, and application menu on Linux. System app names show the app title without internal IDs. Once added, app details and the tab header offer Update system app and Uninstall system app. Updating refreshes older numbered or oversized launchers. Uninstalling removes only owned system entries, retaining the MiniApp, saved data and running tasks; it can be added again later. Removing an entry through the OS is also detected when app details are reopened.
The system launcher can start OpenBitFun in the background. Closing the last tab or independent window stops that MiniApp's Worker, Agent runs and streams. During a window/tab transfer, a prepared destination keeps the instance alive; late close notifications from the source do not stop it. Stop ends an instance even while a presentation is open. A crashed shell is cleaned up after its lease expires. Refreshing, hiding a window or switching device surfaces is not a close. An explicit quit of OpenBitFun ends its host runtime. Window/tab switching reopens the document and retains saved data; save unsaved page state before confirming. The source closes only after the destination is ready. One installed app is bound to one workspace per host until its instance is stopped.
Each native window uses a separately built MiniApp Shell, which contains only the window, local system adapters and private IPC. Agent execution, storage, tasks and instance management stay in the shared OpenBitFun host. macOS exports copy this small executable plus the icon and app identity; Windows/Linux entries point to the installed shell. The full Desktop program is never copied into an export. Updating an older system app upgrades its executable while retaining its launch identity and saved data.
On macOS, Dock icons come from each MiniApp bundle's own ICNS. Independent windows and system installations use the same catalog glyph and fixed brand watermark, with a rounded body and transparent canvas inset. The shell always uses bundled resources, including beside a development host, so Tauri cannot replace the Dock icon with the OpenBitFun development icon. After upgrading an older exported app, choose Update system app, then quit and reopen that MiniApp to load the new executable and icon.
The normal development and packaging commands prepare the optimized shell
automatically. After changing shell Rust/HTML during an existing development
session, run pnpm run prepare:miniapp-shell before reopening the MiniApp.
Direct Cargo Desktop builds need the same preparation once. Packaging uses
Tauri's externalBin for platform placement and signing; raw builds require the
adjacent openbitfun-miniapp-shell executable (.exe on Windows).
An independent window displays its own Agent permission requests through the
shared runtime mailbox. Legacy MiniApps that call agent.cancelStaleRuns on every
reload must use explicit per-turn cancellation instead in managed instances;
automatic reload cleanup is rejected to preserve running work during transfers.
The independent shell uses the configured light/dark mode at launch; its native
chrome does not yet mirror live theme customizations from the main window.
Independent windows have an app-specific Hi chat panel, enabled when the app
claims the composer through app.chat.claimComposer(). The app can supply its
title, welcome text and suggestions; messages continue through its existing
chat:userMessage handler and Agent session. The panel follows only the app's
bound topic and supports streaming replies, per-turn Stop and approval hints.
Its launcher, floating panel, message typography/bubbles and composer compile
the same style sources used by the main window's Hi and FlowChat. This shares
appearance without adding React or the main window's runtime to the Shell.
Collapsing or releasing chat does not stop accepted work. After a MiniApp closes
or transfers to an independent window, the main window's Hi collapses when only
its pinned control conversation remains (or no tabs remain). Other conversation
tabs keep it expanded; topic switches and reloads do not trigger this cleanup.
Compatible hosts advertise
miniapp_chat_snapshot_v1 to restore the latest 16 turns plus current execution
after a reload or event gap; older hosts keep live chat and display the history
limitation. Existing system apps need Update system app and a restart to
load the new Shell chat assets.
Independent presentation is unavailable for remote workspaces, Peer Device Mode, mobile/bot control, and detached CLI jobs. These entry points do not silently launch a local copy. App content control is reserved but not yet advertised.
In Settings → Models, add OpenCode Zen for free models or pay-as-you-go access, or OpenCode Go with your Go API key. Zen uses the public key when the API key field is empty; paid models require your own key. Fetch the model list to select each model's Chat Completions, Responses, or Messages route.
Free-model requests include OpenCode gateway headers and map the session's Bash
and Read tools to the gateway's lowercase names. If the model requests a tool
that the session does not provide, the request ends with an explicit error.
The public free-model route uses matching session headers and excludes the old
request header. This compatibility applies to the public Zen API-key connection;
it does not reroute Console OAuth credentials. To use a free model on this path,
choose OpenCode Zen with API-key authentication and leave the key empty (or use
the literal public).
Console OAuth remains a separate Zen authentication option; existing account
credentials and saved model configurations are retained.
In Settings → Models → Subscription accounts, sign in to ZCode. ZCode asks you to choose Z.ai or BigModel when you start sign-in. Connected regional accounts stay visible separately under ZCode so each can be imported or signed out. Finish sign-in in the browser, return to OpenBitFun, choose Use as model, then select models from the live catalog. Authorization runs on the model host; the verification page can be opened on another device. Credentials are kept in OpenBitFun's vault.
ZCode browser sign-in reuses or creates the selected project's zcode-api-key
(a team's zcode-team-api-key) and uses the Coding Plan Messages endpoint.
Personal subscriptions and assigned team seats are supported. ZCode Start/gift
plans are separate routes and are not imported by this channel. A missing plan
or unavailable catalog returns an error instead of selecting another plan.
The real authorization-init endpoints were exercised on 2026-10-19. Completing sign-in and model inference require the user's account; initialization alone does not verify subscription eligibility or model availability.
WorkBuddy (China) and WorkBuddy AI (international) subscriptions were removed after the vendor stopped supporting subscription access through OpenBitFun. Existing model configurations and vault credentials are preserved: a model still pointing at either product shows a removed-provider notice instead of a sign-in or import action, and the account stays listed only while this host holds its state so it can still be signed out and cleaned up. Recreate those models as an API-key provider to keep using them, and do not reintroduce the retired request/authorization protocol.
Subscription accounts are grouped by service. Each account has its own identity, status, allowance and actions; identical email addresses retain distinct account markers. Add account starts a new slot on hosts that advertise support. Refreshing, importing and signing out target that exact slot; sign-out confirmation lists only models and pools which reference it. ZCode keeps one service heading with Z.ai/BigModel labels on its account rows.
Connected accounts show allowance in Settings → Models → Subscription accounts and beside selected accounts in the model editor. Codex shows its reported rate windows and additional model limits. ZCode (Z.ai and BigModel) shows its reported Coding Plan windows, with MCP calls shown separately. Totals, reset times and plan expiry appear only when the provider reports them. Other channels explicitly show that allowance lookup is unsupported.
OpenBitFun reads allowance from the account's host, supports manual refresh, and refreshes visible active panels once per minute. A failed refresh retains the last observation with a stale indicator. Unknown amounts stay unknown; zero and unlimited are distinct. These ZCode observations are informational and do not change model routing. Older peers without the quota capability show an unsupported-host state.
Grok OAuth accounts show the reported billing period and optional on-demand
usage in the existing subscription account panel. Reads use the selected account,
including its CLI headers. Peers require grok_quota_v1; older account-quota
support does not imply Grok billing support. These observations are display-only.
OpenCode Go API-key models show 5-hour, weekly and monthly usage in their connection
editor after the connection is saved. Keys stay on the configuration's host, and
older peers require the separate subscription_model_quota_v1 capability before
receiving model-scoped requests. Invalid keys and keys without Go entitlement show
distinct messages. Idle windows do not show placeholder reset dates. This endpoint
does not report monetary balances. Zen/Console balance lookup is unavailable while
the upstream balance API request
remains open; use Open console. Public free-model access explicitly states that
remaining free usage and reset times are not exposed. Console OAuth is never sent
to Go's API-key billing endpoint. Local HTTP/DOM fixtures cover these paths; live account
billing and physical remote hosts have not been verified.
The Models settings frontend was restored to upstream/main on 2026-10-16. That rollback withdrew the earlier account cards, quota panels, account/channel editors, enhanced usage displays, chat diagnostics and account-aware frontend API/Peer wiring. The current subscription sign-in and allowance panels are described above. Existing account credentials and configurations are retained; this UI rollback does not enable, disable or clear account pools.
The native backend still supports provider-scoped account slots, credential isolation and explicit account/channel failover. See the architecture and current limitations. Earlier full Desktop mock results refer to the withdrawn UI version and do not prove integration of these controls in the current Models page. Real providers and physical remote hosts remain unverified.
The backend retains account/channel usage attribution; the frontend has its original statistics display.
The workspace and session action menus include Open session storage directory. The workspace action opens its managed sessions root; the session action opens that session's subdirectory. Empty workspaces create the sessions root on demand. Remote SSH/Docker workspaces open their locally stored session mirror, not the remote project directory. These actions are unavailable in Peer Device Mode, browser clients, and for detached sessions owned by another host.
For manual recovery, exit every OpenBitFun instance using the session and back up its complete directory before editing or moving files. Moving a directory does not update the workspace bindings in its metadata or saved configuration.
Computer Use opens a visible control session. Its status card shows the current target and mode, with a preview and Stop control button. The preview displays the agent's virtual pointer separately from the captured application pixels. Stopping from the card, the operating system, or task cancellation revokes the session; a new explicit start is required. Sharing also ends when the turn ends.
The tool's start_control action selects observe, background (the default),
or foreground. Observation cannot send input. Background actions preserve your
foreground application and physical pointer where the application supports
semantic or directed input. A request that needs the desktop keyboard/mouse seat
returns a foreground requirement rather than silently switching control mode.
On macOS, native accessibility actions and an exact-window directed-input route
support background operation without taking over the human foreground window.
The directed route prepares only the target's internal focus, preserving the
requested mouse modifiers. Foreground mode remains separate and can use your
physical pointer and focused keyboard input.
The directed route passed a full five-action Tool/native program on a dedicated canvas: click, text, menu shortcut, scroll and emoji input, with one final observation. The observer kept its foreground focus and window order; its input and the human cursor were untouched. The measured 466 ms includes observation, not model reasoning. This proves the tested controls, not universal support for every application.
Claw, Cowork, Standard and Creative agents can use ComputerUse directly; the
ComputerUse subagent is optional. Known input sequences can use app_batch with
one final observation. Every step is checked before the first input, including
image identities, coordinate ranges and grid indices. A partial failure retains completed input receipts so
already submitted text or commands are not replayed. Visual models can target
unlabeled controls and canvases using pixels from the bound window screenshot;
missing accessibility labels do not by themselves prevent visual observation.
The capabilities reported by control_status describe available backend routes,
not support for every control or gesture. For a known search field that submits
on Return, combine text with focus and a Return key step, then inspect the
results before selecting one. Focus-and-type by itself is already one call;
batching cannot repair an unavailable input route.
ComputerUse also reads and writes plain text on the runtime host's clipboard:
{"action":"clipboard_set","text":"Text to copy\nincluding another line"}
{"action":"clipboard_get","max_output_bytes":65536}These actions work on Windows, macOS and Linux without activating an application.
They do not paste or submit text. clipboard_set replaces the clipboard; an empty
string clears its text. Unicode and line endings are preserved. Text containing
NUL or exceeding 16 MiB is rejected. clipboard_get returns text, the original
byte_length, and truncated; its output limit defaults to 64 KiB and caps at
1 MiB, ending on a UTF-8 boundary. The older max_bytes name remains accepted.
Windows uses the built-in Windows PowerShell and Windows Forms clipboard APIs;
macOS uses AppKit through the built-in JavaScript bridge. On Linux, install
wl-clipboard for Wayland, or xclip/xsel for X11, and run OpenBitFun in that
desktop session. Wayland does not silently fall back to an XWayland clipboard.
Helpers have a ten-second deadline. A failed write may already have changed the
clipboard; inspect it before retrying. Image/file clipboard formats are not
exposed by these text actions.
Clipboard access obeys the ComputerUse session and Stop. Window-only sharing does not grant access to the global clipboard. SSH workspaces and headless hosts reject these actions; a remote controller uses the executing desktop host's clipboard, never its own local clipboard as a fallback.
On macOS, persistent ScreenCaptureKit window sharing uses the system's sharing controls. macOS 12.3 or newer is required for capture; system indicator appearance depends on the OS version. Hidden or minimized targets must be restored before observation. On Windows, Windows.Graphics.Capture retains the native capture border. Background controls use UI Automation and supported window messages; standard Edit/RichEdit fields preserve unselected text when inserting or replacing the current selection. A field may be selected by its observed node, image coordinates, or the bound window’s existing focused native edit control. Unsupported providers return a capability error. These Windows paths have cross-compilation coverage; interactive Windows acceptance is still pending. Windows does not have the macOS Sharing menu.
Windows accessibility requests return after ten seconds if the application's
provider stops responding. Stop remains available and prevents input that has
not yet been submitted. An INPUT_OUTCOME_UNKNOWN error means the application
may still complete the input: wait for it to recover, then observe before taking
another action. OpenBitFun does not automatically repeat the click or text. While
that native call is still running, new accessibility requests return
AX_PROVIDER_BUSY; a provider that never returns requires the target to recover
or OpenBitFun to restart. A missing preview only means no valid image is available
(including observations requested without images); it does not mean that the
control status is unavailable.
See the cross-platform control contract for platform limits, lifecycle guarantees, and native verification scenarios.
On Ubuntu GNOME, Computer Use uses the system desktop portal for screen sharing and foreground input. Start a control session, then approve the surface and input devices in the system dialog on the machine running Desktop. GNOME owns its sharing indicator and stop control. Stopping sharing revokes further input; OpenBitFun never falls back from Wayland to X11 input after denial or disconnect. Foreground portal input controls the desktop seat and can move your real pointer.
Background application operations use AT-SPI semantic actions and EditableText when the application exposes them. Read the application state first, then use its node index. Background coordinate clicks, arbitrary key chords, and games without accessibility support require foreground mode. A portal-selected image is bound when portal authorization completes and is not automatically assumed to belong to an AT-SPI application selected by PID. A running session cannot silently rebind its stream within the same generation. Applications must expose accessibility information for semantic control.
The .deb package declares GStreamer and portal runtime dependencies. For an
AppImage or a directly launched development binary on Ubuntu, install:
sudo apt install gstreamer1.0-pipewire gstreamer1.0-plugins-base gstreamer1.0-plugins-good xdg-desktop-portal xdg-desktop-portal-gnomeSource builds additionally need libgstreamer1.0-dev and
libgstreamer-plugins-base1.0-dev. A running user D-Bus, PipeWire, and a compatible
desktop portal backend are required. Other desktops must install their own
matching portal backend; installing GNOME's backend does not give a headless
server a desktop. Missing plugins or portal capabilities produce explicit errors.
Remote controllers see the execution host's authorization state. System portal consent may require a person at that host. SSH workspaces and headless CLI jobs do not acquire a GUI or control the controller's local desktop implicitly.
For focused Linux verification, run
node scripts/test-linux-computer-use-native.mjs -- --test-threads=1.
bash scripts/test-linux-computer-use-atspi.sh creates its own GTK controls and
checks semantic activation and Unicode selection replacement while preserving
unselected text.
For a headless fixture run, use
dbus-run-session -- xvfb-run -a bash -lc 'export NO_AT_BRIDGE=0; bash scripts/test-linux-computer-use-atspi.sh'. bash scripts/test-linux-computer-use-portal.sh requires a
real desktop and asks you to select its dedicated fixture window in the system
picker. These fixtures require Node.js 22/24, Rust, python3-gi and GTK 3; the
portal fixture also needs the runtime packages listed above.
Development uses the committed macOS ICNS fallback and does not require Icon
Composer. Packaged macOS 26/27 builds also include the native Icon Composer
catalog; earlier macOS releases use the static fallback. Building a distributable
macOS bundle requires full Xcode 26 or newer selected through DEVELOPER_DIR or
xcode-select. The packaging command rejects a missing catalog instead of
silently shipping only the legacy icon. See the
brand resource guide for regeneration
and focused validation commands.
The menu bar uses the solid ring brand mark as a transparent template logo that follows the system's light and dark appearance. The number beside it counts sessions with unread completed, failed, or interrupted results in the current device's loaded session list. Transient and internal subagent sessions are excluded. Viewing the result in a focused conversation acknowledges it; the number disappears when none remain. Clicking the menu bar icon always shows and focuses the main window, including when it is already visible; repeated clicks never hide it. Opening the main window alone does not mark other conversations as read.
When viewing a peer device or SSH workspace, the count follows the displayed sessions and updates the controller Mac's menu bar. It does not change the remote host's tray or execution state.
New versions appear in a fixed 368 × 224 px bottom-left card that stays until an explicit action. Only narrow viewports constrain its width; content and update status do not resize the card. Clicking outside does not dismiss it. The card shows up to three lines from the first paragraph of the release notes, omitting Markdown formatting and decorative content. Releases without a summary use a quiet brand imprint and fine rules in the reserved body area, without placeholder copy. The actions remain at the bottom, and long error messages use a two-line preview with the full text on hover. Errors reduce the introduction to one line to keep feedback and retry controls visible. Select the version title to open the full release notes in a 600 × 480 px details dialog, constrained by the viewport. Both cards share a frosted surface with an even, softly blurred blue and cyan tint at low intensity. Semantic color and blur tokens keep this material consistent across themes. Dark mode gently lifts the base, adds a broad soft reflection and a thin inner highlight, and retains a muted cyan companion to the icy blue tint. High-contrast and reduced-transparency modes use a solid surface. The dialog header shows the product and release version once. Release notes are always expanded, with a clear introduction, compact section headings, and evenly spaced list entries. Typography uses semantic dialog-title, body-md, section-heading, and supporting-text roles; introduction and details share the same standard body size. A space-8 gap separates the title from the reading area. The header, reading column, and footer share one inset. The body scrolls while the standard dialog footer keeps update actions and download progress visible. The footer uses the primary action for download or install and a neutral action for skip or restore; downloading shows only a progress bar. No-summary releases center a larger official brand mark on the same frosted surface, filling the reserved body without placeholder copy or a repeated version number. Closing the card or reading the notes keeps a blue dot on the bottom-left settings gear and Check for updates in its More menu. Hover to see the available version, or select the menu item to reopen the download/skip card. Skip this version suppresses reminders for that version only. An explicit manual check can revisit a skipped release without restoring automatic reminders.
Choose More → Check for updates for a manual check. The single menu row shows
only the action and its temporary checking state. Up-to-date, already-downloaded,
and failure results use the notification system; an available release reuses the
download/skip card. Results still arrive if the menu closes during the check,
and reopening the menu does not replay them. Checking never opens About or starts a download.
The menu item is always present in desktop builds, including development.
Manual checks use the configured update feed. Development builds skip automatic
checks and do not restore discovery from previous runs; starting the app or opening
More cannot create a new-version reminder without a manual check in that run.
Packaged builds restore only valid discovery for the installed host version.
Available releases must have a higher semantic version: an equal version, an older
release, or build metadata alone never creates a reminder. The displayed -dev
suffix is a UI label and does not participate in update comparisons.
Browser-only surfaces omit desktop update actions.
About only contains
product identity and build information.
Choose Download update to download and verify an update while continuing to use OpenBitFun. Downloading does not install or restart the application. When the navigation footer is visible, a dot travels from the download button to the circle beside More. Only after it arrives does the navigation circle appear and the card close. The circle fills from bottom to top using downloaded bytes, with a gently flowing wave along the rising surface. Hover for the percentage, or select it to reopen the progress card. Hover subtly enlarges and brightens the circle itself, and pressing gently compresses it. The surrounding hit area stays transparent, while keyboard focus retains the design-system focus ring. Reduced motion keeps this feedback stationary. The progress card retains the version and introduction, with only a progress bar along the bottom and no status sentence or percentage text. Until the total size is known, a shallow wave flows continuously along the bottom of the same circle, with a steady fill level and a tooltip showing the downloading status. As soon as the total arrives, the same wave continues flowing while its level follows real byte progress. No dot-grid spinner or fabricated percentage is shown; reduced motion disables the wave animation and fill transitions. Keyboard activation and reduced motion use an immediate handoff; hidden navigation keeps progress in the card.
When the download finishes, the navigation circle changes to a solid success color with no border and a contrasting checkmark. No completion card or dialog opens automatically. Select the checkmark to open the installation confirmation directly. More no longer shows a reminder for the downloaded version. Failed downloads return to the reminder in More; an older downloaded package keeps its own installation checkmark if a newer download fails. Installation restarts OpenBitFun on this device and interrupts its active sessions. Choosing Later, or closing the confirmation, keeps the downloaded update. The checkmark remains available whenever you are ready.
Downloaded updates remain available after closing and reopening OpenBitFun. After reopening, the current updater requires access to the update server to restore installer metadata, but does not download the package again. If this step or installation fails, the pending update remains available to retry. Use Download again in the error dialog if the cached package is damaged.
Application updates always belong to the local desktop, including while viewing a peer device or a remote workspace. They do not install software on the peer or cancel independently running detached jobs on another host. Connections through the restarting desktop are interrupted.
Choose Work location → This computer, then Work environment → WSL. Select an installed Linux distribution, optionally enter a Linux user under Advanced settings, then connect and choose a folder inside that distribution. Refresh distributions reloads the list after you install another distribution. No SSH server or SSH credentials are needed. WSL must already be installed and the distribution must have completed its first-run setup on the Windows host.
Files, search, Git, Agent commands, and terminal sessions use the selected Linux filesystem and processes. Paths use POSIX separators. Saved connections retain the distribution and optional user for reconnect; omitting the user uses the distribution's configured default user.
In Peer Device Mode, the selected Windows Desktop host owns WSL discovery and execution. Older peers and CLI peers explicitly refuse native WSL setup; non-Windows hosts show an unsupported state. Existing mobile and bot session controls can continue driving a host session, but do not expose WSL connection setup. Detached Dispatch does not provision WSL connections. SSH port forwarding is unavailable for native WSL targets; use Windows WSL networking to reach a Linux service.
If writing files and browsing directories both start failing with
Limit exceeded: handle limit reached, update OpenBitFun to a build containing
the SFTP handle-lifecycle fix. Earlier builds can exhaust a client-side counter
even when the server has already closed the files. Save ongoing work before
manually disconnecting and reconnecting the remote workspace as a temporary
recovery; reconnecting can interrupt its terminals and commands.
This message alone does not establish a server configuration problem. Raising
server limits only delays a leaked-counter failure. Running ulimit in a new
SSH shell does not change the limits of the already-running SFTP subsystem.
OpenBitFun does not modify the remote user's shell startup files, SSH daemon
configuration, or OS limits automatically. If the problem persists after the
fix, capture the OpenBitFun version and logs plus the server's SFTP implementation
and advertised limits so genuine concurrent-handle or server resource exhaustion
can be distinguished from a client lifecycle problem.
Run pnpm run desktop:dev from the repository root. The launcher prepares
Flashgrep and the locked Sherpa speech libraries before compiling Desktop.
Sherpa libraries or archives are reused from the current target cache or the
main Git checkout's target cache. If absent, curl downloads the version-specific
archive, supporting HTTP and SOCKS proxies; Cargo handles extraction and linking.
Explicit SHERPA_ONNX_LIB_DIR and SHERPA_ONNX_ARCHIVE_DIR overrides are preserved.
Development shares the installed app's existing settings, workspaces and sessions by default. Both launchers keep a separate development host channel and retain the existing session writer and scheduler leases:
pnpm run desktop:dev
pnpm run desktop:preview:debugUse --isolated-data for separate development data under the current checkout's
.openbitfun/desktop-dev/{user,home} directories. Existing isolated data stays in
place and remains accessible with this flag; switching modes does not move or
delete sessions. Explicit storage environment overrides remain authoritative,
and --shared-data is still accepted as an explicit alias for the default.
To run an independent instance in another worktree, isolate its data and choose different ports. Changing ports alone does not separate the development host that owns the shared product data:
OPENBITFUN_DEV_PORT=1432 pnpm run desktop:dev -- --isolated-dataHMR uses port 1431 in this example; OPENBITFUN_DEV_HMR_PORT can override it.
The launcher supplies the same HTTP URL to Tauri that Vite listens on, and both
the main window and companion window read that configured URL.
Desktop Package uses Certum SimplySign on the hosted Windows runner. Configure
these repository Actions secrets before publishing a release:
| Secret | Value |
|---|---|
CERTUM_USERNAME |
SimplySign login account |
CERTUM_OTP_URI |
Full otpauth://totp/... provisioning URI, including its original algorithm, digits and period |
CERTUM_KEY_ID |
SHA-1 fingerprint of the activated Code Signing certificate |
The OTP URI is provisioning data from the activation QR code, not a current mobile token, an email activation code or the certificate PIN. Do not paste it into an issue, PR, log or online QR decoder. Certum's activation instructions describe the activation-link email and separate activation-code email used to show the QR code. If the original provisioning data is unavailable, contact Certum/the reseller about regaining access; do not assume the Desktop login can export it. Replacing the provisioning seed also requires updating the CI secret and potentially reactivating the mobile app.
The workflow compiles with --no-bundle, then opens the SimplySign session.
--bundle-only in the Desktop build wrapper runs tauri bundle using the same
product and updater configuration, without recompiling. Tauri signs the NSIS
payload and installer before generating updater .sig files. Since Tauri
restores the unsigned raw Desktop EXE after bundling, the workflow separately
signs that EXE before the custom installer hashes and embeds it. Finally, it
signs the custom installer. Subsequent release staging copies/renames those
bytes and generates the existing updater/manual-download signatures.
Verification requires Windows Authenticode trust, the configured signer and a timestamp. Any failure blocks artifact upload. A publication run requires all three secrets; an artifact-only run with none configured explicitly builds unsigned packages. Partial configuration always fails. No PFX/private-key export is required, and the existing Tauri updater key remains unchanged.
Login uses a pinned community action, not an official Certum CI API. Its GUI login compatibility and any additional certificate PIN prompt must be validated with the actual account before the first signed release. Diagnostic screenshots are disabled. A timed-out signing step must be investigated rather than bypassed. Only trusted release code should receive the secrets. Code signing identifies the publisher; it does not guarantee that SmartScreen reputation warnings vanish.
Focused checks:
pnpm run check:github-config
node --test scripts/desktop-tauri-build.test.mjs OpenBitFun-Installer/scripts/build-installer.test.cjs
pwsh -NoProfile -File scripts/ci/sign-windows.test.ps1The PowerShell test uses mocked signing results; a Windows build with the real certificate is still required to prove cloud signing and timestamp/trust validation.
Open the Skills scene, choose Skill Marketplace, then Marketplace sources.
The default source is the enabled official https://skills.sh marketplace. You can
edit, disable or remove it, add sources, and save an empty list to stop searching.
Each source has its own name, API format (skills.sh or SkillHub), deployment
root URL, optional Bearer API token and enabled state. Include any deployment
subpath, but do not append /api/search or /api/v1 to the root URL.
All enabled sources are searched concurrently. Results are interleaved, labelled
with their market name, and retain their installation identity. Errors from one
source are shown without hiding successful sources. The original SKILLS_API_URL
environment override applies to the default official skills.sh URL; an explicitly
configured custom URL takes precedence. Disabling or deleting the official source
also disables its environment override.
SkillHub uses its ClawHub-compatible search API and native ZIP download API, and
installs supporting files into OpenBitFun's user or project Skills directory. An
existing destination is preserved and reported as a conflict. skills.sh-compatible
sources use the existing repository-based npx skills installer: a custom search
API does not supply a custom ZIP download protocol.
Tokens are stored in the serving host's application configuration; browser login sessions are not shared with OpenBitFun. The market is currently available in the local Desktop scene. Peer mode and older hosts show unsupported states. SSH/Docker project installation remains unsupported; choose user scope to install on the serving host. Mobile/bot controls, CLI peers and Detached Dispatch do not gain a marketplace configuration or installation entry point.
Send an ordinary file or a native video in the paired WeChat chat to make it available to the
selected Agent session. OpenBitFun downloads and decrypts the file, saves it in
a unique .openbitfun-inbound-<id> directory inside that session's workspace,
and supplies its path to the Agent. Existing files are not overwritten. File
contents stay out of the message text; the Agent can read the saved file using
its tools. Files remain available for later turns and can be removed manually
when no longer needed. On POSIX filesystems, directories use 0700 and files
use 0600 permissions.
A message accepts up to five files totaling 30 MiB. Missing download information, failed downloads, and unavailable workspaces produce an explicit error instead of submitting an empty message. Complete pending menu selections or approvals before sending a file.
SSH workspaces use the session's remote filesystem and never fall back to the
host's local filesystem. Cross-device bot sessions negotiate session_attachments_v1,
transfer the bytes through encrypted device RPC, and use the path returned by the
execution host. Upgrade an older target host when prompted; file input is never
silently omitted. Accepted writes finish on the target even if the controller
disconnects. Retries with the same attachment identity reuse identical saved bytes
and reject changed or incomplete content. This input path does not submit
attachments to Detached Dispatch jobs.
Messages from one WeChat peer finish attachment preparation and turn admission in
receive order. Model replies run independently, so approvals and follow-up input
remain available. Failed image downloads also reject the message with a visible
error instead of silently submitting empty or partial media input. Native videos
are delivered as unchanged .mp4 files through the same local/remote path;
video analysis depends on the Agent's available tools. Voice messages without
transcripts currently return an unsupported notice.
While the WeChat bot is running, completed turns in its currently selected local session can send their text to WeChat without a new incoming message. This includes scheduled jobs and turns started in the desktop window. Turns started by that same WeChat bot retain their normal reply path and are not pushed a second time. Failed turns and empty output are not pushed. Use a dedicated session if desktop activity should not appear in WeChat.
Delivery still needs a valid WeChat reply context and available channel quota; this feature does not bypass either restriction or impose an additional daily three-message limit. Several queued results are combined into one reply of at most 4,000 UTF-8 bytes, favoring the newest results. Longer output is truncated; the full answer remains in the session. These pushes share the channel quota with normal replies, including any split replies.
Unavailable reply context and send failures keep output in a bounded, in-memory queue for retry. Sending another WeChat message refreshes the reply context and wakes the sender. The queue holds at most 20 results per recipient for 24 hours; older entries are discarded. Restarting or replacing the bot clears pending output. This is best-effort notification, not a durable message inbox. Switching sessions or devices discards output from the previous selection before sending.
Proactive notifications currently require the session runtime and WeChat bot to run on the same OpenBitFun host. A session on that host may use an SSH workspace; this does not make it an account-device session. When switching the bot to another account device, the existing reply includes an explicit notice that proactive scheduled-job and desktop-result notifications are unavailable there. Ordinary WeChat-initiated requests keep their existing cross-device reply path. This feature does not add cross-host Peer Device or Detached Dispatch result delivery.
For a vision model that accepts only a limited number of images, set Images
per request in its model settings (for example, 3). Leave it empty to
keep the existing image-history behavior. The saved model field is
max_images_per_request; it limits input images, including user attachments,
retained history, and tool results, rather than generated images.
User submissions above the configured limit are rejected before entering the conversation. Pools and channel routes use the smallest configured limit among their enabled vision models when admitting new attachments, so fallback models can accept the same batch. Each model request keeps the newest image-bearing messages within that limit and includes an omission notice for pixels left out. The Agent can read omitted files again in smaller batches; the original conversation is retained. Plain text continuation also applies the limit, including to old tool-image history. Each selected model or fallback channel uses its own limit at the executing host; older hosts must be upgraded to enforce this optional setting.
On a development build with Smart mode enabled, a classifier service can use the existing OpenAI Chat Completions configuration. Add its API service and model in Settings > Models, then select it in the Smart mode configuration. For the local classifier service, use:
| Setting | Value |
|---|---|
| Request format | OpenAI Chat Completions |
| Base URL | http://127.0.0.1:8767/v1 |
| Model | router-qwen35-0.8b |
| API key | local-router placeholder when the model editor requires a value |
| Context window | 32768 |
| Router compatibility | Classifier Router |
| Request timeout | Start with the service's documented allowance, such as 120 seconds for cold or long inputs |
Use Save and check connections in the Smart mode configuration. Once the model is selected as the Router service, its model-card and editor checks also use the same classifier protocol with the configuration being tested. An unselected ordinary model still receives chat/tool checks. The existing primary and economy models remain independently configured. Router classification and automatic history preparation do not change subagent model selection.
The Classifier Router profile sends exactly two full system/user messages,
uses router.probabilities.simple_type for the existing economy threshold,
and consumes complete usage.prompt_tokens for input-budget calibration.
It serializes classifier calls across sessions within one execution host and
connects directly to loopback endpoints. Other processes using the same
single-concurrency service can still receive a busy response. Every request
keeps the full prepared input; prefix caching stays on the service.
The default compatibility profile preserves the existing token-logprob Router wire format. An older execution host that does not advertise Classifier Router support cannot configure this profile through the GUI. Unsupported future profiles remain readable and fail explicitly rather than using another protocol. Loopback addresses refer to the machine running the Agent, including a Peer host; an SSH workspace alone does not move the Router off the execution host.
In Settings → AI models → Model pools, create named pools from existing model configurations. Add/remove members, move them up/down, and choose Ordered or Balanced. In a native chat's model menu, switch to Battery mode and choose a pool. Its summary shows the pool name, the last accepted concrete member and the routing strategy. Click the pool name to choose another pool; manual mode continues to list only individual models. Pool requests use each member's configured defaults; session reasoning overrides are hidden while a pool is selected. A successful explicit model or pool choice is remembered for new native chats. Existing chats keep their selection; automatic pool failover does not change the preference. Subagent defaults, built-in overrides and fork defaults accept a model, a pool, or parent inheritance; a child owns its binding.
Ordered uses the first eligible member when establishing or replacing a binding. Balanced rotates new assignments across eligible members. Both preserve a healthy chat binding, even when a higher-priority member recovers or the pool is reordered. This balances assignments within one Runtime, not tokens, cost, or work across independent hosts. Native child agents that inherit the parent choice inherit the pool and receive their own assignment; externally approved fixed bindings stay fixed.
Health comes from actual requests, without background quota or balance queries. Network failures, throttling, quota exhaustion and unavailable providers cool down; authentication, billing and permission failures require attention. After cooldown, one live request probes the member. The Retry action permits another attempt after repairing configuration. Disabled, deleted and incompatible members are skipped. Health is shared when multiple pools refer to the same model configuration.
Image requests are checked before submission and again when selecting a member.
A text-only member remains eligible when it can call analyze_image, the Agent
allows that tool, and a separate enabled image understanding model is configured.
Otherwise the pool skips it and selects a compatible multimodal member. Images
in inherited context and later user steering follow the same rule; tool-result
pixels require native image support. Request incompatibility does not mark a
member unhealthy or change the pool's shared context budget.
Each member retains ordinary response recovery, up to ten attempts including the initial request, with the existing early-stop rules. Before effective output, network failures, timeouts, throttling and unavailable providers switch promptly when another eligible member is ready. Without an alternative, the same member retains ordinary retries. Authentication, permission, billing and quota failures can also switch members; invalid requests, content-policy rejections and cancellation cannot. Empty or malformed responses and unclassified provider failures use member-local recovery first, then may switch without marking that member globally unhealthy.
Pool traversal has no shared ten-attempt cap: each failed member is excluded for the current recovery traversal, so an eleventh member remains reachable. Retry backoff and cooldown use the current member's attempt count. Failover is forbidden once any attempt in the logical round has produced effective output, even if a later attempt fails before output. Existing same-model stream recovery remains available; cross-member recovery never replays output or completed tools. Switching updates model-specific prompt/tool configuration and provider replay projection. If no compatible member is available, the request fails with member states and the earliest known retry time; it never silently uses the primary model or another pool. Deleting a pool preserves chat references and any default pointer so users can explicitly select a replacement. Pool edits run under the main configuration write lock; concurrent edits to the same pool use the last saved value without stale-draft conflict detection.
Pool chats and subagents use the smallest resolved context window among valid, enabled text members. The compression trigger is the smallest member input budget (its context window minus its output allowance and safety reserve), which can come from a different member. Offline and cooling-down members still contribute; request-specific tool/image filtering does not expand the common budget. Member configuration and output limits remain unchanged. Automatic and manual compression share this budget, and each model round refreshes it after configuration edits. The selector tooltip shows the pool window and compression trigger above the current member's details. Limits come from the execution host, including Peer Device Mode; the controller never substitutes its own model configuration.
Pool definitions are stored in the main configuration at ai.model_pools.
New pools receive an ID such as model-pool-a7k2m9: six random lowercase letters
or digits after the prefix. The execution host checks for collisions while saving;
renaming or editing a pool preserves its ID and chat references.
Typed defaults live in ai.agent_model_selections (mode, plus
subagents.default, subagents.builtin and subagents.fork). An absent selections
section is converted from ai.agent_model_defaults during configuration loading
and persisted atomically. Migration only adds the new field to the existing file;
startup recovery does not rewrite unrelated source records. Once present, the
whole typed section is authoritative, including empty builtin overrides. Invalid
or null selections are retained and fail visibly instead of falling back.
Imports, whole-config/AI replacements and installer-config repairs perform the
same migration at their ingestion boundary. Fresh configs and resets initialize
typed defaults directly. Runtime reads require the new section; ordinary field
updates and saves do not perform legacy migration.
Legacy defaults remain readable by older versions but are no longer updated.
Legacy client write requests are translated into typed updates on the new host.
Old versions may discard new fields when rewriting the main config; preserving
new settings across downgrade/write/upgrade is not guaranteed. The unreleased standalone pool file is no longer read.
Runtime health, cursors and bindings remain in model-pool-runtime.v1.json;
session metadata retains selection intent and the last accepted binding. Runtime
state and pool records do not contain copied API credentials. Pool definitions
and their containing section have no edit revision fields. Typed operations require the execution
host's advertised model_pool_selection_v1 capability.
Desktop supports local and SSH/Docker workspace sessions through the shared Runtime.
BTW side questions inherit the parent's pool and current binding under both
routing strategies, preserving cache reuse while that member remains usable.
The BTW selector can explicitly choose another pool or a fixed model, including
before the first selected-text question. Peer hosts must advertise
btw_model_pool_selection_v1 for these typed initial choices; older hosts retain
the inherited path without silently replacing a pool with its current member.
Peer Device Mode routes pool operations to capable Desktop/CLI hosts. Standalone browser clients, detached dispatch, and ACP/external-agent model selection do not expose pool selection in this version. Mobile/IM clients can continue a host-owned pool chat, but their legacy model-change operation explicitly refuses to replace a pool; use a capable desktop controller to change that selection. Defaults are snapshotted when native sessions are created; unrelated auxiliary model slots and externally approved immutable model bindings keep their rules.
Validation uses local unit tests, mock HTTP streams, transport capability tests, and host compilation. These do not establish live SSH/Docker, relay/mobile/IM, Peer Device or detached-dispatch behavior. No interactive UI validation was run.
Open Project resources and click the large project icon in the header to edit it. Choose a built-in Lucide icon for code, learning, design, documents, research, websites, mobile apps, games, data, business or tools. The Life & hobbies group includes travel, food, fitness, music, reading, home, finance, photography, pets, nature, shopping and movies. Apply a preset or custom HEX color to any built-in icon. The default shape remains a folder for local projects and a server for remote projects. Alternatively, upload a PNG/JPEG/WebP (up to 5 MB and 20 megapixels). The square editor centers light/dark previews below a fixed mode switch. Click either preview to open the built-in icon picker; choosing a glyph closes the picker. Custom colors also open separately, keeping the editor size stable. Image mode supports centered square cropping and zoom independently for each theme. Click the light or dark preview to upload its image; the emphasized Choose image button always sets the default image, preserving any separately selected dark variant. An existing single image works in both themes until a separate dark image is set. Uploading a dark image first also supplies the light/legacy fallback, which can then be replaced independently. To return to the original icon, select Built-in icons, choose the Default glyph and color swatch, and save. Closing the editor cancels its draft.
Icons belong to the workspace catalog on the selected OpenBitFun host. Desktop and CLI Peer Hosts support reading/editing them; mobile Remote Control displays them read-only. Older peers and the retired standalone Web Server explicitly do not offer editing. SSH/Docker projects store this presentation metadata on their catalog host, so an offline execution filesystem does not need to be opened. Worktrees inherit their main project's icon. Separate hosts opening the same Git repository do not automatically synchronize their catalogs.
Glyph choices are advertised by supportedGlyphs in the icon snapshot. Hosts without
that offer retain color/image editing; glyph writes re-check the offer before submitting.
Legacy color-only edits preserve the stored glyph. Unknown future glyphs retain their
saved identity and display the default artwork with an availability hint. Separate theme
uploads similarly require the supportsThemedImages snapshot capability. Older controllers
keep working with the base image, and their image edits preserve the stored dark variant.
When one image asset is unavailable, current controllers use the available variant with
a load warning and keep both saved references intact.
Uploaded images are normalized and stored under the host's user data workspace-icons/
directory; moving/deleting the source image does not change the saved icon. Include that
directory together with workspace_data.json in a full backup. A workspace metadata-only
export does not embed image bytes; missing assets display the default icon with a load warning
and preserve the saved reference. Unreferenced assets are retained; resetting an icon does
not delete another record's shared image. Unknown icon versions remain intact.
