Canton Administration APIs

Canton provides a console as a builtin mode for administrative interaction. However, under the hood, all administrative console actions are effected using the administration gRPC API. Therefore, it is also possible to write your own administration application and connect it to the administration gRPC endpoints of both types of nodes, participant and domain.

There are three categories of admin-apis: participant, domain and identity.

Participant Admin APIs

The participant exposes the following admin-api services:

Package Service

The package service is used to manage the installed packages.

syntax = "proto3";

package com.digitalasset.canton.participant.admin.api.v0;

import "google/protobuf/empty.proto";

service PackageService {
    // List all DAML-LF archives on the participant node - return their hashes
    rpc ListPackages(google.protobuf.Empty) returns (ListPackagesResponse);

    // Lists all the modules in package on the participant node
    rpc ListPackageContents (ListPackageContentsRequest) returns (ListPackageContentsResponse);

    // Upload a DAML archive to the participant node
    rpc UploadPackage (UploadPackageRequest) returns (google.protobuf.Empty);

    // Obtain a Package by packageId -- for inspection & download
    rpc GetPackage (GetPackageRequest) returns (GetPackageResponse);

    // List all DARs on the participant node - return their hashes and filenames
    rpc ListDars(google.protobuf.Empty) returns (ListDarsResponse);

    // Upload a DAR file and all packages inside to the participant node
    rpc UploadDar (UploadDarRequest) returns (UploadDarResponse);

    // Obtain a DAR file by hash -- for inspection & download
    rpc GetDar(GetDarRequest) returns (GetDarResponse);

    // Share a DAR with another participant
    rpc Share(ShareRequest) returns (google.protobuf.Empty);

    // List requests this participant has made to share DARs with another participant
    rpc ListShareRequests(google.protobuf.Empty) returns (ListShareRequestsResponse);

    // List offers to share a DAR that this participant has received
    rpc ListShareOffers(google.protobuf.Empty) returns (ListShareOffersResponse);

    // Accept a DAR sharing offer (this will install the DAR into the participant)
    rpc AcceptShareOffer(AcceptShareOfferRequest) returns (google.protobuf.Empty);

    // Reject a DAR sharing offer
    rpc RejectShareOffer(RejectShareOfferRequest) returns (google.protobuf.Empty);

    // Add party to our DAR distribution whitelist
    rpc WhitelistAdd(WhitelistChangeRequest) returns (google.protobuf.Empty);

    // Remove party from our DAR distribution whitelist
    rpc WhitelistRemove(WhitelistChangeRequest) returns (google.protobuf.Empty);

    // List all parties currently on the whitelist
    rpc WhitelistList(google.protobuf.Empty) returns (WhitelistListResponse);
}

message ListPackageContentsRequest {
    string packageId = 1;
}

message ListPackageContentsResponse {
    repeated ModuleDescription modules = 1;
}

message ModuleDescription {
    string name = 1;
}

message ListPackagesResponse {
    repeated PackageState packageStates = 1;
}

message UploadPackageRequest {
    bytes archive = 1;
}

message ListDarsResponse {
    repeated DarDescription dars = 1;
}

message DarDescription {
    string hash = 1;
    string name = 2;
}

message UploadDarRequest {
    bytes data = 1;
    string filename = 2;
}

message UploadDarResponse {
    string hash = 1;
}

message GetDarRequest {
    string hash = 1;
}

message GetDarResponse {
    bytes data = 1;
    string name = 2;
}

message GetPackageRequest {
    string packageId = 1;
}

message GetPackageResponse {
    bytes archive = 1;
}

message DeactivatePackageRequest {
    string packageId = 1;
}

message ActivatePackageRequest {
    string packageId = 1;
}

message PackageState {
    string packageId = 1;
    enum State {
        ACTIVE = 0;
        DISABLED = 1;
        VETTING_REQUEST = 2;
    };

    State state = 2;
}

message ShareRequest {
    string darHash = 1;
    string recipientId = 2;
}

