---
title: 'FTP server'
url: 'https://wiki.teletypegames.org/infrastructure/ftp-server'
markdown: 'https://wiki.teletypegames.org/infrastructure/ftp-server.md'
lang: en
date: '2026-10-14'
description: 'The DOS and Windows 9x tools cannot speak HTTPS, so they read FTP. One server shares everything they need: anonymous, read-only, passive mode only, on port 21. It also shares the WarpEngine game files of the portal, for any FTP client. It runs alone as its own stack. The programs that write into i…'
taxonomy:
  tag:
    - howto
---

# FTP server

> The DOS and Windows 9x tools cannot speak HTTPS, so they read FTP. One server shares everything they need: anonymous, read-only, passive mode only, on port 21. It also shares the WarpEngine game files of the portal, for any FTP client. It runs alone as its own stack. The programs that write into it run elsewhere.

- **Repository:** [git.teletypegames.org/infra/ftp-server](https://git.teletypegames.org/infra/ftp-server)
- **Address:** `ftp://ftp.teletypegames.org` (port 21, passive data 30000–30019)

# The folders

```
/
  APPS/           the DOSINST program store, the DOS shelf: a folder per program, each with an INFO.INI and a FILES.LST
  C64/            the C64 shelf: the latest build of every C64 game, versionless names, INDEX.TXT
  PLUS4/          the Plus/4 shelf, the same way
  WARP/           the WarpEngine files of teletypegames.org: every build of every game
  WIKI/           the TTGWIKI export: WIKI.INI, FILES.LST, EN/, HU/
```

| Folder | Read by | Written by |
|---|---|---|
| `APPS` | [DOSINST](https://wiki.teletypegames.org/dos/dosinst) (`PATH=/APPS`) | the [WarpEngine](https://wiki.teletypegames.org/engines/warp-engine) of teletypegames.org: its DOS shelf (`layout: "folders"`), rebuilt on every publish of DOSINST, [GMENU](https://wiki.teletypegames.org/dos/gmenu), [BMAGIC](https://wiki.teletypegames.org/dos/bmagic) and [TTGWIKI](https://wiki.teletypegames.org/dos/ttgwiki) |
| `C64` | [WARP64U](https://wiki.teletypegames.org/stores/warp64u) over HTTP, and any FTP client | the WarpEngine: its C64 shelf, rebuilt on every publish |
| `PLUS4` | any FTP or HTTP client | the WarpEngine: its Plus/4 shelf |
| `WARP` | any FTP client (the names are not DOS 8.3 names) | every [WarpEngine](https://wiki.teletypegames.org/engines/warp-engine) build that reaches teletypegames.org: the same files as `https://teletypegames.org/file/...` |
| `WIKI` | [TTGWIKI](https://wiki.teletypegames.org/dos/ttgwiki) (`PATH=/WIKI`) | the exporter of the `ttgwiki` stack, every 15 minutes |

**`WARP` is not a copy.** It is the storage folder of the portal (`/srv/stacks/teletype-games/data/softwares`), mounted read-only, so a new build is there at once. It also has the old versions, and the files of software deleted from the catalog while they stay in that folder.

**`APPS`, `C64` and `PLUS4` are not copies either.** They are read-only mounts of the shelves the WarpEngine keeps in that folder (`shelves/APPS`, `shelves/C64`, `shelves/PLUS4`); see *Shelves* on the [WarpEngine](https://wiki.teletypegames.org/engines/warp-engine) page. Since WarpEngine 1.7 no pipeline writes `APPS` over SSH: the engine rebuilds it from the catalog on every publish, without the programs' settings files, so a program hidden or deleted in the catalog leaves the store. A folder put there by hand (a test program) stays: copy it into `/srv/stacks/teletype-games/data/softwares/shelves/APPS/` and run DOSINST's `server/mkfiles.sh` on it.

**The same tree is on plain HTTP** at `http://archive.teletypegames.org/` (no redirect to HTTPS), for machines that cannot speak TLS: `http://archive.teletypegames.org/C64`, `/PLUS4`, `/APPS`.

**The root has these five folders and nothing else.** A DOSINST older than 0.6.0 has `PATH=/` in its `DOSINST.CFG`, and an update never changes that file: it finds no program at the root, so set `PATH=/APPS` in it by hand. Until 2026-10-15 the server showed the folders of `APPS` at the root too for such a DOSINST.

# The server

`/srv/stacks/ftp-server` on vps.teletype.hu. The server is one container: [pyftpdlib](https://github.com/giampaolo/pyftpdlib) 2.2.0, without root and with a read-only file system.

- Active mode (`PORT`/`EPRT`) gets a 502, so the container opens no outgoing connection.
- Uploads and deletes get a 550.
- At most 50 connections in all, and 5 from one IP.
- The PASV replies announce `FTP_MASQUERADE` from `.env`. The DOS clients ignore it and connect to the server's own address.

The stack folder is not a git checkout. A push to master that changes the server deploys it from the pipeline, which only asks the server to do it: the `deploy` step calls the server's `deploy` user over SSH (`deploy-ssh` image, referenced by digest), whose key is bound to one fixed command, `ci-deploy ftp-server` (`infra/host-config`). The server clones master from Gitea itself, checks `compose.yaml` against a policy (no privileged mode, no extra capabilities or host namespaces, no docker socket, bind mounts only in the stack and the read-only WarpEngine folder, public ports 21 and 30000–30019 only), copies `compose.yaml` and `server/` in, then runs `docker compose up -d --build`; no trusted volumes are needed. The test step runs in the `pyftpdlib` image of the Gitea registry (`git.teletypegames.org/build/pyftpdlib:3.13`, the server's Python with pyftpdlib 2.2.0), and installs pyftpdlib only when the image has another version. `root/WIKI` and `.env` stay as they are; `root/APPS`, `root/C64`, `root/PLUS4` and `root/WARP` are only empty mount points.

Until 2026-10-14 DOSINST (port 21, the `dosinst` stack) and TTGWIKI (port 2121) each had their own FTP server.

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [Infrastructure](https://wiki.teletypegames.org/infrastructure.md)
- Previous: [Deployment](https://wiki.teletypegames.org/infrastructure/deployment.md)
- Next: [Gitea](https://wiki.teletypegames.org/infrastructure/gitea.md)
