SaluData — Arquitectura de Ficha Médica Permisionada

Diagrama SVG (nodos/reglas) + skeletons de interfaces Solidity para la capa on-chain (consentimiento, índice clínico, auditoría, emergencia, directorios, gobernanza).

Diagrama de Arquitectura (SVG)

Regulador (MINSAL) Nodo Ancla Macrorred Establecimiento (Full/Light + FHIR GW) Prestador Privado Servicios Horizontales (DID/VC, KMS, Investigación) Agente Paciente (Wallet)
MINSAL – Nodo Regulatorio Validador / Auditoría / Gobernanza Macrorred 1 Validador Ancla Macrorred 2 Validador Ancla Macrorred 3 Validador Ancla Macrorred 4 Validador Ancla Macrorred 5 Validador Ancla Macrorred 6 Validador Ancla Estab. A Full + FHIR GW Estab. B Light + FHIR GW Estab. C Full + FHIR GW Estab. D Light + FHIR GW Estab. E Full + FHIR GW Estab. F Light + FHIR GW Prestadores Privados Clínicas / Labs / Centros Validadores seleccionados Servicios Horizontales DID/VC • KMS/HSM • Oráculos • Investigación Repo FHIR/IPFS • ZK/Analítica Agente Paciente (Wallet) Light Client + Edge Vault Portal Profesional/Prestador Consent • Visualización • FHIR HL7 FHIR / openEHR Capa On-Chain (Permisionada) IdentityRegistry • ConsentManager • ClinicalDataIndex • EmergencyAccess • DisclosureLog • ProviderDirectory • GovernanceDAO (Metadatos + Hash + Punteros) — PHI cifrada off-chain

Interfaces Solidity – Skeletons

Cada interfaz define estructuras y eventos mínimos para la capa de permisos, identidad, índice clínico, auditoría y gobernanza.
Pragma sugerida: ^0.8.24. Los identificadores tipo DID se representan como bytes32 o string según preferencia. Aquí se usa bytes32 para eficiencia.

1) IIdentityRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IIdentityRegistry {
    enum Role { NONE, REGULATOR, MACRO_NETWORK, PROVIDER, PROFESSIONAL, PATIENT }

    struct Entity {
        bytes32 did;          // Identificador descentralizado
        Role role;            // Rol verificado
        bool active;          // Estado
        bytes32 meta;         // Hash a metadatos off-chain (especialidad, servicio, etc.)
    }

    event Registered(bytes32 indexed did, address indexed account, Role role);
    event Revoked(bytes32 indexed did, address indexed account);
    event MetaUpdated(bytes32 indexed did, bytes32 meta);

    function register(bytes32 did, address account, Role role, bytes32 meta) external;
    function revoke(bytes32 did, address account) external;
    function setMeta(bytes32 did, bytes32 meta) external;

    function resolve(address account) external view returns (Entity memory);
    function resolveByDID(bytes32 did) external view returns (Entity memory);
}
2) IRoleBasedAccessControl.sol (RBAC/ABAC)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IRoleBasedAccessControl {
    event PolicySet(bytes32 indexed policyId, bytes32 exprHash); // Expr ABAC off-chain (CEL/Rego/ZK), hash on-chain
    event RoleGranted(address indexed account, bytes32 indexed role);
    event RoleRevoked(address indexed account, bytes32 indexed role);

    function setPolicy(bytes32 policyId, bytes32 exprHash) external;
    function grantRole(bytes32 role, address account) external;
    function revokeRole(bytes32 role, address account) external;
    function hasRole(bytes32 role, address account) external view returns (bool);
}
3) IConsentManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IConsentManager {
    struct Scope {
        bool readPermitted;        // lectura
        bool writePermitted;       // anotaciones / adjuntos
        uint64 notBefore;          // ventana temporal (epoch)
        uint64 notAfter;           // expiración
        bytes32 resourceType;      // p.ej. Observation, DiagnosticReport, ImagingStudy
        bytes32 resourceTag;       // etiquetas: especialidad, establecimiento, etc.
    }

    event ConsentGranted(bytes32 indexed resourceId, address indexed grantee, Scope scope);
    event ConsentRevoked(bytes32 indexed resourceId, address indexed grantee);
    event ConsentDelegated(address indexed from, address indexed to, Scope scope);

    function grantConsent(bytes32 resourceId, address grantee, Scope calldata scope) external;
    function revokeConsent(bytes32 resourceId, address grantee) external;

    // Patrón por rol / tipo (p.ej. todos los exámenes Endocrino últimos 6 meses)
    event PatternGranted(bytes32 indexed patternId, address indexed granter, bytes32 roleOrType, Scope scope);
    function grantByPattern(bytes32 patternId, bytes32 roleOrType, Scope calldata scope) external;

    // Delegación de consentimiento (paciente -> profesional/establecimiento)
    function delegateConsent(address to, Scope calldata scope) external;

    // Chequeo de acceso: off-chain helper debería usar este veredicto
    function checkAccess(bytes32 resourceId, address requester) external view returns (bool allowed, Scope memory scope);
}
4) IClinicalDataIndex.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IClinicalDataIndex {
    struct Entry {
        bytes32 ownerDid;     // paciente
        bytes32 fhirType;     // Observation / DiagnosticReport / ImagingStudy / etc.
        bytes32 contentHash;  // hash del blob cifrado
        bytes uriEnc;         // puntero cifrado (p.ej. URL + envelope)
        uint64 createdAt;     // timestamp
        bytes32 tags;         // etiquetas resumidas
    }

    event ResourceIndexed(bytes32 indexed resourceId, bytes32 ownerDid, bytes32 fhirType, bytes32 contentHash);
    event PointerRotated(bytes32 indexed resourceId);

    function registerResource(bytes32 resourceId, Entry calldata e) external;
    function updatePointer(bytes32 resourceId, bytes calldata newUriEnc) external;
    function get(bytes32 resourceId) external view returns (Entry memory);
}
5) IEmergencyAccess.sol (Break-glass)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IEmergencyAccess {
    enum ReviewStatus { PENDING, APPROVED, REJECTED }

    event BreakGlassUsed(address indexed requester, bytes32 indexed patientDid, bytes32 reasonCode, uint64 notAfter);
    event BreakGlassReviewed(uint256 indexed ticketId, ReviewStatus status, address reviewer);

    function requestEmergency(bytes32 patientDid, bytes32 resourceScope, bytes32 reasonCode, uint64 ttl) external returns (uint256 ticketId);
    function reviewEmergency(uint256 ticketId, ReviewStatus status) external;
}
6) IDisclosureLog.sol (Auditoría)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IDisclosureLog {
    struct Record { address who; bytes32 what; uint64 whenTs; bytes32 basis; }
    event Disclosed(address indexed who, bytes32 indexed what, bytes32 basis, uint64 whenTs);

    function seal(address who, bytes32 what, bytes32 basis) external;
    function last(address who, bytes32 what) external view returns (Record memory);
}
7) IProviderDirectory.sol / IFacilityDirectory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IProviderDirectory {
    event EndpointUpdated(bytes32 indexed did, bytes32 fhirEndpoint);
    event KeyRotated(bytes32 indexed did, bytes newPubKey);

    function setEndpoint(bytes32 did, bytes32 fhirEndpoint) external;
    function rotateKey(bytes32 did, bytes calldata newPubKey) external;
    function get(bytes32 did) external view returns (bytes32 fhirEndpoint, bytes memory pubKey);
}
8) IReferralCarePlan.sol (Opcional Fase 2)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IReferralCarePlan {
    struct Referral { bytes32 fromDid; bytes32 toDid; bytes32 scope; uint64 expiry; }
    event Referred(bytes32 indexed patientDid, bytes32 indexed toDid, bytes32 scope, uint64 expiry);

    function createReferral(bytes32 patientDid, bytes32 toDid, bytes32 scope, uint64 expiry) external;
}
9) IPrescriptionRegistry.sol (Opcional)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IPrescriptionRegistry {
    struct RxMeta { bytes32 drugCode; uint64 issuedAt; uint64 validUntil; bytes32 prescriberDid; bytes32 tags; }
    event RxIndexed(bytes32 indexed rxId, bytes32 patientDid, bytes32 hash);

    function indexRx(bytes32 rxId, bytes32 patientDid, bytes32 hash, RxMeta calldata meta) external;
    function getRx(bytes32 rxId) external view returns (RxMeta memory meta, bytes32 hash, bytes32 patientDid);
}
10) IResearchAccess.sol (Datasets desidentificados)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IResearchAccess {
    enum License { NONE, INTERNAL, ACADEMIC, COMMERCIAL }
    struct Dataset { bytes32 id; bytes32 hash; License licenseType; bytes32 meta; }

    event DatasetAdded(bytes32 indexed id, License licenseType);
    event AccessGranted(bytes32 indexed id, address indexed to);

    function addDataset(bytes32 id, bytes32 hash, License licenseType, bytes32 meta) external;
    function grant(bytes32 id, address to) external;
    function info(bytes32 id) external view returns (Dataset memory);
}
11) IGovernanceDAO.sol (Consorcio)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IGovernanceDAO {
    enum ProposalType { ADD_VALIDATOR, REMOVE_VALIDATOR, UPDATE_POLICY, PARAM_CHANGE }

    struct Proposal { uint256 id; ProposalType pType; bytes data; uint64 start; uint64 end; bool executed; }

    event Proposed(uint256 indexed id, ProposalType pType);
    event Voted(uint256 indexed id, address indexed voter, bool support, uint256 weight);
    event Executed(uint256 indexed id);

    function propose(ProposalType pType, bytes calldata data) external returns (uint256 id);
    function vote(uint256 id, bool support) external;
    function execute(uint256 id) external;
    function get(uint256 id) external view returns (Proposal memory);
}

