syntax = "proto3";

// Proto field names should be snake_case, but some plugins don't convert to 
// camel like they should, so we use camelCase here.

package service;
option go_package=".;service";

import "patch/go.proto";

service RPC {  
  rpc KeyGenerate(KeyGenerateRequest) returns (KeyGenerateResponse) {}
  rpc Keys(KeysRequest) returns (KeysResponse) {}
  rpc Key(KeyRequest) returns (KeyResponse) {}
  rpc KeyImport(KeyImportRequest) returns (KeyImportResponse) {}
  rpc KeyExport(KeyExportRequest) returns (KeyExportResponse) {}
  rpc KeyRemove(KeyRemoveRequest) returns (KeyRemoveResponse) {}

  rpc Sign(SignRequest) returns (SignResponse) {}
  rpc SignFile(stream SignFileInput) returns (stream SignFileOutput) {}
  rpc SignStream(stream SignInput) returns (stream SignOutput) {}

  rpc Verify(VerifyRequest) returns (VerifyResponse) {}
  rpc VerifyFile(stream VerifyFileInput) returns (stream VerifyFileOutput) {}
  rpc VerifyStream(stream VerifyInput) returns (stream VerifyOutput) {}  
  rpc VerifyDetached(VerifyDetachedRequest) returns (VerifyDetachedResponse) {}
  rpc VerifyDetachedFile(stream VerifyDetachedFileInput) returns (VerifyDetachedResponse) {}
  rpc VerifyDetachedStream(stream VerifyDetachedInput) returns (VerifyDetachedResponse) {}  

  rpc Encrypt(EncryptRequest) returns (EncryptResponse) {}  
  rpc EncryptStream(stream EncryptInput) returns (stream EncryptOutput) {}  
  rpc EncryptFile(stream EncryptFileInput) returns (stream EncryptFileOutput) {}
  
  rpc Decrypt(DecryptRequest) returns (DecryptResponse) {}  
  rpc DecryptStream(stream DecryptInput) returns (stream DecryptOutput) {}
  rpc DecryptFile(stream DecryptFileInput) returns (stream DecryptFileOutput) {}

  rpc Sigchain(SigchainRequest) returns (SigchainResponse) {}
  rpc Statement(StatementRequest) returns (StatementResponse) {}
  rpc StatementCreate(StatementCreateRequest) returns (StatementCreateResponse) {}  
  rpc StatementRevoke(StatementRevokeRequest) returns (StatementRevokeResponse) {}  

  rpc User(UserRequest) returns (UserResponse) {}  
  rpc UserSearch(UserSearchRequest) returns (UserSearchResponse) {}
  rpc UserService(UserServiceRequest) returns (UserServiceResponse) {}
  rpc UserSign(UserSignRequest) returns (UserSignResponse) {}
  rpc UserAdd(UserAddRequest) returns (UserAddResponse) {}    

  rpc Search(SearchRequest) returns (SearchResponse) {}
  
  rpc Secret(SecretRequest) returns (SecretResponse) {}
  rpc SecretSave(SecretSaveRequest) returns (SecretSaveResponse) {}
  rpc SecretRemove(SecretRemoveRequest) returns (SecretRemoveResponse) {}
  rpc Secrets(SecretsRequest) returns (SecretsResponse) {}

  rpc Pull(PullRequest) returns (PullResponse) {}
  rpc Push(PushRequest) returns (PushResponse) {}

  rpc Wormhole(stream WormholeInput) returns (stream WormholeOutput) {}  
  
  // These requests do not need auth.
  // BEGIN NO AUTH
  rpc AuthSetup(AuthSetupRequest) returns (AuthSetupResponse) {}
  rpc AuthVault(AuthVaultRequest) returns (AuthVaultResponse) {}
  rpc AuthUnlock(AuthUnlockRequest) returns (AuthUnlockResponse) {}
  rpc AuthLock(AuthLockRequest) returns (AuthLockResponse) {}
  rpc AuthReset(AuthResetRequest) returns (AuthResetResponse) {}
  rpc AuthRecover(AuthRecoverRequest) returns (AuthRecoverResponse) {}
  rpc RuntimeStatus(RuntimeStatusRequest) returns (RuntimeStatusResponse) {}
  rpc Rand(RandRequest) returns (RandResponse) {}
  rpc RandPassword(RandPasswordRequest) returns (RandPasswordResponse) {}  
  // END NO AUTH

  // Auth (edit)
  rpc AuthProvision(AuthProvisionRequest) returns (AuthProvisionResponse) {}
  rpc AuthDeprovision(AuthDeprovisionRequest) returns (AuthDeprovisionResponse) {}
  rpc AuthProvisions(AuthProvisionsRequest) returns (AuthProvisionsResponse) {}  
  rpc AuthPasswordChange(AuthPasswordChangeRequest) returns (AuthPasswordChangeResponse) {}
  
  // Vault
  rpc VaultAuth(VaultAuthRequest) returns (VaultAuthResponse) {}
  rpc VaultSync(VaultSyncRequest) returns (VaultSyncResponse) {}  
  rpc VaultUnsync(VaultUnsyncRequest) returns (VaultUnsyncResponse) {}
  rpc VaultStatus(VaultStatusRequest) returns (VaultStatusResponse) {}  
  rpc VaultUpdate(VaultUpdateRequest) returns (VaultUpdateResponse) {}  

  // DB
  rpc Collections(CollectionsRequest) returns (CollectionsResponse) {}
  rpc Documents(DocumentsRequest) returns (DocumentsResponse) {}
  rpc DocumentDelete(DocumentDeleteRequest) returns (DocumentDeleteResponse) {}

  // Config
  rpc ConfigGet(ConfigGetRequest) returns (ConfigGetResponse) {}
  rpc ConfigSet(ConfigSetRequest) returns (ConfigSetResponse) {}  

  // Admin
  rpc AdminSignURL(AdminSignURLRequest) returns (AdminSignURLResponse) {}
  rpc AdminCheck(AdminCheckRequest) returns (AdminCheckResponse) {}

  //
  // Experimental
  //

  // Channels
  rpc Channels(ChannelsRequest) returns (ChannelsResponse) {}
  rpc ChannelCreate(ChannelCreateRequest) returns (ChannelCreateResponse) {}
  rpc ChannelInvite(ChannelInviteRequest) returns (ChannelInviteResponse) {}
  rpc ChannelLeave(ChannelLeaveRequest) returns (ChannelLeaveResponse) {}
  rpc ChannelRead(ChannelReadRequest) returns (ChannelReadResponse) {}

  // Follow
  rpc Follow(FollowRequest) returns (FollowResponse) {}
  rpc Follows(FollowsRequest) returns (FollowsResponse) {}
  
  // Messages
  rpc MessagePrepare(MessagePrepareRequest) returns (MessagePrepareResponse) {}
  rpc MessageCreate(MessageCreateRequest) returns (MessageCreateResponse) {}
  rpc Messages(MessagesRequest) returns (MessagesResponse) {}

  // Relay
  rpc Relay(RelayRequest) returns (stream RelayOutput) {}
}

