Desk stores configuration in SQLite through its Rust configuration API:
The config_* tables hold projects, groups, sessions, provider resume IDs, profiles and settings. Channels uses separate tables in the same database. The public APIs remain separate; callers never write tables directly. On first use, Desk saves the original desk.yml byte-for-byte as desk.yml.before-sqlite, then imports it transactionally. The old path becomes a migration marker. Restarting resumes an interrupted import from that backup, but never overwrites an initialized database with stale YAML. Invalid input leaves the original unchanged. The header’s configuration editor presents YAML generated from the database. Saving validates it and checks the revision read by that editor. A concurrent change produces a conflict instead of losing someone else’s edit. Local readers share an in-memory snapshot; one revision observer serves their subscriptions. Authority checks read the committed revision directly. Plain file edits to the old YAML path no longer change configuration. For explicit export/import:
Import and desk init --force retain a separate backup of the previous configuration. desk config prints the actual database path. --file retains the selected configuration’s identity; its database is channels/channels.sqlite beside that path. The separate FileSnapshot owner still serves the custody current index, node identity and scheduler state. It shares reads within a process; it does not replace the domain’s interprocess lock or durable write procedure. Scheduler status uses this view, while dispatch guards, snapshot export and mutations read fresh state. An active Stop continues observing executions, but unchanged targets require no state transaction; changed targets are committed together before any interrupt. Use a different manifest with --file:

Public origin behind a proxy

DESK_PUBLIC_ORIGIN is a server environment variable, not a manifest setting. Set it to the exact browser origin for each Desk instance behind an authenticated reverse proxy, including its scheme and optional port, without a trailing slash:
The proxy must preserve the browser’s Host. Desk does not infer trust from X-Forwarded-Host or X-Forwarded-Proto. The configured origin adds one public host to the localhost, 127.0.0.1, and [::1] operator hosts; local desk custody commands continue to work. Operator POST requests must also carry the matching Origin and must not be marked cross-site. Without this setting, public-host requests to GitHub App setup, custody execution (status, Stop, resume, acceptance), and writer-authority recovery return 403. A server listening on a non-loopback address logs a warning when the variable is absent. An invalid configured origin returns operator-public-origin-invalid; GitHub setup reports the same errors with the github-app-setup- prefix. Persist the variable in the deployment’s configuration generator so provisioning or reseeding does not remove it. Apply it using the deployment’s restart procedure; cloud bootstrap credentials may need renewal. This setting does not authenticate users or replace the proxy’s access control.

Top-level shape

The manifest has four top-level blocks:
  • profiles: named provider accounts a session can run under
  • groups: root-level agent groups that are not tied to a project
  • projects: named work roots with their own groups and sessions
  • settings: UI and subsystem state

Projects

Projects give Desk named working roots. Project-scoped sessions inherit project.cwd unless the session overrides cwd.
The editor, Git, GitHub, Projects, notes links, terminal context actions, and channel file links can all jump through these roots.

Project custody and genesis

