{
  "openapi": "3.0.3",
  "info": {
    "title": "Mocó Public API",
    "version": "0.2.0",
    "description": "Tenant-isolated, path-based secret management for interactive and\nautomated clients. The contract makes no network-topology assumptions:\nclients can run inside or outside the Mocó Kubernetes cluster.\n",
    "contact": {
      "name": "AraiHu",
      "url": "https://github.com/araihu/moco"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Current deployment origin. Production deployments use TLS."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Service",
      "description": "API discovery and compatibility information."
    },
    {
      "name": "Tenants",
      "description": "Tenant lifecycle operations."
    },
    {
      "name": "Vaults",
      "description": "Tenant-scoped vault lifecycle operations."
    },
    {
      "name": "Secrets",
      "description": "Vault-scoped secret value and metadata operations."
    }
  ],
  "paths": {
    "/api/v1": {
      "parameters": [
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "getServiceInfo",
        "summary": "Get API compatibility information",
        "description": "Returns server and API versions plus stable capability identifiers.",
        "tags": [
          "Service"
        ],
        "responses": {
          "200": {
            "description": "Service information.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/service-info"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/watch": {
      "parameters": [
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "watchChanges",
        "summary": "Wait for persisted resource changes",
        "description": "Returns a durable, process-wide resource version. With no\n`resourceVersion`, the endpoint returns the current checkpoint immediately.\nWith a checkpoint, it returns immediately when the checkpoint is stale or\nwaits up to `timeoutSeconds` for a newer revision. A true `changed` result\nis a signal to resync the tenant, vault, and secret collections; this\nendpoint does not expose payloads, ordering, or tombstones. The checkpoint\nis process-wide, so this operation requires an explicit cluster-wide watch\npermission (`domain: \"*\"`); a tenant-scoped policy cannot authorize it.\nDeployments that need least privilege can use\n`GET /api/v1/tenants/{tenantId}/watch`, whose checkpoint is scoped to one\ntenant and accepts a literal tenant-domain policy.\n",
        "tags": [
          "Service"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resource-version"
          },
          {
            "$ref": "#/components/parameters/watch-timeout"
          }
        ],
        "responses": {
          "200": {
            "description": "Current checkpoint and whether it advanced.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/cache-control"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/watch-result"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/watch": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "watchTenantChanges",
        "summary": "Wait for changes within one tenant",
        "description": "Returns a durable resource checkpoint scoped to the tenant. With no\n`resourceVersion`, the endpoint returns the current checkpoint immediately.\nWith a checkpoint, it returns immediately when that checkpoint is stale or\nwaits up to `timeoutSeconds` for a newer tenant, vault, or secret mutation.\nA true `changed` result is only a signal to relist the collections the\ncaller is authorized to manage; this endpoint does not expose payloads,\nordering, or tombstones. The checkpoint survives tenant deletion as a\ntombstone, so a watcher that already knows the tenant can observe its final\nmutation. A tenant that has never existed returns 404.\n",
        "tags": [
          "Service"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/tenant-resource-version"
          },
          {
            "$ref": "#/components/parameters/watch-timeout"
          }
        ],
        "responses": {
          "200": {
            "description": "Current tenant checkpoint and whether it advanced.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/cache-control"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/tenant-watch-result"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants": {
      "parameters": [
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "listTenants",
        "summary": "List tenants",
        "description": "Lists tenants in the caller's collection authorization scope. The first\nrequest creates a sequence-bounded snapshot; follow `nextCursor` until\n`hasMore` is false. If a cursor expires, restart without one. Name and\nexternalId filters are exact and may be combined. For deterministic\nadoption, controllers should use an exact immutable `externalId` filter\nbefore creating a tenant.\n",
        "tags": [
          "Tenants"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/name-filter"
          },
          {
            "$ref": "#/components/parameters/external-id-filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of tenants ordered by immutable insertion sequence.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "items": [
                    {
                      "id": "11111111-1111-4111-8111-111111111111",
                      "name": "production",
                      "labels": {},
                      "revision": 1,
                      "createdAt": "2026-08-25T12:00:00Z",
                      "updatedAt": "2026-08-25T12:00:00Z"
                    }
                  ],
                  "page": {
                    "hasMore": false,
                    "nextCursor": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/tenant-list"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "410": {
            "$ref": "#/components/responses/cursor-expired"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "post": {
        "operationId": "createTenant",
        "summary": "Create a tenant",
        "description": "Creates a tenant. Name and externalId are independently unique. Automation\nshould supply an externalId and Idempotency-Key. A uniqueness conflict\nreturns the existing resourceId when safe, allowing an atomic adoption\nhandshake. Repeating an identical request with the same Idempotency-Key\nreturns the original result; reusing that key with another body returns 409.\n",
        "tags": [
          "Tenants"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotency-key"
          }
        ],
        "requestBody": {
          "description": "Tenant creation fields.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/tenant-create"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tenant created, or prior identical keyed result replayed.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "Location": {
                "$ref": "#/components/headers/location"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/tenant"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "getTenant",
        "summary": "Get a tenant",
        "description": "Returns one tenant by its stable ID.",
        "tags": [
          "Tenants"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-none-match"
          }
        ],
        "responses": {
          "200": {
            "description": "Tenant representation.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/tenant"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/not-modified"
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "put": {
        "operationId": "updateTenant",
        "summary": "Update a tenant",
        "description": "Replaces all mutable fields. externalId and server fields are immutable.\nIf-Match is optional; omit it only for an intentionally unconditional\nmutation. Retrying clients should read the representation and use its ETag.\n",
        "tags": [
          "Tenants"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          }
        ],
        "requestBody": {
          "description": "Complete mutable tenant state. Use null to clear description.",
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "name": "platform",
                "description": "Platform-owned tenant",
                "labels": {
                  "app.kubernetes.io/managed-by": "moco-operator"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/tenant-update"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated tenant representation.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/tenant"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "delete": {
        "operationId": "deleteTenant",
        "summary": "Delete a tenant",
        "description": "Deletes an empty tenant. With cascade=true, also deletes all child vaults\nand secrets. A missing tenant returns 404; reconcilers may treat it as\nsuccess. If-Match is optional, but recommended for retries that must not\ndelete a newer revision.\n",
        "tags": [
          "Tenants"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          },
          {
            "$ref": "#/components/parameters/cascade"
          }
        ],
        "responses": {
          "204": {
            "description": "Tenant deleted.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/vaults": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "listVaults",
        "summary": "List vaults",
        "description": "Lists vaults in the caller's tenant authorization scope. The first request\ncreates a sequence-bounded snapshot; follow `nextCursor` until `hasMore` is\nfalse. If a cursor expires, restart without one. Name and externalId filters\nare exact and may be combined. For deterministic adoption, controllers\nshould use an exact immutable `externalId` filter before creating a vault.\n",
        "tags": [
          "Vaults"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/name-filter"
          },
          {
            "$ref": "#/components/parameters/external-id-filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of vaults ordered by immutable insertion sequence.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "items": [
                    {
                      "id": "22222222-2222-4222-8222-222222222222",
                      "tenantId": "11111111-1111-4111-8111-111111111111",
                      "name": "application",
                      "labels": {},
                      "revision": 1,
                      "createdAt": "2026-08-25T12:00:00Z",
                      "updatedAt": "2026-08-25T12:00:00Z"
                    }
                  ],
                  "page": {
                    "hasMore": false,
                    "nextCursor": null
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/vault-list"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "410": {
            "$ref": "#/components/responses/cursor-expired"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "post": {
        "operationId": "createVault",
        "summary": "Create a vault",
        "description": "Creates a tenant-scoped vault. Name and externalId are independently unique\nwithin the tenant. Automation should supply an externalId and\nIdempotency-Key. A uniqueness conflict returns the existing resourceId when\nsafe, allowing an atomic adoption handshake. Repeating an identical request\nwith the same Idempotency-Key returns the original result.\n",
        "tags": [
          "Vaults"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotency-key"
          }
        ],
        "requestBody": {
          "description": "Vault creation fields.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/vault-create"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Vault created, or prior identical keyed result replayed.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "Location": {
                "$ref": "#/components/headers/location"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/vault"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/vaults/{vaultId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/vault-id"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "getVault",
        "summary": "Get a vault",
        "description": "Returns one vault by stable tenant-scoped ID.",
        "tags": [
          "Vaults"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-none-match"
          }
        ],
        "responses": {
          "200": {
            "description": "Vault representation.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/vault"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/not-modified"
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "put": {
        "operationId": "updateVault",
        "summary": "Update a vault",
        "description": "Replaces all mutable fields. externalId and server fields are immutable.\nIf-Match is optional; omit it only for an intentionally unconditional\nmutation. Retrying clients should read the representation and use its ETag.\n",
        "tags": [
          "Vaults"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          }
        ],
        "requestBody": {
          "description": "Complete mutable vault state. Use null to clear description.",
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "name": "platform",
                "description": "Platform runtime secrets",
                "labels": {
                  "app.kubernetes.io/managed-by": "moco-operator"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/vault-update"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated vault representation.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/vault"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "delete": {
        "operationId": "deleteVault",
        "summary": "Delete a vault",
        "description": "Deletes an empty vault. With cascade=true, also deletes all stored secrets.\nA missing vault returns 404; reconcilers may treat it as success. If-Match\nis optional, but recommended for retries that must not delete a newer revision.\n",
        "tags": [
          "Vaults"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          },
          {
            "$ref": "#/components/parameters/cascade"
          }
        ],
        "responses": {
          "204": {
            "description": "Vault deleted.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "409": {
            "$ref": "#/components/responses/conflict"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/vaults/{vaultId}/secrets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/vault-id"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "listSecrets",
        "summary": "List secret metadata",
        "description": "Lists metadata only; values are never returned. The first request creates a\nsequence-bounded snapshot. Follow `nextCursor` until `hasMore` is false. If\nthe cursor expires, restart without one. Omit `prefix` to list the complete\nvault only when the authorization policy is vault-scoped. A path-scoped\npolicy requires a non-empty `prefix` within its configured\n`secretPathPrefix`. Use `GET /api/v1/watch` as a full-resync signal after\nmutations, including deletions.\n",
        "tags": [
          "Secrets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/secret-prefix"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of secret metadata ordered by immutable insertion sequence.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/secret-list"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "410": {
            "$ref": "#/components/responses/cursor-expired"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/vaults/{vaultId}/secret/metadata": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/vault-id"
        },
        {
          "$ref": "#/components/parameters/secret-path"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "getSecretMetadata",
        "summary": "Inspect secret state without reading its value",
        "description": "Returns metadata only. Controllers should prefer this operation for drift\nchecks that do not require plaintext. When a deployment configures\n`secretPathPrefix`, the decoded `path` must begin with that literal prefix.\n",
        "tags": [
          "Secrets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-none-match"
          }
        ],
        "responses": {
          "200": {
            "description": "Secret metadata without its value.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/secret-metadata"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/secret-not-modified"
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    },
    "/api/v1/tenants/{tenantId}/vaults/{vaultId}/secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/tenant-id"
        },
        {
          "$ref": "#/components/parameters/vault-id"
        },
        {
          "$ref": "#/components/parameters/secret-path"
        },
        {
          "$ref": "#/components/parameters/request-id"
        }
      ],
      "get": {
        "operationId": "getSecret",
        "summary": "Read a secret",
        "description": "Returns one secret including its base64-encoded value. Clients must keep the\nresponse out of logs, status fields, events, metrics, traces, and caches.\nAuthorization is evaluated at vault granularity unless the deployment's\ninternal authorization policy sets `secretPathPrefix` for this route. When\npresent, the decoded logical `path` must begin with that literal prefix.\nUse a dedicated vault when a secret needs an independent policy scope.\n",
        "tags": [
          "Secrets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-none-match"
          }
        ],
        "responses": {
          "200": {
            "description": "Secret value and metadata.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/cache-control"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/secret"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/secret-not-modified"
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "put": {
        "operationId": "putSecret",
        "summary": "Create or replace a secret",
        "description": "Creates or replaces the value at path. `If-None-Match: *` makes the request\ncreate-only; `If-Match` makes it replace-only at a known ETag. Sending both\nheaders returns 400. Without either header the operation is an unconditional,\nidempotent upsert. The response contains metadata, never the submitted value.\nAuthorization remains vault-scoped for policies without a path prefix. A\ndeployment may narrow this operation with `secretPathPrefix` on its\ninternal authorization snapshot; the requested `path` must be within the\nconfigured prefix.\n",
        "tags": [
          "Secrets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          },
          {
            "$ref": "#/components/parameters/if-none-match"
          }
        ],
        "requestBody": {
          "description": "Secret value. The decoded payload is limited to 1 MiB.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/secret-write"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing secret replaced.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/secret-metadata"
                }
              }
            }
          },
          "201": {
            "description": "New secret created.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/etag"
              },
              "Location": {
                "$ref": "#/components/headers/location"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/secret-metadata"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "413": {
            "$ref": "#/components/responses/payload-too-large"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      },
      "delete": {
        "operationId": "deleteSecret",
        "summary": "Delete a secret",
        "description": "Deletes the value at path. A missing secret returns 404; reconcilers may\ntreat that response as successful convergence.\n",
        "tags": [
          "Secrets"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/if-match"
          }
        ],
        "responses": {
          "204": {
            "description": "Secret deleted.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/request-id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/bad-request"
          },
          "401": {
            "$ref": "#/components/responses/unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/forbidden"
          },
          "404": {
            "$ref": "#/components/responses/not-found"
          },
          "412": {
            "$ref": "#/components/responses/precondition-failed"
          },
          "429": {
            "$ref": "#/components/responses/too-many-requests"
          },
          "500": {
            "$ref": "#/components/responses/internal-error"
          },
          "503": {
            "$ref": "#/components/responses/service-unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Deployment-issued bearer credential sent in the Authorization header.\nTokens may be opaque; clients must not assume JWT structure.\n"
      }
    },
    "parameters": {
      "request-id": {
        "name": "X-Request-ID",
        "in": "header",
        "required": false,
        "description": "Optional caller correlation ID; the server returns the effective ID.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "resource-version": {
        "name": "resourceVersion",
        "in": "query",
        "required": false,
        "description": "Opaque durable change checkpoint returned by the watch endpoint. A caller\nthat supplies a checkpoint is notified when any persisted resource changes;\nit should then resync the collections it is responsible for. Because the\ncheckpoint is process-wide, callers need the cluster-wide watch permission.\n",
        "schema": {
          "type": "string",
          "pattern": "^rv-[0-9]+$",
          "minLength": 3,
          "maxLength": 64,
          "example": "rv-42"
        }
      },
      "watch-timeout": {
        "name": "timeoutSeconds",
        "in": "query",
        "required": false,
        "description": "Maximum number of seconds to wait for a change. Zero returns immediately. The limit leaves headroom for the server write timeout.",
        "schema": {
          "type": "integer",
          "format": "int32",
          "minimum": 0,
          "maximum": 25,
          "default": 0,
          "example": 15
        }
      },
      "tenant-id": {
        "name": "tenantId",
        "in": "path",
        "required": true,
        "description": "Tenant UUID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "tenant-resource-version": {
        "name": "resourceVersion",
        "in": "query",
        "required": false,
        "description": "Opaque durable change checkpoint scoped to this tenant. When supplied, the endpoint reports changed=true after a newer tenant, vault, or secret mutation; use the public collection endpoints to relist resources after a change.",
        "schema": {
          "$ref": "#/components/schemas/tenant-resource-version"
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of items to return.",
        "schema": {
          "type": "integer",
          "format": "int32",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque next-page cursor returned by the preceding list response. Repeat the same `prefix` query value when continuing a path-scoped secret listing.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2048
        }
      },
      "name-filter": {
        "name": "name",
        "in": "query",
        "required": false,
        "description": "Exact, case-sensitive resource name filter.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "external-id-filter": {
        "name": "externalId",
        "in": "query",
        "required": false,
        "description": "Exact immutable external correlation identifier filter.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512
        }
      },
      "idempotency-key": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-generated key for safely retrying a create request. Keys are scoped\nto the authenticated principal and operation, retained for at least 24 hours,\nand limited to visible ASCII. Reuse with a different body returns 409.\n",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[\\x21-\\x7E]+$"
        }
      },
      "if-none-match": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "description": "For GET, return 304 when the current ETag matches. For PUT, only `*` is\naccepted and means create only. A PUT must not send both conditional headers.\n",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "if-match": {
        "name": "If-Match",
        "in": "header",
        "required": false,
        "description": "Opaque ETag from the latest representation. When present, the mutation is\napplied only if the current ETag matches; otherwise the server returns 412.\nOmitting it performs an unconditional mutation. Retrying a mutation safely\nrequires reading the latest representation and sending its ETag.\n",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "cascade": {
        "name": "cascade",
        "in": "query",
        "required": false,
        "description": "Delete child resources when true. The default false returns 409 while child\nresources exist, preventing accidental secret deletion.\n",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "vault-id": {
        "name": "vaultId",
        "in": "path",
        "required": true,
        "description": "Vault UUID scoped to the tenant in the request path.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "secret-prefix": {
        "name": "prefix",
        "in": "query",
        "required": false,
        "description": "Slash-delimited prefix. One trailing slash is allowed; empty, `.`, and `..`\nsegments are invalid. Omit it to list the entire vault only with a\nvault-scoped authorization policy; path-scoped policies require a non-empty\nprefix within their configured `secretPathPrefix`.\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1024,
          "pattern": "^[^/]+(?:/[^/]+)*/?$"
        }
      },
      "secret-path": {
        "name": "path",
        "in": "query",
        "required": true,
        "description": "Slash-delimited logical secret path. Empty, `.`, and `..` segments are\ninvalid. Paths can be sensitive metadata and should not be logged by clients.\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1024,
          "pattern": "^[^/]+(?:/[^/]+)*$"
        }
      }
    },
    "headers": {
      "request-id": {
        "description": "Server-selected request correlation identifier.",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 1
        },
        "example": "11111111-1111-4111-8111-111111111111"
      },
      "www-authenticate": {
        "description": "Authentication challenge for a bearer credential.",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "Bearer"
          ]
        },
        "example": "Bearer"
      },
      "retry-after": {
        "description": "Delay in seconds before retrying the request.",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int32",
          "minimum": 1
        },
        "example": 5
      },
      "cache-control": {
        "description": "Cache directive for secret-bearing responses.",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "no-store"
          ]
        },
        "example": "no-store"
      },
      "etag": {
        "description": "Opaque strong validator for conditional requests. The value is a quoted\nentity-tag; clients must treat it as opaque and send the complete value back.\n`If-Match` accepts one or more strong tags or `*`; `If-None-Match` accepts a\ncomma-separated list (including weak tags) for reads and only `*` for PUT.\n",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 1
        },
        "example": "\"tenant-3\""
      },
      "location": {
        "description": "Relative URI of the created resource.",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uri-reference"
        },
        "example": "/api/v1/tenants/11111111-1111-4111-8111-111111111111"
      }
    },
    "schemas": {
      "service-info": {
        "title": "ServiceInfo",
        "type": "object",
        "description": "Server identity and compatibility information.",
        "additionalProperties": false,
        "required": [
          "apiVersion",
          "serviceVersion",
          "capabilities"
        ],
        "properties": {
          "apiVersion": {
            "type": "string",
            "description": "Public API major version implemented by this endpoint.",
            "enum": [
              "v1"
            ],
            "example": "v1"
          },
          "serviceVersion": {
            "type": "string",
            "description": "Mocó server version; clients must treat it as opaque.",
            "minLength": 1,
            "example": "0.2.0"
          },
          "capabilities": {
            "type": "array",
            "description": "Stable feature identifiers available on this deployment.",
            "uniqueItems": true,
            "items": {
              "type": "string",
              "minLength": 1
            },
            "example": [
              "tenants",
              "vaults",
              "secrets",
              "conditional-writes",
              "resource-watch",
              "tenant-resource-watch"
            ]
          }
        }
      },
      "problem-error": {
        "title": "ProblemError",
        "type": "object",
        "description": "Field-specific validation failure safe to expose to clients.",
        "additionalProperties": false,
        "required": [
          "message"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "JSON Pointer or parameter name when the failure has a location.",
            "example": "/name"
          },
          "code": {
            "type": "string",
            "minLength": 1,
            "description": "Stable machine-readable validation code.",
            "example": "invalid_format"
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "description": "Human-readable message that never contains secret values.",
            "example": "must match the documented name format"
          }
        }
      },
      "problem": {
        "title": "Problem",
        "type": "object",
        "description": "Problem Details response compatible with RFC 9457.",
        "additionalProperties": false,
        "required": [
          "type",
          "title",
          "status",
          "code",
          "requestId"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri-reference",
            "description": "Stable documentation URI for this problem type.",
            "example": "https://moco.araihu.com/problems/not-found"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "example": "Not Found"
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "minimum": 400,
            "maximum": 599,
            "example": 404
          },
          "code": {
            "type": "string",
            "minLength": 1,
            "description": "Stable machine-readable error code. Implementations currently use\nunauthorized, forbidden, invalid_request, invalid_json,\ninvalid_content_type, invalid_request_id, validation_failed,\ninvalid_if_match, invalid_if_none_match, invalid_cursor, cursor_expired,\ninvalid_resource_version, invalid_watch_timeout, resource_version_ahead,\nresource_not_found,\nresource_conflict, resource_has_children, idempotency_key_conflict,\netag_mismatch, secret_too_large, invalid_rotation_checkpoint,\ninvalid_limit, encryption_key_epoch_conflict, service_unavailable,\nand internal_error.\n",
            "example": "resource_not_found"
          },
          "detail": {
            "type": "string",
            "description": "Human-readable detail that never contains secret values.",
            "example": "The requested resource does not exist."
          },
          "instance": {
            "type": "string",
            "format": "uri-reference",
            "description": "Occurrence identifier for support correlation.",
            "example": "urn:moco:request:11111111-1111-4111-8111-111111111111"
          },
          "requestId": {
            "type": "string",
            "minLength": 1,
            "description": "Correlation identifier also returned in X-Request-ID.",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "resourceId": {
            "type": "string",
            "description": "Existing resource ID when safely useful for conflict recovery.",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "errors": {
            "type": "array",
            "example": [
              {
                "field": "/name",
                "code": "invalid_format",
                "message": "must match the documented name format"
              }
            ],
            "items": {
              "$ref": "#/components/schemas/problem-error"
            }
          }
        }
      },
      "watch-result": {
        "title": "WatchResult",
        "type": "object",
        "description": "Durable resource-version checkpoint returned by a watch poll.",
        "additionalProperties": false,
        "required": [
          "resourceVersion",
          "changed"
        ],
        "properties": {
          "resourceVersion": {
            "type": "string",
            "pattern": "^rv-[0-9]+$",
            "minLength": 3,
            "maxLength": 64,
            "description": "Current durable resource revision.",
            "example": "rv-42"
          },
          "changed": {
            "type": "boolean",
            "description": "True when the current revision is newer than the supplied checkpoint.",
            "example": true
          }
        }
      },
      "tenant-resource-version": {
        "title": "TenantResourceVersion",
        "type": "string",
        "pattern": "^trv-[0-9]+$",
        "minLength": 5,
        "maxLength": 64,
        "description": "Opaque durable resource checkpoint scoped to one tenant.",
        "example": "trv-42"
      },
      "tenant-watch-result": {
        "title": "TenantWatchResult",
        "type": "object",
        "description": "Durable tenant-scoped resource-version checkpoint returned by a watch poll.",
        "additionalProperties": false,
        "required": [
          "resourceVersion",
          "changed"
        ],
        "properties": {
          "resourceVersion": {
            "type": "string",
            "pattern": "^trv-[0-9]+$",
            "minLength": 5,
            "maxLength": 64,
            "description": "Current durable resource revision for the tenant.",
            "example": "trv-42"
          },
          "changed": {
            "type": "boolean",
            "description": "True when the current tenant revision is newer than the supplied checkpoint.",
            "example": true
          }
        }
      },
      "labels": {
        "title": "Labels",
        "type": "object",
        "description": "Client-defined metadata used for organization and ownership hints.",
        "maxProperties": 64,
        "additionalProperties": {
          "type": "string",
          "maxLength": 256
        },
        "example": {
          "app.kubernetes.io/managed-by": "moco-operator"
        }
      },
      "tenant": {
        "title": "Tenant",
        "type": "object",
        "description": "Tenant identity, automation metadata, and lifecycle state.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "labels",
          "revision",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true,
            "description": "Server-assigned stable identifier.",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "description": "Human-readable name, unique across tenants.",
            "example": "production"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "description": "Optional human-readable purpose.",
            "example": "Production workloads"
          },
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "readOnly": true,
            "nullable": true,
            "description": "Immutable caller-supplied correlation identifier, unique across tenants.\nAutomation can rediscover a resource after losing local status.\n",
            "example": "kubernetes://cluster-a/platform/tenant-production"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "readOnly": true,
            "description": "Monotonic resource revision; the HTTP ETag remains authoritative.",
            "example": 3
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T13:00:00Z"
          }
        }
      },
      "page-info": {
        "title": "PageInfo",
        "type": "object",
        "description": "Cursor state for one collection snapshot. Items are returned in ascending\nimmutable insertion-sequence order. If `hasMore` is true, `nextCursor` is\nnon-null and must be used to continue the same snapshot; if `hasMore` is\nfalse, `nextCursor` is null.\n",
        "additionalProperties": false,
        "required": [
          "hasMore",
          "nextCursor"
        ],
        "properties": {
          "hasMore": {
            "type": "boolean",
            "description": "Whether another page exists in the same snapshot. True implies a non-null nextCursor.",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "minLength": 1,
            "nullable": true,
            "description": "Opaque cursor for the next page; required when hasMore is true and null on the final page.",
            "example": null
          }
        }
      },
      "tenant-list": {
        "title": "TenantList",
        "type": "object",
        "description": "One page from a stable tenant snapshot.",
        "additionalProperties": false,
        "required": [
          "items",
          "page"
        ],
        "properties": {
          "items": {
            "type": "array",
            "example": [
              {
                "id": "11111111-1111-4111-8111-111111111111",
                "name": "production",
                "labels": {},
                "revision": 1,
                "createdAt": "2026-08-25T12:00:00Z",
                "updatedAt": "2026-08-25T12:00:00Z"
              }
            ],
            "items": {
              "$ref": "#/components/schemas/tenant"
            }
          },
          "page": {
            "$ref": "#/components/schemas/page-info"
          }
        }
      },
      "tenant-create": {
        "title": "TenantCreate",
        "type": "object",
        "description": "Fields accepted when creating a tenant.",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "example": "production"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "example": "Production workloads"
          },
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Immutable caller correlation identifier, unique across tenants. Strongly recommended for automation and adoption.",
            "example": "kubernetes://cluster-a/platform/tenant-production"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          }
        }
      },
      "tenant-update": {
        "title": "TenantUpdate",
        "type": "object",
        "description": "Complete mutable tenant state used for replacement updates.",
        "additionalProperties": false,
        "required": [
          "name",
          "description",
          "labels"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "example": "platform"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "description": "Set to null to clear the description.",
            "example": "Platform-owned tenant"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          }
        }
      },
      "vault": {
        "title": "Vault",
        "type": "object",
        "description": "Tenant-scoped vault identity, automation metadata, and lifecycle state.",
        "additionalProperties": false,
        "required": [
          "id",
          "tenantId",
          "name",
          "labels",
          "revision",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true,
            "description": "Server-assigned stable identifier.",
            "example": "22222222-2222-4222-8222-222222222222"
          },
          "tenantId": {
            "type": "string",
            "format": "uuid",
            "readOnly": true,
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "description": "Human-readable name, unique within its tenant.",
            "example": "application"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "example": "Application runtime secrets"
          },
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "readOnly": true,
            "nullable": true,
            "description": "Immutable caller correlation identifier, unique within its tenant.",
            "example": "kubernetes://cluster-a/platform/vault-application"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          },
          "revision": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "readOnly": true,
            "description": "Monotonic resource revision; the HTTP ETag remains authoritative.",
            "example": 2
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T13:00:00Z"
          }
        }
      },
      "vault-list": {
        "title": "VaultList",
        "type": "object",
        "description": "One page from a stable vault snapshot.",
        "additionalProperties": false,
        "required": [
          "items",
          "page"
        ],
        "properties": {
          "items": {
            "type": "array",
            "example": [
              {
                "id": "22222222-2222-4222-8222-222222222222",
                "tenantId": "11111111-1111-4111-8111-111111111111",
                "name": "application",
                "labels": {},
                "revision": 1,
                "createdAt": "2026-08-25T12:00:00Z",
                "updatedAt": "2026-08-25T12:00:00Z"
              }
            ],
            "items": {
              "$ref": "#/components/schemas/vault"
            }
          },
          "page": {
            "$ref": "#/components/schemas/page-info"
          }
        }
      },
      "vault-create": {
        "title": "VaultCreate",
        "type": "object",
        "description": "Fields accepted when creating a vault.",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "example": "application"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "example": "Application runtime secrets"
          },
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Immutable caller correlation identifier, unique within the tenant. Strongly recommended for automation and adoption.",
            "example": "kubernetes://cluster-a/platform/vault-application"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          }
        }
      },
      "vault-update": {
        "title": "VaultUpdate",
        "type": "object",
        "description": "Complete mutable vault state used for replacement updates.",
        "additionalProperties": false,
        "required": [
          "name",
          "description",
          "labels"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$",
            "example": "platform"
          },
          "description": {
            "type": "string",
            "maxLength": 1024,
            "nullable": true,
            "description": "Set to null to clear the description.",
            "example": "Platform runtime secrets"
          },
          "labels": {
            "$ref": "#/components/schemas/labels"
          }
        }
      },
      "secret-metadata": {
        "title": "SecretMetadata",
        "type": "object",
        "description": "Secret identity and lifecycle metadata without its value.",
        "additionalProperties": false,
        "required": [
          "path",
          "version",
          "digest",
          "contentType",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "pattern": "^[^/]+(?:/[^/]+)*$",
            "description": "Slash-delimited logical path. Empty, `.`, and `..` segments are invalid.\nPaths can be sensitive metadata and must not be logged by default.\n",
            "example": "prod/db/password"
          },
          "version": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "readOnly": true,
            "description": "Monotonic version of this logical secret.",
            "example": 4
          },
          "digest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$",
            "readOnly": true,
            "description": "SHA-256 digest of the decoded value for drift detection.",
            "example": "sha256:2bb80d537b1da3e38bd30361aa855686bde0ba53e611d8a8a5e47993629e366f"
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Media type assigned to the decoded value.",
            "example": "text/plain"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T13:00:00Z"
          }
        }
      },
      "secret-list": {
        "title": "SecretList",
        "type": "object",
        "description": "One page of secret metadata ordered by immutable insertion sequence; values are omitted.",
        "additionalProperties": false,
        "required": [
          "items",
          "page"
        ],
        "properties": {
          "items": {
            "type": "array",
            "example": [
              {
                "path": "prod/db/password",
                "version": 4,
                "digest": "sha256:2bb80d537b1da3e38bd30361aa855686bde0ba53e611d8a8a5e47993629e366f",
                "contentType": "text/plain",
                "createdAt": "2026-08-25T12:00:00Z",
                "updatedAt": "2026-08-25T13:00:00Z"
              }
            ],
            "items": {
              "$ref": "#/components/schemas/secret-metadata"
            }
          },
          "page": {
            "$ref": "#/components/schemas/page-info"
          }
        }
      },
      "secret": {
        "title": "Secret",
        "type": "object",
        "description": "Secret metadata and value. Consumers must never log this object.",
        "x-sensitive": true,
        "additionalProperties": false,
        "required": [
          "path",
          "value",
          "version",
          "digest",
          "contentType",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "pattern": "^[^/]+(?:/[^/]+)*$",
            "description": "Slash-delimited logical path; `.`, `..`, and empty segments are invalid.",
            "example": "prod/db/password"
          },
          "value": {
            "type": "string",
            "format": "byte",
            "minLength": 1,
            "maxLength": 1398104,
            "description": "Base64-encoded value, limited to 1 MiB after decoding.",
            "example": "c2VjcmV0"
          },
          "version": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "readOnly": true,
            "example": 4
          },
          "digest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$",
            "readOnly": true,
            "description": "SHA-256 digest of the decoded value.",
            "example": "sha256:2bb80d537b1da3e38bd30361aa855686bde0ba53e611d8a8a5e47993629e366f"
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "example": "text/plain"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "example": "2026-08-25T13:00:00Z"
          }
        }
      },
      "secret-write": {
        "title": "SecretWrite",
        "type": "object",
        "description": "Secret value and media type accepted for storage.",
        "x-sensitive": true,
        "additionalProperties": false,
        "required": [
          "value"
        ],
        "properties": {
          "value": {
            "type": "string",
            "format": "byte",
            "minLength": 1,
            "maxLength": 1398104,
            "description": "Base64-encoded value, limited to 1 MiB after decoding.",
            "example": "c2VjcmV0"
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "default": "application/octet-stream",
            "example": "text/plain"
          }
        }
      }
    },
    "responses": {
      "unauthorized": {
        "description": "Bearer authentication is missing or invalid.",
        "headers": {
          "WWW-Authenticate": {
            "$ref": "#/components/headers/www-authenticate"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/unauthorized",
              "title": "Unauthorized",
              "status": 401,
              "code": "unauthorized",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "forbidden": {
        "description": "The authenticated principal lacks permission for the operation.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/forbidden",
              "title": "Forbidden",
              "status": 403,
              "code": "forbidden",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "too-many-requests": {
        "description": "The caller exceeded an enforced rate limit. A deployment may enforce this in\nMocó or at its API gateway; clients must honor Retry-After before retrying.\n",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/retry-after"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/too-many-requests",
              "title": "Too Many Requests",
              "status": 429,
              "code": "rate_limited",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "internal-error": {
        "description": "The server failed to complete the request.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/internal-error",
              "title": "Internal Server Error",
              "status": 500,
              "code": "internal_error",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "service-unavailable": {
        "description": "The service or a required dependency is temporarily unavailable. This may be\nemitted by Mocó or by the deployment edge; clients should retry after the\nadvertised interval with bounded exponential backoff.\n",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/retry-after"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/service-unavailable",
              "title": "Service Unavailable",
              "status": 503,
              "code": "service_unavailable",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "bad-request": {
        "description": "Request parameters, headers, or body are invalid.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/bad-request",
              "title": "Bad Request",
              "status": 400,
              "code": "invalid_request",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "not-found": {
        "description": "The requested tenant, vault, or secret does not exist.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/not-found",
              "title": "Not Found",
              "status": 404,
              "code": "resource_not_found",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "cursor-expired": {
        "description": "The cursor snapshot expired; restart listing without a cursor.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/cursor-expired",
              "title": "Cursor Expired",
              "status": 410,
              "code": "cursor_expired",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "conflict": {
        "description": "The request conflicts with current resource state, a uniqueness constraint,\nexisting children, or a prior idempotency-key body. Uniqueness conflicts set\n`resourceId` when the existing resource can be identified safely; clients\nmay use it to adopt rather than create a duplicate.\n",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/conflict",
              "title": "Conflict",
              "status": 409,
              "code": "resource_conflict",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "not-modified": {
        "description": "The representation has not changed since the supplied ETag.",
        "headers": {
          "ETag": {
            "$ref": "#/components/headers/etag"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        }
      },
      "precondition-failed": {
        "description": "A conditional request header does not match current resource state.",
        "headers": {
          "ETag": {
            "$ref": "#/components/headers/etag"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/precondition-failed",
              "title": "Precondition Failed",
              "status": 412,
              "code": "etag_mismatch",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      },
      "secret-not-modified": {
        "description": "The secret representation has not changed since the supplied ETag.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/cache-control"
          },
          "ETag": {
            "$ref": "#/components/headers/etag"
          },
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        }
      },
      "payload-too-large": {
        "description": "The decoded secret value exceeds the 1 MiB limit.",
        "headers": {
          "X-Request-ID": {
            "$ref": "#/components/headers/request-id"
          }
        },
        "content": {
          "application/problem+json": {
            "example": {
              "type": "https://moco.araihu.com/problems/payload-too-large",
              "title": "Payload Too Large",
              "status": 413,
              "code": "secret_too_large",
              "requestId": "11111111-1111-4111-8111-111111111111"
            },
            "schema": {
              "$ref": "#/components/schemas/problem"
            }
          }
        }
      }
    }
  }
}