message SignRequest {
  bytes data = 1;
  
  string signer = 5;
  // Armored, if true, output will be armored.
  bool armored = 10;
  // Detached, if true, output will be just the signature.
  bool detached = 11;
}
message SignResponse {
  // Data is signed output.
  bytes data = 1;  
  string kid = 2 [(go.field) = {name: "KID"}];  
}

message SignFileInput {
  // In is input file path. 
  string in = 1;
  // Out file path (or sig if detached).
  string out = 2;

  string signer = 5;
  // Armored, if true, output will be armored.
  bool armored = 10;
  // Detached, if true, output will be just the signature.
  bool detached = 11;
}
message SignFileOutput {
  string kid = 1 [(go.field) = {name: "KID"}];  
  string out = 2;
  int32 bytes = 10;
  int32 total = 11;
}

message VerifyRequest {
  // Data is verified output.
  bytes data = 1;
}
message VerifyResponse {
  // Data (if out is not specified in request).
  bytes data = 1;
  Key signer = 2;
}

message VerifyDetachedRequest {
  // Data to verify.
  bytes data = 1;
  bytes sig = 2;  
}
message VerifyDetachedResponse {
  Key signer = 1;
}

message VerifyInput {
  // Data to verify.
  bytes data = 1;  
}
message VerifyOutput {
  // Data, verified. If empty, is EOF.
  bytes data = 1;
  Key signer = 2;
}