A project without custody uses ordinary authenticated Channels conversation. Invited managed agents can post and edit their own messages without genesis, Assignment or prepare. Their launch supplies identity automatically. To adopt custody, the operator binds the project to its accepted genesis using the existing custody entry. A file named GENESIS.md, an OKF template or a self-declared verified field does not enable custody or establish that decision:
Desk retains explicit project/channel adoption and the associated session IDs in project-custody-bindings.json under the Channels home. Runtime startup, publication and notification delivery reconcile the current manifest with this record. Once adopted, removing custody, removing a channel from its list or losing the session/project association reports custody-binding-lost; it never silently makes that scope ordinary. A conflicting repository identity reports custody-binding-conflict. Restore the intended binding to repair these cases. A missing/corrupt retained record reports custody-bindings-unavailable. Keep this file and project-custody-bindings-required.json in full-home backups; they are not part of a Channels conversation-only checkpoint. No disable/reset command is provided. File-system owners can still alter these files; this is operational continuity, not isolation between agents sharing OS credentials. Existing explicit bindings are adopted automatically on upgrade. Other channels remain ordinary without manual classification. Historic unbound context packets alone do not imply activation. A project binding applies only to its configured channels and project sessions; it does not impose custody on unrelated projects sharing a channel. This record has no separate project/channel/thread policy hierarchy or policy-version workflow. Previously removed pre-upgrade bindings cannot be reconstructed from unbound notification history. repositoryId is an explicit stable repository identity chosen for the project; Desk does not derive it from a local path or a mutable remote URL. Keep it the same across bindings for that repository. genesis selects up to 32 files, relative to the repository root, without moving the existing OKF. A fragment is preserved as a navigation hint; the complete file is archived. Each source is limited to 256 KiB. Linked documents outside this selection are not traversed unless a selected document belongs to an OKF bundle identified by an ancestor index with okf_version: "0.2". Within that bundle, Desk follows Markdown links and declared knowledge relations, retaining exact document and resource versions. Discovery is bounded to 256 reached nodes, 2,048 edges and 8 MiB; missing targets, invalid fragments and exhausted limits remain explicit. External URLs remain source declarations and are not fetched. For a bundle without an ancestor index, select its root explicitly:
knowledgeRoots replaces index-based root discovery with 1–32 unique repository-relative directory paths. Use . for the repository root; paths must not contain .., empty segments, backslashes, fragments or queries. Nested roots are allowed: the most specific root determines each reached concept’s identity and bundle-relative links. Traversal starts only from genesis and follows reached links; it does not scan the directories or create an index. Sources outside the selected roots remain resource leaves. The same read limits and checkout boundaries apply. Omitting this field preserves index discovery. Recorded snapshots retain their selected roots; changing or removing the current selection invalidates authority based on the previous scope. The recipient’s session identifies the project when multiple projects share a channel. Genesis is read from that session’s checkout, including a linked Git worktree, after verifying that it belongs to the project’s Git common directory. An unrelated clone is not silently substituted. Full HEAD, exact source bytes, content digest and separate HEAD/index/working-file observations are retained; missing sources and merge conflicts remain explicit. This records the selected sources, not a complete atomic worktree snapshot. Historical lookup also checks the recorded local Git directory observation before attributing current files to that binding. Replacing a repository at the same path reports unavailable continuity; the old sources remain readable. Existing add/edit project operations accept custody. Omitting it during an edit preserves the binding; custody: null clears it. Notifications include desk custody --anchor genesis-<hash>. Reading a context package shows its recorded genesis and the current binding from the manifest. Reading a genesis ID directly shows its recorded sources and current files at that recorded binding. Historical reading requires neither a running server nor an existing manifest. Frontmatter and source text do not themselves grant operator authority. An assignment can use an exact knowledge-<hash> Objective anchor as its basis. Desk selects applicable constraints, gates, dependencies and resources through the declared relations of that objective. A navigation link alone does not make a document an assignment requirement. The recorded assignment basis and the observed version in the current checkout appear separately; a changed document does not silently replace the historical basis.

Custody journal and peer transport

Publication uses an explicit policy inside the same project entry:
remote names an existing Git remote alias in this project. Desk resolves that alias without changing the agent’s checkout or index. Only the configured node publishes the journal ref. Other nodes retain their outboxes until remote read-back verifies the records and required Git bundles. The service retries temporary outages with a capped delay and checks the current publication policy before sending or pushing. A removed policy leaves local records available. Published Git history retains earlier shared content. An enabled journal reports pending while preparing source closures and Git payloads. pendingIds contains durable inbox/outbox records and updates as new closures enter the queue; an empty list during preparation is not an archival receipt. New roots are retained individually before preparing later roots; cached roots use bounded batches. Completed Git captures can be reused across priority interruptions after verifying their retained bytes. Only remote read-back changes the journal state to archived. For multiple Desk nodes, initialize each local public identity with desk custody identity --init. This creates a node key when absent and prints nodeId, nodeKeyId, and publicKey as JSON. Repeating it preserves the key; desk custody identity only reads the existing public pin. Private keys stay with their existing local key owner. This command creates no assignment or execution and does not register the node at another Desk. On each sender, set journal.transport.writerUrl to the writer’s HTTPS origin. On the writer, add the sender’s three public fields to journal.transport.peers, with an explicit publishChannels list. For example:
Peer channel lists must be subsets of the project’s journal.publishChannels. An empty effective list permits no intake, including payload uploads. The writer’s existing HTTP backend serves /api/custody/v1/projects/; expose only that authenticated route through the configured HTTPS proxy. The rest of Desk retains its local-trust boundary. Literal loopback HTTP origins are also supported for local connections and tests. Requests use the registered node key to bind the exact body digest, length and target project/repository/ref/writer. The receiver keeps unfinished bundle uploads separate for each sender and key. The transport opens a streamed 202 response with x-desk-custody-receipt: body-required-v1 after local authentication and admission. That status and header confirm only admission. Durable intake requires the complete final JSON body with the matching request digest; an admitted error also arrives in that body. The sender retains its outbox until a fresh remote Git read-back supplies the record receipt. A missing final body has an unknown intake outcome and must be reconciled using the same immutable request, not interpreted as a successful publication. Node-key trust, source authorship and operator authority remain distinct. writerNodeId is the bootstrap writer. A managed journal can additionally pin writerAuthority and writerCandidates; its verified transition chain selects the current writer after a credential-fenced handover. The authority and each candidate have distinct pinned node keys and HTTPS origins, and candidates must appear in the transport peers. A UUID change or Git CAS alone does not authorize a transfer. Git bundles, worktree snapshots and execution checkpoints have separate coverage and remote receipts; consult the live custody status before claiming source-machine recovery. A fresh managed-journal reader also needs its own desk custody identity --init pin registered in the authority’s journal.transport.peers with explicit source scope. The authority supplies short-lived read credentials after checking that pin and the project binding. Keep the GitHub App private key on the authority; placing it on a worker is rejected as custody-writer-app-key-on-worker. Check desk custody writer status --project ID for the registered authority and current writer before a cold remote restore. A peer’s public pin alone grants no write authority or operator role.

