syntax = "proto3";

option java_package = "com.soulfiremc.grpc.generated";
option java_multiple_files = true;

package soulfire.v1;

import "soulfire/common.proto";
import "soulfire/api_docs.proto";
import "soulfire/bot.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";

// Represents a user associated with an instance, typically used in audit logs
// to track who performed an action.
message InstanceUser {
  // Unique identifier for the user (UUID format).
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The username of the user.
  string username = 2 [(google.api.field_behavior) = REQUIRED];
  // The email address of the user.
  string email = 3 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "email"];
}

// Contains the complete configuration for an instance, including settings,
// Minecraft accounts, and proxies.
message InstanceConfig {
  // Instance settings organized by namespace. Each namespace contains
  // key-value pairs for configuration options.
  repeated SettingsNamespace settings = 1 [(google.api.field_behavior) = UNORDERED_LIST];
  // List of Minecraft accounts available to this instance for bot connections.
  repeated MinecraftAccountProto accounts = 2;
  // List of proxies available to this instance for routing bot connections.
  repeated ProxyProto proxies = 3;
  // Persistent metadata stored with the instance across sessions.
  // Organized as namespace -> key -> value mappings.
  // Used by plugins and the system to store instance-specific state
  // that should persist across restarts.
  repeated SettingsNamespace persistent_metadata = 4 [(google.api.field_behavior) = UNORDERED_LIST];
}

