{
  "openapi": "3.1.0",
  "info": {
    "title": "resfss API",
    "version": "1.1.0",
    "description": "Public API for anonymous and member access. Members get virtual directory storage under `/v1/me/storage`. Admin endpoints: see openapi.admin.json."
  },
  "servers": [{ "url": "http://localhost:8080" }],
  "tags": [
    { "name": "health" },
    { "name": "auth" },
    { "name": "me" },
    { "name": "storage" },
    { "name": "files" },
    { "name": "share" }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": ["health"],
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "status": { "type": "string", "example": "ok" } },
                  "required": ["status"]
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/register": {
      "post": {
        "tags": ["auth"],
        "summary": "Register member",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RegisterResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/auth/login": {
      "post": {
        "tags": ["auth"],
        "summary": "Login",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LoginRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LoginResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/auth/issue-key": {
      "post": {
        "tags": ["auth"],
        "summary": "Issue API key with password",
        "description": "Login alternative — validates email/password and returns a new API key (shown once).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/IssueKeyRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IssueKeyResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": ["me"],
        "summary": "Current user",
        "security": [{ "MemberAuth": [] }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UserProfile" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/me/usage": {
      "get": {
        "tags": ["me"],
        "summary": "Quota and bandwidth usage",
        "security": [{ "MemberAuth": [] }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UsageResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/me/api-keys": {
      "get": {
        "tags": ["me"],
        "summary": "List API keys",
        "security": [{ "MemberAuth": [] }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/APIKeyMeta" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      },
      "post": {
        "tags": ["me"],
        "summary": "Create API key",
        "security": [{ "MemberAuth": [] }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateAPIKeyRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key shown once",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CreateAPIKeyResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/me/api-keys/{id}": {
      "delete": {
        "tags": ["me"],
        "summary": "Revoke API key",
        "security": [{ "MemberAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/KeyId" }],
        "responses": {
          "204": { "description": "Deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/me/storage": {
      "get": {
        "tags": ["storage"],
        "summary": "List directory",
        "description": "List immediate child directories and files at `path` (member cloud storage).",
        "security": [{ "MemberAuth": [] }],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "schema": { "type": "string", "default": "/" },
            "description": "Directory path with trailing slash, e.g. `/` or `/Photos/`"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StorageListing" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/me/storage/dirs": {
      "post": {
        "tags": ["storage"],
        "summary": "Create directory",
        "security": [{ "MemberAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MkdirRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DirEntry" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      },
      "delete": {
        "tags": ["storage"],
        "summary": "Remove empty directory",
        "security": [{ "MemberAuth": [] }],
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Full directory path, e.g. `/Photos/`"
          }
        ],
        "responses": {
          "204": { "description": "Deleted" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/me/files/{id}": {
      "patch": {
        "tags": ["storage"],
        "summary": "Move or rename file",
        "description": "Update `dir_path` and/or `name`. Member owner only.",
        "security": [{ "MemberAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/FileId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MoveFileRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FileMeta" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/files": {
      "post": {
        "tags": ["files"],
        "summary": "Upload file",
        "description": "Anonymous or member. **Share** (default): no `dir_path`/`storage` — anonymous limits, encryption OK. **Cloud** (member): set `dir_path` or `storage=true` — quota, plain only.",
        "security": [{}, { "MemberAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": { "$ref": "#/components/schemas/UploadForm" }
            },
            "application/octet-stream": {
              "schema": { "type": "string", "format": "binary" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Uploaded",
            "headers": {
              "X-Encrypted-By": {
                "schema": { "type": "string", "enum": ["server"] },
                "description": "Present when delegated encryption was used"
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UploadResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "413": { "$ref": "#/components/responses/TooLarge" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/files/{id}": {
      "get": {
        "tags": ["files"],
        "summary": "File metadata",
        "security": [{}, { "MemberAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/FileId" }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FileMeta" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "410": { "$ref": "#/components/responses/Gone" }
        }
      },
      "delete": {
        "tags": ["files"],
        "summary": "Delete file",
        "description": "Owner (member, same anon IP) only.",
        "security": [{}, { "MemberAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/FileId" }],
        "responses": {
          "204": { "description": "Deleted" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/files/{id}/download": {
      "get": {
        "tags": ["files"],
        "summary": "Download file",
        "security": [{}, { "MemberAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/FileId" },
          {
            "name": "password",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Delegated decrypt (optional)"
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes",
            "headers": {
              "Content-Disposition": { "schema": { "type": "string" } },
              "X-Encrypted": { "schema": { "type": "string" } },
              "X-Decrypted-By": { "schema": { "type": "string", "enum": ["server"] } },
              "X-Wrapped-Key": { "schema": { "type": "string" } },
              "X-Server-Decrypt-Capable": { "schema": { "type": "string" } }
            },
            "content": {
              "application/octet-stream": { "schema": { "type": "string", "format": "binary" } },
              "*/*": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "410": { "$ref": "#/components/responses/Gone" },
          "413": { "$ref": "#/components/responses/TooLarge" }
        }
      }
    },
    "/v1/files/{id}/share": {
      "post": {
        "tags": ["share"],
        "summary": "Get or create share token",
        "security": [{}, { "MemberAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/FileId" }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ShareLinks" }
              }
            }
          },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/share/{token}": {
      "get": {
        "tags": ["share"],
        "summary": "Share metadata",
        "parameters": [{ "$ref": "#/components/parameters/ShareToken" }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FileMeta" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "410": { "$ref": "#/components/responses/Gone" }
        }
      }
    },
    "/v1/share/{token}/download": {
      "get": {
        "tags": ["share"],
        "summary": "Download via share token",
        "parameters": [
          { "$ref": "#/components/parameters/ShareToken" },
          {
            "name": "password",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Delegated decrypt (optional)"
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes",
            "headers": {
              "Content-Disposition": { "schema": { "type": "string" } },
              "X-Encrypted": { "schema": { "type": "string" } },
              "X-Decrypted-By": { "schema": { "type": "string", "enum": ["server"] } },
              "X-Wrapped-Key": { "schema": { "type": "string" } }
            },
            "content": {
              "application/octet-stream": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "410": { "$ref": "#/components/responses/Gone" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "MemberAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Member API key (`rfs_...`)"
      }
    },
    "parameters": {
      "FileId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      },
      "KeyId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      },
      "ShareToken": {
        "name": "token",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" } },
        "required": ["error"]
      },
      "RegisterRequest": {
        "type": "object",
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string", "minLength": 1 }
        },
        "required": ["email", "password"]
      },
      "RegisterResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string" },
          "quota_bytes": { "type": "integer", "format": "int64" },
          "api_key": { "type": "string", "description": "Shown once at registration" }
        }
      },
      "IssueKeyRequest": {
        "type": "object",
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string" },
          "name": { "type": "string", "description": "Key label (default: cli)" }
        },
        "required": ["email", "password"]
      },
      "IssueKeyResponse": {
        "type": "object",
        "properties": {
          "api_key": { "type": "string" },
          "name": { "type": "string" }
        }
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "email": { "type": "string", "format": "email" },
          "password": { "type": "string" }
        },
        "required": ["email", "password"]
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string" },
          "quota_bytes": { "type": "integer", "format": "int64" },
          "used_bytes": { "type": "integer", "format": "int64" }
        }
      },
      "UserProfile": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string" },
          "role": { "type": "string", "enum": ["member", "admin"] },
          "quota_bytes": { "type": "integer", "format": "int64" },
          "used_bytes": { "type": "integer", "format": "int64" }
        }
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "quota_bytes": { "type": "integer", "format": "int64" },
          "used_bytes": { "type": "integer", "format": "int64" },
          "bandwidth_upload": { "type": "integer", "format": "int64" },
          "bandwidth_download": { "type": "integer", "format": "int64" }
        }
      },
      "CreateAPIKeyRequest": {
        "type": "object",
        "properties": { "name": { "type": "string" } }
      },
      "CreateAPIKeyResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "api_key": { "type": "string", "description": "Shown once" },
          "name": { "type": "string" }
        }
      },
      "APIKeyMeta": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "created_at": { "type": "string" }
        }
      },
      "UploadForm": {
        "type": "object",
        "properties": {
          "file": { "type": "string", "format": "binary" },
          "name": { "type": "string" },
          "encrypted": { "type": "boolean" },
          "wrapped_key": { "type": "string" },
          "password": { "type": "string", "description": "Delegated server encryption" },
          "dir_path": { "type": "string", "description": "Cloud upload target directory", "example": "/Photos/" },
          "storage": { "type": "string", "description": "Set true with member auth for cloud upload (dir defaults to /)" },
          "max_downloads": { "type": "integer", "minimum": 0 },
          "expires_at": { "type": "string", "format": "date-time" }
        },
        "required": ["file"]
      },
      "FileMeta": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "size": { "type": "integer", "format": "int64" },
          "content_type": { "type": "string" },
          "encrypted": { "type": "boolean" },
          "wrapped_key": { "type": "string" },
          "share_token": { "type": "string", "description": "Owner only" },
          "user_id": { "type": "string" },
          "created_at": { "type": "string" },
          "max_downloads": { "type": "integer" },
          "download_count": { "type": "integer" },
          "expires_at": { "type": "string", "format": "date-time" },
          "idle_expires_at": { "type": "string", "format": "date-time" },
          "last_download_at": { "type": "string" },
          "expired": { "type": "boolean" },
          "dir_path": { "type": "string", "example": "/Photos/" },
          "full_path": { "type": "string", "example": "/Photos/vacation.jpg" },
          "download_url": { "type": "string", "format": "uri" }
        }
      },
      "DirEntry": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "example": "Photos" },
          "path": { "type": "string", "example": "/Photos/" }
        }
      },
      "StorageListing": {
        "type": "object",
        "properties": {
          "path": { "type": "string", "example": "/" },
          "parent": { "type": "string", "nullable": true, "example": null },
          "dirs": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/DirEntry" }
          },
          "files": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/FileMeta" }
          }
        }
      },
      "MkdirRequest": {
        "type": "object",
        "properties": {
          "path": { "type": "string", "description": "Parent directory", "example": "/" },
          "name": { "type": "string", "example": "Photos" }
        },
        "required": ["name"]
      },
      "MoveFileRequest": {
        "type": "object",
        "properties": {
          "dir_path": { "type": "string", "example": "/Docs/" },
          "name": { "type": "string", "description": "New filename (optional)" }
        }
      },
      "UploadResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/FileMeta" },
          {
            "type": "object",
            "properties": {
              "share_url": { "type": "string", "format": "uri" },
              "download_url": { "type": "string", "format": "uri" },
              "web_download_url": { "type": "string", "format": "uri" },
              "encrypted_by": { "type": "string", "enum": ["server"] }
            }
          }
        ]
      },
      "ShareLinks": {
        "type": "object",
        "properties": {
          "share_token": { "type": "string" },
          "share_url": { "type": "string", "format": "uri" },
          "download_url": { "type": "string", "format": "uri" },
          "web_download_url": { "type": "string", "format": "uri" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Unauthorized",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Forbidden",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Gone": {
        "description": "Expired or download limit reached",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Conflict": {
        "description": "Conflict (duplicate name, directory not empty, etc.)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "TooLarge": {
        "description": "Payload too large",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limited",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