message ListShareRequestsResponse {
    repeated Item shareRequests = 1;

    message Item {
        string id = 1;
        string darHash = 2;
        string recipientId = 3;
        string name = 4;
    }
}

message ListShareOffersResponse {
    repeated Item shareOffers = 1;

    message Item {
        string id = 1;
        string darHash = 2;
        string ownerId = 3;
        string name = 4;
    }
}

message AcceptShareOfferRequest {
    string id = 1;
}

message RejectShareOfferRequest {
    string id = 1;
    // informational message explaining why we decided to reject the DAR
    // can be empty
    string reason = 2;
}

message WhitelistChangeRequest {
    string partyId = 1;
}

message WhitelistListResponse {
    repeated string partyIds = 1;
}

Participant Status Service

syntax = "proto3";

package com.digitalasset.canton.participant.admin.api.v0;

import "google/protobuf/empty.proto";
import "google/protobuf/duration.proto";

service ParticipantStatusService {
    rpc GetStatus(google.protobuf.Empty) returns (StatusResponse);
}

message StatusResponse {
    string id = 1;
    google.protobuf.Duration uptime = 2;
    map<string, int32> ports = 3;
    repeated string connectedDomains = 4;
}

Ping Pong Service

Canton uses a default simple ping-pong workflow to smoke-test a deployment.

syntax = "proto3";

package com.digitalasset.canton.participant.admin.api.v0;

service PingService {
    rpc ping(PingRequest) returns (PingResponse);
}

message PingRequest {

    repeated string targedParty = 1;
    uint64 timeoutMiliseconds = 2;
    uint64 levels = 3;
    uint64 gracePeriodMilliseconds = 4;

}

message PingSuccess {
    uint64 pingTime = 1;
    string responder = 2;
}

message PingFailure {
}

message PingResponse {
    oneof response {
        PingSuccess success = 1;
        PingFailure failure = 2;
    }

}

Domain Connectivity Service

syntax = "proto3";

package com.digitalasset.canton.participant.admin.api.v0;

import "com/digitalasset/canton/identity/v0/identity_transaction.proto";

/**
 * Domain connectivity service for adding and connecting to domains
 *
 * The domain connectivity service allows to register to new domains and control the
 * participants domain connections.
 */
service DomainConnectivityService {
    // configure a new domain connection
    rpc RegisterDomain(RegisterDomainRequest) returns (RegisterDomainResponse);
    // reconfigure a domain connection
    rpc ModifyDomain(ModifyDomainRequest) returns (ModifyDomainResponse);
    // connect to a configured domain
    rpc ConnectDomain(ConnectDomainRequest) returns (ConnectDomainResponse);
    // disconnect from a configured domain
    rpc DisconnectDomain(DisconnectDomainRequest) returns (DisconnectDomainResponse);
    // list connected domains
    rpc ListConnectedDomains(ListConnectedDomainsRequest) returns (ListConnectedDomainsResponse);
    // list configured domains
    rpc ListConfiguredDomains(ListConfiguredDomainsRequest) returns (ListConfiguredDomainsResponse);
    // Get the service agreement for the domain
    rpc GetAgreement(GetAgreementRequest) returns (GetAgreementResponse);
    // Accept the agreement of the domain
    rpc AcceptAgreement(AcceptAgreementRequest) returns (AcceptAgreementResponse);
}

message DomainConnectionConfig {
    // participant local identifier of the target domain
    string domainAlias = 1;
    // connection information to domain (http[s]://<host>:<port>")
    string connection = 2;
    // if true, then domain will be automatically connected to (default false)
    bool autoConnect = 3;
    // optional essential state (if TLS isn't to be trusted)
    com.digitalasset.canton.identity.v0.EssentialState essentialState = 4;
    // optional. if not using system certificates this should be the bytes of a X.509 certificate collection in PEM format chain.
    bytes certificates = 5;
}