GitHub webhook intake

The backend reads the machine-local JSON file named by DESK_CUSTODY_GITHUB_CONFIG. When the variable is absent, the receiver is disabled. Credentials and receiver configuration stay outside project knowledge. For example:
The secret file contains the exact shared-secret bytes; a trailing newline is part of the secret. An environment reference is also supported: {"kind":"env","name":"DESK_GITHUB_WEBHOOK_SECRET"}. The configuration file contains the reference, not the secret value. providerInstance must be a canonical HTTPS origin without a trailing slash; GitHub Enterprise uses its own explicit origin. The origin is retained in each received record, so changing configuration does not reassign historical events to another host. Configure the App webhook URL to end in /api/custody/v1/github/webhook on the receiver’s HTTPS origin. Desk verifies the exact raw request body with HMAC SHA-256, checks the current installation/repository scope, and durably records the delivery before responding with 202. Configuration and secret changes take effect on subsequent authorization checks without a backend restart. Repository-less events require allowRepositorylessEvents: true on their explicit installation scope; this does not grant operator authority. App-wide ping payloads without an installation are currently rejected by this scope rule. Expose this authenticated route through the HTTPS proxy while preserving Desk’s existing local-trust boundary. The receiver retains a reconciliation obligation after startup or intake failure. GitHub does not automatically retry failed webhook deliveries, and accepting a webhook does not prove complete history. The App integration below recovers recent deliveries; API reconciliation is configured separately. See GitHub’s failed-delivery behavior. Accepted events asynchronously produce local object observations in the existing backend. The original source and every object membership are durably saved before the event’s projection is marked complete. Startup resumes pending work; one unreadable event remains a visible gap while other events can progress. desk custody --anchor github-observation-<hash> reads the exact observation and raw body. desk custody --anchor github-object-<hash> reads recorded history; the output supplies --after <observation-id> when further pages remain. Object history is paged by both record count and bytes. Arrival order and timestamps do not establish current GitHub state, and a projected merge or answer does not accept the operator’s goal. Authorized outgoing GitHub operations have a separate durable intention, remote publication receipt and outcome reconciliation path; an unknown result is not blindly retried. These records do not themselves establish QA or operator acceptance.

GitHub App installation and delivery recovery