message VerifyFileInput {
  // In is input file path.  
  string in = 1;
  // Out is the output file name or directory to save to.
  string out = 2;  
}
message VerifyFileOutput {
  Key signer = 1;
  string out = 2;  
}

message VerifyDetachedFileInput {
  // In is input file path.  
  string in = 1;
  // Signature (detached).
  bytes sig = 2;
}

message VerifyDetachedInput {
  // Data to verify.
  bytes data = 1;
  // Signature (detached).
  bytes sig = 2;
}

message Statement {
  // Sig is the signature bytes.
  bytes sig = 1;
  // Data that was signed.
  bytes data = 2;
  // KID is the key that signed.
  string kid = 3 [(go.field) = {name: "KID"}];
  // Seq in a sigchain (1 is root).
  int32 seq = 4;
  // Prev is a hash of the previous item in the sigchain.
  bytes prev = 5;
  // Revoke refers to a previous signed seq to revoke.
	int32 revoke = 6;
  // Timestamp ...
  int64 timestamp = 7;
	// Type (optional).
  string type = 8;  
}

message SigchainRequest {
  string kid = 1 [(go.field) = {name: "KID"}];  
}
message SigchainResponse {
  Key key = 1;
  repeated Statement statements = 2;
}

message StatementRequest {
  string kid = 1 [(go.field) = {name: "KID"}];  
  int32 seq = 2;
}
message StatementResponse {
  Statement statement = 1;
}

message StatementCreateRequest {
  bytes data = 1;
  string kid = 2 [(go.field) = {name: "KID"}];  

  // Local, if true, won't save to the current key server.
  bool local = 5;
}
message StatementCreateResponse {
  Statement statement = 1;
}

message StatementRevokeRequest {
  // Seq to revoke.
  int32 seq = 1;
  string kid = 2 [(go.field) = {name: "KID"}];  

  // Local, if true, won't save to the current key server.
  bool local = 5;
}
message StatementRevokeResponse {
  Statement statement = 1;
}

message SignInput {
  bytes data = 1;
  // In is input file path (if data is not specified).  
  string in = 2;
  // Out is output file path (required if in specified).
  string out = 3;

  string signer = 5;
  // Armored, if true, output will be armored.
  bool armored = 10;
  // Detached, if true, output will be just the signature.
  bool detached = 11;
}
message SignOutput {
  // Data, signed.
  bytes data = 1;
  string kid = 2 [(go.field) = {name: "KID"}];  
}

enum EncryptMode {
  option (go.enum) = {name: "EncryptMode"};
  
  DEFAULT_ENCRYPT = 0 [(go.value) = {name: "DefaultEncrypt"}];
  SALTPACK_ENCRYPT = 1 [(go.value) = {name: "SaltpackEncrypt"}];
  SALTPACK_SIGNCRYPT = 3 [(go.value) = {name: "SaltpackSigncrypt"}];  
}

message EncryptOptions {
  // Armored, if true will return armored string output.
  bool armored = 1;
  // Mode is the encryption mode.
  EncryptMode mode = 2;
  // NoSenderRecipient if true, won't add sender to recipients list.
  bool noSenderRecipient = 3;
  // NoSign if true, won't sign with sender.
  bool noSign = 4;
}

message EncryptRequest {
  // Data to encrypt.
  bytes data = 1;
  // Recipients to encrypt to.
  repeated string recipients = 3;
  // Sender, or anonymous.
  string sender = 4;  

  // Options for encrypt.
  EncryptOptions options = 10;
}
message EncryptResponse {
  bytes data = 1;
  string info = 2;
}

message EncryptFileInput {
  // In is input file path. 
  string in = 1;
  // Out is output file path.
  string out = 2;
  // Recipients to encrypt to.
  repeated string recipients = 3;
  // Sender, or anonymous.
  string sender = 4;  

  // Options for encrypt.
  EncryptOptions options = 10;
}

