sconext-ckan
One extension, three faces of a CKAN-hosted asset. Install it and the SingularConnector has a complete dataspace path for every dataset in your CKAN: catalogued, negotiable, transferable, and enforced.
| interface | class | what it does |
|---|---|---|
ICatalogService |
CKANCatalogService |
how to find the assets |
IDataService |
CKANDataService |
how to fetch the data |
IPolicyEvaluator |
CKANPolicyEvaluator |
how to apply a duty to it |
pip install sconext-ckan
export sconext.ckan.url=https://data.example.eu
export sconext.ckan.admin_key=... # optional, for private datasets
export sconnector.participant_id=urn:example:city-of-athens
export sconnector.dsp.endpoint=https://connector.example.eu/2025-1
That is the whole installation. All three classes are declared in the single
sconext entry-point group; ExtensionRegistry registers each instance under
every scontoolkit.interfaces base in its MRO, so the connector finds all
three in one discovery pass and needs no change.
Configuration
| variable | required | meaning |
|---|---|---|
sconext.ckan.url |
yes | CKAN base URL |
sconext.ckan.admin_key |
no | CKAN API key; without it only public datasets are visible |
sconext.ckan.timeout |
no | seconds, default 30 |
sconnector.participant_id |
yes | dspace:participantId for the root Catalog |
sconnector.dsp.endpoint |
yes | control-plane base including the version segment — this becomes DataService.endpointURL |
sconext.ckan.catalog_id |
no | root Catalog @id; derived from the participant id otherwise |
sconext.ckan.data_service_id |
no | @id of the advertised DataService |
sconnector.dsp.endpoint is the control plane, not the data plane. DSP:
"A DataService.endpointURL property contains the URL of the service the
Contract Negotiation endpoints extend." Pointing it at /data is the
intuitive mistake and it sends consumers to an endpoint that will refuse them,
because they have no Agreement yet.
Which datasets appear
A CKAN dataset appears in the DSP catalog only once an offer has been
exposed for it, because DSP requires every Dataset to carry at least one
hasPolicy Offer. Expose offers through the connector's management API. The
operator-facing list_assets deliberately shows every CKAN dataset, exposed
or not — you have to see one in order to publish an offer for it.
What the data service reads
Per asset it picks one resource: the one named in params["resource_id"], else
the first DataStore-backed resource, else the first parseable one.
- DataStore-backed →
datastore_search, paged at 1000 rows. Field projection and filters push to source. - Otherwise → the resource
urlis downloaded and parsed. CSV, TSV, JSON and GeoJSON only.
A dataset whose only resources are in another format is refused by name,
not returned empty. read() on a PDF-only dataset raises UnsupportedAction
naming the formats found, because a transfer that returns nothing must not look
like a transfer of nothing.
Read-only. write, write_setpoint, derive, train, evaluate and
transfer raise UnsupportedAction with a reason. supported_actions()
advertises only what it will actually do, so an agreement over an action this
service refuses is declined at negotiation rather than failing at request time.
Policy-blind, and not as a style preference: nothing here anonymises,
aggregates, suppresses or throttles. A bound enforced inside a policy-blind
plug-in is a bound the PEP cannot observe, so it must not be reported as
enforced. stream() in particular does not pace itself — the connector paces
subscriptions on its own side where the decision is auditable.
What the evaluator does
It subclasses BaseUedsEvaluator, so all 18 UEDS egress duties are inherited
and supported_duties() reports all 18. Two overrides:
Record identity. CKAN DataStore rows key on _id and carry no meterId.
With the base class's identity fields, every row counts as identity None, the
distinct count collapses to zero, and every ueds:meteringPointCount floor
suppresses the whole payload — over-restrictive, and invisibly wrong. There is
a test that fails without this.
Provenance. attach_provenance adds the upstream CKAN licence, publisher
and landing page. An open-data publisher sets terms upstream of the dataspace;
a consumer that receives the records without them cannot honour them, and no
generic evaluator can supply them.
Everything else — k-anonymity (including the absent-quasi-identifier guard), aggregation, differential privacy, pseudonymisation, coarsening, embargo, projection, the cohort guard itself — is inherited unchanged.
Tests
pip install -e '.[test]'
python -m pytest tests/ -q # 31 passed, no network, no credentials
CKAN is stubbed in tests/conftest.py. The previous suite hardcoded a live
CKAN admin API key and pointed at a real instance, so it could not run offline
or in CI. If you have not already, rotate that key — it is in the git
history of this folder even though it is gone from the working tree.
One connector change still needed
The catalog and data service work against the connector as it stands today.
CKANPolicyEvaluator registers and is discoverable, but the connector's PEP
still applies duties through its own pep/transformers.py rather than
consulting IPolicyEvaluator. Until apply_transforms dispatches through
DUTY_METHODS to the evaluator that serves() the asset, this class is
installed and unused — the two CKAN-specific overrides above will not take
effect, which for the cohort floor means the connector suppresses payloads it
should deliver.
Metadata
Release files for sconext-ckan 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sconext_ckan-0.1.1.tar.gz | 25.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sconext_ckan-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.9 kB
Release files / sconext_ckan-0.1.1.tar.gz
| Download URL | sconext_ckan-0.1.1.tar.gz |
|---|---|
| Size | 25.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
019edab17ab2c44f3f610de324dd30bdbbc8b8dbfffc4a1fa30058b9dcd5ab06
|
|
BLAKE2b-256 checksum How to use checksums |
620466d5e4b02c205d83ad4ef179d853c34ce9dd8575f94688356b5bc6bd8125
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.7
|
Release files / sconext_ckan-0.1.1-py3-none-any.whl
| Download URL | sconext_ckan-0.1.1-py3-none-any.whl |
|---|---|
| Size | 25.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cfe4bd3ad20043185b89ab22f128c91fa511e6c2bd73972ad7c51b1a857495ac
|
|
BLAKE2b-256 checksum How to use checksums |
ec2cd83d0ae94aaa06b49f000475583a303876dc0487a1ea0b5a0f49a78639bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.7
|
Security for sconext-ckan 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.