Hosting and connections

Players need a route from their game to your modpack files. AutoModpack ships a built-in host that can carry that route over the Minecraft connection itself, over a dedicated port, or over infrastructure you provide. What the client actually dials is the advertised endpoint: advertised-endpoint-host and advertised-endpoint-port when set, otherwise the address the player connected to. modpack-host decides whether this server process handles the connection, and connection-mode decides how.

Connection modes and ports

Modebind-port: -1Explicit bind-port
HOLEPUNCHServes the pack over your Minecraft server port.bind-port is ignored; HOLEPUNCH always uses your Minecraft server port.
MAGICServes the pack over your Minecraft server port.Serves the pack on bind-port.
HTTPThis server won't be able to serve modpack, this combination only works when modpack is hosted on a different server using advertised-endpoint-host/port.Serves the pack on bind-port as plain HTTPS file semantics.

HOLEPUNCH is the default and needs no open port beyond Minecraft's. The client sends a Minecraft Login Start whose UUID carries a marker, and the server claims that connection for AutoModpack before vanilla builds a login listener. Unmarked logins reach Minecraft unchanged.

MAGIC marks its connections with the bytes AMMH followed by an AMOK confirmation before any TLS. On the shared Minecraft port, traffic without the marker passes through to Minecraft untouched. On a dedicated listener, non-magic traffic is rejected.

The opening differs per mode, but the wire is the same everywhere: after the opening, every mode serves the same HTTP contract - ordinary GET requests, Authorization: Bearer credentials when the pack validates secrets, keep-alive connections, and per-request negotiated compression.

The advertised mode is strict: the client uses the one mode the server advertises and does not fall back to another after a failure.

The HTTP contract

HTTP serves the pack as plain HTTPS file semantics. The whole wire is three GET routes: /head (the current generation's head document), /journal (the generation's object journal), and /objects/<sha1> (one content-addressed pack file, referenced by its hash from the head document). Responses use the ordinary HTTP answers: ETag/If-None-Match with 304 Not Modified, so an unchanged generation costs one header round trip, and a single Range with 206 Partial Content, so a download resumes at the byte it stopped at - the resume start is validated against Content-Range, never assumed.

Compression is plain HTTP negotiation, per request, with no heuristics: a request carrying Accept-Encoding gets its body encoded with the first offered codec the server knows (the client offers zstd, then gzip) and streamed as chunked frames; a request without the header gets identity with an exact Content-Length. Ranges negotiate like everything else - the slice is selected in file bytes, encoded on the wire, and Content-Range keeps describing file offsets, so resume and parallel takes are unaffected. The waiting track is the one requester that asks for identity: it is stored content that is already compressed. Nothing is ever buffered whole.

When validate-secrets is on, every request has to carry the login-issued secret as an Authorization: Bearer header, so secret-lifetime and revocation apply per request: a revoked secret dies at the next request, not at the next connection. The certificate verification ladder does not change.

The optional waiting track is an object like any other: drop an .ogg at automodpack/host-modpack/waiting-music.ogg, and the next publish stores it in the object store and writes its sha1 into the head document. Clients fetch objects/<sha1> only when they do not already hold those bytes, so a track that did not change costs nothing on any host. The track is waiting music: files past 5 MiB are refused at publish and aborted at the response head, and a generation that publishes without the file serves the bundled track. Licensing is the operator's responsibility.

Three hosting shapes serve byte-identical bytes; a client cannot tell them apart.

  • The embedded listener on bind-port, with the built-in certificate. disable-internal-tls hands termination to whatever sits in front: the listener serves plaintext on the internal hop while the front-end holds the certificate.
  • An external front-end. For a Cloudflare quick tunnel: run cloudflared tunnel --url http://127.0.0.1:<bind-port>, set connection-mode to HTTP, disable-internal-tls to true (the origin hop is plain HTTP), and advertise the tunnel host with advertised-endpoint-host plus advertised-endpoint-port: 443. The client's SNI follows the endpoint host, so the tunnel's public certificate is what clients see, and their first connection verifies it before anything downloads. To make that verification silent - and to survive the tunnel's certificate rotations - publish the front-end's fingerprint as an AMP1 record when you have a DNSSEC domain; without one, share the fingerprint out of band or hand out a pinned join address. A plain reverse proxy works the same way, with accept-proxy-protocol so the real client address survives the hop.
  • A static host of an exported tree, described under HTTP contract export: any web server that can serve files over HTTPS can serve the contract.