message EncryptFileOutput {
  // Out is the output decrypted file path.
  string out = 2;
  int32 bytes = 10;
  int32 total = 11;
}

message EncryptInput {
  // Data to encrypt. Send empty byte slice as last message.
  bytes data = 1;
  // Recipients to encrypt to.
  repeated string recipients = 3;
  // Sender, or anonymous.
  string sender = 4;  

  // Options for encrypt.
  EncryptOptions options = 10;
}

message EncryptOutput {
  // Data, encrypted.
  bytes data = 1;
}

message DecryptRequest {
  // Data to decrypt.
  bytes data = 1;  
}
message DecryptResponse {
  // Data decrypted.
  bytes data = 1;
  // Sender (if not anonymous)
  Key sender = 2;
  // Mode is the encryption mode for the data.
  EncryptMode mode = 5;
}

message DecryptFileInput {
  // In is the input encrypted file path. 
  string in = 1;
  // Out is the output file name or directory to save to.
  string out = 2;
}
message DecryptFileOutput {
  Key sender = 1;
  // Out is the output file path.
  string out = 2;
  
  int32 bytes = 10;
  int32 total = 11;
  
  // Mode is the encryption mode we detected.
  EncryptMode mode = 5;
}

message DecryptInput {
  // Data, encrypted.
  bytes data = 1;  
}
message DecryptOutput {
  // Data, decrypted. If empty, is EOF.
  bytes data = 1;
  // Sender (if not anonymous)
  Key sender = 2;
  // Mode is the encryption mode for the data.
  EncryptMode mode = 5;
}

message RuntimeStatusRequest {}
message RuntimeStatusResponse {
  // Version of running service.
  string version = 1;
  // AppName app name.
  string appName = 2;
  // Exe is the service executable path.
  string exe = 4;
  // AuthStatus is the status of auth.
  AuthStatus authStatus = 5;
  // SyncStatus is the status of vault sync.
  bool sync = 6;
  // FIDO2 available.
  bool fido2 = 20 [(go.field) = {name: "FIDO2"}];
}

enum AuthStatus {
  option (go.enum) = {name: "AuthStatus"};
  
  AUTH_UNKNOWN = 0 [(go.value) = {name: "AuthUnknown"}];
  // AuthSetupNeeded if setup needed.
  AUTH_SETUP_NEEDED = 1 [(go.value) = {name: "AuthSetupNeeded"}];  
  // AuthUnlocked if unlocked.
  AUTH_UNLOCKED = 2 [(go.value) = {name: "AuthUnlocked"}];  
  // AuthLocked if locked.
  AUTH_LOCKED = 3 [(go.value) = {name: "AuthLocked"}];  
}

enum AuthType {
  option (go.enum) = {name: "AuthType"};
  
  UNKNOWN_AUTH = 0 [(go.value) = {name: "UnknownAuth"}];
  // PasswordAuth uses password with salt (argon2id).
  PASSWORD_AUTH = 10 [(go.value) = {name: "PasswordAuth"}];
  // PaperKeyAuth uses a BIP39 phrase representing a key.
  PAPER_KEY_AUTH = 11 [(go.value) = {name: "PaperKeyAuth"}];
  // FIDO2HMACSecretAuth uses a FIDO2 HMAC-Secret extension.
  FIDO2_HMAC_SECRET_AUTH = 20 [(go.value) = {name: "FIDO2HMACSecretAuth"}];
}

message AuthSetupRequest {
  // Secret for auth depending on auth type, e.g. password, FIDO2 pin, etc.
  string secret = 1;
  // Type for auth.
  AuthType type = 2;  
  // Device path (for FIDO2).
  string device = 5;
}
message AuthSetupResponse {}

message AuthVaultRequest {
  string phrase = 1;
}
message AuthVaultResponse {}

message AuthUnlockRequest {
  // Secret for auth depending on auth type, e.g. password, pin, etc.
  string secret = 1;
  // Type for auth.
  AuthType type = 2;
  // Client name.
  string client = 6;
}
message AuthUnlockResponse {
  // AuthToken to use for requests.
  string authToken = 1;
}

