From b95bce2d912c1ba5bbd89aa1366fcb2fc80899ee Mon Sep 17 00:00:00 2001
From: goncalo-frade-iohk <goncalo.frade@iohk.io>
Date: Thu, 30 Jan 2025 12:59:09 +0000
Subject: [PATCH] feat(pollux): pollux will be a plugin architecture from now
 on

---
 EdgeAgentSDK/Domain/Sources/BBs/Pollux.swift  | 92 +++++++++++++++++++
 .../DIDCommAgent+Credentials.swift            | 15 ++-
 .../EdgeAgent/Sources/EdgeAgent.swift         |  3 +
 .../Plugins/DIDCommPlugin/DIDCommPlugin.swift | 62 +++++++++++++
 .../DIDCommPlugin/JWTCredentialPlugin.swift   | 77 ++++++++++++++++
 .../PrismJWTCredentialPlugin.swift            | 38 ++++++++
 .../DIDCommPlugin/SDJWTCredentialPlugin.swift | 80 ++++++++++++++++
 Package.swift                                 | 15 +++
 .../project.pbxproj                           | 48 +++++++++-
 9 files changed, 422 insertions(+), 8 deletions(-)
 create mode 100644 EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/DIDCommPlugin.swift
 create mode 100644 EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/JWTCredentialPlugin.swift
 create mode 100644 EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/PrismJWTCredentialPlugin.swift
 create mode 100644 EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/SDJWTCredentialPlugin.swift

diff --git a/EdgeAgentSDK/Domain/Sources/BBs/Pollux.swift b/EdgeAgentSDK/Domain/Sources/BBs/Pollux.swift
index 0c4f5827..94e0257c 100644
--- a/EdgeAgentSDK/Domain/Sources/BBs/Pollux.swift
+++ b/EdgeAgentSDK/Domain/Sources/BBs/Pollux.swift
@@ -128,3 +128,95 @@ public extension Pollux {
         )
     }
 }
+
+public enum OperationResult {
+    case credential(Credential)
+    case forward(type: String, format: String?, payload: Data)
+    case verification(verified: Bool)
+
+    public var credential: Credential? {
+        switch self {
+        case .credential(let credential):
+            return credential
+        default:
+            return nil
+        }
+    }
+
+    public var forwardType: String? {
+        switch self {
+        case .forward(type: let type, format: _, payload: _):
+            return type
+        default:
+            return nil
+        }
+    }
+
+    public var forwardPayload: Data? {
+        switch self {
+        case .forward(type: _, format: _, payload: let payload):
+            return payload
+        default:
+            return nil
+        }
+    }
+
+    public var isVerified: Bool? {
+        switch self {
+        case .verification(verified: let verified):
+            return verified
+        default:
+            return nil
+        }
+    }
+}
+
+public protocol PolluxPlugin {
+    var version: String { get }
+    var supportedOperations: [String] { get }
+
+    func requiredOptions(operation: String) -> [CredentialOperationsOptions]
+
+    func operation(type: String, format: String?, payload: Data?, options: [CredentialOperationsOptions]) async throws -> OperationResult
+}
+
+public protocol CredentialPlugin: PolluxPlugin {
+    var credentialType: String { get }
+
+    func createCredential(_ credentialData: Data) async throws -> Credential
+    func credential(_ imported: Data) async throws -> Credential
+}
+
+public protocol ProtocolPlugin: PolluxPlugin {
+    var supportedCredentialTypes: [String] { get }
+}
+
+public protocol ProtocolCreateIssuancePlugin: ProtocolPlugin {
+    var protocolType: String { get }
+    var version: String { get }
+
+    // This is just a mock still to define
+    func issueOffer(withClaims: [ClaimFilter], issuer: DID, subject: DID) async throws -> OperationResult
+    // This is just a mock still to define
+    func issueCredential(withClaims: [ClaimFilter], issuer: DID, subject: DID) async throws -> OperationResult
+}
+
+public protocol ProtocolCreatePresentationPlugin: ProtocolPlugin {
+    var protocolType: String { get }
+    var version: String { get }
+
+    // This needs to be mocked still
+    func requestPresentation(withClaims: [ClaimFilter]) async throws -> OperationResult
+}
+
+extension ProtocolPlugin {
+    var credentialIssuance: ProtocolCreateIssuancePlugin? {
+        return self as? ProtocolCreateIssuancePlugin
+    }
+}
+
+extension ProtocolPlugin {
+    var credentialPresentation: ProtocolCreatePresentationPlugin? {
+        return self as? ProtocolCreatePresentationPlugin
+    }
+}
diff --git a/EdgeAgentSDK/EdgeAgent/Sources/DIDCommAgent/DIDCommAgent+Credentials.swift b/EdgeAgentSDK/EdgeAgent/Sources/DIDCommAgent/DIDCommAgent+Credentials.swift
index d49879cc..9ad13bf0 100644
--- a/EdgeAgentSDK/EdgeAgent/Sources/DIDCommAgent/DIDCommAgent+Credentials.swift
+++ b/EdgeAgentSDK/EdgeAgent/Sources/DIDCommAgent/DIDCommAgent+Credentials.swift
@@ -146,15 +146,22 @@ public extension DIDCommAgent {
             throw EdgeAgentError.invalidAttachmentFormat(nil)
         }
 
