@twin.org/dataspace-control-plane-service 0.0.3-next.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +119 -0
  3. package/dist/es/dataspaceControlPlanePolicyRequester.js +228 -0
  4. package/dist/es/dataspaceControlPlanePolicyRequester.js.map +1 -0
  5. package/dist/es/dataspaceControlPlaneRoutes.js +202 -0
  6. package/dist/es/dataspaceControlPlaneRoutes.js.map +1 -0
  7. package/dist/es/dataspaceControlPlaneService.js +1420 -0
  8. package/dist/es/dataspaceControlPlaneService.js.map +1 -0
  9. package/dist/es/index.js +11 -0
  10. package/dist/es/index.js.map +1 -0
  11. package/dist/es/models/IDataspaceControlPlaneServiceConfig.js +4 -0
  12. package/dist/es/models/IDataspaceControlPlaneServiceConfig.js.map +1 -0
  13. package/dist/es/models/IDataspaceControlPlaneServiceConstructorOptions.js +2 -0
  14. package/dist/es/models/IDataspaceControlPlaneServiceConstructorOptions.js.map +1 -0
  15. package/dist/es/models/INegotiationState.js +4 -0
  16. package/dist/es/models/INegotiationState.js.map +1 -0
  17. package/dist/es/restEntryPoints.js +15 -0
  18. package/dist/es/restEntryPoints.js.map +1 -0
  19. package/dist/es/schema.js +11 -0
  20. package/dist/es/schema.js.map +1 -0
  21. package/dist/es/utils/dataHelpers.js +22 -0
  22. package/dist/es/utils/dataHelpers.js.map +1 -0
  23. package/dist/es/utils/transferErrorUtils.js +78 -0
  24. package/dist/es/utils/transferErrorUtils.js.map +1 -0
  25. package/dist/types/dataspaceControlPlanePolicyRequester.d.ts +80 -0
  26. package/dist/types/dataspaceControlPlaneRoutes.d.ts +17 -0
  27. package/dist/types/dataspaceControlPlaneService.d.ts +153 -0
  28. package/dist/types/index.d.ts +8 -0
  29. package/dist/types/models/IDataspaceControlPlaneServiceConfig.d.ts +20 -0
  30. package/dist/types/models/IDataspaceControlPlaneServiceConstructorOptions.d.ts +69 -0
  31. package/dist/types/models/INegotiationState.d.ts +27 -0
  32. package/dist/types/restEntryPoints.d.ts +7 -0
  33. package/dist/types/schema.d.ts +4 -0
  34. package/dist/types/utils/dataHelpers.d.ts +1 -0
  35. package/dist/types/utils/transferErrorUtils.d.ts +39 -0
  36. package/docs/API.md +341 -0
  37. package/docs/changelog.md +31 -0
  38. package/docs/examples.md +1 -0
  39. package/docs/reference/classes/DataspaceControlPlanePolicyRequester.md +244 -0
  40. package/docs/reference/classes/DataspaceControlPlaneService.md +549 -0
  41. package/docs/reference/functions/generateRestRoutesDataspaceControlPlane.md +29 -0
  42. package/docs/reference/functions/initSchema.md +9 -0
  43. package/docs/reference/index.md +22 -0
  44. package/docs/reference/interfaces/IDataspaceControlPlaneServiceConfig.md +26 -0
  45. package/docs/reference/interfaces/IDataspaceControlPlaneServiceConstructorOptions.md +160 -0
  46. package/docs/reference/interfaces/INegotiationState.md +43 -0
  47. package/docs/reference/variables/restEntryPoints.md +7 -0
  48. package/docs/reference/variables/tagsDataspaceControlPlane.md +5 -0
  49. package/locales/en.json +93 -0
  50. package/package.json +70 -0
