openapi: 3.0.3
info:
  title: PPN Hub API
  version: 1.0.0
  description: |
    Central Hub API for PPN Services

    PPN Hub provides unified authentication, API key management, and proxy access to all PPN services.
    Built on Cloudflare Workers with edge-native JWT authentication and usage tracking.

    ## Features
    - JWT-based user authentication
    - API key creation and management
    - Usage tracking with D1 database
    - API proxy to all PPN services
    - Server-side rendering with HTMX + Alpine.js
    - Fast cold starts, no Next.js overhead
  contact:
    name: PPN Hub Support
    email: support@ppn-hub.org
  license:
    name: MIT License
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://www.ppn-hub.com
    description: Production
  - url: http://localhost:8787
    description: Local development
paths:
  /:
    get:
      summary: Landing page
      tags:
        - Public
      security: []
      responses:
        '200':
          description: HTML landing page
          content:
            text/html:
              schema:
                type: string
      operationId: getRoot
  /auth/signin:
    get:
      summary: Sign in page
      tags:
        - Authentication
      security: []
      responses:
        '200':
          description: HTML sign in page
          content:
            text/html:
              schema:
                type: string
      operationId: getAuthSignin
    post:
      summary: Sign in handler
      tags:
        - Authentication
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
      responses:
        '200':
          description: Sign in successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  token:
                    type: string
        '401':
          description: Invalid credentials
      operationId: createAuthSignin
  /auth/signout:
    post:
      summary: Sign out
      tags:
        - Authentication
      responses:
        '200':
          description: Signed out successfully
      operationId: createAuthSignout
  /console:
    get:
      summary: User console (dashboard)
      tags:
        - Dashboard
      security:
        - CookieAuth: []
      responses:
        '200':
          description: HTML console dashboard page
        '401':
          description: Not authenticated
      operationId: getConsole
  /api/v1/health:
    get:
      summary: Health check
      tags:
        - API
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: Service healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
      operationId: getHealth
  /api/v1/usage:
    get:
      summary: Get API usage statistics
      tags:
        - API
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  total_requests:
                    type: integer
                  requests_today:
                    type: integer
                  quota_remaining:
                    type: integer
      operationId: getUsage
  /api/v1/onokoro/{path}:
    get:
      summary: Proxy to Onokoro API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: 'api/v1/shrine/search?q=meiji'
      responses:
        '200':
          description: Proxied response from Onokoro
      operationId: getOnokoroPath
  /api/v1/plantunify/{path}:
    get:
      summary: Proxy to PlantUnify API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: 'api/v1/plants?limit=10'
      responses:
        '200':
          description: Proxied response from PlantUnify
      operationId: getPlantunifyPath
  /api/v1/weathio/{path}:
    get:
      summary: Proxy to Weathio API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: v1/current/tokyo
      responses:
        '200':
          description: Proxied response from Weathio
      operationId: getWeathioPath
  /api/v1/celestora/{path}:
    get:
      summary: Proxy to Celestora API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: 'api/v1/celestial/sun?location=35.6762,139.6503'
      responses:
        '200':
          description: Proxied response from Celestora
      operationId: getCelestoraPath
  /api/v1/ubusna/{path}:
    get:
      summary: Proxy to Ubusna API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: 'api/v1/festivals?region=kanto'
      responses:
        '200':
          description: Proxied response from Ubusna
      operationId: getUbusnaPath
  /api/v1/enrich/{path}:
    get:
      summary: Proxy to Enrich API
      tags:
        - Proxy
      security:
        - ApiKeyAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: api/v1/ip/8.8.8.8
      responses:
        '200':
          description: Proxied response from Enrich
      operationId: getEnrichPath
  /admin/ai-traffic:
    get:
      summary: AI agent traffic analytics (admin)
      tags:
        - Admin
        - Discovery
      security:
        - CookieAuth: []
      description: |
        Aggregated counts of AI agent traffic against discovery, catalog, and
        OpenAPI endpoints. UA classes include claude, chatgpt, cursor,
        perplexity, google-extended, cohere, meta-ai, bytespider, plus generic
        bot / human / unknown buckets.
      parameters:
        - name: days
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 365
            default: 30
          description: Number of days to look back from today (UTC).
      responses:
        '200':
          description: Aggregated traffic
          content:
            application/json:
              schema:
                type: object
                properties:
                  range:
                    type: object
                    properties:
                      since:
                        type: string
                      days:
                        type: integer
                  totals:
                    type: object
                    properties:
                      all:
                        type: integer
                      ai:
                        type: integer
                  by_ua:
                    type: array
                  by_path:
                    type: array
                  by_date:
                    type: array
                  rows:
                    type: array
        '403':
          description: Not authorized
      operationId: getAdminAiTraffic
  /admin/{path}:
    get:
      summary: Admin panel
      tags:
        - Admin
      security:
        - CookieAuth: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: Path
          example: status
      responses:
        '200':
          description: Admin panel page
        '403':
          description: Not authorized
      operationId: getAdminPath

  # --- BEGIN auto-generated stubs ---
  /.well-known/openid-configuration:
    get:
      summary: Get .well-known/openid-configuration
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /api:
    post:
      summary: Create api
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /api-groups:
    get:
      summary: Get api-groups
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /api-groups/{id}:
    get:
      summary: Get api-groups/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
    put:
      summary: Update api-groups/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /api/{id}:
    delete:
      summary: Delete api/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
    get:
      summary: Get api/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
    put:
      summary: Update api/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /apis:
    get:
      summary: Get apis
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create apis
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /apis/{apiId}/plan-features:
    post:
      summary: Create apis/apiId/plan-features
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /apis/{apiId}/pricing-plans:
    post:
      summary: Create apis/apiId/pricing-plans
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /apis/{apiId}/statistics:
    post:
      summary: Create apis/apiId/statistics
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /apis/{id}:
    delete:
      summary: Delete apis/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
    get:
      summary: Get apis/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
    put:
      summary: Update apis/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /apply:
    post:
      summary: Create apply
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /auth/login:
    get:
      summary: Get auth/login
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /authorize:
    get:
      summary: Get authorize
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create authorize
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /backup-codes/regenerate:
    post:
      summary: Create backup-codes/regenerate
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /baseline/update:
    post:
      summary: Create baseline/update
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /baselines:
    get:
      summary: Get baselines
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /boundaries:
    get:
      summary: Get boundaries
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /boundaries/{id}:
    get:
      summary: Get boundaries/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /boundaries/{id}/evidence:
    get:
      summary: Get boundaries/id/evidence
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /boundaries/{id}/papers:
    get:
      summary: Get boundaries/id/papers
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /cancel:
    post:
      summary: Create cancel
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /quickstart:
    get:
      summary: Quickstart guide (HTML) — first 5 minutes for new developers.
      tags:
        - Discovery
      security: []
      description: |
        Static HTML quickstart page covering the canonical 3-step flow
        (sign in → pick an API → make a call), with copy-pasteable curl,
        pointers to per-API Scalar Try-it-out (`{api_url}/docs`), the MCP
        gateway for AI agents, and a pricing summary.
      responses:
        '200':
          description: Quickstart HTML page
          content:
            text/html:
              schema:
                type: string
      operationId: getQuickstart
  /catalog:
    get:
      summary: Public catalog (HTML)
      tags:
        - Discovery
      security: []
      description: |
        Human-readable catalog page listing every public PPN API by group.
        For machine clients, prefer `/catalog.json`.
      responses:
        '200':
          description: HTML catalog page
          content:
            text/html:
              schema:
                type: string
      operationId: getCatalogHtml
  /catalog.json:
    get:
      summary: Machine-readable API catalog
      tags:
        - Discovery
      security: []
      description: |
        JSON listing of every public PPN API, grouped by category. Each entry
        includes the API URL, OpenAPI URL, status, pricing model, and tags so
        AI agents and tools can auto-discover available services.
      responses:
        '200':
          description: Catalog JSON
          content:
            application/json:
              schema:
                type: object
                properties:
                  service:
                    type: string
                  generated_at:
                    type: string
                    format: date-time
                  total_apis:
                    type: integer
                  total_groups:
                    type: integer
                  mcp:
                    type: object
                    description: MCP (Model Context Protocol) server endpoint for AI agents.
                    properties:
                      url:
                        type: string
                        format: uri
                      protocol:
                        type: string
                      tools:
                        type: array
                        items:
                          type: string
                      description:
                        type: string
                  groups:
                    type: array
                    items:
                      type: object
      operationId: getCatalogJson
  /llms.txt:
    get:
      summary: llmstxt.org index
      tags:
        - Discovery
      security: []
      description: |
        Plain-text index per the [llmstxt.org](https://llmstxt.org/) specification.
        Lists discovery endpoints and one bullet per public API.
      responses:
        '200':
          description: llms.txt content
          content:
            text/plain:
              schema:
                type: string
      operationId: getLlmsTxt
  /llms-full.txt:
    get:
      summary: llmstxt.org long-form variant
      tags:
        - Discovery
      security: []
      description: |
        Long-form llms.txt variant. Includes status, pricing, version, docs URL,
        and OpenAPI URL for each public API.
      responses:
        '200':
          description: llms-full.txt content
          content:
            text/plain:
              schema:
                type: string
      operationId: getLlmsFullTxt
  /.well-known/openapi.yaml:
    get:
      summary: Well-known OpenAPI spec mirror
      tags:
        - Discovery
      security: []
      description: |
        Mirror of `/openapi.yaml` exposed at the well-known path used by
        AI agents (Cursor, Continue, GPT Actions, etc.) for automatic discovery.
      responses:
        '200':
          description: OpenAPI 3.0 YAML
          content:
            text/yaml:
              schema:
                type: string
      operationId: getWellKnownOpenapi
  /clear-db:
    post:
      summary: Create clear-db
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /clear-oauth-data:
    post:
      summary: Create clear-oauth-data
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /contact:
    get:
      summary: Get contact
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /create-api-key:
    post:
      summary: Create create-api-key
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /create-donate-sandbox-plans:
    get:
      summary: Get create-donate-sandbox-plans
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create create-donate-sandbox-plans
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /create-user:
    post:
      summary: Create create-user
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /datalake-status:
    get:
      summary: Get datalake-status
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /db-info:
    get:
      summary: Get db-info
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /disable:
    get:
      summary: Get disable
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create disable
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /disable/confirm:
    post:
      summary: Create disable/confirm
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /docs/developer-compliance-guide:
    get:
      summary: Get docs/developer-compliance-guide
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /donate/health:
    get:
      summary: Get donate/health
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /enable-mfa:
    post:
      summary: Create enable-mfa
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /exchange:
    post:
      summary: Create exchange
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /feedback:
    get:
      summary: Get feedback
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /fingerprint:
    post:
      summary: Create fingerprint
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /flags:
    get:
      summary: Get flags
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /flags/{id}:
    get:
      summary: Get flags/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /flags/{id}/review:
    post:
      summary: Create flags/id/review
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /get-client-secret:
    post:
      summary: Create get-client-secret
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /google/callback:
    get:
      summary: Get google/callback
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /group/{groupId}:
    get:
      summary: Get group/groupId
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /groups:
    get:
      summary: Get groups
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /init-db:
    post:
      summary: Create init-db
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /ip/{ip}:
    get:
      summary: Get ip/ip
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: ip
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /keys:
    get:
      summary: Get keys
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /keys/rotate-emergency:
    post:
      summary: Create keys/rotate-emergency
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /keys/rotation/config:
    get:
      summary: Get keys/rotation/config
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    put:
      summary: Update keys/rotation/config
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /keys/rotation/history:
    get:
      summary: Get keys/rotation/history
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /keys/{keyId}/rotate:
    post:
      summary: Create keys/keyId/rotate
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: keyId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /list:
    get:
      summary: Get list
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /logs:
    get:
      summary: Get logs
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /lp:
    get:
      summary: Get lp
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /marduk-compliance:
    get:
      summary: Get marduk-compliance
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /me:
    get:
      summary: Get me
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /mfa:
    get:
      summary: Get mfa
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /mfa/verify:
    post:
      summary: Create mfa/verify
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /mfa/verify-backup:
    post:
      summary: Create mfa/verify-backup
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /monitoring:
    get:
      summary: Get monitoring
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /my:
    get:
      summary: Get my
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /news:
    get:
      summary: Get news
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /plans:
    get:
      summary: Get plans
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /plans/{apiId}:
    get:
      summary: Get plans/apiId
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /pricing:
    get:
      summary: Get pricing
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /pricing-plans/{id}:
    put:
      summary: Update pricing-plans/id
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /pricing/{apiId}:
    get:
      summary: Get pricing/apiId
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /public:
    get:
      summary: Get public
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /revenue:
    get:
      summary: Get revenue
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /revenue-summary:
    get:
      summary: Get revenue-summary
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /scheduled-status:
    get:
      summary: Get scheduled-status
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /search:
    get:
      summary: Get search
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /seed-pricing-data:
    post:
      summary: Create seed-pricing-data
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /send:
    post:
      summary: Create send
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /service-accounts:
    get:
      summary: Get service-accounts
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create service-accounts
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /service-accounts/{name}/rotate:
    post:
      summary: Create service-accounts/name/rotate
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /setup:
    post:
      summary: Create setup
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /stats:
    get:
      summary: Get stats
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /stats/overview:
    get:
      summary: Get stats/overview
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /stats/primary:
    get:
      summary: Get stats/primary
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /status/{apiId}:
    get:
      summary: Get status/apiId
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: apiId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /subscribe:
    post:
      summary: Create subscribe
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /subscribe/test:
    get:
      summary: Get subscribe/test
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /subscriptions:
    get:
      summary: Get subscriptions
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /token:
    post:
      summary: Create token
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /user/{userId}:
    get:
      summary: Get user/userId
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /userinfo:
    get:
      summary: Get userinfo
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /v1/account:
    get:
      summary: Get v1/account
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /v1/info:
    get:
      summary: Get v1/info
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /v1/keys:
    get:
      summary: Get v1/keys
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
    post:
      summary: Create v1/keys
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /validate-key:
    post:
      summary: Create validate-key
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /verify:
    post:
      summary: Create verify
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /verify-45:
    get:
      summary: Get verify-45
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /verify-phone:
    get:
      summary: Get verify-phone
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /violations:
    get:
      summary: Get violations
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /violations/{id}/resolve:
    post:
      summary: Create violations/id/resolve
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /vulnerabilities:
    get:
      summary: Get vulnerabilities
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      responses:
        '200':
          description: Successful response (schema TBD)
  /vulnerabilities/{id}/resolve:
    post:
      summary: Create vulnerabilities/id/resolve
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /{id}/counter-notice:
    post:
      summary: Create id/counter-notice
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /{id}/rename:
    post:
      summary: Create id/rename
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /{id}/rotate:
    post:
      summary: Create id/rotate
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  /{id}/update:
    post:
      summary: Create id/update
      description: |
        Auto-generated stub. Schema/responses/parameters TBD.
        Verify path matches the actual mounted URL (sub-router prefix may be missing).
      x-stub: true
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response (schema TBD)
  # --- END auto-generated stubs ---

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key sent via header (X-API-Key). Obtain from the PPN Hub developer portal.
    CookieAuth:
      type: apiKey
      in: cookie
      name: session
      description: API key sent via cookie (session). Obtain from the PPN Hub developer portal.
  schemas:
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
        message:
          type: string
tags:
  - name: Public
    description: Public pages
  - name: Authentication
    description: User authentication
  - name: Dashboard
    description: User dashboard and API key management
  - name: API
    description: Hub API endpoints
  - name: Proxy
    description: Proxy to PPN services
  - name: Admin
    description: Admin panel (requires admin role)
security:
  - ApiKeyAuth: []