The OS keyring storage from #1115 works for npm install -g apify-cli but not for bundle distributions (install-cli.sh / install-cli.ps1). Bun's --compile doesn't include the @napi-rs/keyring native module, so bundle users silently fall back to plaintext file storage.
No crash, no regression — the fallback in loadKeyringModule() handles it gracefully and the success message tells bundle users to npm-install if they want keyring storage. But bundle install is the recommended path in the README, so we should make it work properly.
Reproduction
- Run a recent bundle binary (e.g.
apify-1.6.2-linux-arm64) with APIFY_CLI_DEBUG=1 on a Linux box with gnome-keyring running.
- stderr shows:
[credentials] failed to load @napi-rs/keyring error: Cannot find module '@napi-rs/keyring' from '/$bunfs/root/apify.js'
~/.apify/auth.json ends up with "secretsBackend": "file" and the token in plaintext, despite a working keyring being present.
What we tried (and why it didn't work)
We attempted to embed all platform .node binaries via Bun's with { type: "file" } import attribute, with pnpm.supportedArchitectures forcing all sub-packages into node_modules at build time. The .node files visibly ended up in the compiled binary (strings confirmed it), but no runtime API could read them back:
| Attempt |
Result |
fs.readFileSync(importedPath) |
ENOENT (six path variants tried) |
Bun.file(importedPath).bytes() |
ENOENT |
Bun.embeddedFiles lookup |
Array is empty |
Root cause hypothesis: Bun recognizes .node as a native-module extension and handles it specially during --compile. The with { type: "file" } asset pipeline doesn't apply to .node files — they're relocated or stripped via a mechanism not exposed to user code.
Proposed future approach (per-target shims)
Instead of embedding all platforms in every bundle and picking at runtime, generate a per-target shim at build time. Each compile target gets a shim that statically imports exactly the one sub-package matching that target:
// scripts/build-cli-bundles.ts (conceptual)
const targetToSubpackage = {
'bun-linux-x64': '@napi-rs/keyring-linux-x64-gnu',
'bun-linux-x64-musl': '@napi-rs/keyring-linux-x64-musl',
'bun-linux-arm64': '@napi-rs/keyring-linux-arm64-gnu',
'bun-linux-arm64-musl': '@napi-rs/keyring-linux-arm64-musl',
'bun-darwin-x64': '@napi-rs/keyring-darwin-x64',
'bun-darwin-arm64': '@napi-rs/keyring-darwin-arm64',
'bun-windows-x64': '@napi-rs/keyring-win32-x64-msvc',
'bun-windows-arm64': '@napi-rs/keyring-win32-arm64-msvc',
};
for (const target of targets) {
const subpkg = targetToSubpackage[target.replace(/-baseline$/, '')];
await writeFile('src/lib/keyring-bundle-shim.ts',
`export { Entry } from '${subpkg}';\n`);
// run bun build --compile as before
}
This avoids with { type: "file" } entirely — the hope is that a regular static import lets Bun's --compile resolve the native module the same way it resolves any other dependency.
Open questions to validate before/during implementation
- Does Bun's
--compile actually bundle .node modules from a normal static import? (Untested — needs a small experiment.)
- Does the
napi.binaryName indirection in @napi-rs/keyring-<platform>/package.json need special handling?
- Musl/glibc detection: since we ship separate musl bundles anyway, no runtime detection needed — just make sure the labeling is right.
Acceptance criteria
- Bundle binary stores the token in the OS keyring on systems with a working Secret Service / Keychain / Credential Manager (
auth.json shows secretsBackend: "keyring" and no token field).
secret-tool search service com.apify.cli (Linux) / Keychain Access (macOS) / Credential Manager (Windows) finds the entry.
- On systems without a working keyring, the bundle still falls back to file storage gracefully — no crash.
- The npm install path keeps working as today.
References
The OS keyring storage from #1115 works for
npm install -g apify-clibut not for bundle distributions (install-cli.sh/install-cli.ps1). Bun's--compiledoesn't include the@napi-rs/keyringnative module, so bundle users silently fall back to plaintext file storage.No crash, no regression — the fallback in
loadKeyringModule()handles it gracefully and the success message tells bundle users to npm-install if they want keyring storage. But bundle install is the recommended path in the README, so we should make it work properly.Reproduction
apify-1.6.2-linux-arm64) withAPIFY_CLI_DEBUG=1on a Linux box withgnome-keyringrunning.~/.apify/auth.jsonends up with"secretsBackend": "file"and the token in plaintext, despite a working keyring being present.What we tried (and why it didn't work)
We attempted to embed all platform
.nodebinaries via Bun'swith { type: "file" }import attribute, withpnpm.supportedArchitecturesforcing all sub-packages intonode_modulesat build time. The.nodefiles visibly ended up in the compiled binary (stringsconfirmed it), but no runtime API could read them back:fs.readFileSync(importedPath)Bun.file(importedPath).bytes()Bun.embeddedFileslookupRoot cause hypothesis: Bun recognizes
.nodeas a native-module extension and handles it specially during--compile. Thewith { type: "file" }asset pipeline doesn't apply to.nodefiles — they're relocated or stripped via a mechanism not exposed to user code.Proposed future approach (per-target shims)
Instead of embedding all platforms in every bundle and picking at runtime, generate a per-target shim at build time. Each compile target gets a shim that statically imports exactly the one sub-package matching that target:
This avoids
with { type: "file" }entirely — the hope is that a regular static import lets Bun's--compileresolve the native module the same way it resolves any other dependency.Open questions to validate before/during implementation
--compileactually bundle.nodemodules from a normal static import? (Untested — needs a small experiment.)napi.binaryNameindirection in@napi-rs/keyring-<platform>/package.jsonneed special handling?Acceptance criteria
auth.jsonshowssecretsBackend: "keyring"and notokenfield).secret-tool search service com.apify.cli(Linux) / Keychain Access (macOS) / Credential Manager (Windows) finds the entry.References
BUNDLE_KEYRING_ISSUE.mdin the repo root (full investigation notes this issue is summarizing)