/** Register and optionally auto-connect to a new domain */
message RegisterDomainRequest {
    DomainConnectionConfig add = 1;
}

message RegisterDomainResponse {

}

message ModifyDomainRequest {
    DomainConnectionConfig modify = 1;
}

message ModifyDomainResponse {

}

message ListConfiguredDomainsRequest {

}

message ListConfiguredDomainsResponse {
    message Result {
        DomainConnectionConfig config = 1;
        bool connected = 2;
    }
    repeated Result results = 1;
}

message ConnectDomainRequest {
    string domainAlias = 1;
}

message ConnectDomainResponse {
}

message DisconnectDomainRequest {
    string domainAlias = 1;
}

message DisconnectDomainResponse {
}

message ListConnectedDomainsRequest {
}

message ListConnectedDomainsResponse {
    message Result {
        string domainAlias = 1;
        string domainId = 2;
    }
    repeated Result connectedDomains = 1;
}

message GetAgreementRequest {
    string domain_alias = 1;
}

message GetAgreementResponse {
    Agreement agreement = 1;
    bool accepted = 2;
}

message Agreement {
    string id = 1;
    string text = 2;
}

message AcceptAgreementRequest {
    string domain_alias = 1;
    string agreement_id = 2;
}

message AcceptAgreementResponse {
}

Domain Admin APIs

The domain exposes the following admin-api services:

Domain Identity Manager Service

syntax = "proto3";

package com.digitalasset.canton.domain.admin.api.v0;

import "google/protobuf/empty.proto";
import "com/digitalasset/canton/identity/v0/identity_transaction.proto";


/**
 * The Domain identity manager service
 *
 * This service is used to extend the basic identity manager service with domain specific operations on the
 * identity state.
 */
service DomainIdentityManagerService {
    /**
     * Set the state of a participant on the given domain.
     */
    rpc SetParticipantState(com.digitalasset.canton.identity.v0.ParticipantState) returns (google.protobuf.Empty);

    /**
     * Download the essential state from this identity manager
     */
    rpc GetEssentialState(google.protobuf.Empty) returns (com.digitalasset.canton.identity.v0.EssentialState);

}

Domain Status Service

syntax = "proto3";

package com.digitalasset.canton.domain.admin.api.v0;

import "google/protobuf/empty.proto";
import "google/protobuf/duration.proto";

service DomainStatusService {
    rpc GetStatus(google.protobuf.Empty) returns (StatusResponse);
}

message StatusResponse {
    string id = 1;
    google.protobuf.Duration uptime = 2;
    map<string, int32> ports = 3;
    repeated string connectedParticipants = 4;
}

Identity Admin APIs

Both, domain and participant nodes expose the following services:

Key Management Service

syntax = "proto3";

package com.digitalasset.canton.identity.admin.api.v0;

import "com/digitalasset/canton/identity/v0/identity_transaction.proto";

/**
 * Key service providing programmatic access to locally stored keys.
 *
 * We have two key-stores: a private key store where we are storing our pairs of
 * public and private keys and a public key store where we are storing other
 * public keys that we learned.
 *
 * We learn public key stores in different ways: either by importing them or
 * by picking them up from internal sources (such as identity management updates).
 *
 * The only purpose of the public key store (where we import foreign keys) is convenience for
 * identity management such that when we add identity management transactions, we can refer to
 * fingerprints in commands while building them rather than having to attach public-key files.
 */