Register a GitHub App with the repository read permissions needed by your project: Metadata, Contents, Issues, Pull requests, Discussions, Checks and Commit statuses. Organization Projects requires its own organization permission. Select the corresponding webhook events, enable the JSON webhook with SSL verification, and use the receiver URL above. Installation events invalidate authorization; they never create operator decisions. Use the same machine-local DESK_CUSTODY_GITHUB_CONFIG file for the registration and allowed installations. Private-key and webhook-secret values stay outside this file and outside the project manifest:
An environment reference ({"kind":"env","name":"DESK_GITHUB_APP_PEM"}) can replace the key file reference. The key must be an RSA PEM. Permissions are an explicit local ceiling and must also be granted in GitHub. Omit permissions your installation does not use, such as organization Projects for a repository-only binding. Keep metadata: read for the native repository identity lookup. Start with scopes: [] if the installation does not exist yet. With the registered App configured, desk custody github install prints its installation URL without accessing the private key. Complete installation in GitHub, start desk serve, then run desk custody github inspect to obtain actual installation IDs, account IDs, granted permissions and suspension state. Add the selected IDs to scopes. Use actual repository database IDs, for example from gh api repos/OWNER/REPO --jq .id; local permission selection does not grant access to a repository excluded in GitHub. Inside the project’s custody.github, explicitly select the installation:
Each API subprocess gets a bounded installation credential lease. Tokens and JWTs stay in backend memory. App mode removes ambient GitHub tokens, debug variables and configured secret environment references from the gh child. Key/configuration changes and installation events invalidate active leases; authorization is checked again after the request before its result is accepted. The token cache rechecks grants after at most 60 seconds on its next use, even when the installation webhook was lost. Repository roots use individual token scopes; a Project root spanning more than GitHub’s 500 repository IDs per token is rejected explicitly rather than silently dropping repositories. desk custody github status reports authorization observations and delivery recovery state; use --after <nextCursor> --limit <N> for subsequent delivery pages. Installation inspection supports --page <nextPage> if a pass reaches its limit. desk custody github recover durably requests a new scan; its response means requested, not delivered or processed. Startup, periodic checks and configuration repair also start recovery automatically. The worker checks the authenticated App identity and configured webhook URL before reading delivery attempts or requesting redelivery. It saves its intention before each POST and reconciles an unknown outcome before another request. GitHub redelivers to its configured endpoint. The ordinary Desk HMAC intake must accept the real request before the recovery state becomes local-accepted; JSON returned by the delivery API is never relabeled as an authenticated raw webhook. Projection, agent notification and operator acceptance remain separate states. Recent delivery records cannot recover edits outside GitHub’s retention window: status preserves that historical gap and requests current API reconciliation. The existing user gh adapter remains available with auth: { kind: user }. Use it explicitly for personal Projects and other sources requiring user authorization. Existing configurations without an App preserve their prior gh behavior; when an App is enabled, each project must choose an auth adapter. Missing or rejected installation credentials never fall back to a user login. Registration, installation and permission grants require the real GitHub account. Local HTTP/process tests do not qualify a deployed App or public receiver. See GitHub App installation, installation tokens, and failed deliveries.

GitHub API reconciliation

Set reconcile: true inside a project’s custody.github to enable authenticated API reads using the existing gh executor. Binding observations locally or receiving webhooks does not enable these reads. API-only operation needs no webhook configuration or public endpoint; the configured provider origin controls GH_HOST, including for Enterprise. Startup resumes saved page cursors. Repository database IDs resolve through the provider’s REST API to native node IDs; the exact parsed mapping is preserved as github-api-repository-<hash>. Project node IDs come from the explicit binding. Independent connection jobs cover repository Issues, PRs and Discussions, Project fields/items and their discovered child connections. A page is saved before child jobs and the cursor checkpoint. Partial responses, access failures and missing saved dependencies remain coverage gaps. desk custody --anchor github-api-page-<hash> reads an exact saved parsed gh representation and links its discovery page and repository identity source. desk custody --anchor github-api-repository-<hash> reads that native mapping. desk custody --anchor github-api-observation-<hash> reads the corresponding typed graph. The existing backend projects saved API sources automatically, retries interrupted writes, and wakes the project journal after durable completion. All three commands work without a running server or current API credentials. Native object history combines API and webhook observations without electing a current version from their arrival order. The exact discovery page and REST root mapping accompany a published API source through the journal to another Desk. Its CLI shows any verified remote archival receipt separately from current remote availability, delivery and acceptance. These are parsed API observations, distinct from raw webhook authentication. The end of a connection and complete membership in an individual page are separate facts: a final continuation page contains only its own subset. New graphs use projector v2; reading an archived v1 graph explains its older end-marker meaning without rewriting its bytes or ID. A full paginated pass does not reconstruct unavailable historical edits or provide an atomic global snapshot. Missing jobs can be recovered from saved parents; a lost source requires a new pass with an explicit historical gap. Access failures retry under the current opt-in. Revoking that opt-in stops further API work; saved history stays readable. Ordinary webhook changes request another pass without resetting unfinished cursors; periodic reconciliation also catches changes missed by webhook delivery.

