From 73beaaff125e0b07ad989f2e489d6a922c88af4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Tue, 29 Sep 2026 18:12:01 +0200
Subject: [PATCH 1/6] The configuration reference documents
CLIENT_SELF_REQUEST_TIMEOUT, the connect and read bound on the platform's
requests to its own URLs, beside the other HTTP client timeouts.
Co-Authored-By: Claude Fable 5.1
---
docs/reference/configuration.ttl | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/reference/configuration.ttl b/docs/reference/configuration.ttl
index aebbee2..c450960 100644
--- a/docs/reference/configuration.ttl
+++ b/docs/reference/configuration.ttl
@@ -217,6 +217,8 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com
Socket (read) timeout — how long to wait for data on an established connection. Defaults to 120000.
CLIENT_CONNECT_TIMEOUT
Connection timeout — how long to wait to establish a connection. Defaults to 10000.
+ CLIENT_SELF_REQUEST_TIMEOUT
+ Connect and read timeout for requests the platform makes to its own URLs, such as a server-side render fetching its labels from the dataspace's /sparql and /ns. Those are answered by the platform's own request threads, so a wait that is too long lets a burst of renders hold every thread waiting on each other; after this bound the render gives up on the label. Defaults to 5000.
CLIENT_CONNECTION_TIME_TO_LIVE
Maximum lifetime of a pooled connection before it is closed. Defaults to 300000.
CLIENT_VALIDATE_AFTER_INACTIVITY
From af7591d7e4b815ab64567c6037b1d3a63ced068f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Tue, 29 Sep 2026 20:20:28 +0200
Subject: [PATCH 2/6] The package registry publishes its catalog.
packages/root.ttl is the registry root, listing each package as an
rdfs:member with its title and description, which the settings modal and ldh
packages list read; the platform used to serve a bundled copy in its place.
The taxonomy descriptor names its ontology , the IRI ns.ttl declares,
rather than the ns/ document, and the README says why, adds the catalog to
the steps for a new package and no longer mentions bundled descriptions.
Co-Authored-By: Claude Fable 5.1
---
packages/README.md | 14 ++++++++++----
packages/editor/taxonomy.ttl | 2 +-
packages/root.ttl | 27 +++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 5 deletions(-)
create mode 100644 packages/root.ttl
diff --git a/packages/README.md b/packages/README.md
index 619a2ec..311ff0a 100644
--- a/packages/README.md
+++ b/packages/README.md
@@ -48,12 +48,15 @@ which `ldh push` PUTs to `https://packages.linkeddatahub.com/editor/taxonomy/`:
<#this> a lds:Package ;
dct:title "Taxonomy Editor" ;
dct:description "Turns a dataspace into a taxonomy editor: ..." ;
- lds:ontology ;
+ lds:ontology ;
ac:stylesheet .
```
-`lds:ontology ` is registry-relative: the same push PUTs `ns.ttl` as the `ns/` document beside the
-descriptor, so the ontology is served by the registry itself. `ac:stylesheet` is the raw file in this
+`lds:ontology ` is registry-relative: the same push PUTs `ns.ttl` as the `ns/` document beside the
+descriptor, so the ontology is served by the registry itself. It names the ontology IRI that `ns.ttl`
+declares (`<#>` resolved against the `ns/` document), not the document URL: the imports closure keys
+graphs by ontology IRI, and a descriptor naming the document loads it a second time under the IRI,
+which fails with "Another graph with name ... is already in the hierarchy". `ac:stylesheet` is the raw file in this
repository, on the branch the registry was published from — a branch tip, not a release, so a push to
that branch changes the rendering of every dataspace that imports the package next time it
materializes the copy (see [What the Declaration Does](#what-the-declaration-does)).
@@ -172,7 +175,7 @@ next request, and lives in the running dataspace's context dataset. Declaring th
From the next request onwards, the server resolves it:
-1. **Resolves the package description** from the package URI. Bundled descriptions and cached graphs
+1. **Resolves the package description** from the package URI. Cached graphs
come from the graph repository; other URIs are dereferenced over HTTP.
2. **Materializes the package ontology** (`lds:ontology`) as a document under the admin dataspace's
`ontologies/` container, named after the package path — `ontologies/editor-taxonomy/` for the
@@ -216,6 +219,9 @@ vocabulary stays in the dataspace, and may not display or validate correctly wit
3. Write the stylesheet with XSLT templates (using system modes like `ac:*`, `ldh:*`, `xhtml:*`, etc.), naming the file for the vocabulary it covers
4. Publish package metadata as Linked Data at `https://packages.linkeddatahub.com//#this`
5. Ensure the metadata contains `lds:ontology` and `ac:stylesheet` properties pointing to the package resources
+6. List the package in the catalog, `root.ttl`, as an `rdfs:member` of the registry root with the same
+ `dct:title` and `dct:description` as its descriptor: the settings modal and `ldh packages list` read
+ the catalog, not the descriptors
## Vocabulary Reference
diff --git a/packages/editor/taxonomy.ttl b/packages/editor/taxonomy.ttl
index 4db89d9..2f4ff27 100644
--- a/packages/editor/taxonomy.ttl
+++ b/packages/editor/taxonomy.ttl
@@ -25,7 +25,7 @@
dct:title "Taxonomy Editor" ;
dct:description "Turns a dataspace into a taxonomy editor: SKOS concepts, schemes and collections gain a concept tree beside the content, hierarchy views that read both assertion directions, and constructors and constraints that keep a concept labelled and in a scheme." ;
dct:creator ;
- lds:ontology ;
+ lds:ontology ;
ac:stylesheet .
a foaf:Organization ;
diff --git a/packages/root.ttl b/packages/root.ttl
new file mode 100644
index 0000000..786e77c
--- /dev/null
+++ b/packages/root.ttl
@@ -0,0 +1,27 @@
+@prefix def: .
+@prefix ldh: .
+@prefix rdf: .
+@prefix rdfs: .
+@prefix dct: .
+
+# The package catalog. The dataspace settings modal and `ldh packages list` read this document and
+# offer each rdfs:member, labelled with the dct:title and dct:description given here, so a new
+# package is listed by adding it below with the same title and description as its descriptor.
+
+<> a def:Root ;
+ dct:title "LinkedDataHub packages" ;
+ dct:description "Catalog of packages available for LinkedDataHub applications" ;
+ rdfs:member ;
+ rdf:_1 <#content> ;
+ rdf:_2 <#select-children> .
+
+<#content> a ldh:XHTML ;
+ rdf:value """
+
Packages add a vocabulary to a dataspace together with the views, forms and templates that render it. A dataspace imports one from its settings.
+
"""^^rdf:XMLLiteral .
+
+<#select-children> a ldh:Object ;
+ rdf:value ldh:ChildrenView .
+
+ dct:title "Taxonomy Editor" ;
+ dct:description "Turns a dataspace into a taxonomy editor: SKOS concepts, schemes and collections gain a concept tree beside the content, hierarchy views that read both assertion directions, and constructors and constraints that keep a concept labelled and in a scheme." .
From 936b856fbfdf543ebafd9f440e81ea315c47d66c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Tue, 29 Sep 2026 21:20:53 +0200
Subject: [PATCH 3/6] A package's stylesheet is published by the registry
itself, as the upload the push makes, instead of from this repository's
develop branch on raw.githubusercontent.com. The descriptor no longer names
it: install.sh adds ac:stylesheet after the push, pointing at
{base}uploads/{SHA-1 of the .xsl}, so the reference always names the
stylesheet published with it, and a changed stylesheet gets a new URI rather
than changing under the dataspaces that copied it. The push uploads it as
text/xsl, which needs an ldh built with that detection. ns.ttl describes the
document the registry serves it as - a dh:Item titled "Taxonomy Editor
ontology" whose primary topic is the ontology - which it never did, and which
the platform's PUT let through untitled because it types the document after
validating the payload.
Co-Authored-By: Claude Fable 5.1
---
packages/README.md | 34 ++++++++++++++++++++-------------
packages/editor/taxonomy.ttl | 4 +---
packages/editor/taxonomy/ns.ttl | 8 ++++++++
packages/install.sh | 26 +++++++++++++++++++------
4 files changed, 50 insertions(+), 22 deletions(-)
diff --git a/packages/README.md b/packages/README.md
index 311ff0a..ae24907 100644
--- a/packages/README.md
+++ b/packages/README.md
@@ -16,8 +16,8 @@ packages//
The directory path is the package URI: a package under `packages/a/b/` is published at
`https://packages.linkeddatahub.com/a/b/`, so a path may carry as many segments as the grouping
-needs. The stylesheet filename is not a convention — it is whatever the package's `ac:stylesheet`
-names.
+needs. A package folder holds one `.xsl` file, and `install.sh` makes it the package's `ac:stylesheet`;
+its name is not a convention.
Package metadata is Linked Data that resolves from the package URI (e.g., `https://packages.linkeddatahub.com/editor/taxonomy/#this`).
@@ -42,24 +42,32 @@ which `ldh push` PUTs to `https://packages.linkeddatahub.com/editor/taxonomy/`:
```turtle
@prefix lds: .
-@prefix ac: .
@prefix dct: .
<#this> a lds:Package ;
dct:title "Taxonomy Editor" ;
dct:description "Turns a dataspace into a taxonomy editor: ..." ;
- lds:ontology ;
- ac:stylesheet .
+ lds:ontology .
```
`lds:ontology ` is registry-relative: the same push PUTs `ns.ttl` as the `ns/` document beside the
descriptor, so the ontology is served by the registry itself. It names the ontology IRI that `ns.ttl`
declares (`<#>` resolved against the `ns/` document), not the document URL: the imports closure keys
graphs by ontology IRI, and a descriptor naming the document loads it a second time under the IRI,
-which fails with "Another graph with name ... is already in the hierarchy". `ac:stylesheet` is the raw file in this
-repository, on the branch the registry was published from — a branch tip, not a release, so a push to
-that branch changes the rendering of every dataspace that imports the package next time it
-materializes the copy (see [What the Declaration Does](#what-the-declaration-does)).
+which fails with "Another graph with name ... is already in the hierarchy".
+
+The descriptor in this repository has no `ac:stylesheet`. The push uploads the package's `.xsl` file into
+the package document as `text/xsl`, and `install.sh` then adds the reference to that upload, whose URI it
+computes from the same file:
+
+```turtle
+
+ ac:stylesheet .
+```
+
+An upload's URI is the SHA-1 of its content, so the published reference names exactly the stylesheet
+that was published with it, and a changed stylesheet gets a new URI rather than changing under the
+dataspaces that already copied it (see [What the Declaration Does](#what-the-declaration-does)).
**Note**: Uses standard `lds:ontology` and `ac:stylesheet` properties instead of inventing new ones.
@@ -216,10 +224,10 @@ vocabulary stays in the dataspace, and may not display or validate correctly wit
1. Create directory: `packages//`, naming it for what the package does
2. Write `ns.ttl` with vocabulary and property views (using `ldh:view` or `ldh:inverseView`)
-3. Write the stylesheet with XSLT templates (using system modes like `ac:*`, `ldh:*`, `xhtml:*`, etc.), naming the file for the vocabulary it covers
-4. Publish package metadata as Linked Data at `https://packages.linkeddatahub.com//#this`
-5. Ensure the metadata contains `lds:ontology` and `ac:stylesheet` properties pointing to the package resources
-6. List the package in the catalog, `root.ttl`, as an `rdfs:member` of the registry root with the same
+3. Write the stylesheet with XSLT templates (using system modes like `ac:*`, `ldh:*`, `xhtml:*`, etc.), naming the file for the vocabulary it covers; it is the only `.xsl` file in the folder
+4. Describe the package in `packages/.ttl` as `<#this>`, the `foaf:primaryTopic` of the document, with an `lds:ontology`
+ naming the ontology IRI `ns.ttl` declares; leave `ac:stylesheet` out, `install.sh` adds it
+5. List the package in the catalog, `root.ttl`, as an `rdfs:member` of the registry root with the same
`dct:title` and `dct:description` as its descriptor: the settings modal and `ldh packages list` read
the catalog, not the descriptors
diff --git a/packages/editor/taxonomy.ttl b/packages/editor/taxonomy.ttl
index 2f4ff27..460c24c 100644
--- a/packages/editor/taxonomy.ttl
+++ b/packages/editor/taxonomy.ttl
@@ -1,6 +1,5 @@
@prefix lds: .
@prefix ldh: .
-@prefix ac: .
@prefix rdf: .
@prefix dh: .
@prefix dct: .
@@ -25,8 +24,7 @@
dct:title "Taxonomy Editor" ;
dct:description "Turns a dataspace into a taxonomy editor: SKOS concepts, schemes and collections gain a concept tree beside the content, hierarchy views that read both assertion directions, and constructors and constraints that keep a concept labelled and in a scheme." ;
dct:creator ;
- lds:ontology ;
- ac:stylesheet .
+ lds:ontology .
a foaf:Organization ;
foaf:name "AtomGraph" ;
diff --git a/packages/editor/taxonomy/ns.ttl b/packages/editor/taxonomy/ns.ttl
index 11ee744..2e98c7b 100644
--- a/packages/editor/taxonomy/ns.ttl
+++ b/packages/editor/taxonomy/ns.ttl
@@ -7,6 +7,14 @@
@prefix spin: .
@prefix dct: .
@prefix skos: .
+@prefix dh: .
+@prefix foaf: .
+
+# the document the registry serves this file as (ns/), which is what the package's lds:ontology dereferences to
+
+<> a dh:Item ;
+ dct:title "Taxonomy Editor ontology" ;
+ foaf:primaryTopic : .
: a owl:Ontology ;
owl:imports ;
diff --git a/packages/install.sh b/packages/install.sh
index a3268c9..817e2bd 100755
--- a/packages/install.sh
+++ b/packages/install.sh
@@ -1,12 +1,16 @@
#!/usr/bin/env bash
-# Publishes the package registry onto a LinkedDataHub instance with the ldh CLI: makes it public and
-# pushes the document tree, whose folders are the package URIs. A dataspace importing a package
-# dereferences its descriptor, ontology and stylesheet anonymously, so the registry must be readable
-# without a certificate.
+# Publishes the package registry onto a LinkedDataHub instance with the ldh CLI: makes it public,
+# pushes the document tree, whose folders are the package URIs, and uploads each package's stylesheet
+# into its package document. A dataspace importing a package dereferences its descriptor, ontology and
+# stylesheet anonymously, so the registry must be readable without a certificate.
+#
+# A descriptor carries no ac:stylesheet in this repository: the push uploads the package's .xsl file into
+# the package document, as text/xsl, at {base}uploads/{SHA-1 of its content}, so the reference is added
+# here, from the same file, and a changed stylesheet cannot leave a descriptor pointing at the previous one.
#
# Reads LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and optionally LDH_PROXY; `make install` prompts
-# for them. Re-running converges: PUT replaces each document. make-public is a POST and adds another
-# authorization per run.
+# for them. Re-running converges: PUT replaces each document and the stylesheet reference is added back
+# to it. make-public is a POST and adds another authorization per run.
set -euo pipefail
app_dir="$(cd "$(dirname "$0")" && pwd)"
@@ -18,3 +22,13 @@ ldh admin make-public
ldh_app_step "Pushing package descriptors and files"
ldh push --dir "$app_dir" "$LDH_BASE"
+
+ldh_app_step "Declaring package stylesheets"
+# a package is the folder its stylesheet is in, e.g. editor/taxonomy/skos.xsl -> ${LDH_BASE}editor/taxonomy/#this
+(cd "$app_dir" && find . -name '*.xsl' -not -path './.*' | sed 's|^\./||' | sort) | while read -r stylesheet; do
+ package_doc="${LDH_BASE}$(dirname "$stylesheet")/"
+ upload="${LDH_BASE}uploads/$(shasum -a 1 "$app_dir/$stylesheet" | cut -d' ' -f1)"
+ printf "Declaring %s as the stylesheet of %s#this\n" "$upload" "$package_doc"
+ echo "INSERT { <${package_doc}#this> <${upload}> } WHERE { }" |
+ ldh patch "$package_doc"
+done
From 179bca9632adf3cd0297ac3c32fa1567501b53ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Tue, 29 Sep 2026 21:34:19 +0200
Subject: [PATCH 4/6] The taxonomy editor package is called the Taxonomy
Editor, not the SKOS package: its ontology is labelled "Taxonomy Editor
ontology", and the Northwind UNESCO mappings say which package renders the
categories as concepts.
Co-Authored-By: Claude Fable 5.1
---
demo/northwind-traders/categories/unesco-mappings.ttl | 2 +-
packages/editor/taxonomy/ns.ttl | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/demo/northwind-traders/categories/unesco-mappings.ttl b/demo/northwind-traders/categories/unesco-mappings.ttl
index 849d80e..05d8e12 100644
--- a/demo/northwind-traders/categories/unesco-mappings.ttl
+++ b/demo/northwind-traders/categories/unesco-mappings.ttl
@@ -1,6 +1,6 @@
# Mappings between Northwind product categories and UNESCO Thesaurus concepts.
# Each category doubles as a skos:Concept, which makes the SKOS mapping relations
-# applicable and lets the SKOS package render the categories as concepts.
+# applicable and lets the Taxonomy Editor package render the categories as concepts.
# Import this file with an RDF import (see the documentation) - .ldhignore keeps
# it out of ldh push, and install.sh does not import it.
diff --git a/packages/editor/taxonomy/ns.ttl b/packages/editor/taxonomy/ns.ttl
index 2e98c7b..9c4560b 100644
--- a/packages/editor/taxonomy/ns.ttl
+++ b/packages/editor/taxonomy/ns.ttl
@@ -18,7 +18,7 @@
: a owl:Ontology ;
owl:imports ;
- rdfs:label "SKOS package ontology".
+ rdfs:label "Taxonomy Editor ontology".
# Concept
From be2fde9564127fdc3e9dfab6434521347c2bd13b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Tue, 29 Sep 2026 21:50:28 +0200
Subject: [PATCH 5/6] The Northwind imports that write into another import's
documents title them the same way, and order lines have stable URIs.
order_details and employee_territories run concurrently with orders and
employees and can reach an order's or an employee's document first, which
then creates it; a document without a dct:title can no longer be created, so
both write the title their sibling writes - the order ID, and the employee's
last name, which employee_territories.csv now carries. An order line is the
SHA-1 of its order and product rather than a STRUUID(), so re-running the
import writes the same lines instead of a second copy of each.
Co-Authored-By: Claude Fable 5.1
---
.../employees/employee_territories.csv | 100 +++++++++---------
.../employees/employee_territories.rq | 7 ++
.../northwind-traders/orders/order_details.rq | 9 +-
3 files changed, 64 insertions(+), 52 deletions(-)
diff --git a/demo/northwind-traders/employees/employee_territories.csv b/demo/northwind-traders/employees/employee_territories.csv
index 2188e0a..84c94a8 100644
--- a/demo/northwind-traders/employees/employee_territories.csv
+++ b/demo/northwind-traders/employees/employee_territories.csv
@@ -1,50 +1,50 @@
-employeeID,territoryID
-1,06897
-1,19713
-2,01581
-2,01730
-2,01833
-2,02116
-2,02139
-2,02184
-2,40222
-3,30346
-3,31406
-3,32859
-3,33607
-4,20852
-4,27403
-4,27511
-5,02903
-5,07960
-5,08837
-5,10019
-5,10038
-5,11747
-5,14450
-6,85014
-6,85251
-6,98004
-6,98052
-6,98104
-7,60179
-7,60601
-7,80202
-7,80909
-7,90405
-7,94025
-7,94105
-7,95008
-7,95054
-7,95060
-8,19428
-8,44122
-8,45839
-8,53404
-9,03049
-9,03801
-9,48075
-9,48084
-9,48304
-9,55113
-9,55439
+employeeID,lastName,territoryID
+1,Davolio,06897
+1,Davolio,19713
+2,Fuller,01581
+2,Fuller,01730
+2,Fuller,01833
+2,Fuller,02116
+2,Fuller,02139
+2,Fuller,02184
+2,Fuller,40222
+3,Leverling,30346
+3,Leverling,31406
+3,Leverling,32859
+3,Leverling,33607
+4,Peacock,20852
+4,Peacock,27403
+4,Peacock,27511
+5,Buchanan,02903
+5,Buchanan,07960
+5,Buchanan,08837
+5,Buchanan,10019
+5,Buchanan,10038
+5,Buchanan,11747
+5,Buchanan,14450
+6,Suyama,85014
+6,Suyama,85251
+6,Suyama,98004
+6,Suyama,98052
+6,Suyama,98104
+7,King,60179
+7,King,60601
+7,King,80202
+7,King,80909
+7,King,90405
+7,King,94025
+7,King,94105
+7,King,95008
+7,King,95054
+7,King,95060
+8,Callahan,19428
+8,Callahan,44122
+8,Callahan,45839
+8,Callahan,53404
+9,Dodsworth,03049
+9,Dodsworth,03801
+9,Dodsworth,48075
+9,Dodsworth,48084
+9,Dodsworth,48304
+9,Dodsworth,55113
+9,Dodsworth,55439
diff --git a/demo/northwind-traders/employees/employee_territories.rq b/demo/northwind-traders/employees/employee_territories.rq
index a89f38b..aaa51f3 100644
--- a/demo/northwind-traders/employees/employee_territories.rq
+++ b/demo/northwind-traders/employees/employee_territories.rq
@@ -1,15 +1,22 @@
+PREFIX dct:
PREFIX schema:
CONSTRUCT
{
GRAPH ?graph
{
+ # the employee's title, exactly as employees.rq writes it (the CSV carries the last name for that
+ # alone): the two imports run concurrently, and whichever reaches an employee's document first
+ # creates it, which a document without a dct:title cannot be
+ ?graph dct:title ?lastName .
+
?employee schema:areaServed ?territory
}
}
WHERE
{
?employee_territory <#employeeID> ?employeeID ;
+ <#lastName> ?lastName ;
<#territoryID> ?territoryID .
BIND(uri(concat(str($base), "employees/")) AS ?container)
diff --git a/demo/northwind-traders/orders/order_details.rq b/demo/northwind-traders/orders/order_details.rq
index 4533114..89f8e7e 100644
--- a/demo/northwind-traders/orders/order_details.rq
+++ b/demo/northwind-traders/orders/order_details.rq
@@ -7,7 +7,10 @@ CONSTRUCT
{
GRAPH ?graph
{
- ?graph foaf:topic ?orderItem, ?listPrice .
+ # the order's title, exactly as orders.rq writes it: the two imports run concurrently, and whichever
+ # reaches an order's document first creates it, which a document without a dct:title cannot be
+ ?graph dct:title ?orderID ;
+ foaf:topic ?orderItem, ?listPrice .
?order schema:orderedItem ?orderItem .
@@ -45,7 +48,9 @@ WHERE
BIND(uri(concat(str($base), "orders/", encode_for_uri(?orderID), "/")) AS ?graph)
BIND(uri(concat(str(?graph), "#this")) AS ?order)
- BIND(uri(concat(str(?graph), "#", STRUUID())) AS ?orderItem)
+ # a line is identified by its order and product (an order has at most one line per product), so re-running
+ # the import writes the same resources rather than minting a second copy of every line
+ BIND(uri(concat(str(?graph), "#", sha1(concat(?orderID, "/", ?productID)))) AS ?orderItem)
BIND(uri(concat(str(?orderItem), "-list-price")) AS ?listPrice)
BIND(uri(concat(str($base), "products/", encode_for_uri(?productID), "/#this")) AS ?product)
BIND (STRDT(?unitPrice, xsd:float) AS ?price)
From 1ed1321664e63a9fa275515e3aff3baebaf60b94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?=
Date: Wed, 30 Sep 2026 00:21:42 +0200
Subject: [PATCH 6/6] The configuration and packages references catch up with
6.0.1.
The connector and the client pool are sized to each other, and neither side was
documented: HTTP_MAX_THREADS is new, MAX_CONN_PER_ROUTE and MAX_TOTAL_CONN have grown
to the connector's thread count, and CONNECTION_REQUEST_TIMEOUT has a default where it
used to wait forever. The two pool settings are servlet parameters rather than
CATALINA_OPTS system properties, so they get a section of their own, and it says to
keep the pool at least as large as the connector - a request the platform makes to its
own URL holds a pooled connection while it waits for one of the same Tomcat's threads.
A package is a descriptor document with an ontology and a stylesheet under it, and the
registry publishes all three itself: the descriptor names the ontology beside it
(lds:ontology ) rather than a file on raw.githubusercontent.com, and carries no
ac:stylesheet until the stylesheet is uploaded and the reference added to name that
upload. The property list said rdfs:label where the descriptor says dct:title, and
called lds:ontology the LDT vocabulary, which went away in 6.0.0. Naming the ontology's
document instead of the ontology now resolves, so the recommendation is stated rather
than assumed. And ns.ttl has to describe the document it is served as, titled: a write
is held to the constraints of the document as it will be written, so an ontology file
describing only the ontology is refused 422.
Co-Authored-By: Claude Opus 5 (1M context)
---
docs/reference/administration/packages.ttl | 93 ++++++++++++++++------
docs/reference/configuration.ttl | 20 +++++
2 files changed, 87 insertions(+), 26 deletions(-)
diff --git a/docs/reference/administration/packages.ttl b/docs/reference/administration/packages.ttl
index 7136352..a655842 100644
--- a/docs/reference/administration/packages.ttl
+++ b/docs/reference/administration/packages.ttl
@@ -30,8 +30,10 @@
Package structure
-
Each package consists of two files:
+
Each package is a descriptor document with two files under it:
+ - Descriptor — the
lds:Package resource
+ - The document the package URI resolves to, describing the package itself. In the LinkedDataHub-Apps repository it is the RDF file beside the package's folder, so editor/taxonomy.ttl is the document published at editor/taxonomy/ — see Package metadata.
- ns.ttl — Package ontology
- An RDF ontology file that imports the external vocabulary using
owl:imports and attaches views to properties using ldh:view (forward relationships) or ldh:inverseView (inverse relationships). Views are typically ldh:View resources with SPARQL queries that render related data for property values.
- XSLT stylesheet — named by
ac:stylesheet
@@ -39,10 +41,14 @@
Package files are organized in the LinkedDataHub-Apps repository, where the directory path is the package URI — a package under packages/a/b/ is published at https://packages.linkeddatahub.com/a/b/, so a path carries as many segments as the grouping needs:
packages/
-├── editor/
-│ └── taxonomy/
-│ ├── ns.ttl # Ontology with views
-│ └── skos.xsl # XSLT stylesheet
+├── root.ttl # the registry's own document
+├── editor.ttl # the "Editors" container document
+└── editor/
+ ├── taxonomy.ttl # the package descriptor, published at editor/taxonomy/
+ └── taxonomy/
+ ├── ns.ttl # ontology with views, published at editor/taxonomy/ns/
+ └── skos.xsl # XSLT stylesheet, uploaded into the package document
+
The registry is published with ldh push, which is what pairs each name.ttl with the name/ folder holding its children — so the ontology's document URL is ns/, not ns.ttl.
The ontology is always ns.ttl; the stylesheet filename is not a convention, it is whatever the package's ac:stylesheet names. The taxonomy editor is named for what it does and its stylesheet for the vocabulary it speaks.
@@ -50,33 +56,50 @@
Package metadata is published as Linked Data that resolves from the package URI (e.g. https://packages.linkeddatahub.com/editor/taxonomy/#this).
A package descriptor is an instance of the lds:Package class with these properties:
- rdfs:label
+ dct:title
- Human-readable package name
dct:description
- Package description and purpose
+ dct:creator
+ - Who publishes the package (optional)
lds:ontology
- - The package ontology URI (LDT vocabulary)
+ - The package ontology URI (LinkedDataHub dataspaces vocabulary)
ac:stylesheet
- - The package stylesheet URI (AtomGraph Client vocabulary)
+ - The package stylesheet URI (AtomGraph Client vocabulary). Not written by hand in the registry: the stylesheet is uploaded when the package is published, and the reference is added afterwards, naming that upload — so a changed stylesheet gets a new URI instead of changing under the dataspaces that already imported it.
Example package metadata:
-
@prefix lds: <https://w3id.org/atomgraph/linkeddatahub/dataspaces#> .
-@prefix ac: <https://w3id.org/atomgraph/client#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+ @prefix lds: <https://w3id.org/atomgraph/linkeddatahub/dataspaces#> .
@prefix dct: <http://purl.org/dc/terms/> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix dh: <https://w3id.org/atomgraph/linkeddatahub/document-hierarchy#> .
-<https://packages.linkeddatahub.com/editor/taxonomy/#this> a lds:Package ;
- rdfs:label "Taxonomy Editor" ;
+<> a dh:Container ;
+ dct:title "Taxonomy" ;
+ foaf:primaryTopic <#this> .
+
+<#this> a lds:Package ;
+ dct:title "Taxonomy Editor" ;
dct:description "Taxonomy editing on SKOS, with custom templates" ;
- lds:ontology <https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/ns.ttl#> ;
- ac:stylesheet <https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/skos.xsl> .
+ dct:creator <https://atomgraph.com/#company> ;
+ lds:ontology <ns/#> .
+
The URIs are relative to the descriptor's own document, so the registry serves the ontology it names and the package carries no reference to where its sources are kept.
+
Package ontology
-
The package ontology file contains two parts:
+
The package ontology file describes the document the registry serves it as, and the ontology inside that document in two parts:
Vocabulary import
Imports the external vocabulary using owl:imports. See the Ontologies reference for ontology management details.
-
<https://raw.githubusercontent.com/AtomGraph/LinkedDataHub-Apps/master/packages/editor/taxonomy/ns.ttl#> a owl:Ontology ;
+ <> a dh:Item ;
+ dct:title "Taxonomy Editor ontology" ;
+ foaf:primaryTopic <#> .
+
+<#> a owl:Ontology ;
owl:imports <http://www.w3.org/2004/02/skos/core> .
Property views
SPARQL-based views attached to properties from the imported vocabulary:
@@ -175,8 +198,12 @@ WHERE {}
Creating custom packages
Developers can create custom packages for their own domain vocabularies. The process has four steps.
Write package ontology
-
Create ns.ttl with vocabulary import and views:
-
<https://raw.githubusercontent.com/you/repo/master/packages/schema.org/ns.ttl#> a owl:Ontology ;
+ Create ns.ttl describing both the document the registry serves it as and the ontology inside it, with the vocabulary import and the views:
+ <> a dh:Item ;
+ dct:title "Schema.org ontology" ;
+ foaf:primaryTopic <#> .
+
+<#> a owl:Ontology ;
owl:imports <https://schema.org/> .
# Attach view to a property
@@ -192,17 +219,31 @@ schema:knows ldh:view :PersonKnows .
WHERE { GRAPH ?graph { $about schema:knows ?person } }
ORDER BY ?person
\"\"\" .
+
+
warning
+
+
The dct:title on <> is not optional. A write is held to the constraints of the document as it will be written, and a LinkedDataHub document must be titled, so an ns.ttl that describes only the ontology is refused 422 Unprocessable Entity when it is pushed.
+
+
Write XSLT stylesheet
Create the stylesheet — name the file for the vocabulary it covers — with XSLT templates using system modes like ac:*, ldh:* and xhtml:*. See the Stylesheets reference for template customization patterns.
<xsl:template match="schema:knows" mode="ac:PropertyEditor"/>
- Publish package metadata as Linked Data at your package URI:
- <https://packages.linkeddatahub.com/schema.org/#this> a lds:Package ;
- rdfs:label "Schema.org Package" ;
+ Publish the descriptor as Linked Data at your package URI, naming the ontology beside it:
+ <> a dh:Container ;
+ dct:title "Schema.org" ;
+ foaf:primaryTopic <#this> .
+
+<#this> a lds:Package ;
+ dct:title "Schema.org Package" ;
dct:description "Schema.org vocabulary support" ;
- lds:ontology <https://raw.githubusercontent.com/you/repo/master/packages/schema.org/ns.ttl#> ;
- ac:stylesheet <https://raw.githubusercontent.com/you/repo/master/packages/schema.org/schema.xsl> .
- Ensure the metadata contains lds:ontology and ac:stylesheet properties pointing to the package resources.
+ lds:ontology <ns/#> .
+ Then upload the stylesheet into the package document and add ac:stylesheet naming that upload, which is where a registry published with ldh push gets it:
+ package="https://packages.example.com/schema.org/"
+
+upload=$(ldh add file --title schema.xsl --file schema.xsl "$package")
+echo "INSERT { <${package}#this> <https://w3id.org/atomgraph/client#stylesheet> <$upload> } WHERE { }" | ldh patch "$package"
+ A dataspace importing the package dereferences its descriptor, ontology and stylesheet anonymously, so all three have to be readable without a certificate.
Test installation
Declare the ldh:import triple in a test dataspace's settings document — see Management for the command.
@@ -210,7 +251,7 @@ schema:knows ldh:view :PersonKnows .
Available packages
Published packages resolve from packages.linkeddatahub.com; their sources live in the
LinkedDataHub-Apps repository, where each package directory
- contains the package ontology (ns.ttl) and the stylesheet its ac:stylesheet names.
+ contains the package ontology (ns.ttl) and its stylesheet, with the descriptor in the RDF file beside that directory.
- Taxonomy Editor
- Taxonomy editing on SKOS: a concept tree beside the content, hierarchy views in both assertion directions, and constructors and constraints for concepts, schemes and collections. Package URI:
https://packages.linkeddatahub.com/editor/taxonomy/#this
diff --git a/docs/reference/configuration.ttl b/docs/reference/configuration.ttl
index c450960..5800385 100644
--- a/docs/reference/configuration.ttl
+++ b/docs/reference/configuration.ttl
@@ -187,6 +187,8 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com
Defaults to true.
- MAX_CONTENT_LENGTH
- Maximum allowed request body size (nginx has a separate setting for this). Defaults to 2097152.
+ - HTTP_MAX_THREADS
+ - Maximum request threads on Tomcat's plain HTTP connector, the one the proxy forwards to. Defaults to Tomcat's own 200. Beyond capacity it bounds how many server-side renders can be in flight against each other: a render calls back into this same connector through the proxy for the page's labels, so it holds one thread while it waits for another to answer — see the HTTP client pool.
@@ -217,6 +219,8 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com
Socket (read) timeout — how long to wait for data on an established connection. Defaults to 120000.
CLIENT_CONNECT_TIMEOUT
Connection timeout — how long to wait to establish a connection. Defaults to 10000.
+ CONNECTION_REQUEST_TIMEOUT
+ How long a request waits for a free connection from the pool before it gives up. Defaults to 30000; left unset the underlying client waits indefinitely, and a thread parked on the pool never comes back on its own.
CLIENT_SELF_REQUEST_TIMEOUT
Connect and read timeout for requests the platform makes to its own URLs, such as a server-side render fetching its labels from the dataspace's /sparql and /ns. Those are answered by the platform's own request threads, so a wait that is too long lets a burst of renders hold every thread waiting on each other; after this bound the render gives up on the label. Defaults to 5000.
CLIENT_CONNECTION_TIME_TO_LIVE
@@ -225,6 +229,22 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com
Idle time after which a pooled connection is validated before reuse. Defaults to 10000.
+
+
HTTP client pool
+
The size of the connection pool those clients share. Both are passed as servlet parameters rather than as CATALINA_OPTS system properties.
+
+ - MAX_CONN_PER_ROUTE
+ - Maximum pooled connections per host. Defaults to 200.
+ - MAX_TOTAL_CONN
+ - Maximum pooled connections in total. Defaults to 400.
+
+
+
warning
+
+
Keep MAX_CONN_PER_ROUTE at least as large as HTTP_MAX_THREADS. A request the platform makes to its own URL holds a pooled connection for as long as it waits for one of the same Tomcat's threads to answer it, so a pool smaller than the connector queues those calls behind each other and a burst of renders drains one bounded wait at a time. The image's defaults are sized to each other; changing one means changing the other.
+
+
+
Varnish services