Each service can now be run directly with sudo bash <service>.sh on any
machine with Docker installed, without needing the full post-install repo.
Uses the shared bootstrap pattern from docs/standalone-template.sh.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
dest/current/ stays a plain mirror; deleted/overwritten files are moved to
dest/versions/YYYY-MM-DD/ so accidental deletions are recoverable while
intentional --delete still propagates. Unchanged files in version folders
are hardlinked via --link-dest to avoid extra disk cost. Supports local and
remote (SSH) source/destination paths, saved jobs, and a cron hint.
https://claude.ai/code/session_015SmW4EAD6mMLZGVCygy3GR
Each service can now be run directly with sudo bash <service>.sh on any
machine with Docker installed, without needing the full post-install repo.
Uses the shared bootstrap pattern from docs/standalone-template.sh.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Each service can now be run directly with sudo bash <service>.sh on any
machine with Docker installed, without needing the full post-install repo.
Uses the shared bootstrap pattern from docs/standalone-template.sh.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
- Swaps image from filebrowser/filebrowser to gtstef/filebrowser:stable
- Uses ./data:/home/filebrowser/data volume layout (Quantum convention)
- Generates data/config.yaml instead of settings.json + database.db
- Mounts primary path as /files with defaultEnabled: true
- Deploys fbq-add-source.sh for adding extra directories post-install
- Updates README with Quantum-specific instructions
docs: save standalone bootstrap as docs/standalone-template.sh
Preserves the full standalone pattern from filebrowser.sh as the
reference template for adding standalone support to other services.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Previously only edited config.yaml. Now:
- Shows existing volume mounts with host→container paths
- Detects when a host path isn't mounted yet and adds it to compose
- Remove flow optionally removes the volume mount from compose too
- Show displays both compose mounts and config sources side by side
- Uses down/up (not just restart) when compose changes
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Previously only detected the first volume mount, so sources on a
second mount (e.g. /files2) were not reachable. Now lists all mounts
from docker-compose.yml and lets the user pick by number or type a
full container path directly.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Allows editing share path, mount point, and options for an existing
fstab entry. CIFS entries also offer credential update (backs up the
old credentials file before overwriting). Backs up fstab before any
change and offers immediate remount to apply.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Interactive script to mount network drives and persist them in /etc/fstab:
- SMB/CIFS: prompts for share path, mount point, credentials (saved to
/etc/samba/credentials.<name> at 600/root), SMB version
- NFS: prompts for export path, mount point, NFS version
- Tests the mount before writing fstab
- Backs up /etc/fstab before any edit
- Remove flow: unmounts, removes fstab entry, optionally removes creds file
- Shows currently mounted network shares and fstab entries
- Uses nofail + _netdev so a missing share doesn't block boot
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
(( n++ )) returns exit code 1 when the pre-increment value is 0,
which kills the script under set -euo pipefail. Replace with
n=$(( n + 1 )) which always returns 0.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
yq stops mid-array when it encounters inline comments like
'# grant per-user only'. Strip comments via sed before piping
to yq in list_sources, source_exists, and before in-place edits.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Sources added manually without a name or config block showed "null"
for name and readOnly. Use yq // operator to fall back to sensible
defaults: "(unnamed)" for missing name, false for missing booleans.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Assumes the parent data directory is already broadly mounted in
docker-compose.yml so subdirectories are already inside the container.
Script now only edits config.yaml and restarts the container — no
docker-compose surgery needed per new source.
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Interactive script that keeps docker-compose.yml and config.yaml in sync
when adding or removing file sources in a FileBrowser Quantum installation.
- Prompts for host path, source name, defaultEnabled, readOnly
- Validates against duplicate volume mounts and source entries
- Backs up both files before editing
- Uses yq v4 for safe YAML editing (no fragile sed/awk on YAML)
- Offers container restart after changes
- Works standalone; no post-install framework dependency
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
Root cause: user scopes inside /data/... (the main data bind-mount) mean
any extra mount would nest inside another bind-mount — Docker does not
reliably layer these, so directories appeared in `ls` but were empty
and FileBrowser returned 404.
Fix: detect nested scopes and migrate them to the named volume (fb_users)
before adding any extra mounts.
Migration flow:
- Detects scope is inside /data bind-mount
- Suggests a new scope path in the named volume (e.g. /alice)
- Creates the scope directory via docker exec (no host-side clutter)
- Offers to mount the user's personal files dir as my-files/
- Updates the user's scope in FileBrowser via API PUT /api/users/:id
- Then adds requested extra dirs as non-nested bind-mounts
Also added: let user choose the display name for each added folder
(e.g. mount audiobookshelf but show it as "audiobooks").
https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
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
Services on caddy_net talk container-to-container using the internal
port. The host-mapped port (left side of ports:) is only for direct
access from another machine. Added this as a comment block in the
generated Caddyfile starter and in the README.
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
Replace the "configure manually" stub with a real implementation that
matches common.sh's configure_caddy_for_service behaviour:
- Prompts to configure (skippable)
- Asks for domain with example hint
- Backs up Caddyfile before writing
- Checks for duplicate domain entry, offers overwrite
- Appends site block with reverse_proxy, security headers, JSON log
- fmt + reload via docker exec caddy caddy fmt/reload
Used when filebrowser.sh is run standalone without the repo present.
When the repo is present (common.sh sourced), the real function is used.
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
DOCKER_DIR was computed before ACTUAL_HOME, so $HOME resolved to
/root (the root user's home) instead of the invoking user's home.
Files were created under /root/docker/ rather than ~/docker/.
Fix: compute ACTUAL_USER and ACTUAL_HOME first, then DOCKER_DIR.
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
Add standalone bootstrap block at the top (BASH_SOURCE[0] == $0 guard):
- If lib/common.sh is present (repo cloned), source it — gets real helpers
and picks up any existing ~/docker/.config site settings automatically
- If not (one-off copy), inline minimal stubs: logging, require_docker
(checks Docker is installed, gives install hint if not), prompt_text/yn
(match common.sh eval pattern so install_filebrowser locals work),
configure_caddy_for_service (no-op with manual hint), write_readme
- Sets DOCKER_DIR, ACTUAL_USER, SITE_TZ etc. with sensible defaults
- register_service becomes a no-op (no wizard menu to register into)
- Execution deferred via _RUN_STANDALONE flag to after function definition
Still works identically when sourced by setup.sh — the bootstrap block
is skipped entirely in that path.
https://claude.ai/code/session_01UZus2Q9gNTfUdqSMrhuX29
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