GitHub project binding and journal publication

Project custody configuration binds local observations by native provider identity, independently of webhook reception. For example, inside a project’s custody:
github grants local context selection. journal.publishGithub separately permits publishing the exact raw source to the configured project journal; channel consent does not grant GitHub publication. Each peer needs its own publishGithub subset. Cross-repository forks and dependency payloads can include additional native scopes: include all scopes in the publication binding or explicitly set allowMixedScopeRaw: true in the applicable publication policy. URL slugs and Issue numbers never grant source scope. journal.githubRepository identifies the journal’s GitHub repository by native ID. A matching push to the exact journal ref is transport activity and is not republished as project work. When that native identity is absent, a push-shaped source on the configured journal ref stays local with custody-github-journal-origin-unresolved; Desk does not guess from the remote URL. Journal pushes also skip API invalidation across projects sharing the backend, so publishing a new observation cannot recursively trigger another API pass. If the journal origin is unresolved, a matching ref produces that diagnostic and waits for the periodic pass instead of starting another pass from the push. Other refs and ordinary Issue changes retain the usual event-driven path. Explicit GitHub object or observation anchors in assignment sources and applicable knowledge enter the ordinary channel custody package. The notification supplies desk custody --anchor commands. Historical packages retain their selected observations when an Issue changes; unrelated observations do not alter the package. A completed projection wakes the existing journal worker. A second Desk imports complete verified remote records into its local archive and rebuilds object indexes without generating a new provider event. The existing Channels runtime also sends relevant local GitHub changes to the current assignment’s agent. It uses the same configured project binding and ordinary role/channel/custody reminders; no additional agent tool or MCP setup is needed. API acquisition still requires reconcile: true; webhook acquisition uses the separately configured intake. Imported history does not create live notifications. Exact assignment blocks and their applicable knowledge determine the watched objects, so a shared message or common repository does not subscribe every assignment to every change. Submission recovery and source comparison are described in the Channels protocol.

Managed writer and execution policy

The optional managed writer requires custody.github.auth.kind: installation, journal.githubRepository, the authority’s pinned {nodeId, nodeKeyId, publicKey, url} endpoint, and at least one eligible writerCandidates endpoint. The bootstrap candidate must match writerNodeId and transport.writerUrl; every candidate must also be a transport peer with the same pin. The authority owns the App credential boundary and records a signed generation change only after the previous writer has been fenced and its accepted work drained. A new writer rechecks the remote chain before issuing GitHub effects. writerAuthorityGenesis and writerAuthorityRecoveries pin explicit operator-approved authority recovery history; a remote signature cannot amend the trust root on its own. Automatic assignments and worktree snapshots require a project execution binding to a repository-relative JSON policy and its exact SHA-256 content digest:
The selected file must also be in custody.genesis. The policy controls assignment grants and start budgets separately from snapshot capture, publication, retry interval and coverage. A changed policy file needs a new operator-approved digest; a local edit does not silently widen autonomy. Snapshots record Git objects, index, working files and supported LFS/submodule content with explicit omissions and consistency status. Only a confirmed remote record supports restore after loss of the original node. The current snapshot policy includes ignored files unless explicitly excluded, so do not enable remote publication for a worktree containing secrets until the secret-exclusion and remote access policy in the cloud boundary design is implemented. The local server still follows the single-user trust model. A GitHub App installation authenticates GitHub traffic, not the human using Desk. Cloud deployment needs the authenticated HTTP/WebSocket and source-level boundary in the linked design and a fresh independent security/privacy audit.

Groups and layouts

Groups organize sessions in the agents multiplexer.
Supported layout kinds:
  • 1x1
  • 2x2
  • 3x3
  • 4x4
  • linear
  • custom
linear and custom use cells from 1 to 16. A sizes block, written by earlier Desk versions or by hand, is the initial split for a browser that has none for the group; drags are kept by the browser and are not written back.
The UI writes order on projects, groups, and sessions when you drag-reorder the tree.

Sessions