message AuthProvisionRequest {
  // Secret for auth depending on auth type, e.g. password, phrase, FIDO2 pin, etc.
  string secret = 1;
  // Type for auth.
  AuthType type = 2;
  // Device path (for FIDO2).
  string device = 5;
  // Generate (for FIDO2 make credential).
  bool generate = 7;
}
message AuthProvisionResponse {
  AuthProvision provision = 1;
}

message AuthDeprovisionRequest {
  string id = 1 [(go.field) = {name: "ID"}];
}
message AuthDeprovisionResponse {}

message AuthPasswordChangeRequest {
  string old = 1;
  string new = 2;
}
message AuthPasswordChangeResponse {}

message AuthProvision {
  string id = 1 [(go.field) = {name: "ID"}];
  AuthType type = 2;
  int64 createdAt = 3;

  // For FIDO2
  // AAGUID is a device "identifier" (only unique across batches for privacy reasons).
  string aaguid = 100 [(go.field) = {name: "AAGUID"}];
  bool noPin = 101;
}

message AuthProvisionsRequest {}
message AuthProvisionsResponse {
  repeated AuthProvision provisions = 1;
}

message AuthLockRequest {}
message AuthLockResponse {}

message AuthResetRequest {
  string appName = 1;
}
message AuthResetResponse {}

message AuthRecoverRequest {
  string paperKey = 1;
  string newPassword = 2;
}
message AuthRecoverResponse {
  string authToken = 1;
}

message KeyGenerateRequest {
  string type = 1;
}
message KeyGenerateResponse {
  string kid = 1 [(go.field) = {name: "KID"}];  
}

message UserServiceRequest {
  // KID to use, or if empty the current key.
  string kid = 1 [(go.field) = {name: "KID"}];
  // Service such as twitter, github.
  string service = 2;
}
message UserServiceResponse {
  // Service such as twitter, github.
  string service = 1;
}

message UserSignRequest {
  // KID to use, or if empty the current key.
  string kid = 1 [(go.field) = {name: "KID"}];
  // Service such as twitter, github.
  string service = 2;
  // Name is username on the service.
  string name = 3;
}
message UserSignResponse {
  // Message is signed message.
  string message = 1;
  // Name in request.
  string name = 2;
}

message UserAddRequest {
  // KID to use, or if empty the current key.
  string kid = 1 [(go.field) = {name: "KID"}];
  // Service such as twitter, github.
  string service = 2;
  // Name is username on the service.
  string name = 3; 
  // URL is location of signed message on the service.
  string url = 4 [(go.field) = {name: "URL"}];

  // Local, if true, won't save to the current key server.
  bool local = 5;
}
message UserAddResponse {
  User user = 1;
  Statement statement = 2;
}

enum ExportType {
  option (go.enum) = {name: "ExportType"};
  
  DEFAULT_EXPORT_TYPE = 0 [(go.value) = {name: "DefaultExport"}];
  SSH_EXPORT_TYPE = 1 [(go.value) = {name: "SSHExport"}];
}

message KeyExportRequest {
  string kid = 1 [(go.field) = {name: "KID"}];
  string password = 2;
  ExportType type = 3;
  bool public = 4;
  bool noPassword = 5;
}
message KeyExportResponse {
  bytes export = 1;
}

message KeyImportRequest {
  bytes in = 1;
  string password = 2;  
  bool update = 3;
}
message KeyImportResponse {
  string kid = 1 [(go.field) = {name: "KID"}];
}

message KeyRemoveRequest {
  // KID of key to remove.
  string kid = 1 [(go.field) = {name: "KID"}];
}
message KeyRemoveResponse {}

message Key {
  // ID identifier.
  string id = 1 [(go.field) = {name: "ID"}];
  // Type of key.
  string type = 3;
  // IsPrivate if private key.
  bool isPrivate = 4;
  // User associated with this key.
  User user = 6;  
  // Saved if saved locally.
  bool saved = 10;
  

  // SigchainLength is length of sigchain (if any).
  int32 sigchainLength = 40;
  int64 sigchainUpdatedAt = 41;
}

