Technology Decision Note

Reproduce, Don't Redistribute

The repository ships no add-ons. It describes what should exist and lets OpenTTD acquire the payloads through its own content system — keeping third-party payloads out of Blueprint, while their licences remain their own.

What the repository ships, what it references, and what it deliberately never ships

What’s in the repository, and what isn’t

OpenTTD Blueprint takes an OpenTTD installation you already have and brings it to a specific, documented configuration — a researched set of add-ons, in a known load order, with the parameters and game settings that go with them.

Forty-seven files. Not one of them is an add-on.

There are no NewGRFs in it, no Game Scripts, no AIs, no basesets, no graphics or sounds, no OpenTTD binaries, no downloaded archives, and no cache of any of it. What it contains is Bash and PowerShell, a manifest, two JSON Schemas, tests and documentation.

SHIPS scripts · manifests · schemas tests · documentation REFERENCES content identifiers · last-tested versions licences · source URLs NEVER SHIPS NewGRFs · Game Scripts · AIs · basesets OpenTTD binaries · content archives

The middle band is the whole design: enough to identify and attribute an item, without carrying it.

That is not an accident of how it grew. It is a written rule in the repository: no third-party content, ever — the project references content by identity and lets OpenTTD fetch it, and the repository stays metadata and automation.

The distribution responsibility I’m not taking on

The alternative is obvious enough: collect the add-ons into an archive, ship it, unpack it on the target machine. One download, no moving parts.

It also changes what the project is. The moment I put someone else’s package inside mine, I am distributing a copy of their work, and I take on the job of understanding and satisfying whatever terms apply to redistributing each one — for every item, in every release.

To be clear about what I am not saying: redistributing this content may well be permitted. Most of it is GPL. The point isn’t that bundling would be forbidden — it’s that bundling makes each item’s redistribution terms my responsibility to get right, and that responsibility grows with every package added and every release cut. That is a permanent maintenance cost, and it buys nothing users actually wanted.

Describe the state; let OpenTTD acquire it

So the manifest describes the destination rather than carrying it. Each entry records what the item is, its content identifier, its load order, its parameters, its licence, where it came from, and the version last tested against.

Acquisition is OpenTTD’s job, because OpenTTD already has one. The installer launches the game as a headless dedicated server and writes to its console: refresh the catalogue, look up an item, select it, download. The game’s built-in Online Content system does the rest, fetching from the ecosystem’s own service.

The consequence is the part I’d point at:

Blueprint contains no HTTP client at all.

Not for content, not for metadata, not for anything: no curl, no wget, no Invoke-WebRequest, no download code of any kind. There is nothing to scrape, no mirror to keep alive, no content URL to go stale. The only network operations that happen are OpenTTD’s own, through the mechanism its ecosystem is built around.

One detail from live testing makes the boundary concrete: the identifiers OpenTTD’s console reports are not always the ones its package pages use — some types report the bytes in the opposite order. Blueprint handles that by asking the game what it currently sees and matching against the answer, rather than modelling the catalogue itself. Small, but the same principle: the game owns the catalogue, so ask it.

What that buys — and what it costs

The benefits are the ones above: no third-party payloads, no mirrors, no direct content URLs, no download implementation to maintain, and a clean line between what I wrote and what I merely reference.

The cost is real and I would rather name it than let the architecture sound tidier than it is.

Exact historical versions are not guaranteed. The manifest records the version each item was last tested against, but the console interface offers no documented way to request a specific historical version non-interactively. Acquisition resolves each item against the catalogue as it currently stands. If an add-on has moved on since the manifest was written, what lands is the newer one.

So Blueprint reproduces the intended content identities, load order, parameters and game settings. It does not reproduce a frozen bill of materials. The manifest’s version is something to diff against, not a promise. Giving acquisition back to the application keeps the project small; this is the bill for it.

Where my licence stops

Blueprint’s own code, manifests and documentation are AGPL-3.0-or-later. That covers the work I wrote and nothing else.

It does not relicense OpenTTD, and it does not relicense any NewGRF, Game Script, AI, baseset, graphic or sound that Blueprint causes to be downloaded or configured. A GPL add-on stays GPL whether you fetch it by hand or a script asks the game to fetch it for you. Causing something to be acquired is not the same as having authored it, and my licence has nothing to say about the terms someone else chose for their own work.

That is why the repository records a licence and a source for every item it can fetch, rather than treating “we don’t ship it” as the end of the question. Not shipping a payload removes one responsibility. It does not remove anyone’s interest in knowing whose work this is, and on what terms.

Less code was the safer architecture

The installers execute nothing they downloaded, and there is no eval, no Invoke-Expression, no piping a remote script into a shell — a consequence of the same decision rather than a separate virtue.

The conclusion isn’t that official content systems are automatically safe, legally or otherwise. It’s narrower:

Every part of the acquisition chain I don’t reimplement is one less protocol, mirror, payload and distribution boundary I have to own.

Generalising carefully: when an application already has a trusted native way of acquiring its own ecosystem, automation around it is usually smaller and clearer if it describes the desired state and lets the application do the acquiring — rather than quietly becoming a second distribution system for content it did not create.

open-sourcearchitecturelicensingautomation

More Notes like this


All Notes