-        let credential = try await pollux.parseCredential(
-            type: format,
-            credentialPayload: jsonData,
+        guard let plugin = edgeAgent.credentialPlugins.first( where: { $0.supportedOperations.contains(message.type)
+        }) else {
+            throw EdgeAgentError.invalidAttachmentFormat(nil)
+        }
+        guard let credential = try await plugin.operation(
+            type: message.type,
+            format: attachment.format,
+            payload: jsonData,
             options: [
                 .linkSecret(id: "", secret: linkSecretString),
                 .credentialDefinitionDownloader(downloader: downloader),
                 .schemaDownloader(downloader: downloader)
             ]
-        )
+        ).credential else {
+            throw EdgeAgentError.invalidAttachmentFormat(nil)
+        }
 
         guard let storableCredential = credential.storable else {
             return credential
diff --git a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent.swift b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent.swift
index 4acaf83f..e1ce1853 100644
--- a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent.swift
+++ b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent.swift
@@ -15,6 +15,7 @@ public class EdgeAgent {
     public let castor: Castor
     public let pluto: Pluto
     public let pollux: Pollux & CredentialImporter
+    public let credentialPlugins: [PolluxPlugin]
 
     public static func setupLogging(logLevels: [LogComponent: LogLevel]) {
         SDKLogger.logLevels = logLevels
@@ -35,12 +36,14 @@ public class EdgeAgent {
         castor: Castor,
         pluto: Pluto,
         pollux: Pollux & CredentialImporter,
+        credentialPlugins: [PolluxPlugin] = [],
         seed: Seed? = nil
     ) {
         self.apollo = apollo
         self.castor = castor
         self.pluto = pluto
         self.pollux = pollux
+        self.credentialPlugins = credentialPlugins
         self.seed = seed ?? apollo.createRandomSeed().seed
     }
 
diff --git a/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/DIDCommPlugin.swift b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/DIDCommPlugin.swift
new file mode 100644
index 00000000..9e1a8355
--- /dev/null
+++ b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/DIDCommPlugin.swift
@@ -0,0 +1,62 @@
+import Domain
+import Foundation
+
+struct DIDCommPlugin: ProtocolPlugin {
+    let version: String = "0.1"
+    let supportedOperations: [String] = [
+        "https://didcomm.org/issue-credential/3.0/offer-credential",
+        "https://didcomm.org/issue-credential/3.0/issue-credential"
+    ]
+    var supportedCredentialTypes: [String] {
+        credentialPlugins.map(\.credentialType)
+    }
+    private let supportProtocols: [ProtocolPlugin]
+    private let credentialPlugins: [CredentialPlugin]
+
+    func requiredOptions(operation: String) -> [Domain.CredentialOperationsOptions] {
+        []
+    }
+    func operation(
+        type: String,
+        format: String?,
+        payload: Data?,
+        options: [Domain.CredentialOperationsOptions]
+    ) async throws -> Domain.OperationResult {
+        guard let format else { throw PolluxError.unsupportedIssuedMessage }
+        switch type {
+        case "https://didcomm.org/issue-credential/3.0/offer-credential":
+            guard
+                let supportProtocol = supportProtocols
+                    .first(where: {
+                        $0.supportedOperations.contains("offer-credential") && $0.supportedCredentialTypes.contains(format)
+                    })
+            else {
+                throw PolluxError.unsupportedIssuedMessage
+            }
+            return try await supportProtocol.operation(
+                type: "offer-credential",
+                format: format,
+                payload: payload,
+                options: options
+            )
+        case "https://didcomm.org/issue-credential/3.0/issue-credential":
+            guard
+                let supportProtocol = supportProtocols
+                    .first(where: {
+                        $0.supportedOperations.contains("issue-credential")
+                        && $0.supportedCredentialTypes.contains(format)
+                    })
+            else {
+                throw PolluxError.unsupportedIssuedMessage
+            }
+            return try await supportProtocol.operation(
+                type: "issue-credential",
+                format: format,
+                payload: payload,
+                options: options
+            )
+        default:
+            return .verification(verified: false)
+        }
+    }
+}
diff --git a/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/JWTCredentialPlugin.swift b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/JWTCredentialPlugin.swift
new file mode 100644
index 00000000..869a6a5d
--- /dev/null
+++ b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/JWTCredentialPlugin.swift
@@ -0,0 +1,77 @@
+import Domain
+import Foundation
+import JSONWebKey
+import JSONWebToken
+import JSONWebSignature
+
+struct JWTCredentialPlugin: CredentialPlugin {
+    let version = "0.1"
+    let credentialType = "jwt"
+    let supportedOperations = [
+        "offer",
+        "offer-credential",
+        "issue",
+        "issue-credential"
+    ]
+
+    func requiredOptions(operation: String) -> [Domain.CredentialOperationsOptions] {
+        []
+    }
+
+    func operation(
+        type: String,
+        format: String?,
+        payload: Data?,
+        options: [Domain.CredentialOperationsOptions]
+    ) async throws -> Domain.OperationResult {
+        guard let payload else { throw PolluxError.invalidJWTCredential }
+        switch type {
+        case "offer", "offer-credential":
+            let processedJWTCredentialRequest = try await processJWTCredentialRequest(
+                offerData: payload,
+                options: options
+            )
+            return try .forward(
+                type: "request-credential",
+                format: format,
+                payload: processedJWTCredentialRequest.tryToData()
+            )
+        case "issue", "issue-credential":
+            return try await .credential(createCredential(payload))
+        default:
+            throw PolluxError.unsupportedIssuedMessage
+        }
+    }
+
+    func createCredential(_ credentialData: Data) async throws -> Credential {
+        try JWTCredential(data: credentialData)
+    }
+    
+    func credential(_ imported: Data) async throws -> Credential {
+        try JWTCredential(data: imported)
+    }
+
+    private func processJWTCredentialRequest(offerData: Data, options: [CredentialOperationsOptions]) async throws -> String {
+        guard
+            let subjectDIDOption = options.first(where: {
+                if case .subjectDID = $0 { return true }
+                return false
+            }),
+            case let CredentialOperationsOptions.subjectDID(did) = subjectDIDOption
+        else {
+            throw PolluxError.invalidPrismDID
+        }
+
+        guard
+            let exportableKeyOption = options.first(where: {
+                if case .exportableKey = $0 { return true }
+                return false
+            }),
+            case let CredentialOperationsOptions.exportableKey(exportableKey) = exportableKeyOption
+        else {
+            throw PolluxError.requiresExportableKeyForOperation(operation: "Create Credential Request")
+        }
+
+        return try await CreateJWTCredentialRequest.create(didStr: did.string, key: exportableKey, offerData: offerData)
+    }
+}
diff --git a/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/PrismJWTCredentialPlugin.swift b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/PrismJWTCredentialPlugin.swift
new file mode 100644
index 00000000..29cf92da
--- /dev/null
+++ b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/PrismJWTCredentialPlugin.swift
@@ -0,0 +1,38 @@
+import Domain
+import Foundation
+import JSONWebKey
+import JSONWebToken
+import JSONWebSignature
+
+struct PrismJWTCredentialPlugin: CredentialPlugin {
+    let credentialType = "prismJWT"
+    var version: String { jwtPlugin.version }
+    var supportedOperations: [String] { jwtPlugin.supportedOperations }
+    private let jwtPlugin = JWTCredentialPlugin()
+
+    func createCredential(_ credentialData: Data) async throws -> Credential {
+        try await jwtPlugin.createCredential(credentialData)
+    }
+    
+    func credential(_ imported: Data) async throws -> Credential {
+        try await jwtPlugin.credential(imported)
+    }
+
+    func requiredOptions(operation: String) -> [Domain.CredentialOperationsOptions] {
+        jwtPlugin.requiredOptions(operation: operation)
+    }
+
+    func operation(
+        type: String,
+        format: String?,
+        payload: Data?,
+        options: [Domain.CredentialOperationsOptions]
+    ) async throws -> Domain.OperationResult {
+        try await jwtPlugin.operation(
+            type: type,
+            format: format,
+            payload: payload,
+            options: options
+        )
+    }
+}
diff --git a/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/SDJWTCredentialPlugin.swift b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/SDJWTCredentialPlugin.swift
new file mode 100644
index 00000000..0638604f
--- /dev/null
+++ b/EdgeAgentSDK/Pollux/Sources/Plugins/DIDCommPlugin/SDJWTCredentialPlugin.swift
@@ -0,0 +1,80 @@
+import Domain
+import Foundation
+import JSONWebKey
+import JSONWebToken
+import JSONWebSignature
+
+struct SDJWTCredentialPlugin: CredentialPlugin {
+    let version: String = "0.1"
+    var supportedOperations: [String] {
+        [
+            "offer",
+            "offer-credential",
+            "issue",
+            "issue-credential"
+        ]
+    }
+
+    let credentialType = "vc+sd-jwt"
+
+    func createCredential(_ credentialData: Data) async throws -> Credential {
+        try SDJWTCredential(sdjwtString: credentialData.tryToString())
+    }
+
+    func credential(_ imported: Data) async throws -> Credential {
+        try SDJWTCredential(sdjwtString: imported.tryToString())
+    }
+
+    func requiredOptions(operation: String) -> [Domain.CredentialOperationsOptions] {
+        []
+    }
+
+    func operation(
+        type: String,
+        format: String?,
+        payload: Data?,
+        options: [Domain.CredentialOperationsOptions]
+    ) async throws -> Domain.OperationResult {
+        guard let payload else { throw PolluxError.invalidJWTCredential }
+        switch type {
+        case "offer", "offer-credential":
+            let processedJWTCredentialRequest = try await processSDJWTCredentialRequest(
+                offerData: payload,
+                options: options
+            )
+            return try .forward(
+                type: "request-credential",
+                format: format,
+                payload: processedJWTCredentialRequest.tryToData()
+            )
+        case "issue", "issue-credential":
+            return try await .credential(createCredential(payload))
+        default:
+            throw PolluxError.unsupportedIssuedMessage
+        }
+    }
+
+    private func processSDJWTCredentialRequest(offerData: Data, options: [CredentialOperationsOptions]) async throws -> String {
+        guard
+            let subjectDIDOption = options.first(where: {
+                if case .subjectDID = $0 { return true }
+                return false
+            }),
+            case let CredentialOperationsOptions.subjectDID(did) = subjectDIDOption
+        else {
+            throw PolluxError.invalidPrismDID
+        }
+
+        guard
+            let exportableKeyOption = options.first(where: {
+                if case .exportableKey = $0 { return true }
+                return false
+            }),
+            case let CredentialOperationsOptions.exportableKey(exportableKey) = exportableKeyOption
+        else {
+            throw PolluxError.requiresExportableKeyForOperation(operation: "Create Credential Request")
+        }
+
+        return try await CreateJWTCredentialRequest.create(didStr: did.string, key: exportableKey, offerData: offerData)
+    }
+}
diff --git a/Package.swift b/Package.swift
index 672f92cb..2c6bce10 100644
--- a/Package.swift
+++ b/Package.swift
@@ -129,6 +129,20 @@ let package = Package(
             ],
             path: "EdgeAgentSDK/Pollux/Sources"
         ),
+//        .target(
+//            name: "JWTPlugin",
+//            dependencies: [
+//                "Domain",
+//                "Core",
+//                "jose-swift",
+////                "Sextant",
+////                "eudi-lib-sdjwt-swift",
+//                .product(name: "Gzip", package: "GzipSwift"),
+////                .product(name: "AnoncredsSwift", package: "anoncreds-rs"),
+////                .product(name: "JSONSchema", package: "JSONSchema.swift")
+//            ],
+//            path: "EdgeAgentSDK/Pollux/Sources"
+//        ),
         .testTarget(
             name: "PolluxTests",
             dependencies: ["Pollux", "Apollo", "Castor", "EdgeAgent"],
@@ -173,6 +187,7 @@ let package = Package(
                 "Domain",
                 "Builders",
                 "Core",
+                "jose-swift",
                 .product(name: "OpenID4VCI", package: "eudi-lib-ios-openid4vci-swift")
             ],
             path: "EdgeAgentSDK/EdgeAgent/Sources"
diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj
index ae9f1207..656f1f17 100644
--- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj
+++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj
@@ -115,6 +115,11 @@
 		EEBC938F29C7311C0015A36E /* CredentialListViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBC938E29C7311C0015A36E /* CredentialListViewState.swift */; };
 		EEBC939529C735910015A36E /* CredentialListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBC939429C735910015A36E /* CredentialListViewModel.swift */; };
 		EEBC939729C737DE0015A36E /* CredentialListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBC939629C737DE0015A36E /* CredentialListRouter.swift */; };