service KeyService {

    /**
     * List public keys according to request filter for which we have a private key in our key vault.
     *
     * The request includes a filter for fingerprints which can be used for lookups.
     *
     * @param ListMyKeysRequest: request with optional fingerprint filter
     * @return: all serialized keys and their fingerprints that have the fingerprint filter as a substring in their fingerprint
     */
    rpc ListMyKeys(ListKeysRequest) returns (ListKeysResponse);

    /**
     * Generates a new public / private key
     *
     * Stores the private key in the vault, and returns the public key
     */
    rpc GenerateKey(GenerateKeyRequest) returns (KeyResult);

    /**
     * Import a public key into the registry in order to provide that Fingerprint -> PublicKey lookups
     *
     * @param: ImportPublicKeyRequest serialized public key to be imported
     * @return: fingerprint and serialized public key of imported public key
     */
    rpc ImportPublicKey(ImportPublicKeyRequest) returns (ImportPublicKeyResponse);
    /**
     * Lists all public keys matching the supplied filter which are internally cached
     *
     * Any public key returned here can be referenced in identity transaction building
     * by fingerprint.
     */
    rpc ListPublicKeys(ListKeysRequest) returns (ListKeysResponse);


}


message ImportPublicKeyRequest {
    // import a result of a previous PublicKey.serialize invocation
    bytes publicKey = 1;
    // a context string that should be stored along side the key
    string context = 2;
}

message ImportPublicKeyResponse {
    // fingerprint of imported key
    string fingerprint = 1;
}

message KeyResult {
    // the fingerprint of the search
    string fingerprint = 1;
    // the context string associated with the key
    string context = 2;
    // the serialized version (which can be used to import it at another place)
    bytes publicKey = 3;
    // the purposes of the key (signing/encryption)
    repeated com.digitalasset.canton.identity.v0.KeyPurpose purposes = 4;
}

message ListKeysRequest {
    // the substring that needs to match a given fingerprint
    string filterFingerprint = 1;
    // the substring to filter the context string
    string filterContext = 2;
    // the purposes that the key should support
    repeated com.digitalasset.canton.identity.v0.KeyPurpose purposes = 3;
}

message ListKeysResponse {
    // repetitive result type of list keys response
    repeated KeyResult results = 1;
}

message GenerateKeyRequest {
    // human readable free style description associated with key
    string context = 1;
    // purpose of key. if more than one purpose can be given depends on crypto implementation
    repeated com.digitalasset.canton.identity.v0.KeyPurpose purposes = 2;
}

Initialization Service

The one time initialization service, used to setup the identity of a node.

syntax = "proto3";

package com.digitalasset.canton.identity.admin.api.v0;

import "google/protobuf/empty.proto";

/**
 * The node initialization service
 *
 * This service is used once, on domain or participant start-up.
 */
service InitializationService {

    /**
     * Initialize the node with the unique identifier (can and must be done once)
     *
     * When a domain or participant instance starts for the first time, we need to bind it
     * to a globally unique stable identifier before we can continue with the
     * initialization procedure.
     */
    rpc InitId(InitIdRequest) returns (InitIdResponse);


    /**
     * Returns the id of the node (or empty if not initialized)
     */
    rpc GetId(google.protobuf.Empty) returns (GetIdResponse);

}

message InitIdRequest {
    string identifier = 1;
    string fingerprint = 2;
}

message InitIdResponse {
    string uniqueIdentifier = 1;
}

message GetIdResponse {
    bool initialized = 1;
    string uniqueIdentifier = 2;
}

Identity Aggregation Service

Aggregated view of the sequenced domain identity state.

syntax = "proto3";

package com.digitalasset.canton.identity.admin.api.v0;

import "com/digitalasset/canton/identity/v0/identity_transaction.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

/**
 * Identity information aggregation service
 *
 * This service allows deep inspection into the aggregated identity state.
 * The services runs both on the domain and on the participant and uses the same
 * data. The service provides GRPC access to the information aggregated by the identity providing
 * service client.
 */