message KeyRequest {
  // Key can be kid or user@service.
  string key = 1;
  
  // Search, if true, will search for the key on the server.
  bool search = 4;
  // Update, if true, will update the sigchain from the server.
  bool update = 5;  
}

message KeyResponse {
  Key key = 1;
}

enum SortDirection {
  option (go.enum) = {name: "SortDirection"};

  ASC = 0  [(go.value) = {name: "SortAsc"}];
  DESC = 1 [(go.value) = {name: "SortDesc"}];
}

message KeysRequest {
  string query = 1;
  repeated string types = 2; 

  string sortField = 10;
  SortDirection sortDirection = 11;
}
message KeysResponse {
  repeated Key keys = 1;
  string sortField = 10;
  SortDirection sortDirection = 11;
}

enum SecretType {
  option (go.enum) = {name: "SecretType"};
  
  UNKNOWN_SECRET_TYPE = 0 [(go.value) = {name: "UnknownSecret"}];
  
  PASSWORD_SECRET = 10 [(go.value) = {name: "PasswordSecret"}];  
  CONTACT_SECRET = 11 [(go.value) = {name: "ContactSecret"}];  
  CARD_SECRET = 12 [(go.value) = {name: "CardSecret"}];  
  NOTE_SECRET = 13 [(go.value) = {name: "NoteSecret"}];  
}

message Secret {
  string id = 1 [(go.field) = {name: "ID"}];
  string name = 2;
  SecretType type = 3;

  string username = 10;
  string password = 11;
	
  string url = 20 [(go.field) = {name: "URL"}];

  string notes = 30;

  int64 createdAt = 100;
  int64 updatedAt = 101;
}

message SecretRequest {
  string id = 1 [(go.field) = {name: "ID"}];
}
message SecretResponse {
  Secret secret = 1;
}

message SecretSaveRequest {
  Secret secret = 1;
}
message SecretSaveResponse {
  Secret secret = 1;
}

message SecretRemoveRequest {
  string id = 1 [(go.field) = {name: "ID"}];
}

message SecretRemoveResponse {}

message SecretsRequest {
  string query = 1;  
  string sortField = 10;
  SortDirection sortDirection = 11;  
}
message SecretsResponse {
  repeated Secret secrets = 1;
  string sortField = 10;
  SortDirection sortDirection = 11;  
}

enum Encoding {
  option (go.enum) = {name: "Encoding"};

  HEX = 0  [(go.value) = {name: "Hex"}];
  BASE62 = 1 [(go.value) = {name: "Base62"}];    
  BASE58 = 2 [(go.value) = {name: "Base58"}];    
  BASE32 = 3 [(go.value) = {name: "Base32"}];    
  BASE16 = 4 [(go.value) = {name: "Base16"}];    
  BASE64 = 5 [(go.value) = {name: "Base64"}];    
  SALTPACK = 6 [(go.value) = {name: "Saltpack"}];    
  BIP39 = 7 [(go.value) = {name: "BIP39"}];    
}

message RandRequest {
  int32 numBytes = 1;
  Encoding encoding = 2;
  bool noPadding = 10;
  bool lowercase = 11;
}
message RandResponse {
  string data = 1;
}

message RandPasswordRequest {
  int32 length = 1;
}
message RandPasswordResponse {
  string password = 1;
}

message PullRequest {
  string key = 1;  
}
message PullResponse {
  repeated string kids = 1 [(go.field) = {name: "KIDs"}];
}

message PushRequest {
  string key = 1;
  bool remoteCheck = 2;
}
message PushResponse {
  string kid = 1 [(go.field) = {name: "KID"}];
  repeated string urls = 2 [(go.field) = {name: "URLs"}];
}

message Collection {
  string path = 1;
}

message CollectionsRequest {
  string parent = 1;

  // DB is "service" or "vault".
  string db = 5 [(go.field) = {name: "DB"}];
}
message CollectionsResponse {
  repeated Collection collections = 1;
}

message Document {
  string path = 1;
  string value = 2;

  int64 createdAt = 10;
  int64 updatedAt = 11;
}

