When a user's FileBrowser directory lives inside an existing bind-mount
(e.g. /srv/data/users/alice), Docker needs an empty directory at the
host-side equivalent path before it can overlay an inner bind-mount on
top of the outer one. Without it the inner mount silently fails and
the extra folder never appears.
Now creates the mount-point directory on the host automatically before
adding the compose entry, with a visible note so the user knows a dir
was created.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Symlinks don't work for giving scoped FileBrowser users access to extra
folders — FileBrowser's afero.BasePathFs blocks symlinks that resolve
outside the user's scope directory.
Switch to bind-mount approach: additional_directories.sh edits
docker-compose.yml to add real bind-mount entries for each extra folder,
so FileBrowser sees them as actual subdirectories within the user's root.
No symlinks, no scope-boundary issues.
Features:
- Reads FB_PATH from .env to list available source folders on the host
- Parses docker-compose.yml to show what's already configured per user
- Adds/removes volume entries with a timestamped backup before each edit
- Prompts to restart the container after changes
- Normalises scope paths from the API (handles missing leading slash)
manage_users.sh removed — user CRUD is handled by the FileBrowser web UI.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Removed all user CRUD (add, delete, rename, passwd, scope change).
The FileBrowser web UI handles those. Script is now focused solely
on adding/removing extra folder shortcuts for users who have a
restricted root directory. Simpler menu, ~half the code.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
- Remove silent mkdir -p when adding extra directories. If the user's
directory doesn't exist in the container, ask before creating it.
This avoids creating folders the admin didn't intend.
- Rename all user-facing "scope" text to "directory" throughout:
prompts, banners, menu labels, column headers, help text, error
messages. The FileBrowser API field is still called "scope"
internally, and the CLI subcommand name stays "scope" for compat.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
docker-compose now uses two separate mounts:
fb_users named volume → /srv (user home dirs + shortcuts, Docker only)
FB_PATH bind mount → /srv/data (actual files, unchanged on host)
User dirs and their shortcuts live entirely in the Docker named volume —
they persist across reboots but never appear on the host filesystem.
Full-access scope is /data; per-user scopes are /alice etc.
manage_users.sh:
- Detect layout: get_data_root() returns /srv/data (new) or /srv (legacy)
so the script works with both old and new installs automatically
- "Linked directories" renamed to "additional directories" throughout
(menu labels, prompts, error messages, usage text)
- prompt_add_links → prompt_add_dirs, menu_links → menu_add_dirs
- Available-folder listing and symlink targets use get_data_root()
- Scope examples updated to show /data for full access (new layout)
- Don't offer additional directories when scope is /data (full access)
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Removed dateFormat, hideDotfiles, singleClick, and sorting from the
POST /api/users payload — FileBrowser rejects them as invalid data
types on some versions. Only stable fields are now sent.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
- User creation was silently failing: api_post used curl -sf (fail on
HTTP error) with output piped to /dev/null, so set -Eeuo pipefail
would exit the script with no message. Now captures HTTP status code
and response body, printing the server's error message on failure.
- Symlink folder listing (? prompt) only searched -type d, missing
symlinked directories in /srv. Changed to -type d -o -type l so
all browsable entries appear. Also switched xargs echo to tr for
a cleaner one-line display.
- mkdir -p and ln -s in docker exec were not checked for errors;
failures would silently kill the script under set -e. Both now
show a useful error message and continue/return instead of crashing.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Three fixes in ensure_token:
- Use jq to build the login JSON so special chars in passwords
(quotes, backslashes, etc.) don't break the raw string interpolation
- Add || true to the curl call so set -e doesn't silently exit on
connection refused before the response check runs
- Show FileBrowser's actual response on failure so the user can see
whether it's wrong credentials vs unreachable vs something else
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
List top-level directories from /srv so the user knows what to type
without having to guess. Subdirs (e.g. documents/shared) still work.
Skip link prompt when scope is / (user already has full access).
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
Users think in FileBrowser folder names, not container paths.
- prompt_add_links: prompt now "Folder to add [done]:" with examples
like "music photos documents/shared" — /srv added internally
- list_links: strip /srv prefix from displayed target paths
- menu_links: rename options to "Add folders" / "Remove a folder"
- scope prompts: remove the leading "/" hint (confusing); normalise
internally instead
- ok message shows "(from path/subdir)" only when link name differs
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
- prompt_add_links: shared helper loops asking for /srv paths until
blank Enter, creates symlinks via docker exec, skips bad paths
- cmd_add: offers linked-dir prompt right after user creation
- cmd_scope: offers linked-dir prompt after a scope change
- menu_links: tighter submenu (list + add loop + remove) replacing
the old menu_symlinks; called from Modify option 5
- Scope prompts now show the leading / so the user only types the rest
- Note on delete: symlinks on disk survive user deletion (by design)
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
New option 5 in the Modify submenu: "Manage linked directories"
- Lists existing symlinks inside the user's scope dir (via docker exec)
- Add: prompts for source path (/srv/...) and a display name, creates
the symlink inside /srv<scope>/<name> → /srv<source> in the container
- Remove: lists links, prompts for name, refuses to delete non-symlinks
- Warns if source path doesn't exist yet; offers to create anyway
- Auto-detects container name from docker-compose.yml next to the script
- Checks that the container is running before any docker exec calls
This is the recommended workaround for FileBrowser's single-scope
limitation: symlinks inside the scope dir appear as normal folders.
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
- Interactive menu when run with no args (login once, reuse token)
- Modify submenu: change username, password, scope, or toggle admin
- Fix: prompt_password now uses local -n nameref (bash 4.3+) so the
caller's local variable is actually set; printf -v was writing to
global scope and being shadowed by the caller's local declaration
- One-shot commands unchanged: list/add/delete/passwd/scope/rename/info
- FileBrowser only supports one scope per user — documented clearly
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
Undo the host.docker.internal approach from the previous commit — proper
Docker networking routes Caddy to services by container name on the shared
caddy_net, not via the host gateway.
- lib/common.sh: configure_caddy_for_service now accepts either a plain
port number (localhost:PORT fallback) or container:port (preferred).
The Caddyfile entry uses the container name for direct Docker DNS routing.
- services/caddy.sh: remove extra_hosts hack; update Caddyfile template
comments to show container_name:port format
- All service files: update configure_caddy_for_service calls to pass
container_name:internal_port (e.g. "filebrowser:80", "mealie:9000").
Services using network_mode:host keep plain port numbers.
- tools/manage_users.sh: new FileBrowser user-management script (deployed
to ~/docker/filebrowser/ during installation). Manages users via the
FileBrowser REST API: list, add, delete, passwd, scope, info commands.
Documents username format (letters/numbers/hyphens/underscores),
password rules (min 8 chars, letter + number required), and scope path
convention relative to /srv (= FB_PATH on the host).
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29