A session needs a name, a durable sessionId, and either a built-in agent or a custom command. The UI and desk add mint collision-free ids. When you add a session by hand, use a 3-64-character lowercase id that starts with a letter and contains only letters, digits, and dashes.
Built-in agent values:
  • codex
  • claude
  • opencode
  • qwen
  • kimi
  • grok
  • pi
  • bash
bypassPermissions applies to codex, claude, opencode, qwen, and kimi; it is a manifest error on grok and pi (which have no per-tool approvals) and silently ignored on bash. Agent profiles are available for codex, claude, and pi. See Agent integrations for each agent’s resume-id format, hook file, and credentials. For project sessions, cwd is optional because the project root is inherited. Root-level groups need cwd unless the session uses a command that handles its own directory.

Agent profiles

A profile is a named provider account. Declare profiles once at the top level and point sessions at them with profileId:
id is a lowercase slug; provider is claude, codex, or pi; label is what the UI shows. A session’s profileId must name a profile whose provider matches the session’s agent — a mismatch is a manifest error rather than a silent ambient launch, because launching the wrong account is exactly what profiles exist to prevent. Omit profileId to run under whatever the agent CLI is ambiently logged into. settings.deskSkills is the default boolean, false when absent. A project or session can set its own optional deskSkills boolean; omission inherits its parent. The session override wins over the project override. These settings apply to supported Codex/Claude launches independently of profileId and do not rewrite account configuration. Settings → Desk Skills edits the values in one table and asks about affected running agents only after saving. See Desk Skills for delivery, hook trust and next-launch behavior. Desk implements a profile by pointing the CLI at its own directory:
for CLAUDE_CONFIG_DIR (Claude), CODEX_HOME (Codex), or PI_CODING_AGENT_DIR (Pi). The directory is created 0700. Desk also unsets the provider credential variables that CLI reads for a profiled launch (each provider has its own list), so an ambient ANTHROPIC_API_KEY cannot silently outrank the profile’s own login. Creating a profile or entering an email as its label does not sign in. Complete the provider’s login with the selected profile directory, for example:
A Codex profile is its own CODEX_HOME, so Codex reads Desk’s hooks.json from the profile directory and never from ~/.codex. Desk writes it when the profile is created. For a profile created by an older Desk, run desk hooks install: it also merges Desk’s hooks into every Codex profile in the manifest, keeping hooks you added yourself. Codex then asks you to trust the hooks once in that profile, as it does for ~/.codex; until you do, its new sessions report codex-hook-untrusted and cannot capture a resume ID. Use codex login status or claude auth status with the same directory variable to check that profile. Desk lists profile pointers and does not report the provider’s account status. Credentials remain owned by the provider CLI.
Two consequences worth knowing before you use profiles.Credentials move. The CLI authenticates into the profile directory, so that path holds real credential files. Back it up with the rest of ~/.config/desk, and treat it as sensitively as ~/.claude.Conversation history is per-profile. Claude Code keeps transcripts inside its config directory, so switching a session’s profile points it at a different history and --resume will not find the old conversation. Start a fresh conversation, or move the transcript, when you change a live session’s profile.

UI mode

uiMode selects how a session renders: native (the agent chat surface) or terminal (the CLI’s own TUI in a terminal cell). An omitted field resolves to terminal for every agent; write uiMode: native on a Codex, Claude, or OpenCode session to open the chat surface. Qwen, Kimi, Grok, bash, and custom-command sessions are terminal-only — declaring uiMode: native on them is a manifest error.
The session edit modal switches a live session between modes; the switch restarts the agent process and carries the captured resume id across, so the conversation continues in the other surface. A session with no captured resume id asks for confirmation first, because switching starts it fresh.

Resume metadata

