Deciding if a Derived Database Triggers Share-Alike Jump to heading

The question is not abstract and does not need a lawyer to answer in the ordinary case. It needs four facts about what you are actually shipping, applied in order, and written down where somebody can check them in three years.

Prerequisites Jump to heading

Conceptual minimum Jump to heading

The classification turns on four facts, asked in order because each one can end the enquiry.

Is anything distributed at all? Internal use creates no distribution obligation. A surprising share of pipelines that agonise over this turn out to publish nothing.

Can a recipient get data out? If what they receive is an image, a report or a rendered raster, they cannot — it is a produced work, attribution applies, share-alike does not. If they can query, extract or reconstruct features, it is a database.

Is OSM data combined with the other data, or merely packaged alongside it? Combined means the output cannot be separated back into its parts — a joined table, a single feature carrying attributes from both. Alongside means two distinguishable datasets shipped together, which is collective and leaves the other dataset’s terms alone.

Would the other dataset’s licence permit share-alike? If the answer is no and the output is a combined derived database, the architecture must change rather than the classification.

The four questions, in the order that ends the enquiry soonest Four questions asked in sequence. The first asks whether anything is distributed externally at all, and a no ends the enquiry with no obligation beyond attribution on published outputs. The second asks whether a recipient can extract data, and a no classifies the output as a produced work needing attribution only. The third asks whether the datasets are combined or merely packaged together, and merely packaged leaves the other dataset's terms untouched. The fourth asks whether the other licence permits share-alike, and a no means the architecture must change. Four questions, each can end it distributed? no: nothing owed beyond attribution extractable? no: produced work attribution only combined? no: collective other terms intact compatible? no: change design not the classification Most outputs stop at the first or second question, which is why the enquiry is far shorter in practice than its reputation suggests.
Only an output that reaches the fourth question needs a conversation with anybody outside the team.

Runnable solution Jump to heading

Classification is a decision, not a computation — but recording it as data means it can be reviewed, versioned and checked in a build.

python
from __future__ import annotations

import json
import logging
from dataclasses import dataclass, asdict
from enum import Enum
from pathlib import Path

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger("osm.licence.classify")


class Kind(str, Enum):
    NOT_DISTRIBUTED = "not_distributed"
    PRODUCED_WORK = "produced_work"
    COLLECTIVE = "collective_database"
    DERIVED = "derived_database"


class LicenceConflict(RuntimeError):
    """A combined derived database whose other source forbids share-alike."""


@dataclass(frozen=True)
class Output:
    name: str
    distributed: bool
    recipient_can_extract_data: bool
    other_datasets: tuple[str, ...] = ()
    combined_with_other: bool = False
    other_permits_share_alike: bool | None = None
    reasoning: str = ""


@dataclass(frozen=True)
class Classification:
    output: str
    kind: Kind
    attribution_required: bool
    share_alike_required: bool
    reasoning: str


def classify(output: Output) -> Classification:
    if not output.distributed:
        return Classification(output.name, Kind.NOT_DISTRIBUTED,
                              attribution_required=True,      # on anything shown
                              share_alike_required=False,
                              reasoning="not distributed externally; attribution "
                                        "still applies to any published output")

    if not output.recipient_can_extract_data:
        return Classification(output.name, Kind.PRODUCED_WORK,
                              attribution_required=True,
                              share_alike_required=False,
                              reasoning="recipients receive a rendering, not "
                                        "extractable data")

    if output.other_datasets and not output.combined_with_other:
        return Classification(output.name, Kind.COLLECTIVE,
                              attribution_required=True,
                              share_alike_required=False,
                              reasoning="OSM data shipped alongside, not merged "
                                        "into, the other dataset(s)")

    # A distributed, extractable, combined output is a derived database.
    if output.other_datasets and output.other_permits_share_alike is False:
        raise LicenceConflict(
            f"{output.name}: combined derived database including "
            f"{', '.join(output.other_datasets)}, whose licence does not permit "
            f"share-alike. Change the architecture, not the classification.")
    if output.other_datasets and output.other_permits_share_alike is None:
        raise LicenceConflict(
            f"{output.name}: share-alike compatibility of "
            f"{', '.join(output.other_datasets)} is unknown; establish it before "
            f"distributing")

    return Classification(output.name, Kind.DERIVED,
                          attribution_required=True,
                          share_alike_required=True,
                          reasoning="distributed, extractable and derived from "
                                    "the OSM database")


def record(classifications: list[Classification], path: Path) -> None:
    """Persist the decisions so a reviewer can read them without asking."""
    path.write_text(json.dumps([asdict(c) for c in classifications],
                               indent=2, default=str), encoding="utf-8")
    for c in classifications:
        logger.info("%-28s %-20s attribution=%s share-alike=%s",
                    c.output, c.kind.value, c.attribution_required,
                    c.share_alike_required)


if __name__ == "__main__":
    outputs = [
        Output("public tile archive", distributed=True,
               recipient_can_extract_data=True,
               reasoning="vector tiles carry queryable features"),
        Output("monthly PDF report", distributed=True,
               recipient_can_extract_data=False,
               reasoning="a rendering; no feature data is recoverable"),
        Output("internal warehouse", distributed=False,
               recipient_can_extract_data=True,
               reasoning="never leaves the organisation"),
    ]
    record([classify(o) for o in outputs], Path("licence-classification.json"))

