Backlinks
Git-backed document versioning with Memento access to the history
A dataspace can be configured to mirror every document write into a GitHub repository. The synchronization goes through the GitHub REST API (api.github.com), so it is specific to GitHub rather than generic Git. Every write becomes a commit authored with the writing agent's WebID, so the repository holds a complete, attributable history of the dataspace's documents.
The history is accessible over HTTP through the standard Memento (RFC 7089) protocol — historical versions, a TimeMap, and datetime negotiation through a TimeGate — as well as through the version history dialog in the user interface and the command line interface.
Setup
Versioning is enabled per dataspace:
- Create a GitHub repository to hold the version history, and a fine-grained personal access token with read/write access to that repository's contents
- In config/system.trig, point the dataspace at a repository description with
lds:versioningRepository:The<urn:linkeddatahub:apps/end-user> { <urn:linkeddatahub:apps/end-user> a lds:EndUserDataspace ; lds:service <urn:linkeddatahub:services/end-user> ; lds:versioningRepository <urn:linkeddatahub:versioning/end-user> . } <urn:linkeddatahub:versioning/end-user> { <urn:linkeddatahub:versioning/end-user> a doap:GitRepository ; doap:location <https://github.com/OWNER/REPO> ; github:branch "main" ; github:pathPrefix "graphs" . }github:terms come from thehttps://w3id.org/atomgraph/linkeddatahub/services/github#namespace;github:branchdefaults to main andgithub:pathPrefixto graphs. - Add the access token to secrets/credentials.trig (and make sure the credentials secret is enabled in docker-compose.yml):
@prefix a: <https://w3id.org/atomgraph/core#> . <urn:linkeddatahub:versioning/end-user> { <urn:linkeddatahub:versioning/end-user> a:authToken "github_pat_..." . } - Restart LinkedDataHub
From then on, every successful document write in the dataspace is committed to the repository. Writes from different documents share one commit chain per branch. A conflicting commit is retried rather than dropped, so concurrent writers do not lose versions. Each document is stored as a sorted N-Triples file under the configured path prefix — Northwind order 10248 as graphs/orders/10248.nt:
<https://localhost:4443/orders/10248/> <http://purl.org/dc/terms/title> "10248" . <https://localhost:4443/orders/10248/#this> <https://schema.org/identifier> "10248" . <https://localhost:4443/orders/10248/#this> <https://schema.org/orderDate> "1996-07-04"^^<http://www.w3.org/2001/XMLSchema#date> . …
To disable versioning, remove the lds:versioningRepository triple and restart — the repository configuration is read at startup.
Historical versions
GET with the ?version=<commit-sha> query parameter serves the document as it was at that commit. The
response carries a Memento-Datetime header with the commit datetime, the commit SHA as the ETag, and immutable
Cache-Control — a historical version never changes.
Historical versions are read-only: only acl:Read is advertised in the Link headers and write methods answer
405 Method Not Allowed. In the user interface a historical version page shows a notice banner with a link
back to the current version.
Historical versions are also outside the scope of SPARQL: the dataspace's RDF dataset holds only the current version of every document, while the history lives solely in the Git repository. Queries on the SPARQL endpoint therefore always answer over the latest versions.
GET /orders/10248/?version=8c9f2b1 HTTP/1.1 Host: localhost:4443 Accept: text/turtle HTTP/1.1 200 OK Content-Type: text/turtle Memento-Datetime: Tue, 01 Sep 2026 14:03:59 GMT ETag: "8c9f2b1" Cache-Control: public, max-age=31536000, immutable Link: <https://localhost:4443/orders/10248/>; rel="original"
TimeMap
GET with the ?timemap query parameter serves the document's version history. In RDF formats the TimeMap
is described
with PROV-O: a prov:Collection of prov:Entity mementos, each
prov:specializationOf the document, prov:generatedAtTime its commit datetime, and prov:wasRevisionOf its
predecessor. Requesting application/link-format serves the serialization RFC 7089 requires.
Order 10248's history with two versions, as PROV-O:
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<https://localhost:4443/orders/10248/?timemap> a prov:Collection ;
prov:hadMember <https://localhost:4443/orders/10248/?version=4d1e7aa>,
<https://localhost:4443/orders/10248/?version=8c9f2b1> .
<https://localhost:4443/orders/10248/?version=4d1e7aa> a prov:Entity ;
prov:specializationOf <https://localhost:4443/orders/10248/> ;
prov:generatedAtTime "2026-08-30T10:12:04Z"^^xsd:dateTime .
<https://localhost:4443/orders/10248/?version=8c9f2b1> a prov:Entity ;
prov:specializationOf <https://localhost:4443/orders/10248/> ;
prov:generatedAtTime "2026-09-01T14:03:59Z"^^xsd:dateTime ;
prov:wasRevisionOf <https://localhost:4443/orders/10248/?version=4d1e7aa> .and the same TimeMap as application/link-format:
<https://localhost:4443/orders/10248/>;rel="original", <https://localhost:4443/orders/10248/?timemap>;rel="self";type="application/link-format", <https://localhost:4443/orders/10248/?version=4d1e7aa>;rel="memento";datetime="Sun, 30 Aug 2026 10:12:04 GMT", <https://localhost:4443/orders/10248/?version=8c9f2b1>;rel="memento";datetime="Tue, 01 Sep 2026 14:03:59 GMT"
A TimeMap of a document with no versions answers 404 Not Found. A non-versioned document answers 406 Not Acceptable
when application/link-format is requested.
The Memento hypermedia uses IANA-registered relation types: the document advertises
its timemap and timegate in
Link headers, a historical version links back to the document with rel=original, and the TimeMap identifies itself with
rel=self.
TimeGate
GET with the ?timegate query parameter performs datetime negotiation: the version closest to the
Accept-Datetime request header is answered with 302 Found and its URI in Location, with ties resolved
towards the more recent version. Without Accept-Datetime, the most recent version is selected. The response carries
Vary: accept-datetime.
GET /orders/10248/?timegate HTTP/1.1 Host: localhost:4443 Accept-Datetime: Mon, 31 Aug 2026 00:00:00 GMT HTTP/1.1 302 Found Location: https://localhost:4443/orders/10248/?version=4d1e7aa Vary: accept-datetime Link: <https://localhost:4443/orders/10248/>; rel="original"
Restore and diff
From the version history dialog, a document can be restored to an earlier version — restoring creates a new commit rather than rewriting history, so the intervening versions remain in the TimeMap — and any two versions can be compared as a diff rendered on the document page.
Management
The version history dialog lists a document's versions and drives the restore and diff actions.
ldh get exposes a versioned document's TimeMap, TimeGate and historical versions through three mutually exclusive options:
| Option | Description |
|---|---|
| --timemap | The version history (PROV-O in RDF formats, or application/link-format via --accept) |
| --version <sha> | The historical version at the given commit |
| --timegate | Datetime negotiation; the optional --datetime takes an RFC 1123 or ISO 8601 datetime. Prints the negotiated version's URI as the only line on stdout, so it pipes into another ldh get |