The client runs the same download engine on every mode: five connections, each pipelining within a 64 MiB unsettled-bytes window of at most 2048 requests, each file taken in exact 4 MiB ranges with up to one connection pool's depth of ranges in flight, and idle connections stealing the tails of large files. Zero-byte files are created locally and never requested, and a framed 404 fails only the request that got it, so the pipeline keeps flowing.

TLS

Clients always start TLS 1.3, on every mode. The certificate and its verification are described in security; the built-in host loads automodpack/credentials/certificate.crt and private-key.pem, generating a self-signed pair on first start when they are missing.

disable-internal-tls moves termination out of AutoModpack: the server expects traffic to arrive already decrypted, and some component in front of it must hold the certificate and terminate TLS. Clients behave identically either way, so this only pairs with an external terminator and never allows plaintext.

Proxy protocol

PROXY protocol headers are honored on dedicated MAGIC and HTTP listeners while accept-proxy-protocol is on, so the proxied client address is used. The shared Minecraft listener never consumes them. Enable the option only behind a trusted proxy: a claimed source address feeds IP bans and audit logs, and a forged one lets a banned player past the ban.

Reverse proxies and external endpoints

Setting modpack-host to false turns off every built-in listener. Clients still receive connection information, which can point at a proxy or a separate host. AutoModpack cannot validate that routing from the Minecraft server process, so the endpoint must speak the advertised mode exactly. For HTTP this is a supported advertise-only shape: the game server only advertises, and the URL contract is served externally.

HTTP contract export

The whole URL contract is three GET routes - /head, /journal, and /objects/<sha1> - so any static HTTPS host can serve it. /automodpack generate export-http <directory> writes the tree as byte-for-byte copies of the hosted files: point nginx, S3, or any dumb file host at the directory and clients cannot tell it apart from the embedded listener. The export works in every connection mode and needs no listener, and nothing already in the directory is ever deleted, so stale objects from old generations are yours to collect. Setting export-http-directory in the server config does the same export automatically after every publish. The export refuses to run while validate-secrets is on: a public mirror cannot check the per-request bearer secrets, so a validated pack has no public shape.

By default the export also resolves each object against Modrinth and CurseForge and leaves out the files those platforms still serve themselves - clients download those from the platforms' own CDNs, exactly as they do for the built-in host. A hit only counts when the platform reports the same file size as the hosted object, and anything unresolvable, including API failures, stays in the tree. The command reports the outcome as Exported N files to <dir> (M objects omitted: served by Modrinth/CurseForge; K unresolvable → included); the automatic export after a publish logs the same line. Set export-http-include-all or pass --all to the export command to keep every object in the tree, restoring your host as the backstop for platform link rot.

The exported tree is public to whoever the static host serves, and everything in it is published under your license responsibility. Pruned objects are fetched by clients directly from the platforms' own CDNs, not from your mirror, but a file a platform later deletes has no fallback on a pruned mirror.

S3-compatible buckets

The export tree is a valid bucket layout - head, journal, and objects/<sha1> are exactly S3 keys - so any S3-compatible store (e.g. AWS S3, Cloudflare R2, Backblaze B2, MinIO, Wasabi) serves the pack directly. The recipe, with the MinIO client standing in for aws s3 sync or rclone:

mc alias set local https://<bucket-endpoint> --insecure # omit --insecure for a trusted certificate
mc mb --ignore-existing local/<bucket>
mc mirror --overwrite <export-directory> local/<bucket>
mc anonymous set download local/<bucket> # clients fetch anonymously, no signatures

Advertise the bucket's virtual-hosted endpoint (e.g. AWS: https://<bucket>.s3.<region>.amazonaws.com) in advertised-endpoint-host/advertised-endpoint-port with connection-mode: HTTP. Preferably always use your own domain poiting to your S3 so that you can get CA singed cert or add an DNSSEC record, see.

Bandwidth

bandwidthLimit caps upload speed per client in MiB/s; 0 disables the cap. Much of the typical download never touches your server in the first place, because clients fetch files from Modrinth and CurseForge where possible, as described in client updates.