# Copyparty Copyparty is a lightweight file server and media streamer that runs as a single Python binary. It handles large payloads, media streaming, resumable uploads, directory browsing, and WebDAV from one process. **Host:** Dankserver — **Access:** `files.franksdank.cloud` guarded by the Anubis → Authentik chain ([[Caddy]] routes it post-auth to `192.168.0.235:3923`); the published port itself stays unauthenticated on the LAN — **Docs:** https://github.com/9001/copyparty ## Facts | Item | Value | |------|-------| | Running image | `ghcr.io/9001/copyparty-ac:latest` (ac = full edition; the bare `copyparty:latest` tag does not exist — ghcr answers 403 on the token request for nonexistent repos). Version at research time (2026-08): v1.20.21 | | Port | 3923 (host) → 3923 (container) | | Volume | bind mount the shared root directory | | Auth | Per-directory `.cpy` config files, HTTP basic auth, `dirkeys`, `filekeys` | | Streaming | HTTP range requests, HLS, WebDAV, WOPI (Office editing) | | Docker editions | `dj` (document + media), `iv` (image + video), `v` (video only) | ## Configuration ### Command-line interface (ac edition) The ac edition's entrypoint is `/bin/ash /z/cpp.sh -c /z/initcfg` — it parses a config file, then `exec`s `python3 -m copyparty "$@"`, so accounts and volumes are passed as **argv flags**, not env vars. `COPAY_PASS`/`COPAY_MODE` envs are *not* consumed by this image (zero `COPAY_` references in the package). The deployed interface: ``` command: ["-a", "dank:${COPAY_PASS}", "-v", "/w::r:ra,dank"] ``` `-a` takes `USER:PASS`. `-v` takes `src:dst:perm1:perm2:...` (authoritative: `python3 -m copyparty --help-accounts` inside the image, v1.20.21): each perm field is `[,user[,user...]]` and a field with **no username applies to every unauthenticated request**. Letters: `r` read (list + download), `w` upload, `m` move, `d` delete, `a` admin (control panel, config reload), `A` = `rwmda.` (everything). Empty `dst` mounts at the basename of `src` → URL `/w`. **Read-only posture (2026-08-31):** `::r` = browse + download for anonymous (required: the guarded `files.franksdank.cloud` chain delivers authentik'd traffic to copyparty *unauthenticated*), `:ra,dank` = admin for the `dank` account. **No `w`/`m`/`d` for anyone** — uploads, moves, and deletes are rejected server-side over HTTP, PUT, and WebDAV alike. There is no dedicated "reject uploads" volflag; the perms field is the documented mechanism (`--help-flags` volflags only throttle/reshape uploads). History: the pre-2026-08-31 flag `-v /w::A` was *not* "A for dank, nothing for anonymous" as an earlier revision of this page claimed — the bare `A` granted full read/write/delete to anyone reaching port 3923. Verified live the same day: anonymous `GET /Movies/` → 200, `PROPFIND /` → 207, `PUT` → 201. ### Configuration File Options Copyparty reads its configuration from `initcfg` (or `config` in the `/data` directory). Key options: - `use-bwrap: n` — Disable bwrap sandboxing for ffmpeg (default in Docker). Set to `y` to enable. - `shadowing //NULL` — Full unmap of a volume from the filesystem. - Config env-var expansion: Use `${VAR}` syntax in `[global]` section for environment variable substitution. ### Docker Editions Different editions bundle different libraries: | Edition | Bundled Libraries | Use Case | |---------|-------------------|----------| | `dj` | Document + image + video libraries | Full-featured server | | `iv` | Image + video libraries | Image and video focused | | `v` | Video only | Transcoding only, minimal footprint | The `iv` edition is no longer available for arm32/armv6. Use `dj` for maximum compatibility. ## Volumes Live deployment: the whole LUKS array is served at URL `/w` via `Volume=/mnt/entropy:/w:ro` in the quadlet unit ([[Entropy Volume]]) — read-only **at the mount level**, so no containerized consumer can write into the array through this path regardless of account permissions. The ac edition takes its volume roots from argv (`-v /w::r:ra,dank`), not from a `/data` convention; see [[#Command-line interface (ac edition)]] above. ## Docker Compose (reference — retired 2026-09-01; live deployment is the quadlet unit below) ```yaml copyparty: image: ghcr.io/9001/copyparty-ac:latest container_name: copyparty restart: unless-stopped command: ["-a", "dank:${COPAY_PASS}", "-v", "/w::r:ra,dank"] ports: - "3923:3923" volumes: - /mnt/entropy:/w - /opt/copyparty/cfg:/cfg healthcheck: test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:3923/')"] interval: 30s timeout: 10s retries: 3 ``` `${COPAY_ACCOUNT}`/`${COPAY_PASSWORD}` are **never** written into the unit: they live in the host-local `/home/dank/Containers/copyparty/env` (0600), loaded via `[Service] EnvironmentFile=` so systemd expands them into `Exec=-a ${COPAY_ACCOUNT}:${COPAY_PASSWORD}` at start. The password still lands in the container argv — visible to anyone who can run `podman inspect copyparty` on the host. ## Deployment (Quadlet) Since 2026-09-01: `dankserver/copyparty/quadlet/copyparty.container` → `copyparty.service`, deployed with `./scripts/deploy.sh dankserver --service copyparty`. Cutover details: - v1 generator rejects `Command=` → the argv lives in `Exec=`. - No `HealthCmd=` — the v1 generator splits unquoted spaces in that key; self-healing is `Restart=always`. - `PublishPort=3923:3923` — the first cutover dropped it (unit joined `dankserver_appnet`), copyparty answered inside the netns only; the published-port seam test now pins 3923 alongside 8085/8096/8083. - Config root moved `/opt/copyparty` → `/home/dank/Containers/copyparty/cfg`. - The deployer refuses to start the unit before the env file exists, so a missing seed cannot create an admin account with a garbage password. ## Healthcheck A simple HTTP check on port 3923 works. The root path returns HTTP 200 with the directory listing. ## WOPI Integration Copyparty v1.20.9+ supports WOPI (Web Application Open Platform Interface) for editing Office documents in the web UI via Collabora Online. Enable with the `--wopi-accs` flag to limit WOPI access to specific paths. ## Security Advisories | Version | CVE | Issue | |---------|-----|-------| | v1.20.19 | GHSA-phv8-wgjp-g4p9 | FTP upload outside volume | | v1.20.17 | GHSA-x5pq-m9p8-f4vx | dirkey/filekey confusion | | v1.20.9 | CVE-2026-27948 / GHSA-62cr-6wp5-q43h | XSS via file upload | Upgrade to v1.20.19+ to cover all known CVEs. The dirkey fix (v1.20.17) matters if using both `dirkeys` and `filekeys`. ## Gotchas - **Large transfers:** Disable TLS inside the app and let Caddy terminate TLS. This avoids double encryption overhead. - **Dedicated share directory:** Do not give Copyparty the whole disk. Mount only the intended share path. - **Whole-disk share (live choice):** this deployment *does* serve the whole ~91 T array from `/w`. The volume is mounted `:ro` in the unit AND the permission flags grant anonymous only `r`, so no remote write path exists at all now. But note: **LAN-direct `:3923` is unauthenticated browse/download** (anonymous holds `r`) — Caddy + Anubis + authentik guard only `files.franksdank.cloud`, not the published port. Tighten to `-v /w::r,dank` if anonymous LAN reads become unacceptable. (Before 2026-08-31, `-v /w::A` made the array world-writable on the LAN — see [[#Command-line interface (ac edition)]] — and the mount was rw.) - **Upload tokens scoped per folder:** Prevents accidental global write access. - **CSP nonces (v1.20.17+):** Security hardening for JavaScript execution. May break third-party JS plugins (`--js-browser` / `--html-head`). - **Custom thumbnail extractors (v1.20.19+):** New `thumbex` framework. Requires configuration in `initcfg`. ## See Also - [[Dankserver Stack]] - [[Caddy]] — TLS termination and authentication in front - [[ARR-Stack]] — Copyparty as a viewer alongside automated download management