service IdentityAggregationService {

    /**
      * Obtain information about a certain set of parties matching a given filter criterion.
      *
      * The request allows to filter per (domain, party, asOf) where the domain and party argument are
      * used in order to filter the result list using the `startsWith` method on the respective resulting string.
      *
      * As such, if you just need to do a lookup, then define a precise filter. Given the uniqueness of the
      * identifiers (and the fact that the identifiers contain key fingerprints), we should only ever get a single
      * result back if we are doing a precise lookup.
      *
      * The response is a sequence of tuples (party, domain, participant, privilege, trust-level)
      */
    rpc ListParties(ListPartiesRequest) returns (ListPartiesResponse);

    /**
      * Obtain key owner information matching a given filter criterion.
      *
      * Key owners in the system are different types of entities: Participant, Mediator, Domain Identity Manager and
      * Sequencer. The present method allows to define a filter to search for a key owner
      * using filters on (asOf, domain, ownerType, owner)
      *
      * The response is a sequence of (domain, ownerType, owner, keys) where keys is a sequence of
      * (fingerprint, bytes, key purpose). As such, we can use this method to export currently used signing or encryption
      * public keys.
      *
      * This method is quite general, as depending on the arguments, very different results can be obtained.
      *
      * Using OwnerType = 'Participant' allows to query for all participants.
      * Using OwnerType = 'Sequencer' allows to query for all sequencers defined.
      */
    rpc ListKeyOwners(ListKeyOwnersRequest) returns (ListKeyOwnersResponse);

    // not implemented
    rpc ListPartyMetadata(ListPartyMetadataRequest) returns (ListPartyMetadataResponse);
    // not yet implemented
    rpc SetPrivatePartyMetadata(SetPrivatePartyMetadataRequest) returns (google.protobuf.Empty);

}

message SetPrivatePartyMetadataRequest {
    string partyId = 1;
    com.digitalasset.canton.identity.v0.Metadata metadata = 2;
}

message ListPartyMetadataRequest {
    google.protobuf.Timestamp asOf = 1;
    string filterDomain = 2;
    string filterParty = 3;
}

message ListPartyMetadataResponse {
    message Result {
        string partyId = 1;
        com.digitalasset.canton.identity.v0.Metadata metadata = 2;
        com.digitalasset.canton.identity.v0.MetadataUpdate.Visibility visibility = 3;
    }
    Result results = 1;
}

message ListPartiesRequest {
    google.protobuf.Timestamp asOf = 1;
    string filterDomain = 2;
    string filterParty = 3;
}

message ListPartiesResponse {
    message Result {
        string party = 1;
        string domain = 2;
        string participant = 3;
        com.digitalasset.canton.identity.v0.ParticipantPermission permission = 4;
        com.digitalasset.canton.identity.v0.TrustLevel trustLevel = 5;
    }
    repeated Result results = 2;
}

message ListKeyOwnersRequest {
    google.protobuf.Timestamp asOf = 1;
    string filterDomain = 2;
    com.digitalasset.canton.identity.v0.OwnerType filterOwnerType = 3;
    string filterOwner = 4;
}

message ListKeyOwnersResponse {
    message Result {
        message Key {
            string fingerprint = 1;
            bytes publicKey = 2;
            com.digitalasset.canton.identity.v0.KeyPurpose purpose = 3;
        }
        string domain = 1;
        com.digitalasset.canton.identity.v0.OwnerType ownerType = 2;
        string owner = 3;
        repeated Key keys = 4;
    }
    repeated Result results = 1;
}

Identity Manager Read Service

Raw access to the underlying identity transactions.

syntax = "proto3";

package com.digitalasset.canton.identity.admin.api.v0;

import "com/digitalasset/canton/identity/v0/identity_transaction.proto";
import "google/protobuf/timestamp.proto";


// domain + idm + participant
service IdentityManagerReadService {
    rpc ListAvailableStores(ListAvailableStoresRequest) returns (ListAvailableStoresResponse);
    rpc ListPartyToParticipant(ListPartyToParticipantRequest) returns (ListPartyToParticipantResult);
    rpc ListOwnerToKeyMapping(ListOwnerToKeyMappingRequest) returns (ListOwnerToKeyMappingResult);
    rpc ListNamespaceDelegation(ListNamespaceDelegationRequest) returns (ListNamespaceDelegationResult);
    rpc ListIdentifierDelegation(ListIdentifierDelegationRequest) returns (ListIdentifierDelegationResult);
    rpc ListMetadataUpdate(ListMetadataUpdateRequest) returns (ListMetadataUpdateResult);
}

