PARCID es un registro abierto de identidad de unidades en tránsito: la identidad de lo que viaja. Es el SSCC abierto: un número para cada unidad, como el SSCC de GS1, pero gratuito, opaco, perpetuo y atado al objeto; y si la unidad ya tiene SSCC, el registro lo acepta como clave de entrada y le añade cuerpo e historia (§1, §3.3). Da a cada unidad física que se mueve por una cadena logística (un paquete, un palé cargado, una saca, una cubeta, una caja) un identificador mundial único, opaco y gratuito, la ata al objeto mediante una firma física (su “biometría”) y conserva una historia sellada de lo que le pasa. El paquete es el primer perfil de unidad (§1.1) y el único con captura de estación en esta versión. Nombre del registro: PARCID, dominio parcid.com (DECISIÓN 10, decidida el 23/10/2026); el identificador conserva el prefijo PID (léase Physical unit ID). Cualquiera puede registrar una unidad y resolver un ID sin pedir permiso. Las afirmaciones sobre la identidad (avistamientos, prueba de vida, comparación, resguardo) son servicios de pago que prestan operadores como Know Your Parcel.
Esta especificación se publica con licencia CC BY 4.0. El código de referencia (ref/) está en CC0 1.0. Los datos de resolución pública se ofrecen en CC0 1.0. El resolutor, la API y la herramienta de verificación se publican en Apache-2.0 en un repositorio público, para que cualquiera levante un espejo (DECISIÓN 6, decidida); los modelos de comparación del operador quedan fuera del código abierto: el formato de la firma es público, el motor es del operador. Versión en inglés: SPEC.en.md. Estado: borrador v0.2. La implementación de referencia del registro (registry/) cumple esta versión y se prueba de punta a punta (registry/tests/run-all.sh). Los puntos marcados DECISIÓN pendientes están en §15.
0. Principios
- La identidad es gratis; las afirmaciones se pagan. Alta y resolución no cuestan nada a nadie. “Es el mismo paquete”, “esta captura es real y de ahora”, “así estaba a tal hora” son productos.
- Cero datos personales en el registro global. El registro es de objetos, no de personas. El contenido de la etiqueta (nombres, direcciones, teléfonos) no entra nunca en la base; la evidencia en bruto que pudiera contenerlo se guarda cifrada y solo la lee quien tiene la llave del paquete.
- Abierto y verificable sin el registro. Formato público, resolución pública, exportación completa, actas verificables con herramientas estándar (SHA-256, Ed25519, RFC 3161). Si el operador desapareciera, cada acta seguiría siendo verificable.
- Confianza calibrada, no unicidad absoluta. Ningún rasgo físico es único como una huella dactilar. Se promete una confianza medida y explicada, con nivel según cómo se capturó.
- El operador se comporta como si el registro ya fuera de una entidad neutral: marca sin “KYP”, especificación abierta, compromiso de cesión del espacio de nombres (§13), contabilidad separada.
- La unidad es un estado, no un objeto. Un paquete nace cuando alguien cierra una caja, cambia cuando se abre o se consolida y muere en la entrega; nadie es su dueño permanente y nadie le emite identidad. Entra en el registro lo que cumple las cuatro condiciones: (a) no tiene emisor permanente ni número de serie propio grabado; (b) cambia de estado durante el viaje; (c) cambia de custodia entre partes distintas; (d) vive poco, de horas a meses. Lo que falla la condición (a) ya tiene registro y no recibe ID: el contenedor marítimo (código BIC, ISO 6346) y el vehículo (VIN, matrícula) son contexto de las unidades, como enlace o como custodio (§1.1, §5.2). Este registro no es un registro de objetos ni de activos: GS1 dice qué es una cosa y en qué envío va; este registro dice cuál es físicamente y si sigue siendo la misma.
1. Cómo se hace única una unidad (las tres vías)
1.1 Qué es una unidad (perfiles)
Una unidad es una cosa física que viaja como una sola pieza bajo una custodia (en castellano, un bulto). Cada unidad se registra con un perfil (unit_profile) que fija qué rasgos de la firma física se esperan y dónde se capturan. Perfiles de esta versión:
| Perfil | Qué es | Alcance en v0.1 |
|---|---|---|
parcel |
caja, sobre, bidón o bulto cerrado que viaja como una pieza | núcleo: perfil completo, niveles 0–3, captura de estación |
pallet_load |
un palé cargado: la carga estibada y enfardada, no el palé de madera. El SSCC etiqueta el envío, pero nada ata esa etiqueta a esa carga física; la consolidación N→1 (§5.3) la modela tal cual | núcleo: niveles 0–2; rasgos propios en v0.2 |
container_load |
la carga estibada dentro de un contenedor. El contenedor (código BIC) y el precinto (ISO 17712) son enlaces (§5.2), no la unidad | niveles 0–2; rasgos propios en v0.2 |
tote |
cubeta, jaula, roll, barril, bombona: envases reutilizables, con o sin GRAI | niveles 0–2; rasgos propios en v0.2 |
bag |
saca postal, bolsa de valija, equipaje (la etiqueta IATA bag_tag es por viaje, no por maleta) |
niveles 0–2; rasgos propios en v0.2 |
crate |
caja de transporte de arte, instrumentos o maquinaria | niveles 0–2; rasgos propios en v0.2 |
Lo que no es una unidad: el contenedor marítimo y el vehículo. Tienen emisor permanente y serie grabada (BIC con dígito de control; VIN y matrícula con registros estatales), y la matrícula identifica a una persona. Son el siguiente nivel de la misma jerarquía: el contenedor es a la carga lo que el palé es al paquete, y el registro identifica la carga en cada nivel, no el recipiente permanente. El vehículo entra solo como custodio (holder_class: vehicle, §5.2) con referencia opaca, nunca la matrícula en claro.
1.2 Las tres vías
Una persona tiene un documento emitido por un tercero y una biometría. Una unidad no trae documento: se lo da el registro en el alta, y se ata al objeto por tres vías a la vez. (La tabla describe el perfil parcel; en los demás perfiles cambian los rasgos de la firma, no las vías.) Ninguna sola basta; las tres juntas son más de lo que tiene el KYC de personas, porque la historia sellada no existe allí.
| Vía | Qué es | Quién la garantiza | Qué pasa si falla |
|---|---|---|---|
| Documento | el PARCID impreso o pegado al paquete (QR, texto en la etiqueta) | el registro: aleatorio, con control, sin colisiones | se pierde o se copia → entran las otras dos |
| Biometría | la firma física (§6): geometría, peso, forma 3D, textura del cartón en parches fijos, cinta, disposición de la etiqueta, aspecto, daños | el acta de alta sellada con firma del dispositivo y sello de tiempo (“certificado de nacimiento”) | etiqueta copiada en otra caja → la firma no coincide |
| Historia | la cadena sellada de avistamientos y el linaje (abrir, consolidar, reembalar = nuevo ID enlazado) | cada acta de avistamiento, encadenada | dos cajas idénticas no pueden tener la misma historia |
1.3 Qué hay que subir para crear un ID (perfil parcel)
El registro acepta desde casi nada hasta una captura de estación. Cuanto más se sube, más alto es el nivel de garantía (§7) y más capaz es la comparación posterior.
| Nivel | Qué se sube | Qué distingue |
|---|---|---|
| Mínimo (L0) | nada obligatorio salvo la clase de captura. Opcional: medidas, peso | solo el documento: el ID. Sirve para enlazar referencias, no para reconocer el paquete |
| Básico (L1) | medidas (mm) y peso (g) con tolerancias, 2 fotos (cenital y una perspectiva con las 6 caras implícitas) o sus hashes, firma Ed25519 del cliente | separa el paquete de la mayoría de los que viajan el mismo día por la misma ruta (geometría + peso + aspecto) |
| Recomendado (L2) | lo anterior + parches de textura del cartón en 4 posiciones fijas respecto a la esquina de origen (§6.4), trazado de la cinta, disposición de la etiqueta (sin su contenido), atestación del dispositivo, nonce de frescura | separa dos cajas idénticas de fábrica recién cerradas: la textura de fibras y manchas y el trazado de la cinta son distintos en cada caja |
| Estación (L3) | lo anterior capturado en geometría normalizada (origen 0·0·0, reglas, cámaras fijas, luz propia) + nube de puntos + báscula calibrada + coherencia multisensor | comparable entre máquinas y a lo largo del tiempo; es la “cabina de fotos de pasaporte” del paquete |
Lo que no hace único a un paquete y no se debe usar como identidad: el número de seguimiento (lo pone el transportista, se reutiliza y se copia), el SSCC (identifica la unidad logística de un envío, no el objeto físico ni su estado; GS1 permite reutilizarlo pasado un plazo, lleva el prefijo de empresa del emisor y es enumerable), el contenido de la etiqueta (datos personales; además se reimprime). Estos tres se enlazan al PARCID (§5.2), no lo sustituyen.
El registro no compite con el SSCC: es una capa sobre él. Si la unidad trae SSCC, el registro lo acepta como clave de entrada de primera clase: se da de alta con links[kind=sscc] y se resuelve por /00/{SSCC} (§3.3), y el registro le añade lo que el SSCC no tiene, la firma física y la historia sellada. El PID queda por debajo como clave perpetua, porque el SSCC se reutiliza, identifica al emisor y se puede enumerar. Si la unidad no trae nada, el registro le da un PID gratis.
2. El identificador
2.1 Forma canónica
PID-1-7QC3-Z79D-G6RZ-8E
│ │ └──────┬──────┘ └┬┘
│ │ │ └ 2 símbolos de control (10 bits)
│ │ └ 12 símbolos de carga aleatoria (60 bits) en 3 grupos de 4
│ └ versión del esquema (1 símbolo)
└ prefijo fijo
- Alfabeto: Base32 Crockford,
0123456789ABCDEFGHJKMNPQRSTVWXYZ(sin I, L, O, U). Al leer se aceptan minúsculas,I/L→1,O→0, y se ignoran guiones y espacios. La forma canónica es mayúsculas con guiones. - Longitud: 23 caracteres con guiones; 18 sin ellos (
PID+ 15 símbolos). Cabe en una línea de etiqueta, se dicta por teléfono y se teclea sin ambigüedad. - Carga: 60 bits aleatorios del generador criptográfico del registro (o del bloque reservado, §2.4). Ninguna información dentro: ni fecha, ni emisor, ni secuencia. Espacio: 2^60 ≈ 1,15 × 10^18.
- Versión: un símbolo.
1es esta especificación. Permite alargar la carga o cambiar el control sin romper lo emitido.
2.2 Por qué 60 bits y no más
- La unicidad no la da la aleatoriedad, la da el registro: el ID se emite centralmente (o desde un bloque reservado) y la base rechaza duplicados. No se necesita la propiedad “sin colisiones por cumpleaños”; se necesita que el espacio sea disperso para que nadie enumere la base ni adivine IDs.
- Dispersión: con 10^11 IDs registrados (un año del comercio mundial de paquetes), un ID al azar acierta uno existente con probabilidad 10^11 / 1,15 × 10^18 ≈ 9 × 10^-8. Con límites de consulta (§10) la enumeración es impracticable. Con 10^14 IDs (siglos de uso) la densidad sube a 10^-4; antes de llegar ahí se emite la versión
2con carga de 80 bits. - 60 bits son 12 símbolos: tres grupos de cuatro, legibles y dictables. El borrador del documento 04 proponía 70 bits (
KYP-1-7XBD-04QM-3F7K-9H, 14 símbolos): con la misma longitud, esta especificación dedica 12 símbolos a la carga y los 2 últimos al control. Mantener 70 bits de carga obligaría a alargar el ID a 16 símbolos para conservar el control. DECISIÓN 1 (decidida): 60 bits + 2 símbolos de control con 23 caracteres.
2.3 Control (sistema puro ISO 7064 “MOD 1021-32”)
- Entrada: los 13 símbolos versión + carga, como valores 0–31.
P = Σ v_i · 32^(12−i) mod 1021. - Control:
C = (1 − 1024 · P) mod 1021(y como 1024 ≡ 3,C = (1 − 3P) mod 1021), codificado en dos símbolos:C div 32,C mod 32. SiempreC ≤ 1020 < 1024, así que cabe. - Verificación: la misma suma sobre los 15 símbolos (versión + carga + control) debe ser ≡ 1 (mod 1021).
- Propiedades (1021 es primo, 32 es invertible módulo 1021): detecta todas las sustituciones de un símbolo, todas las transposiciones de símbolos adyacentes, y el 99,9 % de los errores aleatorios dobles (1 de cada 1021 pasa). Comprobado con
ref/parcid.py: 0 de 139.500 sustituciones y 0 de 4.068 transposiciones no detectadas; 30 de 20.000 errores dobles aleatorios no detectados (0,15 %). - Implementación de referencia:
ref/parcid.py(generar, normalizar, validar; sin dependencias). Ejemplos válidos:PID-1-B92B-YPQ0-8303-JW,PID-1-91QW-5PA4-R8CT-KZ,PID-1-5GP2-J3D9-G4WF-RE,PID-1-Y51Z-HM66-Q1YQ-FE,PID-1-7QC3-Z79D-G6RZ-8E,PID-1-7G61-S4XF-X5P2-HA.
2.4 Emisión
- En línea: el registro genera la carga, comprueba que no existe y la inserta en una transacción; si colisiona (probabilidad ≈ n/2^60), repite.
- Fuera de línea (estaciones y SDK sin red): el dispositivo pide por adelantado un bloque reservado de hasta 1.000 IDs con
POST /reservations, firmando con su clave Ed25519 la petición{ schema:"parcid.reservation-request/1", public_key, count, requested_at, nonce }(a menos de 5 minutos de la hora del registro). Solo para dispositivos homologados o con clave de desarrollador, con límite diario por dispositivo. El alta con un ID reservado ("pid"enPOST /parcels) exige la firma de ese mismo dispositivo. Los IDs no usados en 90 días se queman: nunca se emiten a otro (DECISIÓN 8, decidida). La reserva es un acta (§7.5). - Un ID nunca se reutiliza, ni siquiera revocado.
3. Resolución pública
3.1 URL
- Forma canónica:
https://{host}/{PID}, p. ej.https://parcid.com/PID-1-7QC3-Z79D-G6RZ-8E. El host del registro esparcid.com(DECISIÓN 10, decidida el 23/10/2026). Si el host cambiara alguna vez, el antiguo redirigiría con 301 para siempre. - Variantes aceptadas (redirigen 301 a la canónica): minúsculas, sin guiones, con
I/L/O. - HTML para navegadores; JSON si
Accept: application/jsono con sufijo.json. Igual contenido. - Metadatos del registro:
https://{host}/.well-known/parcid(versión de la especificación, claves públicas del registro conkid, URL de la exportación, política de límites).
3.2 QR
- Contenido del QR: la URL canónica completa (no solo el ID), para que cualquier lector lo abra. Corrección de errores M. Junto al QR, el ID en texto en una línea.
- Si el QR se imprime en la estación, va en una etiqueta aparte o dentro de la etiqueta de envío existente; nunca tapa los códigos del transportista.
3.3 GS1 Digital Link
- El PARCID no es una clave GS1 y no se pretende que lo sea. El estándar GS1 Digital Link admite pares clave-valor no GS1 en la cadena de consulta, de modo que un enlace de un SSCC puede llevar el ID:
https://{host}/00/{SSCC18}?pid=PID-1-.... - El SSCC es clave de entrada de primera clase.
POST /parcelsconlinks[kind=sscc]registra la unidad bajo su SSCC desde el alta (el registro verifica el dígito de control GS1), y el resolutor aceptaGET /00/{SSCC18}y devuelve los PIDs enlazados a ese SSCC por su titular (§5.2), si existen y si el titular los hizo públicos. Como GS1 permite reutilizar un SSCC pasado un plazo, la respuesta lista todos los PID enlazados con su fecha de enlace, el activo primero; el PID es la clave perpetua, el SSCC la puerta de entrada. - Quién respalda el enlace. Cualquier titular puede enlazar públicamente un SSCC a su unidad, así que
GET /00/{SSCC}devuelve como mucho 20 PIDs y cada uno conclaim:operator(unidad dada de alta por un operador identificado con clave de desarrollador) oself_declared(sin verificar); primero los de operador. - A la inversa, la resolución de un ID lista los SSCC y seguimientos enlazados solo si el titular los publicó; por defecto son privados.
3.4 Respuesta de resolución (lo público)
Lo que cualquiera ve: existe, cuándo se registró, con qué perfil de unidad, clase de captura y nivel de garantía, en qué estado está, si tiene linaje, el último avistamiento verificado (fecha y clase, no lugar exacto), y las huellas que permiten verificar el acta de alta. Nunca: imágenes, medidas, peso, referencias privadas, quién lo registró, direcciones.
{
"pid": "PID-1-7QC3-Z79D-G6RZ-8E",
"spec": "parcid/1",
"status": "active",
"unit_profile": "parcel",
"registered_at": "2026-11-18T09:41:07Z",
"capture_class": "station",
"assurance_level": 3,
"lineage": { "parents": 0, "children": 0 },
"last_sighting": { "at": "2026-11-21T16:02:33Z", "capture_class": "station", "assurance_level": 3, "result": "match" },
"sightings_count": 2,
"enrolment": {
"record_hash": "sha256:4c3f1a…",
"registry_signature_kid": "reg-2026-1",
"timestamp": { "rfc3161": true, "tsa": "tsa.example", "gen_time": "2026-11-18T09:41:09Z" },
"anchor": { "day": "2026-11-18", "merkle_root": "sha256:a91e…", "url": "https://parcid.com/anchors/2026-11-18" }
},
"public_links": [ { "kind": "sscc", "value": "003412345678901238" } ],
"resolved_at": "2026-11-22T10:00:00Z"
}
3.5 Códigos
| HTTP | Significado |
|---|---|
| 200 | existe (cualquier estado, incluido revoked) |
| 301 | forma no canónica → canónica |
| 404 | formato válido, no registrado ({"error":"not_found","valid_format":true}) |
| 422 | formato o control inválidos ({"error":"invalid_id","reason":"check"}) |
| 429 | límite de consultas (§10) |
4. API libre (v1)
Base: https://{host}/api/v1. JSON UTF-8. Sin cuenta para registrar y resolver. Toda llamada queda en el registro de API del propio registro durante 90 días, sin IP y sin llaves (se enmascaran); los cuerpos de respuesta solo se guardan en errores y operaciones que cambian algo.
| Método y ruta | Auth | Qué hace |
|---|---|---|
GET /verify/{PID} |
ninguna | valida formato y control, normaliza, dice si existe y su estado. No cuenta como resolución completa |
GET /parcels/{PID} |
ninguna | resolución pública (§3.4) |
GET /parcels/{PID} |
llave del paquete | resolución completa: acta de alta, firma física, evidencias (URLs firmadas de corta vida), enlaces privados, avistamientos con detalle |
POST /parcels |
ninguna (o clave de desarrollador para subir límites) | alta: crea el ID, devuelve la llave del paquete una sola vez |
POST /parcels/{PID}/links |
llave | añade referencias externas (seguimiento, SSCC, pedido…) con visibilidad |
POST /parcels/{PID}/lineage |
llave de los padres | declara apertura, consolidación, división o reembalaje y crea el hijo |
POST /parcels/{PID}/status |
llave | retired (entregado y cerrado) o lost |
POST /parcels/{PID}/rotate-key |
llave | emite una llave nueva y anula la anterior |
POST /parcels/{PID}/sightings |
llave + servicio de pago del operador | avistamiento con comparación (§8) |
GET /parcels/{PID}/proof |
ninguna | prueba de inclusión de cada acta de la unidad en su árbol diario, con la declaración firmada y las pruebas OpenTimestamps (§7.3) |
POST /reservations |
firma del dispositivo + dispositivo homologado o clave de desarrollador | bloque de hasta 1.000 IDs para un dispositivo sin red (§2.4) |
GET /anchors · GET /anchors/{día} · GET /anchors/{día}.ots |
ninguna | cadena de declaraciones diarias, cada una con su firma y sus pruebas .ots (§7.3) |
GET /export · GET /export/{fichero} |
ninguna | índice de exportaciones diarias y descarga (JSON Lines + gzip, CC0, §12) |
GET /stats |
ninguna | cifras agregadas del registro: unidades por perfil, nivel y mes; actas por tipo; última ancla y exportación; volumen y latencia de 24 h (§10.1) |
GET /health |
ninguna (detalle con el token del operador) | salud para monitores: estado y comprobaciones básicas; con token, también almacén, disco, atraso de anclas y de tareas |
GET /metrics |
token del operador | exposición Prometheus (§10.1) |
POST /beacon |
ninguna | contador de páginas de la web del registro, sin cookies ni IP (§10.1) |
GET /.well-known/parcid · GET /00/{SSCC} · GET /{PID} |
ninguna | metadatos y claves · GS1 Digital Link · resolución corta (§3) |
4.1 La llave del paquete (owner key)
- Formato:
pidk_+ 52 símbolos Crockford (260 bits). Se devuelve una sola vez en la respuesta del alta. El registro guarda soloSHA-256(llave). - Da control total sobre ese paquete y nada más: leerlo entero, enlazar, declarar linaje, cambiar estado, rotar. No identifica a nadie: no hay cuenta detrás obligatoria. Quien la pierde no puede recuperarla (no hay datos personales a los que atar una recuperación); por eso se recomienda guardarla en el sistema del cliente junto al ID, o usar una cuenta de desarrollador (opcional) que custodie las llaves de sus paquetes.
- Se envía en
Authorization: Bearer pidk_…. Nunca en la URL. - Linaje: el hijo nace con llave propia; se devuelve a quien declaró la operación.
4.2 Alta: POST /parcels
Cuerpo multipart/form-data (si se suben archivos) o application/json (si solo se envían hashes y la firma física ya calculada). Campos:
| Campo | Obl. | Contenido |
|---|---|---|
capture_class |
sí | station · mobile_sdk · third_party_device · api |
unit_profile |
no | parcel (por defecto) · pallet_load · container_load · tote · bag · crate (§1.1). En v0.1 solo parcel alcanza el nivel 3 |
captured_at |
sí | ISO 8601 UTC del dispositivo |
dimensions_mm |
no | { "l":..,"w":..,"h":.., "tolerance_mm":.. } |
weight_g |
no | `{ “value”:.., “tolerance_g”:.., “scale_class”:“calibrated |
evidence[] |
no | archivos (JPEG/PNG/WebP ≤ 25 MB, MP4 ≤ 200 MB, PLY/PCD ≤ 50 MB) o { "role":"top","sha256":"…","mime":"image/jpeg","bytes":… } si el cliente los conserva. Roles: top, perspective, side_*, label_layout, pointcloud, video |
signature |
no | firma física ya calculada por el cliente según §6 (la estación y el SDK la calculan; un cliente simple la deja al registro) |
device |
no | { "kid":"…", "public_key":"ed25519:base64", "attestation": {...}, "sequence": 1234, "nonce":"…" }. Si se envía, device_signature y sequence son obligatorios: un dispositivo sin firma no cuenta (nadie puede usar la clave pública ajena de una estación para gastarle el cupo, adelantarle la secuencia o hacer que un acta parezca suya) |
device_signature |
con device |
Ed25519 sobre capture_hash (§7.2) |
links[] |
no | referencias externas con kind, value, visibility |
evidence_retention |
no | hashes_only (por defecto en el nivel gratuito tras 30 días) · store (resguardo de pago del operador) |
consent |
sí | { "no_personal_data": true, "terms_version": "2026-10" }: quien registra declara que no sube datos personales fuera de las imágenes y acepta que las imágenes se guardan cifradas |
Respuesta 201:
{
"pid": "PID-1-7QC3-Z79D-G6RZ-8E",
"owner_key": "pidk_3K9FZX1Q7MWB8Y2RHA0C5TDVNEG4PJ6S9ZX1Q7MWB8Y2RHA0C5TDVN",
"owner_key_notice": "Guárdala ahora. No se vuelve a mostrar y no se puede recuperar.",
"assurance_level": 3,
"resolution_url": "https://parcid.com/PID-1-7QC3-Z79D-G6RZ-8E",
"qr_url": "https://parcid.com/PID-1-7QC3-Z79D-G6RZ-8E/qr.svg",
"enrolment_record": { "...": "acta completa, §7.3" },
"free_tier": { "evidence_kept_until": "2026-12-18T09:41:07Z", "hashes_kept": "forever" }
}
4.3 Verificación: GET /verify/{PID}
{ "input": "pid-1-7qc3-z79d-g6rz-8e", "canonical": "PID-1-7QC3-Z79D-G6RZ-8E", "format": "valid", "check": "valid", "registered": true, "status": "active", "resolution_url": "https://parcid.com/PID-1-7QC3-Z79D-G6RZ-8E" }
Con control erróneo: { "input":"PID-1-7QC3-Z79D-G6RZ-8F", "format":"valid", "check":"invalid", "registered":null } y HTTP 422. Este endpoint es para lectores de etiqueta, formularios y agentes: barato, sin límite estricto y sin contar como resolución.
4.4 Errores
{"error":"code","message":"texto","request_id":"…"} con códigos: invalid_id, invalid_request, not_found, method_not_allowed, unauthorized (llave incorrecta), forbidden (estado no lo permite), payload_too_large, unsupported_media, rate_limited, consent_required, lineage_conflict, paid_service (servicio del operador no contratado), unavailable, server_error.
5. Estructura del registro
5.1 Núcleo inmutable (no cambia nunca)
pid, spec_version, unit_profile, registered_at, capture_class, assurance_level al alta, dimensions_mm y weight_g (inmutables pero privados: solo con llave), signature_hash (SHA-256 de la firma física canónica), evidence[] (rol, mime, bytes, sha256), device.kid, device_signature, record_hash, registry_signature, tsa_token, anchor_day. Es exactamente el contenido del acta de alta (§7.3).
5.2 Enlaces mutables (con historia; nada se borra, se añade)
- Referencias externas:
kind∈tracking,sscc(clave de entrada de primera clase, §1 y §3.3; dígito de control GS1 verificado),sgtin,grai(envase reutilizable GS1),bic(contenedor, ISO 6346: 4 letras + 6 dígitos + dígito de control, que el registro verifica),seal(precinto ISO 17712),bag_tag(etiqueta de equipaje IATA, 10 dígitos),order,invoice,customs_declaration,custom;value;visibility∈private(por defecto),public;added_at,removed_at. Contenedores, precintos y envases son contexto de la unidad (§0.6), nunca unidades. - Custodia:
holder_class∈sender,warehouse,forwarder,carrier,vehicle,customs,recipient,other;holder_refopaco elegido por el titular (nunca un nombre personal; paravehicle, nunca la matrícula ni el VIN en claro);from,to. - Avistamientos: §8.
- Estado:
active→retired(cerrado por el titular) ·superseded(consumido por linaje) ·lost(declarado por el titular) ·revoked(por el registro, §11). Cada cambio con fecha y actor (owner,registry).
5.3 Linaje
Un paquete es un objeto en un estado. Si se abre, consolida, divide o reembala, nace un ID nuevo enlazado:
| Operación | Padres → hijos | Estado de los padres |
|---|---|---|
open |
1 → 1 (misma caja abierta y vuelta a cerrar) | superseded |
repack |
1 → 1 (otra caja) | superseded |
consolidate |
N → 1 | superseded todos |
split |
1 → N | superseded |
attach |
1 → 1 (el mismo objeto gana una captura de nivel superior, p. ej. móvil → estación) | sigue active; es un avistamiento, no linaje |
Cada operación es un acta (§7) firmada por quien la hace, con los pid de padres e hijos y las huellas de las evidencias. La resolución de un hijo muestra sus padres y la de un padre sus hijos; la historia completa se sigue por el grafo.
6. La firma física
6.1 Formato
Objeto JSON canónico, compacto (objetivo < 300 KB), separado de las evidencias en bruto. Se guarda íntegro en el registro (no contiene datos personales) y se publica su hash en el acta; su contenido lo lee quien tiene la llave y los servicios de comparación.
{
"schema": "parcid.signature/1",
"unit_profile": "parcel",
"capture_class": "station",
"frame": { "origin": "corner_0_0_0", "axes": "l_w_h", "unit": "mm", "calibration": "pns-ruler-60x60/1" },
"features": [
{ "type": "geometry", "model": "geom/1", "value": { "l": 412, "w": 306, "h": 218, "volume_cm3": 27493, "ratios": [1.346, 1.404] },
"tolerance": { "mm": 3 }, "quality": 0.98, "stability": "high" },
{ "type": "weight", "model": "weight/1", "value": { "g": 3840, "tolerance_g": 5, "scale_class": "calibrated" }, "quality": 1.0, "stability": "high" },
{ "type": "shape3d", "model": "pcd-desc/1", "dim": 256, "dtype": "float16", "vector": "base64…", "quality": 0.91, "stability": "medium" },
{ "type": "texture_patch", "model": "paper-fp/1", "patch": "A", "face": "top", "pos_mm": [30, 30], "size_mm": 25, "dim": 512, "dtype": "uint8", "vector": "base64…", "quality": 0.87, "stability": "high" },
{ "type": "texture_patch", "model": "paper-fp/1", "patch": "B", "face": "top", "pos_mm": [382, 30], "size_mm": 25, "dim": 512, "dtype": "uint8", "vector": "base64…", "quality": 0.84, "stability": "high" },
{ "type": "texture_patch", "model": "paper-fp/1", "patch": "C", "face": "front", "pos_mm": [30, 30], "size_mm": 25, "dim": 512, "dtype": "uint8", "vector": "base64…", "quality": 0.80, "stability": "high" },
{ "type": "texture_patch", "model": "paper-fp/1", "patch": "D", "face": "right", "pos_mm": [30, 30], "size_mm": 25, "dim": 512, "dtype": "uint8", "vector": "base64…", "quality": 0.78, "stability": "high" },
{ "type": "tape", "model": "tape-path/1", "value": { "faces": ["top", "bottom"], "polyline_mm": [[0, 153], [412, 153]], "width_mm": 48, "overlaps": 1, "color": "brown" }, "quality": 0.9, "stability": "high_until_opened" },
{ "type": "label_layout", "model": "label-geo/1", "value": { "face": "top", "bbox_mm": [110, 40, 210, 148], "rotation_deg": 0.8, "barcodes": 2, "print_defects": 3 }, "quality": 0.93, "stability": "high_until_relabel", "note": "solo geometría, nunca el contenido" },
{ "type": "appearance", "model": "box-emb/1", "views": ["top", "perspective"], "dim": 512, "dtype": "float16", "vector": "base64…", "quality": 0.9, "stability": "medium" },
{ "type": "damage_map", "model": "damage/1", "value": { "items": [ { "face": "front", "bbox_mm": [300, 150, 360, 210], "kind": "dent", "severity": 0.3 } ] }, "quality": 0.85, "stability": "changes_informatively" }
],
"hash": "sha256:9b2d…"
}
6.2 Reglas
modelversiona el algoritmo que produjo el rasgo; dos rasgos solo se comparan si compartenmodelo hay un adaptador declarado. Nunca se recalcula una firma antigua: se añade una nueva captura (avistamiento) con los modelos nuevos.quality∈ [0,1] es la calidad de la captura del rasgo (enfoque, oclusión, cobertura).stabilitydescribe cómo envejece el rasgo.- Fusión ponderada: la comparación (§8) puntúa por rasgo y luego globalmente; el peso de cada rasgo depende de su
qualityen ambas capturas, de sustabilityy del perfil de umbrales calibrado para el par de clases de captura. - Cero datos personales:
label_layoutguarda caja, rotación, número de códigos y defectos de impresión. Está prohibido incluir texto OCR, códigos de barras decodificados o recortes de etiqueta en la firma. El OCR, si se hace, vive en la cuenta del cliente del operador, no en el registro.
6.3 Rasgos por clase de captura (perfil parcel)
| Rasgo | station | mobile_sdk | third_party_device | api |
|---|---|---|---|---|
| geometry | medida (nube de puntos) | estimada (reglas/ARKit/ARCore) | según dispositivo | declarada |
| weight | báscula calibrada | no (o declarado) | según dispositivo | declarada |
| shape3d | sí | parcial | según dispositivo | no |
| texture_patch | 4 parches, 2 cámaras | 2–4 parches, menor resolución | según dispositivo | no |
| tape, label_layout, appearance, damage_map | sí | sí | según dispositivo | no |
6.4 Posiciones fijas de los parches de textura
Los parches se toman en coordenadas fijas respecto a la esquina de origen (0·0·0: la que apoya en el ángulo de las reglas en la estación; en móvil, la esquina inferior izquierda de la cara superior vista desde la etiqueta). Parche de 25 × 25 mm: A cara superior a (30, 30) mm; B cara superior a (L − 30, 30); C cara frontal a (30, 30); D cara derecha a (30, 30). Si una posición cae sobre etiqueta o cinta, se desplaza al punto libre más cercano y se anota pos_mm real. Así la misma caja produce parches comparables en capturas distintas y la textura separa cajas idénticas de fábrica.
6.5 Perfiles de unidad
La firma física lleva unit_profile junto a capture_class. El perfil parcel es el definido en §6.1–6.4. Para pallet_load, container_load, tote, bag y crate, la v0.2 admite los rasgos genéricos (geometry, weight, appearance, damage_map, label_layout) y deja para la v0.3 los rasgos propios y las posiciones fijas de los parches: film de enfardado y patrón de estiba en el palé cargado; veta, clavos y sellos de bloque en envases de madera; cremalleras, asas y textura de tela en el equipaje. Hasta entonces esos perfiles no pasan del nivel 2 (DECISIÓN 9).
7. Actas: formato, hash, firmas, sello
7.1 Canonicalización y hash
- JSON canónico según RFC 8785 (JCS): claves ordenadas, sin espacios, números en formato ES6, UTF-8. Los binarios van en base64; las huellas como
sha256:+ hex minúsculas. Restricción de números para evitar ambigüedades entre implementaciones: solo enteros hasta 2^53 o decimales menores de 10^9 con un máximo de 6 decimales; cualquier otro número se rechaza. record_hash = SHA-256(bytes canónicos del acta sin los campos "signatures" ni "timestamp").
7.2 Firmas
- Del dispositivo o cliente (opcional, sube el nivel): Ed25519 sobre
capture_hash= SHA-256 del subobjeto canónico que el dispositivo controla (pid,captured_at,capture_class,device,measurements,signature_hash,evidence,liveness). El dispositivo no puede firmar el acta completa porque no conocereceived_atni el nivel asignado; como el acta incluyecapture_hash, la firma del registro sobrerecord_hashcubre también lo firmado por el dispositivo. La firma se calcula sobre los 32 bytes de la huella.kid=sha256:de la clave pública, 16 primeros bytes en hex. Las claves de estación se registran en el alta del dispositivo con el operador; las de SDK, por instalación (atestación Play Integrity / App Attest / WebAuthn cuando existe). - Del registro (siempre): Ed25519 con la clave publicada en
/.well-known/parcid(kidtiporeg-2026-1), sobrerecord_hash. Prueba que el registro aceptó el acta tal cual. - Secuencia y cadena:
device.sequencemonotónica por clave;prev_record_hashencadena las actas de un mismo dispositivo. Rechazo de secuencias repetidas o retrocesos (cuarentena).
7.3 Libro de actas, ancla diaria y Bitcoin (DECISIÓN 3)
- Libro de actas (solo se añade). Toda acta sellada —alta, linaje, avistamiento, cambio de estado, rotación de llave, reserva— entra en el libro con un número de orden global
seqy el día del libro en que se selló. El día es el día UTC del sellado, salvo que ese día ya esté cerrado, en cuyo caso es el primer día abierto: ningún acta puede caer en un día ya anclado (el sellado toma un cerrojo compartido del libro y el cierre de un día, uno exclusivo). - Árbol del día (RFC 6962, SHA-256). Las hojas son los 32 bytes de cada
record_hashdel día en orden deseq.hoja = SHA-256(0x00 ‖ record_hash),nodo = SHA-256(0x01 ‖ izquierda ‖ derecha), árbol vacío =SHA-256(""). La prueba de inclusión se verifica con el algoritmo de RFC 9162 §2.1.3.2. - Declaración diaria encadenada (
parcid.anchor/1), JSON canónico RFC 8785:{ schema, registry, day, tree:"rfc6962-sha256", leaf:"record_hash", leaf_count, first_seq, last_seq, merkle_root, prev_statement_hash, registry_kid }.statement_hash = SHA-256(canónico); el registro firma esos 32 bytes con Ed25519. Cada declaración lleva la huella de la del día anterior: los días sin actas también se anclan (árbol vacío) y un hueco en la cadena es, por sí solo, una alarma. - Bitcoin con OpenTimestamps. El
statement_hashde cada día se envía a varios calendarios públicos (por defectoa.pool.opentimestamps.org,b.pool.opentimestamps.org,a.pool.eternitywall.com) y se guarda una prueba.otsestándar por calendario; cuando el calendario la confirma en un bloque, la prueba se completa con la rama hasta la cabecera de Bitcoin. Gratis, sin tokens, sin carteras. Cualquiera verifica con el cliente oficial (ots verify) o contools/verify: el mensaje en la atestación es la raíz Merkle del bloque. - Confirmado en Bitcoin significa que el registro comprobó, en un explorador de bloques configurado, que la raíz Merkle del bloque es el mensaje de la atestación; la palabra del calendario no basta. Las URIs de calendario que se siguen están en una lista cerrada, sin redirecciones y con tope de tamaño.
- Prueba por unidad.
GET /parcels/{PID}/proofdevuelve, por cada acta de la unidad:record_hash, día,leaf_index,tree_size,audit_path,merkle_root, la declaración con su firma y sus pruebas.ots. Con eso y la clave pública de/.well-known/parcidse verifica todo sin consultar la base del registro. - RFC 3161 (opcional):
timestamp.rfc3161.token= TimeStampToken DER en base64 sobrerecord_hash, contsaygen_time, de una TSA cualificada eIDAS para actas de operador que necesiten valor probatorio en la UE. No cambia el nivel de garantía: el tiempo lo fija el ancla diaria. - Nada de NFT ni de tokens con dueño. En cadena solo van huellas; la llave del paquete queda fuera de cadena. DECISIÓN 12 (opcional, pendiente): segunda capa en una L2 de Ethereum publicando por lotes el par
PID + record_hash, para consulta en cadena sin descargar la exportación.
7.4 Ejemplo de acta de alta (enrolment)
{
"schema": "parcid.record/1",
"type": "enrolment",
"pid": "PID-1-7QC3-Z79D-G6RZ-8E",
"captured_at": "2026-11-18T09:40:58Z",
"received_at": "2026-11-18T09:41:07Z",
"capture_class": "station",
"assurance_level": 3,
"device": { "kid": "sha256:7f3a9c1e2b4d6f8091a2b3c4d5e6f708", "class": "station", "model": "packnsign-60", "sequence": 18233, "prev_record_hash": "sha256:11d0…", "attestation": "operator_registered", "nonce": "b64:Q3Z…" },
"measurements": { "dimensions_mm": { "l": 412, "w": 306, "h": 218, "tolerance_mm": 3 }, "weight_g": { "value": 3840, "tolerance_g": 5, "scale_class": "calibrated" } },
"signature_hash": "sha256:9b2d…",
"evidence": [
{ "role": "top", "mime": "image/jpeg", "bytes": 2418833, "sha256": "sha256:0c7e…" },
{ "role": "perspective", "mime": "image/jpeg", "bytes": 2601120, "sha256": "sha256:51aa…" },
{ "role": "pointcloud", "mime": "application/vnd.ply", "bytes": 8120044, "sha256": "sha256:e3f9…" }
],
"liveness": { "level": "high", "signals": ["device_attested", "scene_physics_ok", "nonce_in_frame", "multisensor_coherent"] },
"links": [ { "kind": "sscc", "value": "003412345678901238", "visibility": "public" } ],
"consent": { "no_personal_data": true, "terms_version": "2026-10" },
"record_hash": "sha256:4c3f1a…",
"signatures": [
{ "by": "device", "alg": "Ed25519", "kid": "sha256:7f3a9c1e2b4d6f8091a2b3c4d5e6f708", "sig": "base64…" },
{ "by": "registry", "alg": "Ed25519", "kid": "reg-2026-1", "sig": "base64…" }
],
"timestamp": { "rfc3161": { "tsa": "tsa.example", "gen_time": "2026-11-18T09:41:09Z", "token": "base64…" }, "anchor": { "day": "2026-11-18", "merkle_root": "sha256:a91e…", "proof_url": "https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/proof" } }
}
(Las firmas y tokens del ejemplo son ilustrativos; los hashes se abrevian.)
7.5 Actas de cambio
Todo cambio de una unidad es un acta firmada y entra en el libro, encadenada con el acta de alta (unit_record_hash):
status:{ schema, type:"status", pid, spec, at, unit_record_hash, from, to, actor:"owner"|"registry", reason_code?, registry }. La emiten el titular (retired,lost), el linaje (superseded) y el registro (revoked, §11).rotation:{ …, type:"rotation", owner_key_commitment }al rotar la llave del paquete.owner_key_commitment = "sha256:" + hex(SHA-256("parcid-owner-key-v1|" + hex(SHA-256(llave)))): no revela nada de una llave de 260 bits y permite a la auditoría comprobar que la llave vigente es la de la última rotación sellada (o la del alta, que lleva el mismo campo).reservation:{ schema, type:"reservation", reservation_id, device_kid, count, pids_sha256, expires_at, request_hash, at, registry }.pids_sha256es la huella de la lista de IDs reservados (uno por línea); la lista solo la recibe el dispositivo.
7.6 Lo que el acta afirma y lo que no
capture_class_verified:truesolo si la clase de captura la respalda un dispositivo homologado de esa clase que firmó (o si esapi, que es declarativa). Una capturastationsin estación homologada queda como declarada.device.attestation:operator_registeredsolo con firma válida de un dispositivo homologado; si no,unverified.owner_key_commitment: ver §7.5.timestampno forma parte de la huella: lo que fija el tiempo es la prueba de/proof(día del libro, árbol y ancla).
8. Avistamientos (sightings)
Un avistamiento es una nueva captura del mismo paquete reclamando un ID, con el resultado de la comparación 1:1 contra la firma del alta (o la del último avistamiento aceptado) y una explicación. Es un acta (§7) de tipo sighting. La parte pública (fecha, clase, nivel, resultado) aparece en la resolución; el detalle solo con la llave.
{
"schema": "parcid.record/1",
"type": "sighting",
"pid": "PID-1-7QC3-Z79D-G6RZ-8E",
"captured_at": "2026-11-21T16:02:20Z",
"capture_class": "station",
"assurance_level": 3,
"device": { "kid": "sha256:2a…", "class": "station", "model": "packnsign-60", "sequence": 902 },
"compared_to": { "record_hash": "sha256:4c3f1a…", "signature_hash": "sha256:9b2d…" },
"match": {
"profile": "station-station/v1",
"score": 0.94,
"decision": "match",
"thresholds": { "match": 0.85, "review": 0.65 },
"per_feature": [
{ "type": "geometry", "score": 0.99, "note": "±2 mm" },
{ "type": "weight", "score": 0.98, "note": "−6 g" },
{ "type": "texture_patch", "patch": "A", "score": 0.93 },
{ "type": "texture_patch", "patch": "B", "score": 0.91 },
{ "type": "tape", "score": 0.97 },
{ "type": "damage_map", "score": 0.70, "note": "abolladura nueva en cara frontal, severidad 0,3" }
],
"explanation": "Misma geometría y peso; la textura de los parches A y B coincide; la cinta es la misma; hay un daño nuevo en la cara frontal."
},
"liveness": { "level": "high", "signals": ["device_attested", "scene_physics_ok", "nonce_in_frame", "multisensor_coherent"] },
"custody": { "holder_class": "warehouse", "holder_ref": "opaque:mde-vlc-01" },
"record_hash": "sha256:77be…",
"signatures": [ { "by": "device", "alg": "Ed25519", "kid": "sha256:2a…", "sig": "base64…" }, { "by": "registry", "alg": "Ed25519", "kid": "reg-2026-1", "sig": "base64…" } ],
"timestamp": { "rfc3161": { "tsa": "tsa.example", "gen_time": "2026-11-21T16:02:35Z", "token": "base64…" } }
}
decision∈match,review,no_match. Unno_matchtambién se registra (es información: alguien presentó este ID con otra caja).- Los avistamientos de terceros sin captura (eventos del transportista vía el servicio de seguimiento del operador) se registran como
sightingconcapture_class: "api",assurance_level: 0y sinmatch; informan de la ruta, no de la identidad.
9. Niveles de garantía (assurance level)
| Nivel | Nombre | Requisitos | Cómo se muestra |
|---|---|---|---|
| 0 | unverified |
alta por API sin firma de dispositivo ni evidencias verificadas | “Registrado (sin verificar)” |
| 1 | signed |
firma Ed25519 de la clave del cliente, secuencia válida, al menos 2 evidencias con hash, medidas o peso | “Firmado” |
| 2 | attested |
lo anterior + atestación del dispositivo (SDK, cuando el registro verifique Play Integrity / App Attest) o dispositivo dado de alta en la lista de dispositivos de confianza del registro + nonce de frescura + firma física con parches de textura | “Atestado” |
| 3 | station |
lo anterior en estación registrada con geometría normalizada, báscula calibrada, nube de puntos y coherencia multisensor (el tiempo lo fija el ancla diaria; el sello RFC 3161 es opcional) | “Estación” |
El nivel es el del alta; cada avistamiento lleva el suyo. Un paquete registrado en nivel 1 que pasa por una estación obtiene un avistamiento de nivel 3 (attach), y la resolución muestra “mejor nivel alcanzado: 3”.
10. Nivel gratuito, límites y abuso
| Concepto | Valor (DECISIÓN 4, decidida; se revisa con datos de uso) |
|---|---|
| Altas sin clave de desarrollador | 100 / mes por dirección IP y por clave de dispositivo |
| Altas con clave de desarrollador gratuita (correo verificado) | 1.000 / mes |
| Resolución pública | 60 / min por IP; 600 / min con clave |
GET /verify |
600 / min por IP |
| Evidencias en bruto del nivel gratuito | 30 días cifradas (XChaCha20-Poly1305 por trozos, clave por paquete envuelta con la clave maestra del registro); después solo hashes. El resguardo largo es servicio del operador |
| Firma física y acta | se conservan indefinidamente |
| Tamaño por alta | 25 MB por imagen, 200 MB vídeo, 50 MB nube de puntos; 300 MB en total |
- Las altas sin firma de dispositivo quedan en nivel 0 y cuentan contra límites más estrictos.
- Reputación de origen: cada clave de dispositivo, clave de desarrollador e IP acumula una puntuación; muchas altas huérfanas (sin avistamientos ni enlaces en 90 días) o incoherentes bajan el nivel máximo que puede obtener esa fuente. Pública en agregado, no por fuente.
- Las cuotas de pago del operador (mínimo 300 €/mes por cliente) se aplican a avistamientos con comparación, prueba de vida, comparación 1:1 y 1:N y resguardo, nunca a la alta ni a la resolución.
10.0 Abuso: lo que el registro impide
- Disco: sin clave de desarrollador ni dispositivo homologado no se guarda vídeo ni nube de puntos en bruto (solo sus huellas), y hay un tope de 1 GB al mes por IP y 5 GB al día en total; por debajo de 10 GB libres no se guarda ningún bruto (507). IPv6 cuenta por /64.
- Imágenes: se leen las dimensiones de la cabecera antes de decodificar; más de 40 megapíxeles, 413.
- Libro: cada unidad admite como mucho 100 operaciones del titular y 5 rotaciones de llave al día;
/proofse pagina de 100 en 100; el árbol diario se construye en flujo, con memoria constante. - Reservas: la misma petición firmada no se puede repetir; tope diario por dispositivo y por clave de desarrollador.
- Avistamientos: solo los registra un operador con el servicio contratado (clave de desarrollador con avistamientos; la llave de la unidad va en
X-Parcid-Owner-Key). - Contador de la web: solo del mismo origen, con tope de filas distintas por día.
- IP real: la cabecera del proxy solo se cree si la conexión llega de un proxy de confianza.
- Unidad revocada: no sirve sus evidencias; la revocación por contenido ilegal destruye el bruto en el acto.
10.1 Medición sin datos personales
- Técnica: recuento por minuto, ruta, método y código HTTP, con suma, máximo e histograma acumulado de latencia (≤10, 50, 100, 250, 500, 1000, 5000 ms) y bytes servidos. Se conserva 14 días por minuto.
- Negocio y web: eventos por día con dos dimensiones: altas por clase y perfil, niveles, resoluciones, avistamientos, reservas, cambios de estado, límites alcanzados, errores, tareas programadas, páginas vistas por ruta e idioma y dominio de origen. Las rutas que llevan un PID se cuentan juntas, nunca por ID.
- Nunca se guarda ni se cuenta nada por persona: ni IP (los límites usan un HMAC con sal del registro en contadores que caducan a los 40 días; las unidades no guardan nada de quien las registró), ni cookies, ni huella del navegador. La web respeta
Do Not TrackyGlobal Privacy Controly no envía nada en ese caso. - Exposición pública agregada en
GET /stats; exposición completa para el operador enGET /metrics(Prometheus, con token).
11. Revocación y gobernanza del dato
- El registro solo revoca (
revoked) por abuso demostrado (altas masivas basura, contenido ilegal en evidencias, suplantación de dispositivo). La revocación es pública, motivada por código (abuse,illegal_content,device_compromised) y conserva el acta: un ID revocado sigue resolviendo con su estado. - El titular puede pedir el borrado de las evidencias en bruto en cualquier momento (derecho sobre imágenes que pudieran contener datos personales). Las huellas y la firma física no se borran: no son datos personales y sostienen la verificabilidad de terceros.
- No existe “corrección” de un acta: se añade otra que la enmienda y la antigua queda
superseded_by.
12. Exportación, verificación independiente y permanencia
- Exportación diaria completa (
parcid.export/1, JSON Lines + gzip, CC0) enGET /export/{fichero}, con índice enGET /export. Líneas, en este orden:export_header(registro, día, licencia, claves públicas) · unaunitpor unidad (solo lo público:pid_tag,pid_salt, estado, perfil, clase, niveles,record_hash, firma del registro, día del ancla, linaje porpid_tagy enlaces públicos) · unarecordpor entrada del libro (seq, día,record_hash, tipo,pid_tagde su unidad) · unaanchorpor día (declaración, firma y pruebas.ots) ·export_manifest(recuentos,content_sha256de todas las líneas anteriores,manifest_hashy firma Ed25519 del registro). Con un solo fichero se recalculan todas las raíces diarias, la cadena de declaraciones y todas las firmas (tools/verify/cli.mjs export). - Exportación frente a enumeración (DECISIÓN 7, decidida el 23/10/2026; formato corregido tras la revisión de seguridad). La exportación nunca lleva el PID en claro. Cada unidad lleva una sal aleatoria propia de 16 bytes y su etiqueta:
pid_salt(hex) ypid_tag = "sha256:" + hex(SHA-256(bytes(pid_salt) ‖ PID canónico en ASCII)); el linaje se expresa con laspid_tagde padres e hijos. Quien tiene un PID encuentra su unidad calculando una SHA-256 por unidad (tools/verifyfindInExport). Con una huella sin sal, la lista de PIDs se recuperaría por fuerza bruta en cuanto el registro tuviera millones de unidades (2⁶⁰/N intentos por PID); con sal por unidad cada una cuesta 2⁵⁹ intentos de media y no hay atajo. El volumen se publica como cifra agregada (§10.1). - Con el acta de alta, la clave pública del registro y la prueba de inclusión en el ancla, cualquiera verifica sin consultar al registro: recalcular el
record_hashsobre el JSON canónico, comprobar las firmas Ed25519, seguir la ruta Merkle hasta la raíz publicada y verificar la prueba OpenTimestamps contra la cabecera del bloque de Bitcoin (y el token RFC 3161 con la cadena de la TSA, si lo hay). - Permanencia más allá del operador (DECISIÓN 11, decidida el 22/10/2026). La exportación pública diaria (CC0) se publica además en almacenamiento permanente de pago único (Arweave); el código del resolutor, la API y la herramienta de verificación se archivan en Software Heritage; cada versión de esta especificación se deposita en Zenodo con DOI. Con la exportación, la clave pública del registro y las pruebas de Bitcoin, cualquiera reconstruye el registro público sin el operador.
- Resguardo de la base: cada día, volcado lógico consistente, comprimido y cifrado por trozos (XChaCha20-Poly1305) con una clave de un solo uso sellada para la clave pública de resguardo (X25519). El servidor no tiene la clave privada: quien entre en el servidor no puede leer los resguardos. Rotación 7 diarios, 5 semanales, 12 mensuales, copia a un almacén externo y simulacro de restauración automatizado (
registry/tests/restore-drill.mjs). - Evidencias en bruto: no forman parte de la exportación. El operador ofrece un resguardo perpetuo como servicio de pago a precio de coste, cifrado con la llave del paquete; sin ese servicio rige la caducidad del nivel gratuito (§10).
13. Gobernanza del espacio de nombres
- El operador actual (Know Your Parcel, división de MDE HUB SL) se compromete públicamente a ceder el espacio de nombres
PID, la especificación, el resolutor y la exportación a una entidad sin ánimo de lucro cuando se cumpla una de estas condiciones (DECISIÓN 5, decidida): tres operadores externos emitiendo altas, o un millón de IDs activos, o 24 meses desde el lanzamiento público, o el interruptor de continuidad de abajo. - Interruptor de continuidad (DECISIÓN 11): si el operador deja de publicar el ancla diaria durante 90 días seguidos, cualquier espejo puede continuar el registro a partir de la última exportación y de las claves rotadas publicadas, y esa situación cuenta como condición automática de cesión.
- Mientras tanto: contabilidad separada del coste del registro gratuito, comité técnico abierto para cambios de la especificación (versiones semánticas; los cambios de formato del ID solo con nuevo símbolo de versión), publicación de las claves del registro con fecha de rotación, y código del resolutor, la API y el verificador en Apache-2.0 en repositorio público para que cualquiera levante un espejo (DECISIÓN 6); los modelos de comparación del operador quedan fuera: el formato de la firma es público, el motor es del operador.
14. Compatibilidad con la plataforma del operador
- El registro tiene base de datos propia (migraciones en
registry/sql/), separada de la del operador: así se puede ceder, espejar y auditar entero sin arrastrar datos del operador. El operador es un cliente más de la API. En su plataforma solo guarda elpid(y la llave del paquete, cifrada) junto a sus propios envíos y actas. - El servicio de seguimiento del operador aporta avistamientos de clase
api, nivel 0. - El operador (Know Your Parcel, asociado a GS1/AECOC con prefijo de empresa propio) emite SSCC para las unidades que maneja y los imprime junto al PID en la misma etiqueta. El registro no emite SSCC nunca: GS1 prohíbe compartir prefijos de empresa; el registro solo los acepta como clave de entrada (§1, §3.3).
- “Parcel Match” del operador pasa a ser la comparación 1:1 de §8, calibrada con el piloto (fase 4 de este hilo).
15. Decisiones (resumen)
Decididas por Damián Guerschuny (“todo bien, tira para delante”, 23/10/2026, sobre las propuestas de esta especificación):
Bits: 60 bits de carga + 2 símbolos de control, 23 caracteres (§2.2).Host:parcid.com.Sellado: ancla diaria en Bitcoin con OpenTimestamps; RFC 3161 opcional (§7.3).Límites del nivel gratuito: los de la tabla de §10; se revisan con datos de uso reales.Condición de cesión: la de §13 (tres operadores externos, un millón de IDs activos, 24 meses desde el lanzamiento público o el interruptor de continuidad).Licencias: CC BY 4.0 (especificación), CC0 (referencia, verificador y datos), Apache-2.0 (resolutor y API).Exportación: sin PIDs en claro; cada unidad identificada porSHA-256(sal propia ‖ PID)(§12).Bloques reservados:POST /reservationspara dispositivos homologados o con clave de desarrollador; los IDs caducados se queman (§2.4).Alcance: unidades en tránsito con los perfiles de §1.1; los distintos deparcellimitados al nivel 2 hasta v0.3 (§6.5).Nombre: PARCID; prefijo del identificadorPID.Permanencia: Arweave, Software Heritage, Zenodo, resguardo perpetuo de evidencias a precio de coste, interruptor de 90 días (§12, §13).
Pendiente:
12. Opcional: segunda capa en una L2 de Ethereum con el par PID + record_hash por lotes (§7.3).
16. Historial
- v0.1 · 22/10/2026 · primer borrador completo (identificador, resolución, API libre con llave del paquete, firma física, actas, avistamientos, niveles, límites, gobernanza).
- v0.1 · 22/10/2026 · alcance ampliado a unidades en tránsito: principio 6, perfiles de unidad (§1.1, §6.5),
unit_profileen acta, API y resolución, enlacesbic/seal/grai/bag_tag, custodiovehicle, OpenTimestamps en §7.3, decisiones 9 y 10. - v0.1 · 22/10/2026 · decisiones de Damián: el registro es una capa sobre el SSCC (clave de entrada de primera clase, §1, §3.3, §5.2, §14); DECISIÓN 3 cerrada con ancla en Bitcoin por OpenTimestamps (§7.3); DECISIÓN 6 cerrada con Apache-2.0 para resolutor, API y verificador; DECISIÓN 11 permanencia (Arweave, Software Heritage, Zenodo, resguardo perpetuo, interruptor de 90 días, §12, §13); DECISIÓN 12 nueva (L2 opcional).
- v0.2 · 23/10/2026 · nombre PARCID y host
parcid.com; libro de actas con día de libro y cerrojo de cierre, árbol RFC 6962, declaración diaria encadenada y firmada, OpenTimestamps por calendario y/proof(§7.3); actas de cambiostatus,rotation,reservation(§7.5); medición sin datos personales y rutas/stats,/health,/metrics,/beacon(§4, §10.1); formato de exportaciónparcid.export/1conpid_sha256(§12); resguardo cifrado para clave pública (§12); base de datos propia del registro (§14); decisiones 1, 2, 4, 5, 7, 8, 9 y 10 cerradas (§15). - v0.2 · 23/10/2026 · tras una revisión de seguridad independiente: exportación con sal por unidad (
pid_tag,pid_salt) en lugar desha256(PID); firma y secuencia obligatorias condevice;capture_class_verifiedyowner_key_commitmenten las actas (§7.6);claimen GS1 (§3.3); linaje público solo con recuentos; avistamientos solo de operador; «confirmado en Bitcoin» verificado contra el bloque; límites contra abuso (§10.0).
¿Comentarios? La especificación está abierta al comité técnico: ver Gobernanza. →