resume is optional in YAML. Omit it to let Desk start a fresh conversation and capture a resume id when the agent CLI exposes one.
Desk validates known resume id formats before persisting them. Codex, Claude, and Qwen use UUID-like ids; OpenCode uses ses_... ids; Kimi and Grok ids are opaque tokens (letters, digits, ._-). Capturing a fresh resume id never changes sessionId; terminal, channels, and attention state remain keyed to the same Desk session. Desk captures every provider’s initial session automatically, only from the managed child launch. The terminal daemon always requires its private per-launch proof; for Codex and Claude it additionally verifies the provider transcript on disk before it persists the ID; a generation number by itself is not authorization. Resuming a different conversation manually inside the provider TUI creates a provider-session mismatch. Desk records that transition durably and blocks relaunch instead of silently replacing the configured resume. Review the two IDs shown in the continuity banner, then authorize the observed conversation explicitly:
The rebind is compare-and-swap guarded by the daemon’s pending transition. If the command is interrupted after the manifest update, retry the exact command; an already-applied matching transition succeeds without changing authority. Do not substitute a fuzzy provider ID or edit resume by hand. If the old process is gone after a restart, the same command can recover the saved transition once the daemon confirms holder absence and revalidates the exact launch proof, current generation, transcript, and manifest binding. An unadopted live holder or unknown holder state still blocks recovery; --force does not bypass identity checks. Rebind changes the binding, not the running process: restart that session afterward to resume the selected conversation. rebind-provider-session preserves the manually resumed conversation by moving the durable binding to the observed ID. In contrast, reset-provider-session <name-or-sessionId> --force clears the binding and authorizes exactly one fresh provider launch. Reset is the destructive recovery for intentionally starting over, not a replacement for rebind.

Permission bypass

bypassPermissions controls supported agent CLIs:
For Codex and Claude, Desk passes the agent’s dangerous bypass flag when enabled. For OpenCode, Desk sets per-session OPENCODE_CONFIG_CONTENT so permission["*"] is allow when enabled and ask when disabled.

Custom commands

The session form’s Additional CLI command text field stores commandSuffix. It defaults to an empty string. Desk appends a separating space and the entered text verbatim to a terminal CLI command, including resumed launches and custom commands. It does not trim, quote, split, or validate CLI options in this field. The launching shell interprets the text normally. For example:
The field supplements the managed launch, preserving its profile, hooks and resume handling. It is saved for the next launch; saving does not restart a running CLI. Native SDK sessions do not use terminal command text. Custom commands bypass built-in agent launch logic.
Desk runs the command under Moor and exposes it through the terminal daemon, but it does not provide agent-specific resume, bypass, or attention hooks unless the command implements them itself. A custom command has no Desk hooks, so its session reports unknown activity — Desk says so rather than inferring a state from its output.

Settings

Settings cover shared subsystem configuration. Per-person interface state (theme, sound, the host-metrics switch, the motion switch, the notification kinds, sidebar widths, terminal grid splits, open editor and notes tabs and the autosave mode) is kept by each browser in its local storage and is never written to desk.yml. Values still present under settings: seed a browser that has none, once. settings.notifications seeds the Events kinds a browser shows (agent-blocked, channel-mention, channel-message, agent-error, agent-idle, agent-recovered, agent-exited, each true or false); a kind switched off is left out of that browser’s Events page from the moment of the switch, never sounds, and marks nothing on the journal. settings.motion: false seeds the Motion switch off, which stops every animation in the interface. settings.showHostMetrics: false seeds the Host metrics switch off.

Editor settings

settings.editor.root is the active workspace root for the editor and Git subsystems.

Notes

Notes always live in ~/.config/desk/notes. Open notes, the active note and the default markdown view are kept by the browser, not by the manifest.

LSP settings

LSP support is default-off. Enable it explicitly:
Desk detects languages under the active editor root. disabledLanguages is the normal user control for turning off a detected language. Advanced fields such as serverCommands, maxSessions, and startupTimeoutMs override the built-in language server behavior. See IDE and LSP and Agent integrations before changing them.

Explorer terminals

settings.explorerTerminals lists the shells opened with Open in terminal from the editor’s file tree. Desk writes the rows itself: name is the display name entered in the create form (the form leaves it blank for the user to fill in and refuses to submit until it is non-blank; a duplicate name within the same folder is refused by the server), and sessionId is minted as term-<name> (slugged, with a numeric suffix on collision) in the same Moor session-id namespace as agent sessions, so the manifest is refused at parse time when a terminal id collides with an agent session id or a row is malformed. command is optional: when present it runs in cwd every time the terminal starts or boots; when absent Desk starts the login shell. Terminals are not sessions and never appear in groups or projects. See Terminals panel.

Atomic writes

Desk writes manifest updates through a temporary file and rename, and an open UI follows the manifest on disk: a session or group you add by hand (or through the CLI) appears in the sidebar on its own, without a Refresh.

Next steps