diff --git a/manyfold/CHANGELOG.md b/manyfold/CHANGELOG.md new file mode 100644 index 000000000..c84fc5442 --- /dev/null +++ b/manyfold/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +## 1.0.3 + +- Added the add-on to this repository under the official add-on folder/slug name `manyfold`. +- Updated image namespace and repository metadata for this repository: + - `image: ghcr.io/alexbelgium/manyfold-{arch}` + - `url: https://github.com/alexbelgium/hassio-addons/tree/master/manyfold` +- Updated AppArmor profile name to `hassio-addons/manyfold`. + +## 1.0.2 + +- Added build metadata for Home Assistant CI compatibility: + - `manyfold/build.yaml` with multi-arch `build_from` entries + - image template wiring in `config.yaml` +- Switched Docker base wiring to Home Assistant add-on build conventions: + - `Dockerfile` now uses `ARG BUILD_FROM` and `FROM ${BUILD_FROM}` +- Updated add-on `url` metadata to this repository path. +- Updated repository README to remove obsolete `import_path` references. +- Added ShellCheck compatibility headers (`# shellcheck shell=bash`) to s6/entry scripts using `with-contenv`. +- Removed default-valued metadata keys (`apparmor`, `boot`, `ingress`, `stage`) to satisfy add-on linter rules. + +## 1.0.1 + +- New resource tuning options for smaller HAOS hosts: + - `web_concurrency` + - `rails_max_threads` + - `default_worker_concurrency` + - `performance_worker_concurrency` + - `max_file_upload_size` + - `max_file_extract_size` +- Baseline AppArmor support: + - `apparmor: true` in add-on metadata + - `manyfold/apparmor.txt` profile +- Removed `import_path` option and runtime wiring to reduce confusion (it was not a web import endpoint). +- Kept ingress disabled and documented direct access on port `3214`. +- Host media mappings (`/share`, `/media`) are writable to support writable library paths like `/media/manyfold/models`. +- Home Assistant ingress/panel 404 issue by moving to direct web UI access model. +- Startup/runtime setup improvements: + - Better path validation for configured library and thumbnails paths + - Clearer startup logs and configuration summary + - More robust secret/bootstrap handling and ownership setup +- Recommended small-server baseline (see README): + - `web_concurrency: 1` + - `rails_max_threads: 5` + - `default_worker_concurrency: 2` + - `performance_worker_concurrency: 1` + +## 1.0.0 + +- First Home Assistant add-on packaging for Manyfold (`manyfold`). +- Runs `ghcr.io/manyfold3d/manyfold-solo` with persistent data under `/config`. +- Sidebar/web UI integration on port `3214`. +- Configurable storage paths and startup path safety checks. +- Non-root runtime defaults (`puid`/`pgid`) and startup ownership handling. diff --git a/manyfold/Dockerfile b/manyfold/Dockerfile new file mode 100644 index 000000000..d97c8a354 --- /dev/null +++ b/manyfold/Dockerfile @@ -0,0 +1,27 @@ +ARG BUILD_FROM=ghcr.io/manyfold3d/manyfold-solo:latest +FROM ${BUILD_FROM} + +# hadolint ignore=DL3041 +RUN set -eux; \ + if command -v apk >/dev/null 2>&1; then \ + apk add --no-cache bash coreutils jq openssl; \ + elif command -v apt-get >/dev/null 2>&1; then \ + apt-get update; \ + apt-get install -y --no-install-recommends bash coreutils jq openssl ca-certificates; \ + rm -rf /var/lib/apt/lists/*; \ + elif command -v dnf >/dev/null 2>&1; then \ + dnf install -y bash coreutils jq openssl; \ + dnf clean all; \ + else \ + echo "Unsupported base image: missing apk/apt-get/dnf"; \ + exit 1; \ + fi + +COPY run.sh /run.sh +COPY rootfs / + +RUN chmod +x /run.sh \ + && chmod +x /etc/s6-overlay/s6-rc.d/manyfold/run \ + && chmod +x /etc/s6-overlay/s6-rc.d/manyfold/finish + +ENTRYPOINT ["/init"] diff --git a/manyfold/README.md b/manyfold/README.md new file mode 100644 index 000000000..98354845c --- /dev/null +++ b/manyfold/README.md @@ -0,0 +1,132 @@ +# Manyfold Home Assistant Add-on + +This add-on wraps `ghcr.io/manyfold3d/manyfold-solo` for Home Assistant OS with persistent storage and configurable host-backed media paths. + +Documentation: [manyfold.app/get-started](https://manyfold.app/get-started/) + +## Features + +- Runs Manyfold on port `3214`. +- Persists app data, database, cache, and settings under `/config` (`addon_config`). +- Uses a configurable library path on Home Assistant host storage. +- Refuses startup if configured paths resolve outside `/share`, `/media`, or `/config`. +- No external PostgreSQL or Redis required. +- Supports `amd64` and `aarch64`. +- Includes a baseline AppArmor profile. + +## Default paths + +- Library path: `/share/manyfold/models` +- Thumbnails path: `/config/thumbnails` + +## Installation + +1. In Home Assistant OS Add-on Store, open menu (`...`) -> `Repositories`. +2. Add the Git repository URL for this add-on repository root (the repo includes `repository.yaml` and `manyfold/`). +3. Refresh Add-on Store and install **Manyfold**. +4. Configure options (defaults are safe for first run): + - `library_path`: `/share/manyfold/models` + - `secret_key_base`: leave blank to auto-generate + - `puid` / `pgid`: set to a non-root UID/GID (see "Fix root warning (PUID/PGID)" below) + - optionally tune worker/thread and upload limits in "Small server tuning" below +5. Start the add-on. +6. Open `http://:3214`. + +Before first start, ensure your library folder exists on the host: + +```bash +mkdir -p /share/manyfold/models +``` + +Local development alternative on the HA host: + +1. Copy `manyfold/` to `/addons/manyfold`. +2. In Add-on Store menu (`...`), click `Check for updates`. +3. Install and run **Manyfold** from local add-ons. + +## Library/index workflow + +1. Drop STL/3MF/etc into `/share/manyfold/models` on the host. +2. In Manyfold UI, configure a library that points to the same container path. +3. Thumbnails and indexing artifacts persist in `/config/thumbnails`. + +## Options + +- `secret_key_base`: App secret. Auto-generated and persisted at `/config/secret_key_base` when empty. +- `puid` / `pgid`: Ownership applied to writable mapped directories (`/config` paths). +- `multiuser`: Toggle Manyfold multiuser mode. +- `library_path`: Scanned/indexed path. +- `thumbnails_path`: Persistent thumbnails/index artifacts (must be under `/config`). +- `log_level`: `info`, `debug`, `warn`, `error`. +- `web_concurrency`: Puma worker process count. +- `rails_max_threads`: Max threads per Puma worker. +- `default_worker_concurrency`: Sidekiq default queue concurrency. +- `performance_worker_concurrency`: Sidekiq performance queue concurrency. +- `max_file_upload_size`: Max uploaded archive size in bytes. +- `max_file_extract_size`: Max extracted archive size in bytes. + +## Small server tuning + +For low-memory HAOS hosts, start with: + +```yaml +web_concurrency: 1 +rails_max_threads: 5 +default_worker_concurrency: 2 +performance_worker_concurrency: 1 +max_file_upload_size: 268435456 +max_file_extract_size: 536870912 +``` + +Then restart the add-on and increase gradually only if needed. + +## Fix root warning (PUID/PGID) + +If Manyfold shows: + +`Manyfold is running as root, which is a security risk.` + +set `puid` and `pgid` in the add-on Configuration tab to a non-root UID/GID. + +Example: + +```yaml +puid: 1000 +pgid: 1000 +``` + +How to find the correct values in Home Assistant: + +1. Open the **Terminal & SSH** add-on (or SSH into the HA host). +2. If you know the target Linux user name, run: + +```bash +id +``` + +Use the `uid=` value for `puid` and `gid=` value for `pgid`. + +If you do not have a specific username, use the owner of the Manyfold folders: + +```bash +stat -c '%u %g' /share/manyfold/models +``` + +Set `puid`/`pgid` to those numbers. + +After changing values: + +1. Save add-on Configuration. +2. Restart the Manyfold add-on. +3. Check logs for `puid:pgid=:` and confirm the warning is gone. + +## Validation behavior + +- Startup fails if `library_path` or `thumbnails_path` resolve outside mapped storage roots. +- `thumbnails_path` must resolve under `/config` to guarantee persistence. +- Startup fails if `library_path` is not readable. + +## Notes + +- This baseline avoids Home Assistant ingress and keeps direct port access. +- If `puid`/`pgid` change, restart the add-on to re-apply ownership to mapped directories. diff --git a/manyfold/apparmor.txt b/manyfold/apparmor.txt new file mode 100644 index 000000000..91fcc4abe --- /dev/null +++ b/manyfold/apparmor.txt @@ -0,0 +1,18 @@ +#include + +profile hassio-addons/manyfold flags=(attach_disconnected,mediate_deleted) { + #include + #include + #include + #include + + # Baseline profile for Manyfold in HAOS. Keep broad compatibility while + # denying known high-risk kernel interfaces. + file, + network, + capability, + + deny /proc/kcore rwklx, + deny /proc/sysrq-trigger rwklx, + deny /sys/firmware/** rwklx, +} diff --git a/manyfold/build.yaml b/manyfold/build.yaml new file mode 100644 index 000000000..adc465afa --- /dev/null +++ b/manyfold/build.yaml @@ -0,0 +1,4 @@ +--- +build_from: + aarch64: ghcr.io/manyfold3d/manyfold-solo:latest + amd64: ghcr.io/manyfold3d/manyfold-solo:latest diff --git a/manyfold/config.yaml b/manyfold/config.yaml new file mode 100644 index 000000000..cf40dc0ca --- /dev/null +++ b/manyfold/config.yaml @@ -0,0 +1,53 @@ +name: "Manyfold" +slug: manyfold +description: "Manyfold 3D model manager as a Home Assistant add-on, using the upstream image with configurable library/index paths." +version: "1.0.3" +url: "https://github.com/alexbelgium/hassio-addons/tree/master/manyfold" +image: ghcr.io/alexbelgium/manyfold-{arch} +arch: + - amd64 + - aarch64 +startup: services +init: false + +ports: + 3214/tcp: 3214 +ports_description: + 3214/tcp: "Manyfold Web UI" + +webui: "http://[HOST]:[PORT:3214]" + +map: + - addon_config:rw + - share:rw + - media:rw + +options: + secret_key_base: "" + puid: 1000 + pgid: 1000 + multiuser: true + library_path: "/share/manyfold/models" + thumbnails_path: "/config/thumbnails" + log_level: "info" + web_concurrency: 4 + rails_max_threads: 16 + default_worker_concurrency: 4 + performance_worker_concurrency: 1 + max_file_upload_size: 1073741824 + max_file_extract_size: 1073741824 + +schema: + secret_key_base: str + puid: int + pgid: int + multiuser: bool + library_path: str + thumbnails_path: str + log_level: list(info|debug|warn|error) + web_concurrency: int + rails_max_threads: int + default_worker_concurrency: int + performance_worker_concurrency: int + max_file_upload_size: int + max_file_extract_size: int diff --git a/manyfold/icon.png b/manyfold/icon.png new file mode 100644 index 000000000..bdc50ecbc Binary files /dev/null and b/manyfold/icon.png differ diff --git a/manyfold/logo.png b/manyfold/logo.png new file mode 100644 index 000000000..f6f4b8b3c Binary files /dev/null and b/manyfold/logo.png differ diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/dependencies.d/base b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish new file mode 100755 index 000000000..2c31af381 --- /dev/null +++ b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash +exit 0 diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/run b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/run new file mode 100755 index 000000000..781859916 --- /dev/null +++ b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/run @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash +exec /run.sh diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/type b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/type @@ -0,0 +1 @@ +longrun diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/manyfold b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/manyfold new file mode 100644 index 000000000..e69de29bb diff --git a/manyfold/run.sh b/manyfold/run.sh new file mode 100755 index 000000000..2508043f7 --- /dev/null +++ b/manyfold/run.sh @@ -0,0 +1,253 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash +set -Eeuo pipefail + +CONFIG_DIR="/config" +OPTIONS_JSON="/data/options.json" +SECRET_FILE="${CONFIG_DIR}/secret_key_base" +DEFAULT_LIBRARY_PATH="/share/manyfold/models" +DEFAULT_THUMBNAILS_PATH="/config/thumbnails" +DEFAULT_LOG_LEVEL="info" +DEFAULT_WEB_CONCURRENCY="4" +DEFAULT_RAILS_MAX_THREADS="16" +DEFAULT_DEFAULT_WORKER_CONCURRENCY="4" +DEFAULT_PERFORMANCE_WORKER_CONCURRENCY="1" +DEFAULT_MAX_FILE_UPLOAD_SIZE="1073741824" +DEFAULT_MAX_FILE_EXTRACT_SIZE="1073741824" + +log() { + echo "[manyfold-addon] $*" +} + +die() { + echo "[manyfold-addon] ERROR: $*" >&2 + exit 1 +} + +read_opt() { + local key="$1" + jq -er --arg k "$key" '.[$k]' "$OPTIONS_JSON" 2>/dev/null || true +} + +normalize_path() { + local raw="$1" + if command -v realpath >/dev/null 2>&1; then + realpath -m "$raw" + return + fi + + case "$raw" in + /*) printf '%s\n' "$raw" ;; + *) printf '/%s\n' "$raw" ;; + esac +} + +is_allowed_path() { + local resolved="$1" + case "$resolved" in + /share|/share/*|/media|/media/*|/config|/config/*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +require_mapped_path() { + local label="$1" + local raw="$2" + local resolved + + resolved="$(normalize_path "$raw")" + if ! is_allowed_path "$resolved"; then + die "${label} '${raw}' resolves to '${resolved}', which is outside /share, /media, and /config" + fi + + printf '%s\n' "$resolved" +} + +ensure_dir() { + local dir="$1" + mkdir -p "$dir" +} + +ensure_existing_or_create() { + local label="$1" + local dir="$2" + + if [[ -d "$dir" ]]; then + return + fi + + if mkdir -p "$dir" 2>/dev/null; then + return + fi + + die "${label} '${dir}' does not exist and could not be created. Create it on the host or choose a writable path under /config." +} + +chown_recursive_if_writable() { + local owner="$1" + local path="$2" + + if [[ ! -e "$path" ]]; then + log "Skipping ownership update for ${path} (missing path)" + return + fi + + if [[ -w "$path" ]]; then + chown -R "$owner" "$path" + return + fi + + log "Skipping ownership update for ${path} (read-only mapping)" +} + +generate_secret() { + if command -v openssl >/dev/null 2>&1; then + openssl rand -hex 64 + return + fi + + head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n' +} + +start_manyfold() { + if [[ -x /usr/src/app/bin/docker-entrypoint.sh ]]; then + log "Starting Manyfold via /usr/src/app/bin/docker-entrypoint.sh foreman start" + cd /usr/src/app + exec ./bin/docker-entrypoint.sh foreman start + fi + + if [[ -x /app/bin/docker-entrypoint.sh ]]; then + log "Starting Manyfold via /app/bin/docker-entrypoint.sh foreman start" + cd /app + exec ./bin/docker-entrypoint.sh foreman start + fi + + local candidate + for candidate in \ + /usr/local/bin/docker-entrypoint.sh \ + /usr/local/bin/docker-entrypoint \ + /docker-entrypoint.sh \ + /entrypoint.sh + do + if [[ -x "$candidate" ]]; then + log "Starting Manyfold via ${candidate}" + if [[ "$candidate" == *docker-entrypoint* ]]; then + exec "$candidate" foreman start + fi + exec "$candidate" + fi + done + + if command -v docker-entrypoint >/dev/null 2>&1; then + log "Starting Manyfold via docker-entrypoint" + exec docker-entrypoint foreman start + fi + + if [[ -d /usr/src/app ]]; then + cd /usr/src/app + elif [[ -d /app ]]; then + cd /app + fi + + if command -v bundle >/dev/null 2>&1; then + log "Starting Manyfold via rails server fallback" + exec bundle exec rails server -b 0.0.0.0 -p 3214 + fi + + die "Could not find a known Manyfold entrypoint" +} + +[[ -f "$OPTIONS_JSON" ]] || die "Missing options file at ${OPTIONS_JSON}" + +PUID="$(read_opt puid)"; PUID="${PUID:-1000}" +PGID="$(read_opt pgid)"; PGID="${PGID:-1000}" +MULTIUSER="$(read_opt multiuser)"; MULTIUSER="${MULTIUSER:-true}" +LIBRARY_PATH_RAW="$(read_opt library_path)"; LIBRARY_PATH_RAW="${LIBRARY_PATH_RAW:-$DEFAULT_LIBRARY_PATH}" +THUMBNAILS_PATH_RAW="$(read_opt thumbnails_path)"; THUMBNAILS_PATH_RAW="${THUMBNAILS_PATH_RAW:-$DEFAULT_THUMBNAILS_PATH}" +LOG_LEVEL="$(read_opt log_level)"; LOG_LEVEL="${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" +WEB_CONCURRENCY="$(read_opt web_concurrency)"; WEB_CONCURRENCY="${WEB_CONCURRENCY:-$DEFAULT_WEB_CONCURRENCY}" +RAILS_MAX_THREADS="$(read_opt rails_max_threads)"; RAILS_MAX_THREADS="${RAILS_MAX_THREADS:-$DEFAULT_RAILS_MAX_THREADS}" +DEFAULT_WORKER_CONCURRENCY="$(read_opt default_worker_concurrency)"; DEFAULT_WORKER_CONCURRENCY="${DEFAULT_WORKER_CONCURRENCY:-$DEFAULT_DEFAULT_WORKER_CONCURRENCY}" +PERFORMANCE_WORKER_CONCURRENCY="$(read_opt performance_worker_concurrency)"; PERFORMANCE_WORKER_CONCURRENCY="${PERFORMANCE_WORKER_CONCURRENCY:-$DEFAULT_PERFORMANCE_WORKER_CONCURRENCY}" +MAX_FILE_UPLOAD_SIZE="$(read_opt max_file_upload_size)"; MAX_FILE_UPLOAD_SIZE="${MAX_FILE_UPLOAD_SIZE:-$DEFAULT_MAX_FILE_UPLOAD_SIZE}" +MAX_FILE_EXTRACT_SIZE="$(read_opt max_file_extract_size)"; MAX_FILE_EXTRACT_SIZE="${MAX_FILE_EXTRACT_SIZE:-$DEFAULT_MAX_FILE_EXTRACT_SIZE}" +SECRET_KEY_BASE="$(read_opt secret_key_base)"; SECRET_KEY_BASE="${SECRET_KEY_BASE:-}" + +[[ "$PUID" =~ ^[0-9]+$ ]] || die "puid must be a non-negative integer" +[[ "$PGID" =~ ^[0-9]+$ ]] || die "pgid must be a non-negative integer" +[[ "$WEB_CONCURRENCY" =~ ^[1-9][0-9]*$ ]] || die "web_concurrency must be a positive integer" +[[ "$RAILS_MAX_THREADS" =~ ^[1-9][0-9]*$ ]] || die "rails_max_threads must be a positive integer" +[[ "$DEFAULT_WORKER_CONCURRENCY" =~ ^[1-9][0-9]*$ ]] || die "default_worker_concurrency must be a positive integer" +[[ "$PERFORMANCE_WORKER_CONCURRENCY" =~ ^[1-9][0-9]*$ ]] || die "performance_worker_concurrency must be a positive integer" +[[ "$MAX_FILE_UPLOAD_SIZE" =~ ^[1-9][0-9]*$ ]] || die "max_file_upload_size must be a positive integer (bytes)" +[[ "$MAX_FILE_EXTRACT_SIZE" =~ ^[1-9][0-9]*$ ]] || die "max_file_extract_size must be a positive integer (bytes)" + +LIBRARY_PATH="$(require_mapped_path "library_path" "$LIBRARY_PATH_RAW")" +THUMBNAILS_PATH="$(require_mapped_path "thumbnails_path" "$THUMBNAILS_PATH_RAW")" + +case "$THUMBNAILS_PATH" in + /config|/config/*) ;; + *) die "thumbnails_path must resolve under /config for persistence" ;; +esac + +ensure_dir "$CONFIG_DIR" +ensure_dir "$DEFAULT_THUMBNAILS_PATH" +ensure_existing_or_create "library_path" "$LIBRARY_PATH" +ensure_dir "$THUMBNAILS_PATH" +[[ -r "$LIBRARY_PATH" ]] || die "library_path '${LIBRARY_PATH}' is not readable" + +if [[ -z "$SECRET_KEY_BASE" ]]; then + if [[ -s "$SECRET_FILE" ]]; then + SECRET_KEY_BASE="$(cat "$SECRET_FILE")" + log "Loaded SECRET_KEY_BASE from ${SECRET_FILE}" + else + SECRET_KEY_BASE="$(generate_secret)" + printf '%s' "$SECRET_KEY_BASE" > "$SECRET_FILE" + chmod 600 "$SECRET_FILE" + log "Generated and stored SECRET_KEY_BASE at ${SECRET_FILE}" + fi +else + printf '%s' "$SECRET_KEY_BASE" > "$SECRET_FILE" + chmod 600 "$SECRET_FILE" + log "Saved provided SECRET_KEY_BASE to ${SECRET_FILE}" +fi + +export SECRET_KEY_BASE +export PUID +export PGID +export MULTIUSER +export MANYFOLD_MULTIUSER="$MULTIUSER" +export MANYFOLD_LIBRARY_PATH="$LIBRARY_PATH" +export MANYFOLD_THUMBNAILS_PATH="$THUMBNAILS_PATH" +export RAILS_LOG_LEVEL="$LOG_LEVEL" +export MANYFOLD_LOG_LEVEL="$LOG_LEVEL" +export WEB_CONCURRENCY +export RAILS_MAX_THREADS +export DEFAULT_WORKER_CONCURRENCY +export PERFORMANCE_WORKER_CONCURRENCY +export MAX_FILE_UPLOAD_SIZE +export MAX_FILE_EXTRACT_SIZE +export PORT="3214" + +chown_recursive_if_writable "$PUID:$PGID" "$CONFIG_DIR" +chown_recursive_if_writable "$PUID:$PGID" "$DEFAULT_THUMBNAILS_PATH" +chown_recursive_if_writable "$PUID:$PGID" "$LIBRARY_PATH" +chown_recursive_if_writable "$PUID:$PGID" "$THUMBNAILS_PATH" + +log "Configuration summary:" +log " library_path=${LIBRARY_PATH}" +log " thumbnails_path=${THUMBNAILS_PATH}" +log " multiuser=${MULTIUSER}" +log " puid:pgid=${PUID}:${PGID}" +log " web_concurrency=${WEB_CONCURRENCY}" +log " rails_max_threads=${RAILS_MAX_THREADS}" +log " default_worker_concurrency=${DEFAULT_WORKER_CONCURRENCY}" +log " performance_worker_concurrency=${PERFORMANCE_WORKER_CONCURRENCY}" +log " max_file_upload_size=${MAX_FILE_UPLOAD_SIZE}" +log " max_file_extract_size=${MAX_FILE_EXTRACT_SIZE}" + +start_manyfold diff --git a/manyfold/translations/en.yaml b/manyfold/translations/en.yaml new file mode 100644 index 000000000..e5c2069f6 --- /dev/null +++ b/manyfold/translations/en.yaml @@ -0,0 +1,40 @@ +configuration: + secret_key_base: + name: Secret key base + description: Leave blank to auto-generate and persist at /config/secret_key_base. + puid: + name: PUID + description: User ID for file ownership on writable mapped volumes. + pgid: + name: PGID + description: Group ID for file ownership on writable mapped volumes. + multiuser: + name: Multiuser mode + description: Enable or disable Manyfold multiuser login. + library_path: + name: Library path + description: Folder scanned/indexed by Manyfold. Must be under /share, /media, or /config. + thumbnails_path: + name: Thumbnails path + description: Path for thumbnails/index artifacts. Must resolve under /config. + log_level: + name: Log level + description: Rails log verbosity. + web_concurrency: + name: Web workers + description: Puma worker process count (WEB_CONCURRENCY). Lower this on small servers. + rails_max_threads: + name: Web max threads + description: Max threads per Puma worker (RAILS_MAX_THREADS). Lower values reduce memory use. + default_worker_concurrency: + name: Default worker concurrency + description: Sidekiq concurrency for the default worker queue. + performance_worker_concurrency: + name: Performance worker concurrency + description: Sidekiq concurrency for the performance queue. + max_file_upload_size: + name: Max upload size (bytes) + description: Upper limit for uploaded archive size (MAX_FILE_UPLOAD_SIZE). + max_file_extract_size: + name: Max extract size (bytes) + description: Upper limit for extracted archive size (MAX_FILE_EXTRACT_SIZE).