@@ -0,0 +1,153 @@
1
+ import { type IDataspaceControlPlaneComponent, type IDataspaceControlPlaneResolverComponent, type INegotiationCallback, type ITransferContext } from "@twin.org/dataspace-models";
2
+ import { type IDataspaceProtocolContractNegotiation, type IDataspaceProtocolContractNegotiationError, type IDataspaceProtocolTransferCompletionMessage, type IDataspaceProtocolTransferError, type IDataspaceProtocolTransferProcess, type IDataspaceProtocolTransferRequestMessage, type IDataspaceProtocolTransferStartMessage, type IDataspaceProtocolTransferSuspensionMessage, type IDataspaceProtocolTransferTerminationMessage } from "@twin.org/standards-dataspace-protocol";
3
+ import type { IDataspaceControlPlaneServiceConstructorOptions } from "./models/IDataspaceControlPlaneServiceConstructorOptions.js";
4
+ /**
5
+ * Dataspace Control Plane Service implementation.
6
+ *
7
+ * Handles contract negotiation (via PNP callbacks) and transfer process management.
8
+ * Negotiation is fully callback-driven: negotiateAgreement() returns immediately with
9
+ * a negotiationId, and the caller is notified via INegotiationCallback when complete.
10
+ */
11
+ export declare class DataspaceControlPlaneService implements IDataspaceControlPlaneComponent, IDataspaceControlPlaneResolverComponent {
12
+ /**
13
+ * Runtime name for the class.
14
+ */
15
+ static readonly CLASS_NAME: string;
16
+ /**
17
+ * Create a new instance of DataspaceControlPlaneService.
18
+ * @param options The options for the service.
19
+ */
20
+ constructor(options?: IDataspaceControlPlaneServiceConstructorOptions);
21
+ /**
22
+ * Returns the class name of the component.
23
+ * @returns The class name of the component.
24
+ */
25
+ className(): string;
26
+ /**
27
+ * Register a callback to receive negotiation state change notifications.
28
+ * Upstream modules (e.g. supply-chain) register their callback here.
29
+ * @param key A unique key identifying this callback registration.
30
+ * @param callback The callback interface to register.
31
+ */
32
+ registerNegotiationCallback(key: string, callback: INegotiationCallback): void;
33
+ /**
34
+ * Unregister a previously registered negotiation callback.
35
+ * @param key The key used when registering the callback.
36
+ */
37
+ unregisterNegotiationCallback(key: string): void;
38
+ /**
39
+ * The service needs to be started when the application is initialized.
40
+ * Populates the Federated Catalogue with datasets from registered apps
41
+ * and starts the stalled negotiation cleanup task.
42
+ * @param nodeLoggingComponentType The node logging component type.
43
+ */
44
+ start(nodeLoggingComponentType?: string): Promise<void>;
45
+ /**
46
+ * Stop the service.
47
+ * Removes the stalled negotiation cleanup task.
48
+ * @param nodeLoggingComponentType The node logging component type.
49
+ */
50
+ stop(nodeLoggingComponentType?: string): Promise<void>;
51
+ /**
52
+ * Request a Transfer Process.
53
+ * Creates a new Transfer Process in REQUESTED state.
54
+ * @param request Transfer request message (DSP compliant).
55
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
56
+ * @returns Transfer Process (DSP compliant) with state REQUESTED, or TransferError if the operation fails.
57
+ *
58
+ * Role Performed: Provider
59
+ * Called by: Consumer when it wants to request a new Transfer Process
60
+ */
61
+ requestTransfer(request: IDataspaceProtocolTransferRequestMessage, trustPayload: unknown): Promise<IDataspaceProtocolTransferProcess | IDataspaceProtocolTransferError>;
62
+ /**
63
+ * Start a Transfer Process.
64
+ * Transitions Transfer Process from REQUESTED to STARTED state or resumes from SUSPENDED state.
65
+ * @param message Transfer start message (DSP compliant).
66
+ * @param publicOrigin The public origin URL of this service.
67
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
68
+ * @returns Transfer Start Message (DSP compliant) with dataAddress for PULL transfers, or TransferError if the operation fails.
69
+ *
70
+ * Role Performed: Provider / Consumer
71
+ */
72
+ startTransfer(message: IDataspaceProtocolTransferStartMessage, publicOrigin: string, trustPayload: unknown): Promise<IDataspaceProtocolTransferStartMessage | IDataspaceProtocolTransferError>;
73
+ /**
74
+ * Complete a Transfer Process.
75
+ * @param message Transfer completion message (DSP compliant).
76
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
77
+ * @returns Transfer Process (DSP compliant) with state COMPLETED, or TransferError if the operation fails.
78
+ */
79
+ completeTransfer(message: IDataspaceProtocolTransferCompletionMessage, trustPayload: unknown): Promise<IDataspaceProtocolTransferProcess | IDataspaceProtocolTransferError>;
80
+ /**
81
+ * Suspend a Transfer Process.
82
+ * @param message Transfer suspension message (DSP compliant).
83
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
84
+ * @returns Transfer Process (DSP compliant) with state SUSPENDED, or TransferError if the operation fails.
85
+ */
86
+ suspendTransfer(message: IDataspaceProtocolTransferSuspensionMessage, trustPayload: unknown): Promise<IDataspaceProtocolTransferProcess | IDataspaceProtocolTransferError>;
87
+ /**
88
+ * Terminate a Transfer Process.
89
+ * @param message Transfer termination message (DSP compliant).
90
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
91
+ * @returns Transfer Process (DSP compliant) with state TERMINATED, or TransferError if the operation fails.
92
+ */
93
+ terminateTransfer(message: IDataspaceProtocolTransferTerminationMessage, trustPayload: unknown): Promise<IDataspaceProtocolTransferProcess | IDataspaceProtocolTransferError>;
94
+ /**
95
+ * Get Transfer Process state.
96
+ * @param pid Process ID (consumerPid or providerPid).
97
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
98
+ * @returns Transfer Process (DSP compliant) with current state, or TransferError if the operation fails.
99
+ */
100
+ getTransferProcess(pid: string, trustPayload: unknown): Promise<IDataspaceProtocolTransferProcess | IDataspaceProtocolTransferError>;
101
+ /**
102
+ * Negotiate a contract agreement with a provider.
103
+ * Returns immediately with a negotiationId. The caller is notified
104
+ * via the registered INegotiationCallback when the negotiation completes.
105
+ *
106
+ * @param offerId The offer ID from the provider's catalog.
107
+ * @param providerEndpoint The provider's contract negotiation endpoint URL.
108
+ * @param publicOrigin The public origin URL of this control plane (for callbacks).
109
+ * @param trustPayload The trust payload for authentication.
110
+ * @returns The negotiation ID. Use the registered callback for completion notification.
111
+ */
112
+ negotiateAgreement(offerId: string, providerEndpoint: string, publicOrigin: string, trustPayload: unknown): Promise<{
113
+ negotiationId: string;
114
+ }>;
115
+ /**
116
+ * Get the current state of a contract negotiation.
117
+ * @param negotiationId The unique identifier of the negotiation.
118
+ * @param trustPayload The trust payload for authentication.
119
+ * @returns Current state of the negotiation.
120
+ */
121
+ getNegotiation(negotiationId: string, trustPayload: unknown): Promise<IDataspaceProtocolContractNegotiation | IDataspaceProtocolContractNegotiationError>;
122
+ /**
123
+ * Get negotiation history.
124
+ * @param state Optional filter by negotiation state.
125
+ * @param cursor Optional pagination cursor.
126
+ * @param trustPayload Trust payload for authentication.
127
+ * @returns List of negotiation history entries with pagination.
128
+ */
129
+ getNegotiationHistory(state: string | undefined, cursor: string | undefined, trustPayload: unknown): Promise<{
130
+ negotiations: {
131
+ negotiation: IDataspaceProtocolContractNegotiation | IDataspaceProtocolContractNegotiationError;
132
+ createdAt: string;
133
+ offerId?: string;
134
+ agreementId?: string;
135
+ }[];
136
+ cursor?: string;
137
+ count: number;
138
+ }>;
139
+ /**
140
+ * Resolve consumerPid to Transfer Context.
141
+ * @param consumerPid Consumer Process ID.
142
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
143
+ * @returns Transfer Context with Agreement, datasetId, and Transfer Process metadata.
144
+ */
145
+ resolveConsumerPid(consumerPid: string, trustPayload: unknown): Promise<ITransferContext>;
146
+ /**
147
+ * Resolve providerPid to Transfer Context.
148
+ * @param providerPid Provider Process ID.
149
+ * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
150
+ * @returns Transfer Context with Agreement, datasetId, and Transfer Process metadata.
151
+ */
152
+ resolveProviderPid(providerPid: string, trustPayload: unknown): Promise<ITransferContext>;
153
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./dataspaceControlPlanePolicyRequester.js";
2
+ export * from "./dataspaceControlPlaneRoutes.js";
3
+ export * from "./dataspaceControlPlaneService.js";
4
+ export * from "./models/IDataspaceControlPlaneServiceConfig.js";
5
+ export * from "./models/IDataspaceControlPlaneServiceConstructorOptions.js";
6
+ export * from "./models/INegotiationState.js";
7
+ export * from "./restEntryPoints.js";
8
+ export * from "./schema.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Dataspace Control Plane service configuration.
3
+ */
4
+ export interface IDataspaceControlPlaneServiceConfig {
5
+ /**
6
+ * Override the default trust generator type for token generation.
7
+ * If not specified, the default trust generator configured in the trust component will be used.
8
+ */
9
+ overrideTrustGeneratorType?: string;
10
+ /**
11
+ * Data plane endpoint path for PULL transfers (path only, not full URL).
12
+ * Will be combined with the public origin from the hosting component.
13
+ *
14
+ * REQUIRED if PULL transfers are supported.
15
+ * If not specified, PULL transfers will not be available.
16
+ *
17
+ * Example: "data-plane/data" or "api/data-plane/data"
18
+ */
19
+ dataPlanePath?: string;
20
+ }
@@ -0,0 +1,69 @@
1
+ import type { IDataspaceControlPlaneServiceConfig } from "./IDataspaceControlPlaneServiceConfig.js";
2
+ /**
3
+ * Dataspace Control Plane service constructor options.
4
+ */
5
+ export interface IDataspaceControlPlaneServiceConstructorOptions {
6
+ /**
7
+ * Policy Administration Point component type.
8
+ * Used for Agreement lookup and validation during Transfer Process initiation.
9
+ * @default policy-administration-point
10
+ */
11
+ policyAdministrationPointComponentType?: string;
12
+ /**
13
+ * Policy Negotiation Point component type.
14
+ * Used for contract negotiation to create agreements before transfer processes.
15
+ * @default policy-negotiation-point
16
+ */
17
+ policyNegotiationPointComponentType?: string;
18
+ /**
19
+ * Policy Negotiation Admin Point component type.
20
+ * Used for querying negotiation history.
21
+ * Optional - if not provided, negotiation history will not be available.
22
+ * @default policy-negotiation-admin-point
23
+ */
24
+ policyNegotiationAdminPointComponentType?: string;
25
+ /**
26
+ * Federated Catalogue component type.
27
+ * Used for dataset validation during Transfer Process initiation.
28
+ * Validates that Agreements reference valid catalog datasets.
29
+ * @default federated-catalogue
30
+ */
31
+ federatedCatalogueComponentType?: string;
32
+ /**
33
+ * Logging component type.
34
+ * @default logging
35
+ */
36
+ loggingComponentType?: string;
37
+ /**
38
+ * Identity component type (for token signing/verification).
39
+ * @default identity
40
+ */
41
+ identityComponentType?: string;
42
+ /**
43
+ * Identity Authentication component type (for token validation).
44
+ * @default identity-authentication
45
+ */
46
+ identityAuthenticationComponentType?: string;
47
+ /**
48
+ * Trust component type for trust verification.
49
+ * Used to verify JWT/VC tokens and extract identity information.
50
+ * @default trust
51
+ */
52
+ trustComponentType?: string;
53
+ /**
54
+ * Entity storage type for Transfer Process entities.
55
+ * Used to persist transfer state for the consumerPid flow.
56
+ * Must match the Data Plane's transferProcessEntityStorageType for shared storage.
57
+ * @default transfer-process
58
+ */
59
+ transferProcessEntityStorageType?: string;
60
+ /**
61
+ * Task scheduler component type for periodic cleanup of stalled negotiations.
62
+ * @default task-scheduler
63
+ */
64
+ taskSchedulerComponentType?: string;
65
+ /**
66
+ * The configuration of the Dataspace Control Plane Service.
67
+ */
68
+ config?: IDataspaceControlPlaneServiceConfig;
69
+ }
@@ -0,0 +1,27 @@
1
+ import type { DataspaceProtocolContractNegotiationStateType } from "@twin.org/standards-dataspace-protocol";
2
+ import type { IOdrlAgreement } from "@twin.org/standards-w3c-odrl";
3
+ /**
4
+ * Negotiation state tracked internally for callback routing.
5
+ */
6
+ export interface INegotiationState {
7
+ /**
8
+ * The negotiation ID (self-reference for lookup).
9
+ */
10
+ negotiationId: string;
11
+ /**
12
+ * Current negotiation state.
13
+ */
14
+ state: DataspaceProtocolContractNegotiationStateType;
15
+ /**
16
+ * Agreement received from provider (stored until finalized).
17
+ */
18
+ agreement?: IOdrlAgreement;
19
+ /**
20
+ * Timestamp when negotiation started.
21
+ */
22
+ startedAt: number;
23
+ /**
24
+ * Timestamp when negotiation state was last updated.
25
+ */
26
+ updatedAt: number;
27
+ }
@@ -0,0 +1,7 @@
1
+ import type { IRestRouteEntryPoint } from "@twin.org/api-models";
2
+ /**
3
+ * Entry points for the REST API.
4
+ * Defines REST routes for DSP protocol only.
5
+ * Resolver methods (IDataspaceControlPlaneResolverComponent).
6
+ */
7
+ export declare const restEntryPoints: IRestRouteEntryPoint[];
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Inits schemas for Control Plane entities.
3
+ */
4
+ export declare function initSchema(): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { type IDataspaceProtocolCatalogError, type IDataspaceProtocolTransferError } from "@twin.org/standards-dataspace-protocol";
2
+ import { HttpStatusCode } from "@twin.org/web";
3
+ /**
4
+ * Transform an error to DS Protocol TransferError format.
5
+ * Used by both service and route layers to ensure consistent error responses.
6
+ * Following the same pattern as Federated Catalogue's catalogErrorUtils.ts.
7
+ * The code property uses semantic format "ErrorName:message".
8
+ * The reason property contains the full flattened error chain for debugging.
9
+ * @param error The error to transform.
10
+ * @param pids Optional object containing consumerPid and/or providerPid.
11
+ * @param pids.consumerPid Optional consumer process ID.
12
+ * @param pids.providerPid Optional provider process ID.
13
+ * @returns The TransferError.
14
+ */
15
+ export declare function transformToTransferError(error: unknown, pids?: {
16
+ consumerPid?: string;
17
+ providerPid?: string;
18
+ }): IDataspaceProtocolTransferError;
19
+ /**
20
+ * Transform the DS Protocol result to an HTTP status code.
21
+ * Used by the routes layer to derive HTTP status from TransferError.
22
+ *
23
+ * @param result The result to transform.
24
+ * @returns The transformed status code or undefined if no transformation was found or not an error.
25
+ */
26
+ export declare function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined;
27
+ /**
28
+ * Check if a CatalogError contains a specific error name in its code.
29
+ * @param error The CatalogError to check.
30
+ * @param errorName The error name to check for (e.g., NotFoundError.CLASS_NAME).
31
+ * @returns True if the error code contains the specified error name.
32
+ */
33
+ export declare function isCatalogErrorName(error: IDataspaceProtocolCatalogError, errorName: string): boolean;
34
+ /**
35
+ * Check if a CatalogError contains a specific error name in its code.
36
+ * @param error The CatalogError to check.
37
+ * @returns True if the error code contains the specified error name.
38
+ */
39
+ export declare function isCatalogError(error: unknown): error is IDataspaceProtocolCatalogError;
package/docs/API.md ADDED
@@ -0,0 +1,341 @@
1
+ # Dataspace Control Plane API Documentation
2
+
3
+ REST API endpoints for the Eclipse Dataspace Protocol (DSP) Contract Negotiation and Transfer Process protocols.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Authentication](#authentication)
8
+ - [Contract Negotiation Protocol](#contract-negotiation-protocol)
9
+ - [POST /negotiations](#post-negotiations)
10
+ - [GET /negotiations/:negotiationId](#get-negotiationsnegotiationid)
11
+ - [Transfer Process Protocol](#transfer-process-protocol)
12
+ - [POST /transfers/request](#post-transfersrequest)
13
+ - [GET /transfers/:pid](#get-transferspid)
14
+ - [POST /transfers/:pid/start](#post-transferspidstart)
15
+ - [POST /transfers/:pid/complete](#post-transferspidcomplete)
16
+ - [POST /transfers/:pid/suspend](#post-transferspidsuspend)
17
+ - [POST /transfers/:pid/terminate](#post-transferspidterminate)
18
+ - [Error Handling](#error-handling)
19
+
20
+ ## Authentication
21
+
22
+ All API endpoints require authentication via Bearer token in the `Authorization` header:
23
+
24
+ ```http
25
+ Authorization: Bearer <trust-token>
26
+ ```
27
+
28
+ ## Contract Negotiation Protocol
29
+
30
+ Implements the DSP Contract Negotiation Protocol for negotiating agreements with data providers.
31
+
32
+ **Base Path:** `/control-plane/negotiations`
33
+
34
+ **Specification:** [Eclipse DSP 2025-1 - Contract Negotiation](https://eclipse-dataspace-protocol-base.github.io/DataspaceProtocol/2025-1/#negotiation-protocol)
35
+
36
+ ### POST /negotiations
37
+
38
+ Initiate a contract negotiation with a provider.
39
+
40
+ **Request:**
41
+
42
+ ```http
43
+ POST /control-plane/negotiations
44
+ Authorization: Bearer <trust-token>
45
+ Content-Type: application/json
46
+
47
+ {
48
+ "offerId": "offer-123",
49
+ "providerEndpoint": "https://provider.example.com/negotiations",
50
+ "publicOrigin": "https://consumer.example.com" // Optional
51
+ }
52
+ ```
53
+
54
+ **Request Body:**
55
+
56
+ | Field | Type | Required | Description |
57
+ | ------------------ | ------ | -------- | ----------------------------------------------------------------------------------- |
58
+ | `offerId` | string | Yes | The offer ID from the provider's catalog |
59
+ | `providerEndpoint` | string | Yes | The provider's contract negotiation endpoint URL |
60
+ | `publicOrigin` | string | No | The public origin URL for callbacks (defaults to hosting component's public origin) |
61
+
62
+ **Response (200 OK):**
63
+
64
+ ```json
65
+ {
66
+ "agreement": {
67
+ "@context": "http://www.w3.org/ns/odrl.jsonld",
68
+ "@type": "Agreement",
69
+ "uid": "agreement-456",
70
+ "assigner": "did:iota:provider-xyz",
71
+ "assignee": "did:iota:consumer-abc",
72
+ "target": "urn:uuid:dataset-789",
73
+ "permission": [{ "action": "read" }]
74
+ },
75
+ "agreementId": "agreement-456",
76
+ "negotiationId": "negotiation-123",
77
+ "consumerPid": "consumer-pid-123",
78
+ "providerPid": "provider-pid-456"
79
+ }
80
+ ```
81
+
82
+ **Response Body:**
83
+
84
+ | Field | Type | Description |
85
+ | --------------- | ----------- | ---------------------------------------- |
86
+ | `agreement` | IOdrlPolicy | The negotiated ODRL agreement |
87
+ | `agreementId` | string | The unique identifier of the agreement |
88
+ | `negotiationId` | string | The unique identifier of the negotiation |
89
+ | `consumerPid` | string | The consumer process ID |
90
+ | `providerPid` | string | The provider process ID |
91
+
92
+ **Error Responses:**
93
+
94
+ | Status Code | Description |
95
+ | ----------- | --------------------------------------------- |
96
+ | 400 | Invalid request (missing required fields) |
97
+ | 401 | Unauthorized (invalid or missing trust token) |
98
+ | 404 | Offer not found |
99
+ | 500 | Internal server error during negotiation |
100
+ | 504 | Negotiation timeout |
101
+
102
+ **Example:**
103
+
104
+ ```bash
105
+ curl -X POST https://consumer.example.com/control-plane/negotiations \
106
+ -H "Authorization: Bearer ${TOKEN}" \
107
+ -H "Content-Type: application/json" \
108
+ -d '{
109
+ "offerId": "offer-from-catalog",
110
+ "providerEndpoint": "https://provider.example.com/negotiations"
111
+ }'
112
+ ```
113
+
114
+ ### GET /negotiations/:negotiationId {#get-negotiationsnegotiationid}
115
+
116
+ Get the current state of a contract negotiation.
117
+
118
+ **Request:**
119
+
120
+ ```http
121
+ GET /control-plane/negotiations/{negotiationId}
122
+ Authorization: Bearer <trust-token>
123
+ ```
124
+
125
+ **Path Parameters:**
126
+
127
+ | Parameter | Type | Description |
128
+ | --------------- | ------ | ---------------------------------------- |
129
+ | `negotiationId` | string | The unique identifier of the negotiation |
130
+
131
+ **Response (200 OK):**
132
+
133
+ ```json
134
+ {
135
+ "negotiationId": "negotiation-123",
136
+ "state": "FINALIZED",
137
+ "consumerPid": "consumer-pid-123",
138
+ "providerPid": "provider-pid-456",
139
+ "agreementId": "agreement-456"
140
+ }
141
+ ```
142
+
143
+ **Response Body:**
144
+
145
+ | Field | Type | Description |
146
+ | --------------- | ------ | --------------------------------------------------------------------------------------- |
147
+ | `negotiationId` | string | The unique identifier of the negotiation |
148
+ | `state` | string | Current negotiation state (REQUESTED, OFFERED, AGREED, VERIFIED, FINALIZED, TERMINATED) |
149
+ | `consumerPid` | string | The consumer process ID |
150
+ | `providerPid` | string | The provider process ID |
151
+ | `agreementId` | string | The agreement ID (only present if state is FINALIZED) |
152
+ | `errorDetail` | string | Error details (only present if state is TERMINATED) |
153
+
154
+ **Negotiation States:**
155
+
156
+ | State | Description |
157
+ | ------------ | --------------------------------------------- |
158
+ | `REQUESTED` | Initial request sent to provider |
159
+ | `OFFERED` | Provider sent counter-offer |
160
+ | `AGREED` | Agreement reached |
161
+ | `VERIFIED` | Agreement verified |
162
+ | `FINALIZED` | Negotiation complete, agreement ready for use |
163
+ | `TERMINATED` | Negotiation failed or was cancelled |
164
+
165
+ **Error Responses:**
166
+
167
+ | Status Code | Description |
168
+ | ----------- | --------------------------------------------- |
169
+ | 401 | Unauthorized (invalid or missing trust token) |
170
+ | 404 | Negotiation not found |
171
+ | 500 | Internal server error |
172
+
173
+ **Example:**
174
+
175
+ ```bash
176
+ curl -X GET https://consumer.example.com/control-plane/negotiations/negotiation-123 \
177
+ -H "Authorization: Bearer ${TOKEN}"
178
+ ```
179
+
180
+ ## Transfer Process Protocol
181
+
182
+ Implements the DSP Transfer Process Protocol for managing data transfers.
183
+
184
+ **Base Path:** `/control-plane/transfers`
185
+
186
+ **Specification:** [Eclipse DSP 2025-1 - Transfer Process](https://eclipse-dataspace-protocol-base.github.io/DataspaceProtocol/2025-1/#transfer-process-protocol)
187
+
188
+ ### POST /transfers/request
189
+
190
+ Initiate a transfer request using a previously negotiated agreement.
191
+
192
+ **Request:**
193
+
194
+ ```http
195
+ POST /control-plane/transfers/request
196
+ Authorization: Bearer <trust-token>
197
+ Content-Type: application/json
198
+
199
+ {
200
+ "@context": ["https://w3id.org/dspace/2025/1/context.jsonld"],
201
+ "@type": "TransferRequestMessage",
202
+ "consumerPid": "consumer-pid-123",
203
+ "agreementId": "agreement-456",
204
+ "callbackAddress": "https://consumer.example.com/callbacks",
205
+ "format": "application/json"
206
+ }
207
+ ```
208
+
209
+ _See [Eclipse DSP Transfer Request specification](https://eclipse-dataspace-protocol-base.github.io/DataspaceProtocol/2025-1/#transfer-process-protocol) for complete request schema._
210
+
211
+ ### GET /transfers/:pid {#get-transferspid}
212
+
213
+ Get the current state of a transfer process.
214
+
215
+ **Request:**
216
+
217
+ ```http
218
+ GET /control-plane/transfers/{pid}
219
+ Authorization: Bearer <trust-token>
220
+ ```
221
+
222
+ _Returns the DSP TransferProcess object with current state and metadata._
223
+
224
+ ### POST /transfers/:pid/start {#post-transferspidstart}
225
+
226
+ Start an approved transfer.
227
+
228
+ ### POST /transfers/:pid/complete {#post-transferspidcomplete}
229
+
230
+ Mark a transfer as complete.
231
+
232
+ ### POST /transfers/:pid/suspend {#post-transferspidsuspend}
233
+
234
+ Suspend a running transfer.
235
+
236
+ ### POST /transfers/:pid/terminate {#post-transferspidterminate}
237
+
238
+ Terminate a transfer.
239
+
240
+ _See the main [README.md](../README.md) and [DSP specification](https://eclipse-dataspace-protocol-base.github.io/DataspaceProtocol/2025-1/) for detailed Transfer Process Protocol documentation._
241
+
242
+ ## Error Handling
243
+
244
+ All endpoints return errors in the DSP error format when applicable:
245
+
246
+ **Contract Negotiation Error:**
247
+
248
+ ```json
249
+ {
250
+ "@context": ["https://w3id.org/dspace/2025/1/context.jsonld"],
251
+ "@type": "dspace:ContractNegotiationError",
252
+ "providerPid": "provider-pid-456",
253
+ "consumerPid": "consumer-pid-123",
254
+ "code": "ErrorCode:description",
255
+ "reason": [{ "message": "Human-readable error message", "language": "en" }]
256
+ }
257
+ ```
258
+
259
+ **Transfer Process Error:**
260
+
261
+ ```json
262
+ {
263
+ "@context": ["https://w3id.org/dspace/2025/1/context.jsonld"],
264
+ "@type": "dspace:TransferError",
265
+ "providerPid": "provider-pid-456",
266
+ "consumerPid": "consumer-pid-123",
267
+ "code": "ErrorCode:description",
268
+ "reason": [{ "message": "Human-readable error message", "language": "en" }]
269
+ }
270
+ ```
271
+
272
+ **HTTP Status Codes:**
273
+
274
+ | Status Code | Description |
275
+ | ----------- | ------------------------------------------------ |
276
+ | 200 | Success |
277
+ | 400 | Bad Request (invalid input) |
278
+ | 401 | Unauthorized (missing or invalid authentication) |
279
+ | 404 | Not Found (resource doesn't exist) |
280
+ | 409 | Conflict (invalid state transition) |
281
+ | 500 | Internal Server Error |
282
+ | 504 | Gateway Timeout (negotiation timeout) |
283
+
284
+ ## Full Example Workflow
285
+
286
+ ```bash
287
+ #!/bin/bash
288
+
289
+ # 1. Query provider catalog (not part of Control Plane API)
290
+ CATALOG_RESPONSE=$(curl -X GET https://provider.example.com/catalog \
291
+ -H "Authorization: Bearer ${TOKEN}")
292
+ OFFER_ID=$(echo $CATALOG_RESPONSE | jq -r '.offers[0].uid')
293
+
294
+ # 2. Initiate contract negotiation
295
+ NEGOTIATION_RESPONSE=$(curl -X POST https://consumer.example.com/control-plane/negotiations \
296
+ -H "Authorization: Bearer ${TOKEN}" \
297
+ -H "Content-Type: application/json" \
298
+ -d "{
299
+ \"offerId\": \"${OFFER_ID}\",
300
+ \"providerEndpoint\": \"https://provider.example.com/negotiations\"
301
+ }")
302
+
303
+ AGREEMENT_ID=$(echo $NEGOTIATION_RESPONSE | jq -r '.agreementId')
304
+ NEGOTIATION_ID=$(echo $NEGOTIATION_RESPONSE | jq -r '.negotiationId')
305
+
306
+ echo "Agreement ID: ${AGREEMENT_ID}"
307
+ echo "Negotiation ID: ${NEGOTIATION_ID}"
308
+
309
+ # 3. (Optional) Check negotiation status
310
+ NEGOTIATION_STATUS=$(curl -X GET \
311
+ "https://consumer.example.com/control-plane/negotiations/${NEGOTIATION_ID}" \
312
+ -H "Authorization: Bearer ${TOKEN}")
313
+
314
+ echo "Negotiation Status: $(echo $NEGOTIATION_STATUS | jq -r '.state')"
315
+
316
+ # 4. Request transfer using the agreement
317
+ TRANSFER_RESPONSE=$(curl -X POST https://consumer.example.com/control-plane/transfers/request \
318
+ -H "Authorization: Bearer ${TOKEN}" \
319
+ -H "Content-Type: application/json" \
320
+ -d "{
321
+ \"@context\": [\"https://w3id.org/dspace/2025/1/context.jsonld\"],
322
+ \"@type\": \"TransferRequestMessage\",
323
+ \"consumerPid\": \"my-consumer-pid-001\",
324
+ \"agreementId\": \"${AGREEMENT_ID}\",
325
+ \"callbackAddress\": \"https://consumer.example.com/callbacks\",
326
+ \"format\": \"application/json\"
327
+ }")
328
+
329
+ CONSUMER_PID=$(echo $TRANSFER_RESPONSE | jq -r '.consumerPid')
330
+ echo "Transfer Process ID: ${CONSUMER_PID}"
331
+
332
+ # 5. Get transfer status
333
+ curl -X GET "https://consumer.example.com/control-plane/transfers/${CONSUMER_PID}" \
334
+ -H "Authorization: Bearer ${TOKEN}"
335
+ ```
336
+
337
+ ## See Also
338
+
339
+ - [Eclipse Dataspace Protocol Specification](https://eclipse-dataspace-protocol-base.github.io/DataspaceProtocol/2025-1/)
340
+ - [ODRL (Open Digital Rights Language)](https://www.w3.org/TR/odrl-model/)
341
+ - [Rights Management Contract Negotiation Documentation](../../../../.cursor/docs/dataspace-connector/contract-negotiation/README.md)