diff --git a/src/App/MaintenanceSubject.purs b/src/App/MaintenanceSubject.purs new file mode 100644 index 0000000..9eb1c33 --- /dev/null +++ b/src/App/MaintenanceSubject.purs @@ -0,0 +1,21 @@ +module App.MaintenanceSubject where + +import Data.Codec.Argonaut as CA +import Data.Maybe (Maybe(..)) + +data MaintenanceSubject + = Verbosity + +-- | Codec for just encoding a single value of type `MaintenanceSubject` +codec :: CA.JsonCodec MaintenanceSubject +codec = + CA.prismaticCodec "MaintenanceSubject" from to CA.string + where + from :: String -> Maybe MaintenanceSubject + from = case _ of + "verbosity" -> Just None + _ -> Nothing + + to :: MaintenanceSubject -> String + to = case _ of + Verbosity -> "verbosity" diff --git a/src/App/Messages/DNSManagerDaemon.purs b/src/App/Messages/DNSManagerDaemon.purs index 7226b9b..68f4595 100644 --- a/src/App/Messages/DNSManagerDaemon.purs +++ b/src/App/Messages/DNSManagerDaemon.purs @@ -22,16 +22,14 @@ import Data.Bifunctor (lmap) import App.IPC as IPC -type Password = String - {- UserID should be in a separate module with a dedicated codec. -} type UserID = Int -- UserID is either a login or an uid number {- 7 -} --- data MaintenanceSubject = Verbosity --- type Maintenance = { subject :: MaintenanceSubject, value :: Maybe Int } --- codecMaintenance = CA.object "Maintenance" (CAR.record { subject: CA.string, value: CA.int }) +data MaintenanceSubject = Verbosity +type Maintenance = { subject :: MaintenanceSubject, value :: Maybe Int } +codecMaintenance = CA.object "Maintenance" (CAR.record { subject: CA.string, value: CA.int }) {- 0 -} type Login = { token :: String } @@ -66,359 +64,113 @@ codecDeleteRR = CA.object "DeleteRR" (CAR.record { domain: CA.string, rrid: CA.i - -{- 0 -} -type Login = { login :: String, password :: String } -codecLogin ∷ CA.JsonCodec Login -codecLogin = CA.object "Login" (CAR.record { login: CA.string, password: CA.string }) - -{- 1 -} -type Register = { login :: String - , password :: Password - , email :: Maybe Email.Email - {-, profile :: Maybe Hash(String, JSON::Any) -} } -codecRegister ∷ CA.JsonCodec Register -codecRegister - = CA.object "Register" (CAR.record - { login: CA.string - , password: CA.string - , email: CAR.optional Email.codec }) - -{- 2 -} -type ValidateUser = { user :: UserID, activation_key :: String } -codecValidateUser ∷ CA.JsonCodec ValidateUser -codecValidateUser - = CA.object "ValidateUser" (CAR.record - { user: CA.int - , activation_key: CA.string }) - -{- NOTE: "user" attribute for both PasswordRecovery and AskPasswordRecovery could be UserID, - but they'll be used as login since the user has to type it. -} -{- 3 -} -type AskPasswordRecovery = { user :: String } -codecAskPasswordRecovery ∷ CA.JsonCodec AskPasswordRecovery -codecAskPasswordRecovery = CA.object "AskPasswordRecovery" (CAR.record { user: CA.string }) - -{- 4 -} -type PasswordRecovery = { user :: String - , password_renew_key :: String - , new_password :: Password } -codecPasswordRecovery ∷ CA.JsonCodec PasswordRecovery -codecPasswordRecovery - = CA.object "PasswordRecovery" (CAR.record - { user: CA.string - , password_renew_key: CA.string - , new_password: CA.string }) - -{- 5 -} --- I'll split a message in two: either get a user by UID or by name. --- TODO: change it for an Either Int String type. -type GetUserByUID = { user :: Int } -type GetUserByName = { user :: String } -codecGetUserByUID ∷ CA.JsonCodec GetUserByUID -codecGetUserByUID = CA.object "GetUserByUID" (CAR.record { user: CA.int }) -codecGetUserByName ∷ CA.JsonCodec GetUserByName -codecGetUserByName = CA.object "GetUserByName" (CAR.record { user: CA.string }) - -{- 6 -} -type ModUser = { user :: Maybe UserID - , admin :: Maybe Boolean - , password :: Maybe Password - , email :: Maybe Email.Email } -codecModUser ∷ CA.JsonCodec ModUser -codecModUser - = CA.object "ModUser" (CAR.record - { user: CAR.optional CA.int - , admin: CAR.optional CA.boolean - , password: CAR.optional CA.string - , email: CAR.optional Email.codec }) - -{- 7 -} -{- type EditProfileEntries = { user :: Maybe UserID - , new_profile_entries :: Hash(String, JSON::Any) } -} - -{- 8 -} -type DeleteUser = { user :: Maybe UserID } -codecDeleteUser ∷ CA.JsonCodec DeleteUser -codecDeleteUser = CA.object "DeleteUser" (CAR.record { user: CAR.optional CA.int }) - -{- 9 -} -type AddUser = { login :: String - , password :: Password - , admin :: Boolean - , email :: Maybe Email.Email - {-, profile :: Maybe Hash(String, JSON::Any) -} } -codecAddUser ∷ CA.JsonCodec AddUser -codecAddUser - = CA.object "AddUser" (CAR.record - { login: CA.string - , password: CA.string - , admin: CA.boolean - , email: CAR.optional Email.codec - {-, profile :: Maybe Hash(String, JSON::Any) -} }) - -{- 10 -} -type CheckPermission = { user :: Maybe UserID, service :: String, resource :: String } -codecCheckPermission ∷ CA.JsonCodec CheckPermission -codecCheckPermission - = CA.object "CheckPermission" (CAR.record - { user: CAR.optional CA.int - , service: CA.string - , resource: CA.string }) - -{- 11 -} -type SetPermission = { user :: UserID - , service :: String - , resource :: String - , permission :: PermissionLevel.PermissionLevel } -codecSetPermission ∷ CA.JsonCodec SetPermission -codecSetPermission - = CA.object "SetPermission" (CAR.record - { user: CA.int - , service: CA.string - , resource: CA.string - , permission: PermissionLevel.codec }) - -{- 12 -} -type SearchUser = { regex :: Maybe String, offset :: Maybe Int } -codecSearchUser ∷ CA.JsonCodec SearchUser -codecSearchUser - = CA.object "SearchUser" (CAR.record - { regex: CAR.optional CA.string - , offset: CAR.optional CA.int }) - - {- RESPONSES -} --- TODO: note to myself: messages seem chaotic. Could be simpler. Should be simpler. +-- TODO: Zone actually is a complex data structure. +type DNSZone = String + {- 0 -} -type Error = { reason :: Maybe String } +-- type Error = { reason :: String | Array(String) } +type Error = { reason :: String } codecGotError ∷ CA.JsonCodec Error -codecGotError = CA.object "Error" (CAR.record { reason: CAR.optional CA.string }) +codecGotError = CA.object "Error" (CAR.record { reason: CA.string }) {- 1 -} -type Logged = { uid :: Int, token :: String } -codecGotToken ∷ CA.JsonCodec Logged -codecGotToken = CA.object "Logged" (CAR.record { "uid": CA.int, "token": CA.string }) +type Success = { } +codecSuccess ∷ CA.JsonCodec Success +codecSuccess = CA.object "Success" (CAR.record { }) {- 2 -} -type User = { user :: UserPublic.UserPublic } -codecGotUser ∷ CA.JsonCodec User -codecGotUser = CA.object "User" (CAR.record { user: UserPublic.codec }) +type ErrorInvalidToken = { } +codecErrorInvalidToken ∷ CA.JsonCodec ErrorInvalidToken +codecErrorInvalidToken = CA.object "ErrorInvalidToken" (CAR.record { }) {- 3 -} -type UserAdded = { user :: UserPublic.UserPublic } -codecGotUserAdded ∷ CA.JsonCodec UserAdded -codecGotUserAdded = CA.object "UserAdded" (CAR.record { user: UserPublic.codec }) +type DomainAlreadyExists = { } +codecDomainAlreadyExists ∷ CA.JsonCodec DomainAlreadyExists +codecDomainAlreadyExists = CA.object "DomainAlreadyExists" (CAR.record { }) -{- 4 -} -type UserEdited = { uid :: Int } -codecGotUserEdited ∷ CA.JsonCodec UserEdited -codecGotUserEdited = CA.object "UserEdited" (CAR.record { "uid": CA.int }) +{- 10 -} +-- For now, Error is just an alias on String. +-- type InvalidZone = { errors : Array(Storage::Zone::Error) } +type InvalidZone = { errors :: Array String } +codecInvalidZone ∷ CA.JsonCodec InvalidZone +codecInvalidZone = CA.object "InvalidZone" (CAR.record { errors: CA.array CA.string }) -{- 5 -} -type UserValidated = { user :: UserPublic.UserPublic } -codecGotUserValidated ∷ CA.JsonCodec UserValidated -codecGotUserValidated = CA.object "UserValidated" (CAR.record { user: UserPublic.codec }) +{- 11 -} +type DomainChanged = { } +codecDomainChanged ∷ CA.JsonCodec DomainChanged +codecDomainChanged = CA.object "DomainChanged" (CAR.record { }) -{- 6 -} -type UsersList = { users :: Array UserPublic.UserPublic } -codecGotUsersList ∷ CA.JsonCodec UsersList -codecGotUsersList = CA.object "UsersList" (CAR.record { users: CA.array UserPublic.codec }) +{- 12 -} +-- TODO +type Zone = { zone :: DNSZone } +codecZone ∷ CA.JsonCodec Zone +codecZone = CA.object "Zone" (CAR.record { zone: CA.string }) -{- 7 -} -type PermissionCheck - = { user :: Int - , service :: String - , resource :: String - , permission :: PermissionLevel.PermissionLevel } -codecGotPermissionCheck :: CA.JsonCodec PermissionCheck -codecGotPermissionCheck - = CA.object "PermissionCheck" (CAR.record - { user: CA.int - , service: CA.string - , resource: CA.string - , permission: PermissionLevel.codec }) +{- 13 -} +type UnknownZone = { } +codecUnknownZone ∷ CA.JsonCodec UnknownZone +codecUnknownZone = CA.object "UnknownZone" (CAR.record { }) -{- 8 -} -type PermissionSet - = { user :: Int - , service :: String - , resource :: String - , permission :: PermissionLevel.PermissionLevel } -codecGotPermissionSet :: CA.JsonCodec PermissionSet -codecGotPermissionSet - = CA.object "PermissionSet" (CAR.record - { user: CA.int - , service: CA.string - , resource: CA.string - , permission: PermissionLevel.codec }) +{- 14 -} +type DomainList = { domains :: Array String } +codecDomainList ∷ CA.JsonCodec DomainList +codecDomainList = CA.object "DomainList" (CAR.record { domains: CA.array CA.string }) -{- 9 -} -type PasswordRecoverySent = { user :: UserPublic.UserPublic } -codecGotPasswordRecoverySent ∷ CA.JsonCodec PasswordRecoverySent -codecGotPasswordRecoverySent - = CA.object "PasswordRecoverySent" (CAR.record { user: UserPublic.codec }) +{- 50 -} +type UnknownUser = { } +codecUnknownUser ∷ CA.JsonCodec UnknownUser +codecUnknownUser = CA.object "UnknownUser" (CAR.record { }) -{- 10 -} -type PasswordRecovered = { } -codecGotPasswordRecovered ∷ CA.JsonCodec PasswordRecovered -codecGotPasswordRecovered = CA.object "PasswordRecovered" (CAR.record { }) +{- 51 -} +type NoOwnership = { } +codecNoOwnership ∷ CA.JsonCodec NoOwnership +codecNoOwnership = CA.object "NoOwnership" (CAR.record { }) -{- 11 -} -type MatchingUsers = { users :: Array UserPublic.UserPublic } -codecGotMatchingUsers ∷ CA.JsonCodec MatchingUsers -codecGotMatchingUsers = CA.object "MatchingUsers" (CAR.record { users: CA.array UserPublic.codec }) - -{- 12 -} -type UserDeleted = { uid :: Int } -codecGotUserDeleted ∷ CA.JsonCodec UserDeleted -codecGotUserDeleted = CA.object "UserDeleted" (CAR.record { uid: CA.int }) - -{- 20 -} -type ErrorMustBeAuthenticated = {} -codecGotErrorMustBeAuthenticated :: CA.JsonCodec ErrorMustBeAuthenticated -codecGotErrorMustBeAuthenticated = CA.object "ErrorMustBeAuthenticated" (CAR.record {}) - -{- 21 -} -type ErrorAlreadyUsedLogin = {} -codecGotErrorAlreadyUsedLogin :: CA.JsonCodec ErrorAlreadyUsedLogin -codecGotErrorAlreadyUsedLogin = CA.object "ErrorAlreadyUsedLogin" (CAR.record {}) - -{- 22 -} -type ErrorMailRequired = {} -codecGotErrorMailRequired :: CA.JsonCodec ErrorMailRequired -codecGotErrorMailRequired = CA.object "ErrorMailRequired" (CAR.record {}) - -{- 23 -} -type ErrorUserNotFound = {} -codecGotErrorUserNotFound :: CA.JsonCodec ErrorUserNotFound -codecGotErrorUserNotFound = CA.object "ErrorUserNotFound" (CAR.record {}) - -{- 24 -} -type ErrorPasswordTooShort = {} -codecGotErrorPasswordTooShort :: CA.JsonCodec ErrorPasswordTooShort -codecGotErrorPasswordTooShort = CA.object "ErrorPasswordTooShort" (CAR.record {}) - -{- 25 -} -type ErrorInvalidCredentials = {} -codecGotErrorInvalidCredentials :: CA.JsonCodec ErrorInvalidCredentials -codecGotErrorInvalidCredentials = CA.object "ErrorInvalidCredentials" (CAR.record {}) - -{- 26 -} -type ErrorRegistrationsClosed = {} -codecGotErrorRegistrationsClosed :: CA.JsonCodec ErrorRegistrationsClosed -codecGotErrorRegistrationsClosed = CA.object "ErrorRegistrationsClosed" (CAR.record {}) - -{- 27 -} -type ErrorInvalidLoginFormat = {} -codecGotErrorInvalidLoginFormat :: CA.JsonCodec ErrorInvalidLoginFormat -codecGotErrorInvalidLoginFormat = CA.object "ErrorInvalidLoginFormat" (CAR.record {}) - -{- 28 -} -type ErrorInvalidEmailFormat = {} -codecGotErrorInvalidEmailFormat :: CA.JsonCodec ErrorInvalidEmailFormat -codecGotErrorInvalidEmailFormat = CA.object "ErrorInvalidEmailFormat" (CAR.record {}) - -{- 29 -} -type ErrorAlreadyUsersInDB = {} -codecGotErrorAlreadyUsersInDB :: CA.JsonCodec ErrorAlreadyUsersInDB -codecGotErrorAlreadyUsersInDB = CA.object "ErrorAlreadyUsersInDB" (CAR.record {}) - -{- 30 -} -type ErrorReadOnlyProfileKeys = { read_only_keys :: Array String } -codecGotErrorReadOnlyProfileKeys :: CA.JsonCodec ErrorReadOnlyProfileKeys -codecGotErrorReadOnlyProfileKeys - = CA.object "ErrorReadOnlyProfileKeys" (CAR.record { read_only_keys: CA.array CA.string }) - -{- 31 -} -type ErrorInvalidActivationKey = {} -codecGotErrorInvalidActivationKey :: CA.JsonCodec ErrorInvalidActivationKey -codecGotErrorInvalidActivationKey = CA.object "ErrorInvalidActivationKey" (CAR.record {}) - -{- 32 -} -type ErrorUserAlreadyValidated = {} -codecGotErrorUserAlreadyValidated :: CA.JsonCodec ErrorUserAlreadyValidated -codecGotErrorUserAlreadyValidated = CA.object "ErrorUserAlreadyValidated" (CAR.record {}) - -{- 33 -} -type ErrorCannotContactUser = {} -codecGotErrorCannotContactUser :: CA.JsonCodec ErrorCannotContactUser -codecGotErrorCannotContactUser = CA.object "ErrorCannotContactUser" (CAR.record {}) - -{- 34 -} -type ErrorInvalidRenewKey = {} -codecGotErrorInvalidRenewKey :: CA.JsonCodec ErrorInvalidRenewKey -codecGotErrorInvalidRenewKey = CA.object "ErrorInvalidRenewKey" (CAR.record {}) -- All possible requests. data RequestMessage - = MkLogin Login -- 0 - | MkRegister Register -- 1 - | MkValidateUser ValidateUser -- 2 - | MkAskPasswordRecovery AskPasswordRecovery -- 3 - | MkPasswordRecovery PasswordRecovery -- 4 - | MkGetUserByUID GetUserByUID -- 5 - | MkGetUserByName GetUserByName -- 5 (bis) - | MkModUser ModUser -- 6 - --| MkEditProfileContent EditProfileContent -- 7 - | MkDeleteUser DeleteUser -- 8 - | MkAddUser AddUser -- 9 - | MkCheckPermission CheckPermission -- 10 - | MkSetPermission SetPermission -- 11 - | MkSearchUser SearchUser -- 12 + = MkLogin Login -- 0 + | MkMaintenance Maintenance -- 7 + | MkNewDomain NewDomain -- 9 + | MkAddOrUpdateZone AddOrUpdateZone -- 10 + | MkDeleteZone DeleteZone -- 11 + | MkGetZone GetZone -- 12 + | MkUserDomains UserDomains -- 13 + | MkAddRR AddRR -- 14 + | MkUpdateRR UpdateRR -- 15 + | MkDeleteRR DeleteRR -- 16 -- All possible answers from the authentication daemon (authd). data AnswerMessage - = GotError Error -- 0 - | GotToken Logged -- 1 - | GotUser User -- 2 - | GotUserAdded UserAdded -- 3 - | GotUserEdited UserEdited -- 4 - | GotUserValidated UserValidated -- 5 - | GotUsersList UsersList -- 6 - | GotPermissionCheck PermissionCheck -- 7 - | GotPermissionSet PermissionSet -- 8 - | GotPasswordRecoverySent PasswordRecoverySent -- 9 - | GotPasswordRecovered PasswordRecovered -- 10 - | GotMatchingUsers MatchingUsers -- 11 - | GotUserDeleted UserDeleted -- 12 - | GotErrorMustBeAuthenticated ErrorMustBeAuthenticated -- 20 - | GotErrorAlreadyUsedLogin ErrorAlreadyUsedLogin -- 21 - | GotErrorMailRequired ErrorMailRequired -- 22 - | GotErrorUserNotFound ErrorUserNotFound -- 23 - | GotErrorPasswordTooShort ErrorPasswordTooShort -- 24 - | GotErrorInvalidCredentials ErrorInvalidCredentials -- 25 - | GotErrorRegistrationsClosed ErrorRegistrationsClosed -- 26 - | GotErrorInvalidLoginFormat ErrorInvalidLoginFormat -- 27 - | GotErrorInvalidEmailFormat ErrorInvalidEmailFormat -- 28 - | GotErrorAlreadyUsersInDB ErrorAlreadyUsersInDB -- 29 - | GotErrorReadOnlyProfileKeys ErrorReadOnlyProfileKeys -- 30 - | GotErrorInvalidActivationKey ErrorInvalidActivationKey -- 31 - | GotErrorUserAlreadyValidated ErrorUserAlreadyValidated -- 32 - | GotErrorCannotContactUser ErrorCannotContactUser -- 33 - | GotErrorInvalidRenewKey ErrorInvalidRenewKey -- 34 + = MkError Error -- 0 + | MkSuccess Success -- 1 + | MkErrorInvalidToken ErrorInvalidToken -- 2 + | MkDomainAlreadyExists DomainAlreadyExists -- 3 + | MkInvalidZone InvalidZone -- 10 + | MkDomainChanged DomainChanged -- 11 + | MkZone Zone -- 12 + | MkUnknownZone UnknownZone -- 13 + | MkDomainList DomainList -- 14 + | MkUnknownUser UnknownUser -- 50 + | MkNoOwnership NoOwnership -- 51 + encode ∷ RequestMessage -> Tuple UInt String encode m = case m of - (MkLogin request) -> get_tuple 0 codecLogin request - (MkRegister request) -> get_tuple 1 codecRegister request - (MkValidateUser request) -> get_tuple 2 codecValidateUser request - (MkAskPasswordRecovery request) -> get_tuple 3 codecAskPasswordRecovery request - (MkPasswordRecovery request) -> get_tuple 4 codecPasswordRecovery request - -- Both messages are actually a single message type, so they have the same number. - -- TODO: change the message codec for an Either Int String. - (MkGetUserByUID request) -> get_tuple 5 codecGetUserByUID request - (MkGetUserByName request) -> get_tuple 5 codecGetUserByName request - (MkModUser request) -> get_tuple 6 codecModUser request - -- 7 MkEditProfileContent - (MkDeleteUser request) -> get_tuple 8 codecDeleteUser request - (MkAddUser request) -> get_tuple 9 codecAddUser request - (MkCheckPermission request) -> get_tuple 10 codecCheckPermission request - (MkSetPermission request) -> get_tuple 11 codecSetPermission request - (MkSearchUser request) -> get_tuple 12 codecSearchUser request + (MkLogin request) -> get_tuple 0 codecLogin request + (MkMaintenance request) -> get_tuple 7 codecMaintenance request + (MkNewDomain request) -> get_tuple 9 codecNewDomain request + (MkAddOrUpdateZone request) -> get_tuple 10 codecAddOrUpdateZone request + (MkDeleteZone request) -> get_tuple 11 codecDeleteZone request + (MkGetZone request) -> get_tuple 12 codecGetZone request + (MkUserDomains request) -> get_tuple 13 codecUserDomains request + (MkAddRR request) -> get_tuple 14 codecAddRR request + (MkUpdateRR request) -> get_tuple 15 codecUpdateRR request + (MkDeleteRR request) -> get_tuple 16 codecDeleteRR request where get_tuple :: forall a. Int -> CA.JsonCodec a -> a -> Tuple UInt String get_tuple num codec request = Tuple (fromInt num) (J.stringify $ CA.encode codec request) @@ -431,34 +183,17 @@ data DecodeError decode :: Int -> String -> Either DecodeError AnswerMessage decode number string = case number of - 0 -> error_management codecGotError GotError - 1 -> error_management codecGotToken GotToken - 2 -> error_management codecGotUser GotUser - 3 -> error_management codecGotUserAdded GotUserAdded - 4 -> error_management codecGotUserEdited GotUserEdited - 5 -> error_management codecGotUserValidated GotUserValidated - 6 -> error_management codecGotUsersList GotUsersList - 7 -> error_management codecGotPermissionCheck GotPermissionCheck - 8 -> error_management codecGotPermissionSet GotPermissionSet - 9 -> error_management codecGotPasswordRecoverySent GotPasswordRecoverySent - 10 -> error_management codecGotPasswordRecovered GotPasswordRecovered - 11 -> error_management codecGotMatchingUsers GotMatchingUsers - 12 -> error_management codecGotUserDeleted GotUserDeleted - 20 -> error_management codecGotErrorMustBeAuthenticated GotErrorMustBeAuthenticated - 21 -> error_management codecGotErrorAlreadyUsedLogin GotErrorAlreadyUsedLogin - 22 -> error_management codecGotErrorMailRequired GotErrorMailRequired - 23 -> error_management codecGotErrorUserNotFound GotErrorUserNotFound - 24 -> error_management codecGotErrorPasswordTooShort GotErrorPasswordTooShort - 25 -> error_management codecGotErrorInvalidCredentials GotErrorInvalidCredentials - 26 -> error_management codecGotErrorRegistrationsClosed GotErrorRegistrationsClosed - 27 -> error_management codecGotErrorInvalidLoginFormat GotErrorInvalidLoginFormat - 28 -> error_management codecGotErrorInvalidEmailFormat GotErrorInvalidEmailFormat - 29 -> error_management codecGotErrorAlreadyUsersInDB GotErrorAlreadyUsersInDB - 30 -> error_management codecGotErrorReadOnlyProfileKeys GotErrorReadOnlyProfileKeys - 31 -> error_management codecGotErrorInvalidActivationKey GotErrorInvalidActivationKey - 32 -> error_management codecGotErrorUserAlreadyValidated GotErrorUserAlreadyValidated - 33 -> error_management codecGotErrorCannotContactUser GotErrorCannotContactUser - 34 -> error_management codecGotErrorInvalidRenewKey GotErrorInvalidRenewKey + 0 -> error_management codecError MkError + 1 -> error_management codecSuccess MkSuccess + 2 -> error_management codecErrorInvalidToken MkErrorInvalidToken + 3 -> error_management codecDomainAlreadyExists MkDomainAlreadyExists + 10 -> error_management codecInvalidZone MkInvalidZone + 11 -> error_management codecDomainChanged MkDomainChanged + 12 -> error_management codecZone MkZone + 13 -> error_management codecUnknownZone MkUnknownZone + 14 -> error_management codecDomainList MkDomainList + 50 -> error_management codecUnknownUser MkUnknownUser + 51 -> error_management codecNoOwnership MkNoOwnership _ -> Left UnknownNumber where -- Signature is required since the compiler's guess is wrong.