+		EEC2226A2CF77437004F2CA5 /* Apollo in Frameworks */ = {isa = PBXBuildFile; productRef = EEC222692CF77437004F2CA5 /* Apollo */; };
+		EEC2226C2CF77437004F2CA5 /* Authenticate in Frameworks */ = {isa = PBXBuildFile; productRef = EEC2226B2CF77437004F2CA5 /* Authenticate */; };
+		EEC2226E2CF77437004F2CA5 /* Builders in Frameworks */ = {isa = PBXBuildFile; productRef = EEC2226D2CF77437004F2CA5 /* Builders */; };
+		EEC222702CF77437004F2CA5 /* Castor in Frameworks */ = {isa = PBXBuildFile; productRef = EEC2226F2CF77437004F2CA5 /* Castor */; };
+		EEC222722CF77437004F2CA5 /* Domain in Frameworks */ = {isa = PBXBuildFile; productRef = EEC222712CF77437004F2CA5 /* Domain */; };
 		EEC5A8392BEA5E3A00AED928 /* EdgeAgent in Frameworks */ = {isa = PBXBuildFile; productRef = EEC5A8382BEA5E3A00AED928 /* EdgeAgent */; };
 		EEE61FBA2937CA280053AE52 /* AtalaPrismWalletDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE61FB92937CA280053AE52 /* AtalaPrismWalletDemoApp.swift */; };
 		EEE61FBC2937CA280053AE52 /* FuncionalitiesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE61FBB2937CA280053AE52 /* FuncionalitiesList.swift */; };
