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.
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.
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
- Ask about distribution first. It is the cheapest question and it ends the enquiry for a large share of outputs.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.
Related Jump to heading
- OSM Licensing & ODbL Compliance — the parent topic and the vocabulary this applies.
- Automating ODbL Attribution in Derived Products — the obligation that applies whichever way this lands.
- Recording OSM Data Provenance in a Pipeline — the record that makes a classification checkable.
- Attribute Enrichment from Authoritative Sources — where combining datasets raises this question.
- OSM Vector Tiles & Rendering Pipelines — an output whose classification is regularly misjudged.
Up one level: OSM Licensing & ODbL Compliance.