Step-by-step walkthrough Jump to heading

  1. Ask about distribution first. It is the cheapest question and it ends the enquiry for a large share of outputs.
  2. Define extractability from the recipient’s position. The test is what they can get out, not what format you used internally. A raster image rendered from a database is a produced work even though a database produced it.
  3. Treat collective packaging as a real category. Shipping two distinguishable datasets together is materially different from merging them, and the distinction is worth preserving deliberately in the output’s structure.
  4. Refuse to classify when compatibility is unknown. An unknown answer is not a permissive one. Raising forces somebody to establish it rather than letting the pipeline proceed on an assumption.
  5. Raise on a genuine conflict. The exception message says what to change — the architecture, not the label — because relabelling is the tempting and wrong response.
  6. Record the reasoning, not just the verdict. In three years the verdict alone will be unexplainable, and the reasoning is what lets somebody confirm it still applies after the pipeline has changed.
  7. Persist as data. A JSON record can be versioned, diffed when an output changes, and read by a build check that refuses to ship an unclassified artefact.
Three common outputs and where each lands Three panels. A public vector tile archive is distributed and lets recipients query features, so it is a derived database owing attribution and share-alike. A monthly report or a rendered map image is distributed but yields no extractable features, so it is a produced work owing attribution only. An internal analytics warehouse is extractable but never distributed, so no share-alike obligation arises, though attribution still applies to anything published from it. Three outputs from one pipeline, three answers Vector tile archive Distributed: yes Extractable: yes Derived database Attribution and share-alike Rendered report Distributed: yes Extractable: no Produced work Attribution only Internal warehouse Distributed: no Extractable: yes No distribution Attribution on outputs One pipeline routinely produces all three, which is why the classification belongs per output rather than per project.
Classifying a project rather than each of its outputs is how a tile archive inherits a report's conclusion.
The same underlying data, distributed four ways, with four different answers A grid of four distribution forms for one enriched dataset against their classification and obligations. A rendered map image is a produced work owing attribution only. A vector tile archive is a derived database owing attribution and share-alike. A download containing the OSM part and the other dataset as separate files is a collective database, owing attribution for the OSM part and leaving the other terms alone. A single joined table containing both is a combined derived database, pulling the other dataset into share-alike. One dataset, four distributions, four answers Classification Obligation Rendered map image produced work attribution Vector tile archive derived database plus share-alike Two separate files collective other terms intact One joined table combined derived other data pulled in The bottom two rows contain identical information and differ only in how it is packaged, which is what makes this an engineering decision.
Nothing about the data changes between the last two rows — only whether a recipient receives it already joined.

Verification Jump to heading

  • Every distributed artefact has a classification. An unclassified output is a gap, and a build check can enforce that.
  • The extractability answer matches reality. Hand an artefact to somebody and ask them to get the features out; if they can, it is a database.
  • Collective outputs really are separable. Confirm a recipient can use the other dataset without the OSM part, and vice versa.
  • Unknown compatibility raises. Set the other dataset’s compatibility to unknown and confirm the classifier refuses.
  • The reasoning reads as evidence. Ask a colleague to evaluate the classification from the record alone.

Common errors and fixes Jump to heading

Symptom Root cause One-line fix
Whole project classified once Classification applied per project Classify each distributed output separately
Tile set treated as a produced work Format confused with extractability Ask what the recipient can get out, not what it looks like
Proprietary data pulled into share-alike Combined rather than packaged Keep the datasets separable in the distribution
Classification proceeds on an unknown Unknown treated as permissive Refuse to classify until compatibility is established
Verdict unexplainable later Reasoning not recorded Store the reasoning alongside the verdict
Relabelled to avoid a conflict The label changed, not the design Change the architecture; the classification follows the facts
New output ships unclassified No check for classification coverage Fail the build on any distributed artefact without a record

Specification reference Jump to heading

The Open Database Licence distinguishes a Derivative Database — a database based upon the licensed database — from a Produced Work, defined as a work resulting from the database that is not itself a database, and from a Collective Database, in which the licensed database is included alongside other independent databases without being merged into them. Share-alike obligations attach to distributed derivative databases; produced works carry a notice requirement instead. See the ODbL text for the definitions and the OSM legal FAQ for the project’s own reading of them.

Frequently Asked Questions Jump to heading

Is a database that I only query internally distributed?

No. Distribution is what triggers the share-alike obligation, and a database that never leaves your organisation is not distributed however large or valuable it is. Attribution still applies to anything you publish from it — a chart in a public report, a map shown to customers — but the derived-database question simply does not arise. A great many pipelines that worry about this are entirely internal.

What makes an output extractable?

Whether a recipient can recover structured features from it, not what file format it uses. A vector tile archive is extractable because the features and their attributes are right there; a raster image rendered from the same data is not, because a recipient has pixels. The test is what somebody receiving the artefact can do with it, which is why the question has to be asked from their position rather than from the pipeline’s.

Can I avoid share-alike by calling the output a produced work?

No, and the attempt is the single worst response available. The classification follows the facts about what is distributed, not the label applied to it, and a reviewer evaluating the question will look at the artefact rather than the documentation. If the current architecture produces a combined derived database and that is a problem, the architecture has to change — by keeping the datasets separable, by distributing renderings instead, or by not distributing.

Why record the reasoning as well as the verdict?

Because a verdict alone cannot be re-evaluated. In three years the pipeline will have changed, somebody will ask whether the classification still holds, and “derived database” tells them nothing about which facts produced that answer or which of them might have moved. Recording the reasoning turns a re-review from an investigation into a reading, and it is two sentences per output.

Up one level: OSM Licensing & ODbL Compliance.