Skip to content
simovilabPublic

About

A simple Caddy container to serve GTFS Schedule files

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

9 Commits

Folders and files

Repository files navigation

feeds

GTFS Schedule and GTFS-realtime hosting with Copyparty, following the setup in ../files/. Production routing remains https://feeds.simovi.org behind the existing Traefik instance.

Access and storage

  • Visitors can browse all directories and download files without logging in, but cannot upload, overwrite, create folders, move, rename, or delete.
  • The password-protected admin account has rwmda permissions: read, write, move/rename, delete, and volume administration. Accounts are managed in the private config, not through public registration.
  • GTFS_DATA_PATH still defaults to ./gtfs-data. Existing paths and direct URLs stay unchanged, e.g. /incofer/schedule/gtfs.zip. Everything in this directory is public; keep configuration, state, and backups outside it.
  • config/copyparty.conf and state/ are private and ignored by Git. State includes authentication salts, sessions, and the file index.
  • Cache-Control: no-cache forces clients to revalidate feed downloads. Copyparty uses Last-Modified for conditional requests.

The data mount is now writable by the service so an authenticated admin can manage feeds. Copyparty's permissions enforce read-only visitor access. The container runs non-root with a read-only root filesystem, dropped capabilities, and only the data, private state, and temporary scratch space writable.

Local setup

Requires Docker and Docker Compose >= 2.24.4. Start Docker Desktop first. No Traefik, DNS, external network, or TLS is required for this local preview.

Run once (do not overwrite an existing .env or private config):

test -f .env || cp .env.example .env
test -f config/copyparty.conf || cp config/copyparty.conf.example config/copyparty.conf
mkdir -p gtfs-data state
chmod 700 state
chmod 600 .env config/copyparty.conf
docker compose -f compose.yaml -f compose.local.yaml config --quiet
docker compose -f compose.yaml -f compose.local.yaml up -d

Open http://127.0.0.1:3923. The port is bound only to loopback. The template initially grants public reads only; management stays disabled until you create the admin account below. Existing .env settings are retained; check GTFS_DATA_PATH before starting so you serve the intended local directory.

Set the administrator password

Use the same state/ directory for hashing and for the running service. With usernames enabled, enter admin:YOUR_PASSWORD at the hidden prompt, not just the password. Do not put the password in shell arguments or .env.

docker compose -f compose.yaml -f compose.local.yaml run --rm --no-deps feed-server -c /cfg/copyparty.conf --ah-cli

Copy the generated hash (including its leading +) into the private config/copyparty.conf, and enable the admin permission line:

[accounts]
  admin: +YOUR_GENERATED_HASH

[/]
  /w
  accs:
    r: *
    rwmda: admin

Leave the hash tool with Ctrl-D, then recreate to pick up the edited config (including editors that save by replacing the file):

docker compose -f compose.yaml -f compose.local.yaml up -d --force-recreate

Log in through Copyparty's UI as admin, using just your password in the password field. Never grant visitors (*) w, m, d, or a permissions. Back up all of state/ with the config: losing the authentication salt invalidates the hashes.

Updating feeds

Log in, navigate to the agency/feed-type directory, and upload the file under its existing name, e.g. gtfs.zip. The browser uploader defaults to always replace an existing filename (u2ow: 2), keeping subscriber URLs stable. An existing browser preference can override this default; check the uploader's overwrite setting. PUT/WebDAV replacement is also enabled for the administrator.

You can create directories, rename/move, and delete after login. Files copied onto disk directly are rescanned every five minutes. The optional existing SSH helper remains unchanged:

./upload.sh incofer schedule ./build/incofer-feed.zip

That helper targets the real server, not the local preview. Do not use it for local tests.

Automated permission checks

The integration test starts Copyparty on loopback with disposable files, private state, and a random temporary admin password. It never uses .env, the real config, or GTFS_DATA_PATH. It tests anonymous denials and admin operations, including overwriting at the same URL and conditional downloads.

uv run --with copyparty==1.20.26 --with argon2-cffi python tests/test_access.py

For a manual UI check, upload/replace a disposable file while logged in, then use a private browser window to confirm browsing/downloads work but management does not. Ensure the startup permission summary grants everyone read only.

Later: server migration (not performed by local setup)

Do not deploy until local testing is complete. The production Compose file keeps the feed-server service, FEEDS_DOMAIN, TRAEFIK_NETWORK, certificate resolver, and existing data mount. No public host port is published in production.

Before switching on the server:

  1. Back up the existing feed directory and .env to a private location outside the public feed tree. Record the current image/revision for rollback.
  2. Preserve the exact existing GTFS_DATA_PATH. Do not rename, replace, empty, or copy over that directory; the new service mounts it in place at /w.
  3. Set COPYPARTY_UID/COPYPARTY_GID to its existing non-root owner, and ensure this user can read/write the feeds and state and read the private config. Inspect existing permissions first; do not recursively change ownership blindly.
  4. Create private config/state with that owner. Generate the admin hash on the server using its own persistent state, or transfer the local config together with its state securely. With production Compose, the hash command is docker compose run --rm --no-deps feed-server -c /cfg/copyparty.conf --ah-cli.
  5. Set xff-src in the config to Traefik's actual network IP with /32 (or an exact subnet only if all its members are trusted). Update it if the proxy IP changes. The loopback-only example intentionally does not trust arbitrary proxy headers. rproxy: -1 assumes Traefik is the sole proxy hop.
  6. Match the Traefik network, entrypoints and certificate resolver in .env. Do not add global BasicAuth: direct feed URLs must remain public.
  7. Validate configuration, pull the pinned image, and recreate the service:
docker compose config --quiet
docker compose pull
docker compose up -d --force-recreate
docker compose ps
docker compose logs --follow feed-server

Verify existing feed URLs, bytes/checksums, cache headers, public read-only access, admin management, HTTPS, and HTTP-to-HTTPS redirects. Do not include compose.local.yaml on the server. Back up config, .env, feed data, and state; stop the service or use a consistent snapshot when backing up databases. For rollback, restore the old code/config and recreate Caddy against the same feed directory; do not restore an old data backup over newer uploads blindly.

No server migration commands run automatically in this repository.

About

A simple Caddy container to serve GTFS Schedule files

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages