Skip to content

Build a data service

This guide explains how to build a data service on top of the dataLOFT management services. A data service exposes datasets to the dataspace through RUN-DSP. RUN-DSP handles all dataspace protocol communication (contract negotiation, transfer lifecycle, authentication) and delegates data operations to your service via gRPC.

The proto definitions are published at codeberg.org/go-dataspace/run-dsrpc. Generate a client and server stub for your language from the .proto files in proto/dsp/v1alpha2/.


Integration levels

There are two levels of integration, depending on whether your service acts as a provider only or also drives dataspace operations as a consumer.

Basic integration: implement ProviderService

Your service implements ProviderService from provider.proto. RUN-DSP calls your service when a remote consumer queries the catalogue, negotiates a contract, or initiates a transfer. This is the minimum required to publish datasets into the dataspace.

Full integration: also implement ControlService

Your service calls ControlService from control.proto, which RUN-DSP exposes. This lets your service drive operations from the consumer side: browse remote catalogues, initiate contract negotiations, and manage transfers programmatically. r-squared uses both levels.


Basic integration: ProviderService

Implement the ProviderService gRPC server and configure RUN-DSP to call it.

RPCs

service ProviderService {
  rpc Ping(PingRequest) returns (PingResponse) {}
  rpc GetCatalogue(GetCatalogueRequest) returns (GetCatalogueResponse) {}
  rpc GetDataset(GetDatasetRequest) returns (GetDatasetResponse) {}
  rpc PublishDataset(PublishDatasetRequest) returns (PublishDatasetResponse) {}
  rpc ShutdownTransfer(ShutdownTransferRequest) returns (ShutdownTransferResponse) {}
  rpc SuspendPublishedDataset(SuspendPublishedDatasetRequest) returns (SuspendPublishedDatasetResponse) {}
  rpc UnsuspendPublishedDataset(UnsuspendPublishedDatasetRequest) returns (UnsuspendPublishedDatasetResponse) {}
  rpc ReceiveDataset(ReceiveDatasetRequest) returns (ReceiveDatasetResponse) {}
  rpc PushDataset(PushDatasetRequest) returns (PushDatasetResponse) {}
}

Ping

RUN-DSP calls Ping on startup to verify the provider is reachable and to read its configuration.

Your response must include:

Field Description
provider_name Human-readable name for this provider.
provider_description Short description.
authenticated Whether the forwarded authorization header was valid.
dataservice_id A stable UUID for this data service.
dataservice_url The URL from which consumers download data (used in PublishInfo).
capabilities Supported transfer modes: HTTP_PULL, HTTP_PUSH, or both.

GetCatalogue and GetDataset

RUN-DSP calls GetCatalogue when a consumer queries the catalogue. Return a list of Dataset messages representing the datasets your service makes available.

GetDataset is called when a consumer requests a specific dataset by ID. Return the same Dataset structure.

Both calls include a RequesterInfo field that identifies the requester within the dataspace. Use this to apply access control. You can return different datasets to different participants.

The Dataset message carries standard metadata fields (title, description, keywords, media type, byte size, checksum) plus an open metadata map for anything domain-specific.

PublishDataset

Called when a contract has been agreed and a pull transfer is about to begin. RUN-DSP passes the dataset ID and a publish_id that is unique to this transfer instance. The same dataset can be published concurrently under multiple publish IDs.

Return a PublishInfo containing:

Field Description
url The URL where the consumer can download the data.
authentication_type BEARER or BASIC.
username / password Credentials for the download request.

For a pull transfer, you generate a short-lived URL (or a pre-signed URL if using S3-compatible storage) and return it here. The consumer then downloads directly from that URL without further involvement from RUN-DSP.

ShutdownTransfer

Called when the transfer is complete or terminated. Use the publish_id to clean up any temporary credentials or resources you allocated in PublishDataset.

SuspendPublishedDataset and UnsuspendPublishedDataset

Called if the transfer is suspended or resumed mid-flight. Revoke access temporarily on suspend; restore it on unsuspend.

ReceiveDataset

Called for push transfers. The consumer wants to send data to your service. Return a PublishInfo containing the URL and credentials where the consumer should upload the data.

PushDataset

Called when your service is acting as a provider that pushes data to another provider. Implement this if your service supports HTTP_PUSH capability. Retrieve the upload target from GetProviderDatasetUploadInformation (via ControlService) and perform the upload.


Key types

Dataset

