Fase 1 — Hallazgos (PoC EDC ejecutado)
Resultado: ✅ ciclo DSP completo end-to-end, con dato real transferido.
Ejecutado el 2026-07-02. Approach: fase-1-poc-approach.md · Arnés:services/edc-service/poc/
1. Qué se logró
Dos conectores EDC en Docker recorrieron el ciclo completo del Dataspace Protocol:
Provider: crea Asset → Policy → Contract Definition
Consumer: catálogo → negociación (INITIAL→REQUESTED→FINALIZED) → acuerdo
→ transfer HttpData-PULL (INITIAL→REQUESTED→STARTED) → EDR
Cliente: PULL con token EDR → 5645 bytes reales de jsonplaceholder/users ✓
Un comando levanta todo (docker compose up -d --build), otro recorre el ciclo (bash run-cycle.sh). Cada respuesta quedó capturada en services/edc-service/poc/out/.
2. Versiones (el pin)
| Cosa | Valor | Nota |
|---|---|---|
eclipse-edc/Samples SHA | cc7a7e35dd37eae5f3646d0d87c1593e9f573f0e | ARG SAMPLES_SHA en el Dockerfile. |
| Dataspace Protocol | 2025-1 | path /protocol/2025-1, contexto https://w3id.org/dspace/2025/1/. |
| Management API | v3 | ⚠️ DEPRECADA — el conector avisa "switch to V4, V3 will be removed". Ver §6. |
| IAM | mock (iam.mock) | Sin identidad real; DID/VC es Fase 6. |
| Runtime build | JDK 17 (en Docker) | Host tiene Java 1.8 → todo se construye en Docker. |
3. Superficie de API capturada (semilla de lib/dataspaces/edc-client.ts)
Todas contra la Management API v3. Base: provider :19193, consumer :29193.
| # | Método | Path | Lado | Devuelve |
|---|---|---|---|---|
| 1 | POST | /management/v3/assets | provider | el asset creado |
| 2 | POST | /management/v3/policydefinitions | provider | la policy |
| 3 | POST | /management/v3/contractdefinitions | provider | la contract def |
| 4 | POST | /management/v3/catalog/request | consumer | catálogo DCAT (contiene el offer id) |
| 5 | POST | /management/v3/contractnegotiations | consumer | IdResponse con @id (negotiationId) |
| 6 | GET | /management/v3/contractnegotiations/{id} | consumer | estado + contractAgreementId |
| 7 | POST | /management/v3/transferprocesses | consumer | IdResponse con @id (transferId) |
| 8 | GET | /management/v3/transferprocesses/{id} | consumer | estado |
| 9 | GET | /management/v3/edrs/{transferId}/dataaddress | consumer | DataAddress (endpoint + token bearer) |
| 10 | GET | <edr.endpoint> + header Authorization: <edr.authorization> | data plane | el dato |
Formas clave de respuesta:
IdResponse:{"@type":"IdResponse","@id":"<uuid>","createdAt":<ms>,"@context":[...]}- offer id (catálogo): en
dcat:dataset.odrl:hasPolicy.@id, formatobase64:base64:uuid(p. ej.MQ==:YXNzZXRJZA==:<uuid>). Debe copiarse tal cual en la oferta de negociación (policy.@id). - EDR:
{"@type":"edc:DataAddress","type":"https://w3id.org/idsa/v4.1/HTTP","endpoint":"...","authType":"bearer","authorization":"<JWT>"}
4. Aprendizaje central: es una máquina de estados asíncrona
Negociación y transferencia no son síncronas: el POST devuelve un ID y el estado avanza en background.
- Negociación observada:
INITIAL → REQUESTED → FINALIZED. - Transfer observado:
INITIAL → REQUESTED → STARTED(en consumer pull se queda enSTARTED).
→ Decisión de diseño para Carbon (Fase 4): el edc-sync-worker debe pollear estos estados (o suscribirse a eventos, sample transfer-04, no probado aún). El modelo poll está validado y es simple; evaluar eventos si el volumen lo pide.
5. Respuestas a las sondas de riesgo (§6 del approach)
| Sonda | Resultado |
|---|---|
| Async: poll vs eventos | ✅ Resuelto: poll funciona y es directo (§4). Eventos = pendiente (transfer-04). |
| Forma de payloads JSON-LD | ✅ Capturada (§3). Contextos @vocab EDC + ODRL + DSP 2025/1. |
| Data plane S3 → R2 (Fase 2) | 🟡 Pendiente: este PoC usó HttpData (jsonplaceholder), no S3. La sonda MinIO/R2 no se ejecutó. Es el primer objetivo de Fase 2. |
| Gancho para DataSource Iceberg (Fase 5/Estrategia B) | ✅ Localizado: el provider de transfer-03 es un ejemplo de data plane custom — CustomProxyDataPlaneExtension registra un PortMapping + un generatorFunction de EDRs + un ProxyController. Ese es exactamente el patrón de extensión donde enchufaría un DataSource que sirva una tabla Iceberg vía Lakekeeper. |
6. Sorpresas / notas para producción
- Management API v3 está deprecada → planificar v4 antes de escribir
edc-client.ts(Fase 3). Los payloads y paths pueden cambiar. Acción: al empezar Fase 3, repetir el ciclo contra v4 y recapturar la superficie. - Networking entre contenedores: hubo que sobreescribir
edc.dsp.callback.addressa nombres de servicio docker (http://provider:19194/...) en vez delocalhost; si no, los conectores no se alcanzan. Aprendizaje directo para el despliegue real (Fase 8). - Readiness:
POST /management/v3/assets/requestcon body vacío devuelve 400 (no 200) pero significa "servidor arriba". El probe lo trata como listo. - Entorno Windows: host con Java 1.8 y sin
jq; build en Docker y parseo congrep/sed.sleepen primer plano lo bloquea el harness → el script usaperlcomo fallback para las esperas. - Pull desde el host: el
edc.dataplane.proxy.public.endpointse dejó comolocalhost:19291a propósito, para que el cliente (host) haga el pull con el puerto publicado. El token EDR valida sin importar el host usado.
7. Definition of Done (del approach)
- Consumer obtiene un asset del provider end-to-end (pull). (push = transfer-02, no ejecutado; el pull cubre el objetivo).
- Todo se levanta con un comando y versión pineada, reproducible.
- Capturado cada request Management API del ciclo (
out/). - Sabemos qué endpoints/payloads implementará
edc-client.ts(§3). - Respondidas las sondas (§5) — salvo S3/R2, que arranca Fase 2.
8. Cómo reproducir
cd services/edc-service/poc
docker compose up -d --build # build (~minutos la 1ª vez) + arranque
bash run-cycle.sh # ciclo DSP completo → out/
docker compose down # parar
9. Puente a la Fase 2
- v4: recapturar la superficie de API contra Management API v4.
- Data plane S3 → R2: añadir el módulo S3 al launcher + MinIO local como stand-in de R2; transferir un objeto a un prefijo del bucket. Es la sonda que quedó abierta.
- Empezar a modelar
services/edc-service/"de verdad" (launcher propio con los módulos que necesitamos), tomando estepoc/como referencia.