Siguientes pasos sugeridos: Generar contratos concretos Esquemar tests Definir mapeo DID↔Cuenta Integrar PRE/KMS Definir eventos FHIR

Enfoque

Sostiene una mirada específica que tiene cada actor sobre un mismo concepto dentro de SaluData.

Enfoque Médico: cómo impacta en la práctica clínica y la atención de pacientes.
Enfoque Usuario/Paciente: qué valor aporta en términos de control, confianza y experiencia personal.
Enfoque Inversionista/Analista: qué ventajas ofrece en escalabilidad, sostenibilidad y oportunidad de negocio.

Término Enfoque Médico Enfoque Usuario/Paciente Enfoque Inversionista/Analista
Ficha Médica Digital Permisionada Acceso seguro y controlado a la historia clínica. Solo yo decido quién ve mis datos. Diferenciada frente a registros centralizados.
Paciente (Usuario Final) Actor activo en el manejo de su información. Soy el dueño de mis datos médicos. Dueño del activo de datos; nuevo mercado.
Consentimiento Granular Acceso a exámenes/periodos específicos. Botón para dar y quitar acceso en cualquier momento. Clave de privacidad y confianza.
MINSAL / Nodo Regulatorio Respaldo y auditoría sanitaria. Confianza institucional. Mitiga riesgo regulatorio.
Macrorred de Salud Continuidad de atención en red pública. Mis datos viajan conmigo entre hospitales. Escalabilidad nacional.
Prestadores Privados Acceso unificado a información del paciente. Evita repetir exámenes y gastos. Modelo B2B (clínicas/labs).
Contratos Inteligentes Registro automático de accesos. Nadie entra sin mi permiso. Eficiencia y reducción de costos.
Clinical Data Index Localiza la versión validada del examen. Sin duplicaciones; todo ordenado. Base de interoperabilidad eficiente.
Emergency Access (Break-Glass) Acceso vital en urgencias con justificación. Me notifican y queda registro. Resuelve casos críticos sin perder confianza.
Wallet del Paciente Autorizaciones directas del paciente. Mi app para consentimientos y alertas. Canal a servicios premium.
Interoperabilidad FHIR / HL7 Sistemas hablan el mismo idioma. Menos trámites y fricción. Facilita expansión internacional.
Gobernanza Permisionada Instituciones validadas y confiables. Red cerrada y más segura. Menor riesgo, crecimiento ordenado.
Auditoría Inmutable Respaldo legal de accesos. Historial de quién accedió y cuándo. Cumplimiento normativo y trazabilidad.
Criptografía Avanzada (PRE/ABE) Acceso fluido sin copias extra. Máxima privacidad y control. Ventaja tecnológica competitiva.