From af8689d5bc3185cf843869ecc48e9eaeff00a317 Mon Sep 17 00:00:00 2001 From: David Zhang Date: Sat, 2 May 2026 16:00:40 +0000 Subject: [PATCH] feat: --allow-other flag for `crm mount` (Linux FUSE) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, FUSE mounts are accessible only to the mounting user — any process running as a different uid (e.g. root, container orchestrators enumerating /home/user) gets EACCES. That breaks consumers that need the mount to be readable from other contexts. Add a `--allow-other` CLI flag and matching `[mount].allow_other = true` config option. When set, the FUSE helper is invoked with `-o allow_other` so any uid can access the mount. The kernel still requires `user_allow_other` in /etc/fuse.conf when the mount is invoked by a non-root user; that's a deployment concern documented in the flag help text. Verified: - `crm mount ~/crm --allow-other` → root can lstat/read the mount. - `crm mount ~/crm` (default) → root gets EACCES (existing behavior). macOS NFS path is unaffected (the option only applies to the Linux FUSE mount). --- src/commands/fuse.ts | 14 ++++++++++++-- src/config.ts | 8 ++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/commands/fuse.ts b/src/commands/fuse.ts index b111696..35b1ae1 100644 --- a/src/commands/fuse.ts +++ b/src/commands/fuse.ts @@ -259,9 +259,9 @@ async function mountLinux( config: { database: { path: string } pipeline: { stages: string[] } - mount: { readonly?: boolean } + mount: { readonly?: boolean; allow_other?: boolean } }, - opts: { readonly?: boolean }, + opts: { readonly?: boolean; allowOther?: boolean }, ) { const helperPath = join(homedir(), '.crm', 'bin', 'crm-fuse') @@ -326,6 +326,12 @@ async function mountLinux( if (opts.readonly || config.mount.readonly) { fuseArgs.unshift('-o', 'ro') } + if (opts.allowOther || config.mount.allow_other) { + // `user_allow_other` must be enabled in /etc/fuse.conf for this to work + // when the mount is invoked by a non-root user. The mount call will fail + // loudly if it isn't. + fuseArgs.unshift('-o', 'allow_other') + } const fuseProc = spawn(helperPath, fuseArgs, { stdio: 'ignore', @@ -437,6 +443,10 @@ export function registerFuseCommands(program: Command) { .description('Mount CRM as virtual filesystem') .argument('[mountpoint]', 'Mount point directory') .option('--readonly', 'Mount read-only') + .option( + '--allow-other', + 'Allow processes running as a different uid (e.g. root) to access the mount. Linux-only; requires user_allow_other in /etc/fuse.conf when invoked by a non-root user.', + ) .action(async (mountpoint, opts) => { const { config } = await getCtx() const mp = mountpoint || config.mount.default_path diff --git a/src/config.ts b/src/config.ts index 6d82785..137ff45 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,6 +12,13 @@ export interface CRMConfig { mount: { default_path: string readonly: boolean + /** + * Mount with `-o allow_other` so processes running as a different uid + * (e.g. root, container orchestrators) can read/write the FUSE filesystem. + * Requires `user_allow_other` in /etc/fuse.conf when the mount is invoked + * by a non-root user. Linux-only; ignored by the macOS NFS path. + */ + allow_other: boolean max_recent_activity: number search_limit: number } @@ -44,6 +51,7 @@ function defaultConfig(): CRMConfig { mount: { default_path: join(homedir(), 'crm'), readonly: false, + allow_other: false, max_recent_activity: 10, search_limit: 20, },