message ListMetadataUpdateRequest {
    BaseQuery baseQuery = 1;
    string filterParty = 2;
}

message ListMetadataUpdateResult {
    message Result {
        BaseResult context = 1;
        com.digitalasset.canton.identity.v0.MetadataUpdate item = 2;
    }
    repeated Result results = 1;
}

message ListNamespaceDelegationRequest {
    BaseQuery baseQuery = 1;
}

message ListNamespaceDelegationResult {
    message Result {
        BaseResult context = 1;
        com.digitalasset.canton.identity.v0.NamespaceDelegation item = 2;
        string targetKeyFingerprint = 3;
    }
    repeated Result results = 1;
}

message ListIdentifierDelegationRequest {
    BaseQuery baseQuery = 1;
}

message ListIdentifierDelegationResult {
    message Result {
        BaseResult context = 1;
        com.digitalasset.canton.identity.v0.IdentifierDelegation item = 2;
        string targetKeyFingerprint = 3;
    }
    repeated Result results = 1;
}

message BaseQuery {
    string filterStore = 1;
    google.protobuf.Timestamp from = 2;
    google.protobuf.Timestamp until = 3;
    enum ResultType {
        ALL = 0;
        ADD = 1;
        REMOVE = 2;
        ACTIVE = 3;
    }
    ResultType filterType = 4;
    string filterSignedKey = 5;
}

message BaseResult {
    string domain = 1;
    google.protobuf.Timestamp timestamp = 2;
    com.digitalasset.canton.identity.v0.IdentityChangeOp operation = 3;
    bytes serialized = 4;
    string signedByFingerprint = 5;
}

message ListPartyToParticipantResult {
    message Result {
        BaseResult context = 1;
        com.digitalasset.canton.identity.v0.PartyToParticipant item = 2;
    }
    repeated Result results = 2;
}

message ListPartyToParticipantRequest {
    BaseQuery baseQuery = 1;
    string filterParty = 2;
    string filterParticipant = 3;
    message FilterRequestSide {
        com.digitalasset.canton.identity.v0.RequestSide value = 1;
    }
    FilterRequestSide filterRequestSide = 4;
    message FilterPermission {
        com.digitalasset.canton.identity.v0.ParticipantPermission value = 1;
    }
    FilterPermission filterPermission = 5;
}

message ListOwnerToKeyMappingRequest {
    BaseQuery baseQuery = 1;
    string filterOwner = 2;
    message FilterKeyPurpose {
        com.digitalasset.canton.identity.v0.KeyPurpose value = 1;
    }
    FilterKeyPurpose filterKeyPurpose = 3;
}

message ListOwnerToKeyMappingResult {
    message Result {
        BaseResult context = 1;
        com.digitalasset.canton.identity.v0.OwnerToKeyMapping item = 2;
        string keyFingerprint = 3;
    }
    repeated Result results = 1;
}

message ListAvailableStoresRequest {

}
message ListAvailableStoresResponse {
    repeated string storeIds = 1;
}

Identity Manager Write Service

syntax = "proto3";

package com.digitalasset.canton.identity.admin.api.v0;

import "com/digitalasset/canton/identity/v0/identity_transaction.proto";

/**
 * Write operations on the local identity manager.
 *
 * Both, participant and domain run a local identity manager exposing the same write interface.
 */
