Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Bookmarks (part 2)

The reference half of the Bookmarks page: the localhost CLI routes with their kill switch, curl examples and response shapes, plus the implementation map of the queries, launcher, favicons, IPC channels and renderer state.

What it is

This is part 2 of the Bookmarks page. It carries the reference material: the localhost CLI routes with their kill switch, curl examples and response shapes, and the implementation map of the queries, launcher, favicon service, IPC channels and renderer state behind the popover.

Where to find it

Bookmarks are reached from the bookmark icon in the toolbar pill, which opens the popover holding the search box, the Add button and the hierarchical tree; Bookmarks walks that surface end to end, including the eight kinds, the cmd confirmation gate, reordering and the Settings toggle. The material on this page is reached either from the local CLI control server on 127.0.0.1:19519 or by reading the code paths named below.

How it behaves

CLI control

Bookmarks are exposed through Omniscio's CLI control server on 127.0.0.1:19519 (override with AMC_CLI_PORT). All routes require a bearer token in the Authorization: Bearer <token> header — see the cli-control.md doc for how the token is delivered and how to fetch it from the DPAPI vault. Six endpoints are provided:

  • GET /bookmarks — list every bookmark in display order.
  • POST /bookmarks — create one (URL / Path / Folder / Cmd; never Exe).
  • PATCH /bookmarks/:id — update name, value, parent, or kind.
  • DELETE /bookmarks/:id — delete (cascades to descendants for folders).
  • POST /bookmarks/reorder — reorder all rows under one parent in a single all-or-nothing transaction.
  • POST /bookmarks/:id/launch — launch a URL, Path, or Cmd bookmark. Exe is rejected.

Kill switch

Every bookmarks route is gated by the bookmarksCliEnabled setting (default true, toggle at Settings → CLI Control → Enable bookmarks CLI). When the user flips it off, every route returns 403 { ok: false, error: 'Bookmarks CLI disabled in Settings → CLI Control', disabled: true } before any auth/parse/business work runs. This is the panic-button switch — flip it off and external callers cannot read or mutate bookmarks at all, even with a valid bearer token. The setting is checked on every request, so the switch takes effect immediately without an app restart. This flag is independent from showBookmarksInToolbar — the in-app popover keeps working when this is off, and the CLI keeps working when showBookmarksInToolbar is off (see "Settings" above).

Mutations apply immediately (NOT approval-gated)