// Request to create a new instance.
message InstanceCreateRequest {
  // A human-readable name for the instance. This will be displayed in the UI
  // and can be changed later via UpdateInstanceMeta.
  string friendlyName = 1 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully creating an instance.
message InstanceCreateResponse {
  // The unique identifier (UUID) of the newly created instance.
  // Use this ID for all subsequent operations on the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Request to delete an existing instance.
message InstanceDeleteRequest {
  // The unique identifier (UUID) of the instance to delete.
  // All connected bots are stopped before deletion.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Response after successfully deleting an instance.
// Empty response indicates successful deletion.
message InstanceDeleteResponse {
}

// Request to list all instances visible to the current user.
message InstanceListRequest {
}

// Response containing a list of instances the current user has permission to view.
message InstanceListResponse {
  // Summary information about a single instance.
  message Instance {
    // The unique identifier (UUID) of the instance.
    string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
    // The human-readable name of the instance.
    string friendly_name = 2 [(google.api.field_behavior) = REQUIRED];
    // The icon identifier for the instance (used for UI display).
    string icon = 5 [(google.api.field_behavior) = REQUIRED];
    // The permissions the current user has on this instance.
    // Each permission indicates whether a specific action is allowed.
    repeated InstancePermissionState instance_permissions = 4;
    // Aggregate desired and runtime state for the instance's bots.
    BotFleetSummary bot_summary = 6 [(google.api.field_behavior) = REQUIRED];
  }

  // List of instances the user has READ_INSTANCE permission for.
  repeated Instance instances = 1 [(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = UNORDERED_LIST];
}

// Represents the grant status of a specific instance permission for the current user.
message InstancePermissionState {
  // The permission being described.
  InstancePermission instance_permission = 1 [(google.api.field_behavior) = REQUIRED];
  // Whether the current user has been granted this permission.
  bool granted = 2 [(google.api.field_behavior) = REQUIRED];
}

// Request to get detailed information about a specific instance.
message InstanceInfoRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // Optional timestamp for conditional requests. If provided and the instance
  // configuration has not been modified since this timestamp, the server returns
  // InstanceNotModified instead of the full InstanceInfo. This helps reduce
  // bandwidth for polling clients.
  optional google.protobuf.Timestamp if_modified_since = 2;
}

// Contains comprehensive information about an instance including its
// configuration, state, permissions, and available settings.
message InstanceInfo {
  // The human-readable name of the instance.
  string friendly_name = 1 [(google.api.field_behavior) = REQUIRED];
  // The icon identifier for the instance (used for UI display).
  string icon = 5 [(google.api.field_behavior) = REQUIRED];
  // The complete configuration of the instance including settings,
  // accounts, and proxies.
  InstanceConfig config = 2 [(google.api.field_behavior) = REQUIRED];
  // The permissions the current user has on this instance.
  repeated InstancePermissionState instance_permissions = 4;
  // All available settings definitions that can be rendered by identifier.
  // These define the schema and UI configuration for each setting.
  repeated SettingsDefinition settings_definitions = 8;
  // Pages that group settings together for UI rendering.
  // References settings by their identifiers from settings_definitions.
  repeated SettingsPage instance_settings = 6;
  // List of plugins registered with this instance.
  repeated ServerPlugin plugins = 7;
  // Timestamp of when the configuration was last modified.
  // Used for conditional requests via if_modified_since.
  google.protobuf.Timestamp last_modified = 9 [(google.api.field_behavior) = REQUIRED];
  // Aggregate desired and runtime state for the instance's bots.
  BotFleetSummary bot_summary = 10 [(google.api.field_behavior) = REQUIRED];
}

// Returned when the instance configuration has not changed since
// the if_modified_since timestamp provided in the request.
message InstanceNotModified {
  // The current last_modified timestamp of the instance.
  // Clients should use this for subsequent conditional requests.
  google.protobuf.Timestamp last_modified = 1 [(google.api.field_behavior) = REQUIRED];
}

// Response for GetInstanceInfo containing either full info or a not-modified status.
message InstanceInfoResponse {
  oneof result {
    // Full instance information when the config has been modified
    // or no if_modified_since was provided.
    InstanceInfo info = 1;
    // Returned when the config has not changed since if_modified_since.
    InstanceNotModified not_modified = 2;
  }
}

// Request to update instance metadata (name or icon).
message InstanceUpdateMetaRequest {
  // The unique identifier (UUID) of the instance to update.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The metadata field to update. Only one can be set per request.
  oneof meta {
    // New human-readable name for the instance.
    string friendly_name = 2;
    // New icon identifier for the instance.
    string icon = 3;
  }
}

// Response after successfully updating instance metadata.
// Empty response indicates success.
message InstanceUpdateMetaResponse {
}

// Request to replace the entire instance configuration.
// This is typically used for profile import operations.
// For individual setting changes, use UpdateInstanceConfigEntry instead.
message InstanceUpdateConfigRequest {
  // The unique identifier (UUID) of the instance to update.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The complete new configuration to replace the existing one.
  // Includes settings, accounts, and proxies.
  InstanceConfig config = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully updating instance configuration.
// Empty response indicates success.
message InstanceUpdateConfigResponse {
}

// Request to update a single configuration entry.
// This is the preferred method for individual setting changes as it
// is more efficient than replacing the entire configuration.
message InstanceUpdateConfigEntryRequest {
  // The unique identifier (UUID) of the instance to update.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The namespace of the setting (e.g., "bot", "account", plugin ID).
  string namespace = 2 [(google.api.field_behavior) = REQUIRED];
  // The key of the setting within the namespace.
  string key = 3 [(google.api.field_behavior) = REQUIRED];
  // The new value for the setting. Supports JSON value types
  // (string, number, boolean, null, array, object).
  google.protobuf.Value value = 4 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully updating a configuration entry.
// Empty response indicates success.
message InstanceUpdateConfigEntryResponse {
}

// Request to add a single Minecraft account to an instance.
message InstanceAddAccountRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The Minecraft account to add. The profile_id must be unique
  // within the instance.
  MinecraftAccountProto account = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully adding an account.
// Empty response indicates success.
message InstanceAddAccountResponse {
}

// Request to remove a Minecraft account from an instance.
message InstanceRemoveAccountRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The profile_id (UUID) of the account to remove.
  string profile_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Response after successfully removing an account.
// Empty response indicates success.
message InstanceRemoveAccountResponse {
}

// Request to update an existing Minecraft account in an instance.
// The account is matched by profile_id and replaced with the new data.
message InstanceUpdateAccountRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The updated account data. The profile_id must match an existing
  // account in the instance.
  MinecraftAccountProto account = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully updating an account.
// Empty response indicates success.
message InstanceUpdateAccountResponse {
}

// Request to add multiple Minecraft accounts to an instance in a single operation.
// More efficient than multiple individual AddInstanceAccount calls.
message InstanceAddAccountsBatchRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // List of accounts to add. Each account's profile_id must be unique
  // within the instance.
  repeated MinecraftAccountProto accounts = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully adding accounts in batch.
// Empty response indicates success.
message InstanceAddAccountsBatchResponse {
}

// Request to remove multiple Minecraft accounts from an instance in a single operation.
// More efficient than multiple individual RemoveInstanceAccount calls.
message InstanceRemoveAccountsBatchRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // List of profile_ids (UUIDs) of accounts to remove.
  repeated string profile_ids = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Response after successfully removing accounts in batch.
// Empty response indicates success.
message InstanceRemoveAccountsBatchResponse {
}

// Request to add a single proxy to an instance.
message InstanceAddProxyRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The proxy configuration to add.
  ProxyProto proxy = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully adding a proxy.
// Empty response indicates success.
message InstanceAddProxyResponse {
}

// Request to remove a proxy from an instance by its index.
message InstanceRemoveProxyRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The zero-based index of the proxy to remove.
  // Returns INVALID_ARGUMENT if index is out of bounds.
  int32 index = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully removing a proxy.
// Empty response indicates success.
message InstanceRemoveProxyResponse {
}

// Request to update an existing proxy in an instance.
message InstanceUpdateProxyRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The zero-based index of the proxy to update.
  // Returns INVALID_ARGUMENT if index is out of bounds.
  int32 index = 2 [(google.api.field_behavior) = REQUIRED];
  // The new proxy configuration to replace the existing one.
  ProxyProto proxy = 3 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully updating a proxy.
// Empty response indicates success.
message InstanceUpdateProxyResponse {
}

// Request to add multiple proxies to an instance in a single operation.
// More efficient than multiple individual AddInstanceProxy calls.
message InstanceAddProxiesBatchRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // List of proxy configurations to add.
  repeated ProxyProto proxies = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully adding proxies in batch.
// Empty response indicates success.
message InstanceAddProxiesBatchResponse {
}

// Request to remove multiple proxies from an instance by their addresses.
message InstanceRemoveProxiesBatchRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // List of proxy addresses to remove (in serialized format, e.g., "host:port").
  // Proxies matching these addresses will be removed.
  repeated string addresses = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully removing proxies in batch.
// Empty response indicates success.
message InstanceRemoveProxiesBatchResponse {
}

// Request to retrieve the audit log for an instance.
message InstanceAuditLogRequest {
  // The unique identifier (UUID) of the instance.
  string id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Request to get persistent metadata for a specific Minecraft account.
// Persistent metadata survives bot restarts and is stored with the account.
message GetAccountMetadataRequest {
  // The unique identifier (UUID) of the instance containing the account.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The profile_id (UUID) of the Minecraft account.
  string account_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Response containing the persistent metadata for an account.
message GetAccountMetadataResponse {
  // Metadata organized by namespace. Each namespace contains
  // key-value pairs of metadata.
  repeated SettingsNamespace metadata = 1 [(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = UNORDERED_LIST];
}

// Request to set a single persistent metadata entry for an account.
message SetAccountMetadataEntryRequest {
  // The unique identifier (UUID) of the instance containing the account.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The profile_id (UUID) of the Minecraft account.
  string account_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The namespace for the metadata entry.
  string namespace = 3 [(google.api.field_behavior) = REQUIRED];
  // The key for the metadata entry within the namespace.
  string key = 4 [(google.api.field_behavior) = REQUIRED];
  // The value to set. Supports JSON value types.
  google.protobuf.Value value = 5 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully setting a metadata entry.
// Empty response indicates success.
message SetAccountMetadataEntryResponse {
}

// Request to delete a persistent metadata entry from an account.
message DeleteAccountMetadataEntryRequest {
  // The unique identifier (UUID) of the instance containing the account.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The profile_id (UUID) of the Minecraft account.
  string account_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The namespace of the metadata entry to delete.
  string namespace = 3 [(google.api.field_behavior) = REQUIRED];
  // The key of the metadata entry to delete.
  string key = 4 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully deleting a metadata entry.
// Empty response indicates success.
message DeleteAccountMetadataEntryResponse {
}

// Request to get persistent metadata for an instance.
// Persistent metadata survives restarts and is stored with the instance.
message GetInstanceMetadataRequest {
  // The unique identifier (UUID) of the instance.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Response containing the persistent metadata for an instance.
message GetInstanceMetadataResponse {
  // Metadata organized by namespace. Each namespace contains
  // key-value pairs of metadata.
  repeated SettingsNamespace metadata = 1 [(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = UNORDERED_LIST];
}

// Request to set a single persistent metadata entry for an instance.
message SetInstanceMetadataEntryRequest {
  // The unique identifier (UUID) of the instance.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The namespace for the metadata entry.
  string namespace = 2 [(google.api.field_behavior) = REQUIRED];
  // The key for the metadata entry within the namespace.
  string key = 3 [(google.api.field_behavior) = REQUIRED];
  // The value to set. Supports JSON value types.
  google.protobuf.Value value = 4 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully setting an instance metadata entry.
// Empty response indicates success.
message SetInstanceMetadataEntryResponse {
}

// Request to delete a persistent metadata entry from an instance.
message DeleteInstanceMetadataEntryRequest {
  // The unique identifier (UUID) of the instance.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // The namespace of the metadata entry to delete.
  string namespace = 2 [(google.api.field_behavior) = REQUIRED];
  // The key of the metadata entry to delete.
  string key = 3 [(google.api.field_behavior) = REQUIRED];
}

// Response after successfully deleting an instance metadata entry.
// Empty response indicates success.
message DeleteInstanceMetadataEntryResponse {
}

// Response containing the audit log entries for an instance.
message InstanceAuditLogResponse {
  // Types of actions that are recorded in the audit log.
  enum AuditLogEntryType {
    reserved 5 to 13;
    reserved "AUTOMATION_START", "AUTOMATION_PAUSE", "AUTOMATION_RESUME",
      "AUTOMATION_STOP", "AUTOMATION_UPDATE_SETTINGS",
      "AUTOMATION_APPLY_PRESET", "AUTOMATION_RESET_MEMORY",
      "AUTOMATION_RESET_COORDINATION", "AUTOMATION_RELEASE_CLAIMS";
    // A command was executed on the instance.
    EXECUTE_COMMAND = 0;
    // One or more bots had their desired state changed.
    BOT_DESIRED_STATE_CHANGE = 14;
    // One or more bots were restarted.
    BOT_RESTART = 15;
    // A permission-scoped plugin RPC completed.
    PLUGIN_RPC = 16;
  }

  // A single entry in the audit log.
  message AuditLogEntry {
    // Unique identifier for this audit log entry.
    string id = 1 [(google.api.field_behavior) = REQUIRED];
    // The user who performed the action.
    InstanceUser user = 2 [(google.api.field_behavior) = REQUIRED];
    // The type of action that was performed.
    AuditLogEntryType type = 3 [(google.api.field_behavior) = REQUIRED];
    // When the action was performed.
    google.protobuf.Timestamp timestamp = 4 [(google.api.field_behavior) = REQUIRED];
    // Additional data about the action (e.g., command text for EXECUTE_COMMAND).
    // May be empty for some action types.
    string data = 5 [(google.api.field_behavior) = REQUIRED];
  }

  // List of audit log entries, ordered by timestamp descending (newest first).
  repeated AuditLogEntry entry = 1 [(google.api.field_behavior) = REQUIRED];
}

// Service for managing SoulFire instances.
// An instance is a permission and configuration compartment for independently
// controlled Minecraft bots, settings, accounts, proxies, and remote clients.
//
// All operations require appropriate permissions:
// - CREATE_INSTANCE (global): Required to create new instances
// - READ_INSTANCE: Required to list and view instance details
// - UPDATE_INSTANCE_META: Required to change instance name/icon
// - UPDATE_INSTANCE_CONFIG: Required to modify settings, accounts, and proxies
// - DELETE_INSTANCE: Required to delete an instance
// - READ_INSTANCE_AUDIT_LOGS: Required to view audit logs
// - READ_BOT_INFO: Required to read account metadata
service InstanceService {
  // Creates a new instance with the given friendly name.
  // Requires: CREATE_INSTANCE global permission
  // Returns: The UUID of the newly created instance
  // Errors: INTERNAL if creation fails
  rpc CreateInstance (InstanceCreateRequest) returns (InstanceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/instances"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Create instance"
      permissions: "CREATE_INSTANCE"
      scope: "instance"
      side_effects: "Creates a new instance with no desired bots."
    };
  }

  // Permanently deletes an instance and all its data.
  // Connected bots are stopped before the instance is deleted.
  // This operation cannot be undone.
  // Requires: DELETE_INSTANCE permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc DeleteInstance (InstanceDeleteRequest) returns (InstanceDeleteResponse) {
    option (google.api.http) = { delete: "/v1/instances/{id}" };
    option (soulfire.v1.api_method) = {
      display_name: "Delete instance"
      permissions: "DELETE_INSTANCE"
      scope: "instance"
      side_effects: "Deletes the target instance and its stored data."
    };
  }

  // Lists all instances the current user has permission to view.
  // Only instances where the user has READ_INSTANCE permission are returned.
  // Returns summary information for each instance.
  rpc ListInstances (InstanceListRequest) returns (InstanceListResponse) {
    option (google.api.http) = { get: "/v1/instances" };
    option (soulfire.v1.api_method) = {
      display_name: "List instances"
      scope: "instance"
    };
  }

  // Gets detailed information about a specific instance.
  // Supports conditional requests via if_modified_since to reduce bandwidth.
  // Requires: READ_INSTANCE permission on the instance
  // Returns: Full instance info or not_modified status
  // Errors: NOT_FOUND if instance does not exist
  rpc GetInstanceInfo (InstanceInfoRequest) returns (InstanceInfoResponse) {
    option (google.api.http) = { get: "/v1/instances/{id}" };
    option (soulfire.v1.api_method) = {
      display_name: "Get instance"
      permissions: "READ_INSTANCE"
      scope: "instance"
    };
  }

  // Updates instance metadata (friendly name or icon).
  // Only one field can be updated per request.
  // Requires: UPDATE_INSTANCE_META permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc UpdateInstanceMeta (InstanceUpdateMetaRequest) returns (InstanceUpdateMetaResponse) {
    option (google.api.http) = {
      patch: "/v1/instances/{id}/meta"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Update instance metadata"
      permissions: "UPDATE_INSTANCE_META"
      scope: "instance"
      side_effects: "Updates the instance's friendly name or icon."
    };
  }

  // Replaces the entire instance configuration.
  // Used primarily for profile import operations.
  // For individual setting changes, use UpdateInstanceConfigEntry instead.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc UpdateInstanceConfig (InstanceUpdateConfigRequest) returns (InstanceUpdateConfigResponse) {
    option (google.api.http) = {
      patch: "/v1/instances/{id}/config"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Update instance config"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance"
      side_effects: "Replaces the stored instance configuration."
    };
  }

  // Updates a single configuration entry by namespace and key.
  // More efficient than UpdateInstanceConfig for individual changes.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc UpdateInstanceConfigEntry (InstanceUpdateConfigEntryRequest) returns (InstanceUpdateConfigEntryResponse) {
    option (google.api.http) = {
      put: "/v1/instances/{id}/config/namespaces/{namespace}/entries/{key}"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Update instance config entry"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance"
      side_effects: "Persists a single instance setting entry."
    };
  }

  // Adds a Minecraft account to the instance.
  // The account's profile_id must be unique within the instance.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc AddInstanceAccount (InstanceAddAccountRequest) returns (InstanceAddAccountResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/accounts"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Add instance account"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account"
    };
  }

  // Removes a Minecraft account from the instance by profile_id.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc RemoveInstanceAccount (InstanceRemoveAccountRequest) returns (InstanceRemoveAccountResponse) {
    option (google.api.http) = { delete: "/v1/instances/{id}/accounts/{profile_id}" };
    option (soulfire.v1.api_method) = {
      display_name: "Remove instance account"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account"
    };
  }

  // Updates an existing Minecraft account in the instance.
  // The account is matched by profile_id and replaced.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc UpdateInstanceAccount (InstanceUpdateAccountRequest) returns (InstanceUpdateAccountResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/accounts:update"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Update instance account"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account"
    };
  }

  // Adds multiple Minecraft accounts to the instance in a single operation.
  // More efficient than multiple AddInstanceAccount calls.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc AddInstanceAccountsBatch (InstanceAddAccountsBatchRequest) returns (InstanceAddAccountsBatchResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/accounts:batchAdd"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Batch add instance accounts"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account"
    };
  }

  // Removes multiple Minecraft accounts from the instance by their profile_ids.
  // More efficient than multiple RemoveInstanceAccount calls.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc RemoveInstanceAccountsBatch (InstanceRemoveAccountsBatchRequest) returns (InstanceRemoveAccountsBatchResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/accounts:batchRemove"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Batch remove instance accounts"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account"
    };
  }

  // Adds a proxy to the instance.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc AddInstanceProxy (InstanceAddProxyRequest) returns (InstanceAddProxyResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/proxies"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Add instance proxy"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.proxy"
    };
  }

  // Removes a proxy from the instance by its index.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  // Errors: INVALID_ARGUMENT if index is out of bounds
  rpc RemoveInstanceProxy (InstanceRemoveProxyRequest) returns (InstanceRemoveProxyResponse) {
    option (google.api.http) = { delete: "/v1/instances/{id}/proxies/{index}" };
    option (soulfire.v1.api_method) = {
      display_name: "Remove instance proxy"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.proxy"
    };
  }

  // Updates an existing proxy in the instance by its index.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  // Errors: INVALID_ARGUMENT if index is out of bounds
  rpc UpdateInstanceProxy (InstanceUpdateProxyRequest) returns (InstanceUpdateProxyResponse) {
    option (google.api.http) = {
      put: "/v1/instances/{id}/proxies/{index}"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Update instance proxy"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.proxy"
    };
  }

  // Adds multiple proxies to the instance in a single operation.
  // More efficient than multiple AddInstanceProxy calls.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc AddInstanceProxiesBatch (InstanceAddProxiesBatchRequest) returns (InstanceAddProxiesBatchResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/proxies:batchAdd"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Batch add instance proxies"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.proxy"
    };
  }

  // Removes multiple proxies from the instance by their addresses.
  // More efficient than multiple RemoveInstanceProxy calls.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc RemoveInstanceProxiesBatch (InstanceRemoveProxiesBatchRequest) returns (InstanceRemoveProxiesBatchResponse) {
    option (google.api.http) = {
      post: "/v1/instances/{id}/proxies:batchRemove"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Batch remove instance proxies"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.proxy"
    };
  }

  // Retrieves the audit log for an instance.
  // Returns a list of recorded actions ordered by timestamp (newest first).
  // Requires: READ_INSTANCE_AUDIT_LOGS permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc GetAuditLog (InstanceAuditLogRequest) returns (InstanceAuditLogResponse) {
    option (google.api.http) = { get: "/v1/instances/{id}/audit-log" };
    option (soulfire.v1.api_method) = {
      display_name: "Get audit log"
      permissions: "READ_INSTANCE_AUDIT_LOGS"
      scope: "instance"
    };
  }

  // Gets persistent metadata for a Minecraft account.
  // Persistent metadata survives bot restarts and session changes.
  // Requires: READ_BOT_INFO permission on the instance
  // Errors: NOT_FOUND if instance or account does not exist
  rpc GetAccountMetadata (GetAccountMetadataRequest) returns (GetAccountMetadataResponse) {
    option (google.api.http) = { get: "/v1/instances/{instance_id}/accounts/{account_id}/metadata" };
    option (soulfire.v1.api_method) = {
      display_name: "Get account metadata"
      permissions: "READ_BOT_INFO"
      scope: "instance.account.metadata"
    };
  }

  // Sets a single persistent metadata entry for a Minecraft account.
  // Creates the namespace and key if they don't exist, or updates if they do.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc SetAccountMetadataEntry (SetAccountMetadataEntryRequest) returns (SetAccountMetadataEntryResponse) {
    option (google.api.http) = {
      put: "/v1/instances/{instance_id}/accounts/{account_id}/metadata/namespaces/{namespace}/entries/{key}"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Set account metadata entry"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account.metadata"
    };
  }

  // Deletes a persistent metadata entry from a Minecraft account.
  // Requires: UPDATE_INSTANCE_CONFIG permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc DeleteAccountMetadataEntry (DeleteAccountMetadataEntryRequest) returns (DeleteAccountMetadataEntryResponse) {
    option (google.api.http) = { delete: "/v1/instances/{instance_id}/accounts/{account_id}/metadata/namespaces/{namespace}/entries/{key}" };
    option (soulfire.v1.api_method) = {
      display_name: "Delete account metadata entry"
      permissions: "UPDATE_INSTANCE_CONFIG"
      scope: "instance.account.metadata"
    };
  }

  // Gets persistent metadata for the instance itself.
  // Persistent metadata survives restarts and session changes.
  // Requires: READ_INSTANCE permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc GetInstanceMetadata (GetInstanceMetadataRequest) returns (GetInstanceMetadataResponse) {
    option (google.api.http) = { get: "/v1/instances/{instance_id}/metadata" };
    option (soulfire.v1.api_method) = {
      display_name: "Get instance metadata"
      permissions: "READ_INSTANCE"
      scope: "instance.metadata"
    };
  }

  // Sets a single persistent metadata entry for the instance.
  // Creates the namespace and key if they don't exist, or updates if they do.
  // Requires: UPDATE_INSTANCE_META permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc SetInstanceMetadataEntry (SetInstanceMetadataEntryRequest) returns (SetInstanceMetadataEntryResponse) {
    option (google.api.http) = {
      put: "/v1/instances/{instance_id}/metadata/namespaces/{namespace}/entries/{key}"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Set instance metadata entry"
      permissions: "UPDATE_INSTANCE_META"
      scope: "instance.metadata"
    };
  }

  // Deletes a persistent metadata entry from the instance.
  // Requires: UPDATE_INSTANCE_META permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc DeleteInstanceMetadataEntry (DeleteInstanceMetadataEntryRequest) returns (DeleteInstanceMetadataEntryResponse) {
    option (google.api.http) = { delete: "/v1/instances/{instance_id}/metadata/namespaces/{namespace}/entries/{key}" };
    option (soulfire.v1.api_method) = {
      display_name: "Delete instance metadata entry"
      permissions: "UPDATE_INSTANCE_META"
      scope: "instance.metadata"
    };
  }
}