message Dataset {
  string id = 1;
  string title = 2;
  string access_methods = 3;
  repeated Multilingual description = 4;
  repeated string keywords = 5;
  optional string creator = 6;
  optional google.protobuf.Timestamp issued = 7;
  optional google.protobuf.Timestamp modified = 8;
  map<string, string> metadata = 9;
  optional string license = 10;
  optional string access_rights = 11;
  optional string rights = 12;
  int64 byte_size = 13;
  string media_type = 14;
  optional string format = 15;
  optional string compress_format = 16;
  optional string package_format = 17;
  optional Checksum checksum = 18;
}

Use metadata for any domain-specific fields that do not map to the standard fields. The id must be stable and unique within your service.

PublishInfo

message PublishInfo {
  string url = 1;
  AuthenticationType authentication_type = 2;
  string username = 3;
  string password = 4;
}

For bearer token auth, put the token in password and leave username empty.

RequesterInfo

message RequesterInfo {
  string identifier = 1;
  string external_id = 2;
  AuthenticationStatus authentication_status = 3;
}

identifier is the internal dataspace participant ID. external_id is the DID of the requester. authentication_status tells you whether the request arrived with valid credentials (AUTHENTICATED), no credentials (UNAUTHENTICATED), or from the local RUN-DSP instance (LOCAL_ORIGIN).


Full integration: ControlService

ControlService is the interface RUN-DSP exposes to your application. Call it when your service needs to act as a consumer, browsing remote catalogues, negotiating contracts, and managing transfers.

Browsing remote catalogues

rpc GetProviderCatalogue(GetProviderCatalogueRequest) returns (GetProviderCatalogueResponse) {}
rpc GetProviderDataset(GetProviderDatasetRequest) returns (GetProviderDatasetResponse) {}

Pass the provider_uri of the remote RUN-DSP instance. RUN-DSP fetches the catalogue on your behalf and returns the list of Dataset messages.

Contract negotiation

rpc ContractRequest(ContractRequestRequest) returns (ContractRequestResponse) {}
rpc ContractOffer(ContractOfferRequest) returns (ContractOfferResponse) {}
rpc ContractAccept(ContractAcceptRequest) returns (ContractAcceptResponse) {}
rpc ContractAgree(ContractAgreeRequest) returns (ContractAgreeResponse) {}
rpc ContractVerify(ContractVerifyRequest) returns (ContractVerifyResponse) {}
rpc ContractFinalize(ContractFinalizeRequest) returns (ContractFinalizeResponse) {}
rpc ContractTerminate(ContractTerminateRequest) returns (ContractTerminateResponse) {}

These calls map to IDSA DSP contract negotiation state transitions. For a typical consumer-initiated flow, call ContractRequest with the offer JSON and the provider address. Set auto_accept: true to let RUN-DSP advance the negotiation automatically without waiting for your application to acknowledge each step.

Once the negotiation reaches FINALIZED, use the resulting agreement to request a transfer.

Transfer lifecycle

rpc GetProviderDatasetDownloadInformation(...) returns (...) {}
rpc GetProviderDatasetUploadInformation(...) returns (...) {}
rpc InitiatePushTransfer(...) returns (...) {}
rpc SignalTransferComplete(...) returns (...) {}
rpc SignalTransferCancelled(...) returns (...) {}
rpc SignalTransferSuspend(...) returns (...) {}
rpc SignalTransferResume(...) returns (...) {}

For a pull transfer:

  1. Call GetProviderDatasetDownloadInformation with the provider URL and dataset ID. RUN-DSP negotiates the transfer with the remote provider and returns a PublishInfo and a transfer_id.
  2. Download the data from the URL in PublishInfo using the provided credentials.
  3. Call SignalTransferComplete with the transfer_id to close the transfer on the dataspace side.

For a push transfer, call GetProviderDatasetUploadInformation to get the upload target, perform the upload, then signal completion.

VerifyConnection is a diagnostic call that verifies the connection token between your application and RUN-DSP.


Reference implementations

rdsp-s3 is the reference ProviderService implementation. It indexes files from an S3-compatible object store, exposes them as datasets, and generates pre-signed download URLs in PublishDataset. The source is at codeberg.org/go-dataspace/rdsp-s3.

r-squared is the reference data service for the full integration pattern. It pairs with rdsp-s3 as the provider backend and calls ControlService to drive transfers from the consumer side, browsing remote catalogues, initiating contract negotiations, and managing ECG data transfers. The source is at codeberg.org/dataLOFT-platform/r-squared.