@@ -282,11 +287,16 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EEC222702CF77437004F2CA5 /* Castor in Frameworks */,
 				EE38135A2938D5B100A3A710 /* Mercury in Frameworks */,
 				EE3813582938D5B100A3A710 /* Domain in Frameworks */,
 				EE38135E2938D5B100A3A710 /* Pollux in Frameworks */,
 				EEC5A8392BEA5E3A00AED928 /* EdgeAgent in Frameworks */,
 				EE38135C2938D5B100A3A710 /* Pluto in Frameworks */,
+				EEC2226C2CF77437004F2CA5 /* Authenticate in Frameworks */,
+				EEC2226E2CF77437004F2CA5 /* Builders in Frameworks */,
+				EEC222722CF77437004F2CA5 /* Domain in Frameworks */,
+				EEC2226A2CF77437004F2CA5 /* Apollo in Frameworks */,
 				EE3813542938D5B100A3A710 /* Builders in Frameworks */,
 				EE3813502938D5B100A3A710 /* Apollo in Frameworks */,
 				EE3813562938D5B100A3A710 /* Castor in Frameworks */,
@@ -842,6 +852,11 @@
 				EE38135B2938D5B100A3A710 /* Pluto */,
 				EE38135D2938D5B100A3A710 /* Pollux */,
 				EEC5A8382BEA5E3A00AED928 /* EdgeAgent */,