service IdentityManagerWriteService {

    /**
     * Authorizes a party to participant mapping change (add/remove) on the node local identity manager.
     */
    rpc AuthorizePartyToParticipant(PartyToParticipantAuthorization) returns (AuthorizationSuccess);

    /**
     * Authorizes an owner to key mapping change (add/remove) on the local identity manager
     */
    rpc AuthorizeOwnerToKeyMapping(OwnerToKeyMappingAuthorization) returns (AuthorizationSuccess);

    /**
     * Authorizes a namespace delegation (root or intermediate CA) (add/remove) on the local identity manager
     */
    rpc AuthorizeNamespaceDelegation(NamespaceDelegationAuthorization) returns (AuthorizationSuccess);

    /**
     * Authorizes a new identifier delegation (identifier certificate) (add/remove) on the local identity manager
     */
    rpc AuthorizeIdentifierDelegation(IdentifierDelegationAuthorization) returns (AuthorizationSuccess);

    /**
     * Authorizes new metadata information (add/remove) on the local identity manager
     */
    rpc AuthorizeMetadataUpdate(MetadataUpdateAuthorization) returns (AuthorizationSuccess);

}

message AuthorizationSuccess {
    bytes serialized = 1;
}

message AuthorizationData {

    /** Add / Remove */
    com.digitalasset.canton.identity.v0.IdentityChangeOp change = 1;

    /**
     * Fingerprint of the key signing the authorization
     *
     * The signing key is used to identify a particular `NamespaceDelegation` or `IdentifierDelegation` certificate,
     * which is used to justify the given authorization.
     */
    string signedBy = 2;
}

message NamespaceDelegationAuthorization {
    AuthorizationData authorization = 1;

    // The namespace for which the authorization is issued.
    string namespace = 2;

    /**
     * The fingerprint of the signing key which will be authorized to issue identity transactions for this namespace.
     *
     * The key needs to be present in the local key registry either by being locally
     * generated or by having been previously imported.
     */
    string fingerprintOfAuthorizedKey = 3;
}

message IdentifierDelegationAuthorization {
    AuthorizationData authorization = 1;
    string identifier = 2;

    /**
     * The fingerprint of the signing key which will be authorized to issue identity transaction for this particular identifier.
     *
     * As with `NamespaceDelegation`s, the key needs to be present locally.
     */
    string fingerprintOfAuthorizedKey = 3;
}

message PartyToParticipantAuthorization {
    AuthorizationData authorization = 1;
    /**
     * The request side of this transaction
     *
     * A party to participant mapping can map a party from one namespace on a participant from another namespace.
     * Such a mapping needs to be authorized by both namespace keys. If the namespace is the same, we use
     * RequestSide.Both and collapse into a single transaction. Otherwise, `From` needs to be signed by a namespace key
     * of the party and `To` needs to be signed by a namespace key of the participant.
     */
    com.digitalasset.canton.identity.v0.RequestSide side = 2;

    // The unique identifier of the party
    string party = 3;
    // The unique identifier of the participant
    string participant = 4;
    // The permission of the participant that will allow him to act on behalf of the party.
    com.digitalasset.canton.identity.v0.ParticipantPermission permission = 5;
}

message OwnerToKeyMappingAuthorization {

    AuthorizationData authorization = 1;

    /**
     * The key owner type.
     *
     * An entity in Canton is described by his role and his unique identifier. As such, the same unique identifier
     * can be used for a mediator, sequencer, domain identity manager or even participant. Therefore, we need to
     * indicate for what role the given key can be used.
     */
    com.digitalasset.canton.identity.v0.OwnerType ownerType = 2;

    /**
     * The unique identifier of the entity we are associating the key with
     */
    string uniqueIdentifier = 3;

    /**
     * The fingerprint of the key that will be authorized
     *
     * The key needs to be present in the local key registry (can be imported via KeyService)
     */
    string fingerprintOfKey = 4;

    /**
     * Purpose of the key
     */
    repeated com.digitalasset.canton.identity.v0.KeyPurpose purposes = 5;
}


/**
 * Placeholder to send around `displayNames` and additional information. Not yet fully implemented.
 */
message MetadataUpdateAuthorization {
    AuthorizationData authorization = 1;
    string party = 2;
    com.digitalasset.canton.identity.v0.Metadata metadata = 3;
    com.digitalasset.canton.identity.v0.MetadataUpdate.Visibility visibility = 4;
}