message DocumentsRequest {
  string prefix = 1;

  // DB is "service" or "vault".
  string db = 5 [(go.field) = {name: "DB"}];  
}
message DocumentsResponse {
  repeated Document documents = 1;
}

message DocumentDeleteRequest {  
  string path = 1;
  string db = 5 [(go.field) = {name: "DB"}];
}
message DocumentDeleteResponse {}

enum UserStatus {
  option (go.enum) = {name: "UserStatus"};

  USER_UNKNOWN = 0 [(go.value) = {name: "UserStatusUnknown"}];
  USER_OK = 1 [(go.value) = {name: "UserStatusOK"}];
  
  USER_RESOURCE_NOT_FOUND = 20 [(go.value) = {name: "UserStatusResourceNotFound"}];
  
  USER_CONTENT_NOT_FOUND = 30 [(go.value) = {name: "UserStatusContentNotFound"}];
  USER_CONTENT_INVALID = 31 [(go.value) = {name: "UserStatusContentInvalid"}];
  
  USER_CONN_FAILURE = 40 [(go.value) = {name: "UserStatusConnFailure"}];
  
  USER_FAILURE = 50 [(go.value) = {name: "UserStatusFailure"}];
}

message User {  
  string id = 1 [(go.field) = {name: "ID"}];
  string name = 2;
  string kid = 3 [(go.field) = {name: "KID"}];
  int32 seq = 4;
  string service = 5; 
  string url = 6 [(go.field) = {name: "URL"}];
  
  UserStatus status = 10;
  int64 verifiedAt = 11; 
  int64 timestamp = 12;
  bool proxied = 13;
  
  string err = 20;
}

message UserRequest {
  string kid = 1 [(go.field) = {name: "KID"}];
  bool local = 4;
}
message UserResponse {
  User user = 1;
}

message UserSearchRequest {
  string query = 1;  
  int32 limit = 3;
  bool local = 4;
}
message UserSearchResponse {
  repeated User users = 1;  
}

message SearchRequest {
  string query = 1;  
}
message SearchResponse {
  repeated Key keys = 1;  
}

message VaultSyncRequest {}
message VaultSyncResponse {}

message VaultUnsyncRequest {}
message VaultUnsyncResponse {}

message VaultAuthRequest {}
message VaultAuthResponse {
  string phrase = 1;
}

message VaultStatusRequest {}
message VaultStatusResponse {
  string kid = 1 [(go.field) = {name: "KID"}];
  int64 syncedAt = 2;
}

message VaultUpdateRequest {}
message VaultUpdateResponse {}

enum MessageStatus {
  option (go.enum) = {name: "MessageStatus"};

  MESSAGE_SENT = 0 [(go.value) = {name: "MessageSent"}];
  MESSAGE_PENDING = 1 [(go.value) = {name: "MessagePending"}];
  MESSAGE_ERROR = 2 [(go.value) = {name: "MessageError"}];
}

message Message {
  string id = 1 [(go.field) = {name: "ID"}];
  Key sender = 2;
  repeated string text = 10;
  
  MessageStatus status = 20;
  
  int64 createdAt = 31;
}

message MessagePrepareRequest {  
  string sender = 1;
  string channel = 2;
  string text = 11;
}

message MessagePrepareResponse {
  Message message = 1;
}

message MessageCreateRequest {  
  string sender = 1;
  string channel = 2;
  // ID is optional, if message was prepared you can use that ID, otherwise it 
  // is autogenerated.
  string id = 10 [(go.field) = {name: "ID"}];
  string text = 11;
}

message MessageCreateResponse {
  Message message = 1;
}

message MessagesRequest {  
  string channel = 1;
  
  // Update, if true will update from the server.
  bool update = 5;  
}

message MessagesResponse {
  repeated Message messages = 1;
}

message RelayRequest {
  repeated string keys = 1;
  string user = 2;
}

message RelayOutput {
  string channel = 1;
  string user = 2;
  int64 index = 3;
}

message Channel {
  string id = 1 [(go.field) = {name: "ID"}];
  string name = 2;
  string snippet = 3;
  int64 updatedAt = 4;
  int64 index = 5;
  int64 readIndex = 10;
}