+				EEC222692CF77437004F2CA5 /* Apollo */,
+				EEC2226B2CF77437004F2CA5 /* Authenticate */,
+				EEC2226D2CF77437004F2CA5 /* Builders */,
+				EEC2226F2CF77437004F2CA5 /* Castor */,
+				EEC222712CF77437004F2CA5 /* Domain */,
 			);
 			productName = AtalaPrismWalletDemo;
 			productReference = EEE61FB62937CA280053AE52 /* AtalaPrismWalletDemo.app */;
@@ -872,7 +887,7 @@
 			);
 			mainGroup = EEE61FAD2937CA280053AE52;
 			packageReferences = (
-				EE86C3C82CF5EC750072BEB7 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */,
+				EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */,
 			);
 			productRefGroup = EEE61FB72937CA280053AE52 /* Products */;
 			projectDirPath = "";
@@ -1235,12 +1250,12 @@
 /* End XCConfigurationList section */
 
 /* Begin XCRemoteSwiftPackageReference section */
-		EE86C3C82CF5EC750072BEB7 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */ = {
+		EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/hyperledger/identus-edge-agent-sdk-swift";
 			requirement = {
-				kind = exactVersion;
-				version = 7.0.0;
+				kind = upToNextMajorVersion;
+				minimumVersion = 7.0.1;
 			};
 		};
 /* End XCRemoteSwiftPackageReference section */
@@ -1278,6 +1293,31 @@
 			isa = XCSwiftPackageProductDependency;
 			productName = Pollux;
 		};
+		EEC222692CF77437004F2CA5 /* Apollo */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */;
+			productName = Apollo;
+		};
+		EEC2226B2CF77437004F2CA5 /* Authenticate */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */;
+			productName = Authenticate;
+		};
+		EEC2226D2CF77437004F2CA5 /* Builders */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */;
+			productName = Builders;
+		};
+		EEC2226F2CF77437004F2CA5 /* Castor */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */;
+			productName = Castor;
+		};
+		EEC222712CF77437004F2CA5 /* Domain */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = EEC222682CF77437004F2CA5 /* XCRemoteSwiftPackageReference "identus-edge-agent-sdk-swift" */;
+			productName = Domain;
+		};
 		EEC5A8382BEA5E3A00AED928 /* EdgeAgent */ = {
 			isa = XCSwiftPackageProductDependency;
 			productName = EdgeAgent;