Repository navigation
Conversation
Raycast 2.x changed both halves of the export and the importer only knew the 1.x versions of each, so every 2.x `.rayconfig` failed and the failure surfaced as "Password was not valid" no matter what the user typed. The container is now `RAYCFG3\n` + UInt32LE header length + gzipped header JSON + AES-256-GCM ciphertext + a 16-byte tag, keyed by scrypt over the password and the header's salt. The old path fed the whole file to OpenSSL as salt-less AES-256-CBC, which cannot decrypt it. The payload is also category-keyed (`settings`, `quicklinks`, `notes`, ...) instead of the 1.x `builtin_package_*` keys, so even a successful decrypt would have found nothing to import. `raycast-v2-backup.ts` decrypts the container and maps the payload back onto the 1.x shape, leaving every existing importer, preview and count untouched. Detection is by container signature, so 1.x encrypted backups and plain JSON exports still take exactly the path they took before. Mapped: global hotkey, window mode, pop-to-root timeout, navigation bindings, quicklinks, notes, snippets, store extensions with their per-command enabled state and preference values, command hotkeys and aliases, and AI chats (whose messages 2.x stores in a sibling array and which are rejoined by chat id). Clipboard history and MCP servers are carried as counts only so the preview keeps reporting them unsupported. Command ids are emitted only for the kinds SuperCmd can actually resolve: store extension commands by extension name, applications by path, and built-ins through an explicit id map. Quicklink, Apple Shortcut and AI-tool entries are skipped rather than guessed at, because SuperCmd mints its own ids for those and a near-miss would bind a hotkey to the wrong command. OAuth tokens and BYOK API keys are never read. Aliases are taken verbatim from 2.x's explicit `alias` field instead of the heuristic that has to guess one out of 1.x's `searchTerms`; that heuristic discards anything under three characters and would have dropped real two-character aliases. Claude-Session: https://claude.ai/code/session_01VfHuFnP7ZZ1AYqRHURZPEo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Adds support for the
.rayconfigformat Raycast 2.x writes.src/main/raycast-v2-backup.tsdecrypts the new container and maps the payload back onto the 1.x shape, so every existing importer, preview and count keeps working untouched.Why
A Raycast 2.x export fails today, and the failure is reported as
Password was not validno matter what the user types. Raycast changed both halves of the export:Container. 2.x writes
RAYCFG3\n+ UInt32LE header length + gzipped header JSON + AES-256-GCM ciphertext + a 16-byte auth tag. The key is scrypt over the password and the header's salt (N=16384, r=8, p=1, dkLen=32).decryptRaycastBufferfeeds the whole file to OpenSSL as salt-less AES-256-CBC, which cannot decrypt it, and the resulting failure falls into the wrong-password branch.Payload. The decrypted JSON is category-keyed (
settings,quicklinks,notes,nodeExtensions,ai, ...) rather than the 1.xbuiltin_package_*keys. Even a successful decrypt would find nothing to import.Closes #652.
How it works
Detection is by container signature, checked before the plain-JSON probe. A
RAYCFG3file takes the new path; 1.x encrypted backups and unencrypted JSON exports take exactly the path they took before.Mapped: global hotkey, window mode, pop-to-root timeout, navigation bindings, quicklinks, notes, snippets, store extensions with their per-command enabled state and preference values, command hotkeys, command aliases, and AI chats. Clipboard history and MCP servers are carried as counts only, so the preview keeps listing them as unsupported.
Three details worth review:
c:n:<extension-uuid>::-::<command>). The converter emits root-search records only for the kinds SuperCmd can resolve: store extension commands by extension name, applications by path, and built-ins through an explicit id map. Quicklink, Apple Shortcut and AI-tool entries are skipped rather than approximated, since SuperCmd mints its own ids for those and a near-miss would bind a hotkey to the wrong command.aliasfield, so it is used verbatim (trimmed and lowercased). The existing heuristic that guesses an alias out of 1.xsearchTermsdiscards anything under three characters, which would throw away real two-character aliases. The heuristic still runs for 1.x backups.settings.nodeExtensions[].oauthTokensandsettings.internalExtensions[].meta(which holds BYOK API keys) are never read. A test asserts nothing from those fields reaches the converted backup.Compatibility impact
None for existing backups. The 1.x decrypt path, the plain-JSON path, and every downstream importer are unchanged. The only edits outside the new module are additive: an optional
aliasfield on the root-search record type, an alias resolver that prefers an explicit alias over the guess,emacsaccepted as a navigation-bindings value, and the 2.x built-in id map merged into the existing one.How it was tested
scripts/test-raycast-v2-import.mjs— 11 cases over a syntheticRAYCFG3fixture built with a known password: container recognition, decrypt round-trip, wrong password producing the exact string the retry prompt matches, malformed and truncated containers reporting as malformed rather than as a bad password, unsupported container version, hotkey encoding, the category mapping, extension preference joining, secret exclusion, root-search emission rules, and AI chat message rejoining with tool turns dropped. No real.rayconfigis committed; the harness builds its own.npm test— 95 pass, 0 fail.npm run build— clean.Verified after the fix by importing that same export into a local build: 4 quicklinks, 4 notes, 13 extensions, 33 AI chats, 7 command hotkeys and 16 aliases all landed.