Bookmark mutations (POST / PATCH / DELETE / reorder) apply immediately, the same way /keybindings/* does — they are personal launcher entries with no destructive cascade and the user-in-the-loop gate doesn't add value. Each successful mutation emits a bookmarks:changed push so any open Omniscio popover reloads. The shared rate-limit pool covers them at 10 mutations / minute / token.

This contrasts with /cron/*, /automation/*, and most /email-summarizer/* mutations, which land as pending rows in cli_pending_actions and require user approval in the inbox before any side effect.

Read budget on GET

GET /bookmarks is bearer-gated AND read-budgeted at 60 requests / minute per token-hash. Past the budget, the server returns 429 { ok: false, error: 'read budget exceeded', retryAfter: <seconds> }. The point is to make cheap polling expensive enough that a leaked token cannot be used as a bookmark exfiltration drip.

Security: exe is rejected, cmd first-run is still gated

Two distinct hardening rules apply to launch:

  1. exe kind is rejected at the CLI surface. A POST /bookmarks/:id/launch against an Exe bookmark returns 403 { ok: false, error: 'exe-kind bookmarks cannot be launched via CLI; launch from the Omniscio UI instead' } (no kind field). The CLI cannot launch arbitrary executables — full stop. The in-app popover can still launch Exe bookmarks because that path goes through the IPC handler, which sees a real Electron renderer and a user click.

  2. cmd first-run still requires user confirmation in the Omniscio popover. The IPC handler accepts a confirmed: true field on its launch input that lets the popover skip the first-run modal. The CLI launch schema is z.object({}).strict() — no fields at all, with .strict() so additionalProperties are rejected, not silently stripped. A CLI caller cannot send confirmed: true to bypass the gate. When a CLI caller asks to launch a never-confirmed cmd row, the server returns 200 { ok: true, data: { ok: false, requiresConfirm: true } } to signal "go run this from the Omniscio UI to confirm it the first time, then come back". Note the two-level ok envelope: the outer ok: true only signals "the HTTP route handled the request"; the inner data.ok: false is the launcher's verdict. A CLI caller checking only the outer flag will misclassify unconfirmed-cmd as success — always check response.data.ok AND response.data.requiresConfirm. This is load-bearing security control — never relax the schema to accept additional fields.

Why the strict schema matters: a bearer token leak combined with a permissive launch schema would equal full RCE — any leaked token could fire arbitrary cmd.exe /c ... payloads. The .strict({}) schema is what keeps a leaked token bounded to "things the user has already pre-confirmed in the UI".

Working curl examples

These assume TOKEN is set; the global CLAUDE.md "Omniscio CLI Bearer Token" section shows how to fetch it from the DPAPI vault.

TOKEN=$(powershell.exe -NoProfile -File "$HOME/.claude/secrets/get-secret.ps1" amc-cli | tr -d '\r\n')

# 1. List all bookmarks
curl -s -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:19519/bookmarks

# 2. Create a URL bookmark at the root
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"kind":"url","name":"Anthropic","value":"https://www.anthropic.com"}' \
  http://127.0.0.1:19519/bookmarks

# 3. Create a folder
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"kind":"folder","name":"Work"}' \
  http://127.0.0.1:19519/bookmarks

# 4. Move a bookmark into a folder (PATCH parent)
curl -s -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"parentId":"<folder-id>"}' \
  http://127.0.0.1:19519/bookmarks/<bookmark-id>

# 5. Reorder all rows under one parent (all-or-nothing)
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"parentId":null,"orderedIds":["id-a","id-b","id-c"]}' \
  http://127.0.0.1:19519/bookmarks/reorder

# 6. Launch a URL bookmark
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{}' \
  http://127.0.0.1:19519/bookmarks/<bookmark-id>/launch

# 7. Delete a bookmark (cascades for folders)
curl -s -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:19519/bookmarks/<bookmark-id>

Reorder is all-or-nothing

POST /bookmarks/reorder takes the full list of every row under one parent as orderedIds, in the desired final order. The server validates that:

  • Every id in orderedIds exists in the database.
  • Every id in orderedIds currently has the requested parentId (or null for root).
  • Every existing sibling under that parentId appears in orderedIds exactly once.

If any of those checks fails, the entire reorder is rejected with 400 and no rows are mutated. There is no partial application — you cannot reorder three rows out of five and have the other two end up at random positions. This matches the behaviour the in-app drag handler relies on, so a CLI-driven reorder is indistinguishable from a drag.

Response shapes summary

All bookmarks routes wrap their result in the standard { ok, data } envelope; the inner data shape is what previous versions of this doc showed at the top level.

Endpoint Success Common failures
GET /bookmarks 200 { ok: true, data: { bookmarks: BookmarkRow[] } } 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled), 429 read budget exceeded
POST /bookmarks 201 { ok: true, data: { id: string } } 400 (validation, depth cap, cycle), 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled), 429 (rate limit)
PATCH /bookmarks/:id 200 { ok: true } 400 (validation, cycle, depth), 404 not found, 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled), 429
DELETE /bookmarks/:id 200 { ok: true, data: { deletedCount: number } } 404 not found, 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled), 429
POST /bookmarks/reorder 200 { ok: true } 400 (orderedIds mismatch), 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled), 429
POST /bookmarks/:id/launch (url/path/cmd/session/project/virtual) 200 { ok: true, data: { ok: true } } 200 { ok: true, data: { ok: false, requiresConfirm: true } } (never-confirmed cmd — outer ok is true but inner is false), 200 { ok: true, data: { ok: false, error: '<kind> not found' } } (nav kind whose target was deleted), 400 (folder), 403 (exe rejected, with error 'exe-kind bookmarks cannot be launched via CLI; launch from the Omniscio UI instead'), 404 not found, 401 unauthorized (missing/invalid token), 403 forbidden (feature disabled)

Every successful mutation emits bookmarks:changed so the open Omniscio UI re-renders without polling.

For agents

How it works

The data model lives in src/main/db/bookmarks-queries.ts — listBookmarks, createBookmark, updateBookmark, deleteBookmark, plus internal helpers that enforce the cycle and depth rules. The launcher is src/main/services/bookmarks-launcher.ts — it owns the per-bookmark debounce, the cmd confirmation gate, the URL/path/exe/cmd dispatch, and the confirmed = 1 write that turns first-time cmd rows into silent ones. The favicon service is src/main/services/bookmarks-favicon.ts — fetchFavicon, invalidateOnHostChange, sweepOrphanFavicons.

The IPC channels are bookmarks:list, bookmarks:create, bookmarks:update, bookmarks:delete, bookmarks:launch, plus the bookmarks:changed push event. Every CRUD handler in src/main/ipc/bookmarks-handlers.ts emits BOOKMARKS_CHANGED after a successful write, so any open popover reloads its tree and stays in sync (a second Omniscio window, the same window after a CLI mutation, etc.).

The renderer side is laid out under src/renderer/src/features/bookmarks/. BookmarksToolbarButton.tsx owns the toolbar entry-point — it reads the showBookmarksInToolbar setting and renders nothing when it's off — and lazy-loads the three modal-only components (popover, add modal, confirm modal) per CLAUDE.md's "heavy modal-only components must be React.lazy() in EVERY caller" rule. BookmarksPopover.tsx handles the portal positioning, the search input, the empty state, and the click-outside / Esc / re-click close logic. BookmarksTree.tsx handles the search filter, expand/collapse state, keyboard navigation, the 600ms hover-into-folder auto-expand timer, and the cross-parent drop-into handler; rows render via BookmarkRow.tsx which also computes the paddingLeft = 6 + depth * 12 indent and emits the 3-zone (folder) or 2-zone (leaf) drop-zone metadata that src/renderer/src/hooks/useDragReorder.ts consumes. BookmarkAddModal.tsx handles both add and edit modes; BookmarkConfirmModal.tsx is the cmd first-run gate. The Enable bookmarks toggle lives in the Toolbar settings section, src/renderer/src/features/toolbar/ToolbarSettings.tsx — a single toggle bound to showBookmarksInToolbar (there is no standalone Bookmarks settings section; 'bookmarks' is an 'active'-only legacy alias of 'toolbar'). It does not render the tree or any CRUD controls.

The Zustand store is src/renderer/src/stores/bookmarks-store.ts. It holds the flat row list, exposes load / create / update / delete / launch / reorder actions, and buildBookmarkTree(rows) builds the hierarchical tree the popover consumes. The store re-fetches on BOOKMARKS_CHANGED push so a CLI mutation or a second-window edit shows up in the open popover live.

Related

This page is the companion to Bookmarks, which covers the feature itself: what bookmarks are, where to find the popover, and how to add, reorder and rename them. cli-control.md describes the CLI control server those routes live on, keyboard-shortcuts.md holds the global shortcuts table the tree navigates within, and tray-and-window.md covers the Electron shell and child-process rules the launcher uses.

Last verified 2026-09-23