message ChannelsRequest {
  string user = 1;
}
message ChannelsResponse {
  repeated Channel channels = 1;
}

message ChannelCreateRequest {
  string name = 1;
  string user = 2;
}
message ChannelCreateResponse {
  Channel channel = 1;
}

message ChannelLeaveRequest {
  string channel = 1;
}
message ChannelLeaveResponse {}

message ChannelReadRequest {
  string channel = 1;
  int64 index = 2;
}
message ChannelReadResponse {}

message ChannelInviteRequest {
  string channel = 1;
  repeated string recipients = 2;
  string sender = 3;
}
message ChannelInviteResponse {
  Message message = 1;
}

message Follow {
  string recipient = 1;
  string sender = 2;
}

message FollowRequest {
  string recipient = 1;
  string sender = 2;

}
message FollowResponse {
  Follow follow = 1;
}

message FollowsRequest {
  string recipient = 1;
  bool update = 2;
}
message FollowsResponse {
  repeated Follow follows = 1;
}

message AdminSignURLRequest {
  string signer = 1;
  string method = 2;
  string url = 3 [(go.field) = {name: "URL"}];
}

message AdminSignURLResponse {
  string auth = 1;
  string url = 2 [(go.field) = {name: "URL"}];
  string curl = 3  [(go.field) = {name: "CURL"}];
}

message AdminCheckRequest {
  string signer = 1;
  string check = 2;
}

message AdminCheckResponse {}

message Config {
  message App {
    string location = 1;
    repeated string history = 2;    
    bool navMinimized = 100;
  }

  message Encrypt {
    repeated string recipients = 1;
    string sender = 2;
      
    bool noSenderRecipient = 3;
    bool noSign = 4;
  }

  message Sign {
    string signer = 1;  
  }

  App app = 1;
  Encrypt encrypt = 10;
  Sign sign = 11;
}

message ConfigGetRequest {
  string name = 1;  
}
message ConfigGetResponse {
  Config config = 1;
}

message ConfigSetRequest {
  string name = 1;
  Config config = 2;
}

message ConfigSetResponse {}

message WormholeInput {  
  string sender = 1;
  string recipient = 2;
  string invite = 3;
    
  string id = 10 [(go.field) = {name: "ID"}];
  string text = 11;
}

enum WormholeStatus {
  option (go.enum) = {name: "WormholeStatus"};

  WORMHOLE_DEFAULT = 0 [(go.value) = {name: "WormholeDefault"}];
  
  // WormholeStarting if starting.
  WORMHOLE_STARTING = 10 [(go.value) = {name: "WormholeStarting"}];
  
  // WormholeOffering if offering.
  WORMHOLE_OFFERING = 20 [(go.value) = {name: "WormholeOffering"}];
  // WormholeAnswering if answering.
  WORMHOLE_ANSWERING = 21 [(go.value) = {name: "WormholeAnswering"}];

  // WormholeHandshake if found offer/answer and trying to SCTP/Noise handshake.
  WORMHOLE_HANDSHAKE = 40 [(go.value) = {name: "WormholeHandshake"}];
  
  // WormholeConnected if connected
  WORMHOLE_CONNECTED = 100 [(go.value) = {name: "WormholeConnected"}];
  // WormholeClosed if closed
  WORMHOLE_CLOSED = 200 [(go.value) = {name: "WormholeClosed"}]; 
}

message WormholeMessage {
  string id = 1 [(go.field) = {name: "ID"}];
  
  Key sender = 2;
  Key recipient = 3;

  WormholeMessageStatus status = 5;

  string text = 10;
  int64 createdAt = 21;
}

message WormholeOutput {
  WormholeMessage message = 1;
  WormholeStatus status = 2;
}

enum WormholeMessageStatus {
  option (go.enum) = {name: "WormholeMessageStatus"};

  WORMHOLE_MESSAGE_SENT = 0 [(go.value) = {name: "WormholeMessageSent"}];
  WORMHOLE_MESSAGE_PENDING = 1 [(go.value) = {name: "WormholeMessagePending"}];
  WORMHOLE_MESSAGE_ACK = 2 [(go.value) = {name: "WormholeMessageAck"}];
}
