openapi: 3.0.0
info:
  title: pyMC Repeater API
  description: |
    REST API for pyMC Repeater - LoRa mesh network repeater with room server functionality.

    ## Features
    - System statistics and monitoring
    - Packet history and analysis
    - Identity management
    - Access Control Lists (ACL)
    - Room server messaging
    - CAD calibration
    - Noise floor monitoring
  version: 1.0.0
  contact:
    name: pyMC Repeater
    url: https://github.com/rightup/pyMC_Repeater

servers:
  - url: /api
    description: Current server (relative URLs with /api prefix)
  - url: /
    description: Current server root (for /auth endpoints)
  - url: http://localhost:8080/api
    description: Local development server
  - url: http://{host}:8080/api
    description: Custom host
    variables:
      host:
        default: localhost
        description: Repeater IP address or hostname

tags:
  - name: Authentication
    description: User authentication and API token management
  - name: System
    description: System statistics and control
  - name: Packets
    description: Packet history and statistics
  - name: Charts
    description: Graph data and RRD metrics
  - name: Noise Floor
    description: Noise floor monitoring
  - name: CAD Calibration
    description: Channel Activity Detection calibration
  - name: Adverts
    description: Advertisement and contact management
  - name: Transport Keys
    description: Transport encryption key management
  - name: Network Policy
    description: Network flood policy and neighbor management
  - name: Identities
    description: Identity management
  - name: ACL
    description: Access Control Lists
  - name: Room Server
    description: Room server messaging

paths:
  # ============================================================================
  # Authentication Endpoints
  # ============================================================================
  /auth/login:
    post:
      tags: [Authentication]
      summary: User login
      description: Authenticate with username/password and receive JWT token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password, client_id]
              properties:
                username:
                  type: string
                  example: admin
                password:
                  type: string
                  format: password
                  example: admin123
                client_id:
                  type: string
                  description: Unique client identifier
                  example: web-client-abc123
      responses:
        '200':
          description: Login successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  token:
                    type: string
                    description: JWT token
                  expires_in:
                    type: integer
                    description: Token expiry in seconds
                  username:
                    type: string
        '401':
          description: Invalid credentials

  /auth/refresh:
    post:
      tags: [Authentication]
      summary: Refresh JWT token
      description: Extend session by refreshing JWT token
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [client_id]
              properties:
                client_id:
                  type: string
      responses:
        '200':
          description: Token refreshed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  token:
                    type: string
                  expires_in:
                    type: integer
                  username:
                    type: string

  /auth/verify:
    get:
      tags: [Authentication]
      summary: Verify authentication
      description: Check if current authentication is valid
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: Authentication valid
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  authenticated:
                    type: boolean
                  user:
                    type: object

  /auth/change_password:
    post:
      tags: [Authentication]
      summary: Change admin password
      description: Change the admin password (requires authentication)
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, new_password]
              properties:
                current_password:
                  type: string
                  format: password
                new_password:
                  type: string
                  format: password
                  minLength: 8
      responses:
        '200':
          description: Password changed successfully
        '401':
          description: Current password incorrect
        '400':
          description: Invalid password format

  /auth/tokens:
    get:
      tags: [Authentication]
      summary: List API tokens
      description: Get list of all API tokens (RESTful endpoint)
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: List of tokens
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  tokens:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
                        created_at:
                          type: string
                          format: date-time
                        last_used:
                          type: string
                          format: date-time
    post:
      tags: [Authentication]
      summary: Create API token
      description: Generate a new API token (RESTful endpoint)
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Friendly name for the token
                  example: "My API Token"
      responses:
        '200':
          description: Token created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  token:
                    type: string
                    description: The plaintext token (only shown once)
                  token_id:
                    type: integer
                  name:
                    type: string
                  warning:
                    type: string

  /auth/tokens/{token_id}:
    delete:
      tags: [Authentication]
      summary: Revoke API token
      description: Delete/revoke an API token by ID (RESTful endpoint)
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: token_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Token revoked
        '404':
          description: Token not found

  # ============================================================================
  # System Endpoints
  # ============================================================================
  /stats:
    get:
      tags: [System]
      summary: Get system statistics
      description: Returns repeater uptime, packet counts, and version information
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  uptime_secs:
                    type: integer
                    example: 3600
                  packets_received:
                    type: integer
                    example: 150
                  packets_sent:
                    type: integer
                    example: 120
                  version:
                    type: string
                    example: "1.0.0"
                  core_version:
                    type: string
                    example: "0.5.0"

  /send_advert:
    post:
      tags: [System]
      summary: Send repeater advertisement
      description: Manually trigger sending a repeater advertisement packet
      responses:
        '200':
          description: Advertisement sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '405':
          description: Method not allowed

  /logs:
    get:
      tags: [System]
      summary: Get system logs
      description: Retrieve recent system logs
      responses:
        '200':
          description: Log entries
          content:
            application/json:
              schema:
                type: object

  /hardware_stats:
    get:
      tags: [System]
      summary: Get hardware statistics
      description: CPU, memory, disk usage statistics
      responses:
        '200':
          description: Hardware stats
          content:
            application/json:
              schema:
                type: object
                properties:
                  cpu_percent:
                    type: number
                  memory_percent:
                    type: number
                  disk_usage:
                    type: object

  /hardware_processes:
    get:
      tags: [System]
      summary: Get process information
      description: List running processes and their resource usage
      responses:
        '200':
          description: Process list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

  /restart_service:
    post:
      tags: [System]
      summary: Restart repeater service
      description: Restart the repeater daemon service
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: Service restart initiated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /openapi:
    get:
      tags: [System]
      summary: Get OpenAPI specification
      description: Returns the OpenAPI/Swagger specification for this API
      responses:
        '200':
          description: OpenAPI spec
          content:
            application/json:
              schema:
                type: object

  /set_mode:
    post:
      tags: [System]
      summary: Set repeater mode
      description: |
        Set TX mode. forward = repeat on; monitor = no repeat but companions/tenants can send;
        no_tx = all transmission disabled (receive-only).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [mode]
              properties:
                mode:
                  type: string
                  enum: [forward, monitor, no_tx]
                  description: |
                    - forward: Repeat received packets; allow all local TX (default)
                    - monitor: Do not repeat; allow local TX (companions, adverts, etc.)
                    - no_tx: Do not repeat; no local TX (receive-only)
                  example: forward
            examples:
              forward:
                value:
                  mode: forward
              monitor:
                value:
                  mode: monitor
              no_tx:
                value:
                  mode: no_tx
      responses:
        '200':
          description: Mode changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              examples:
                success:
                  value:
                    success: true
                    data: "Mode set to forward"
        '400':
          description: Invalid mode
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /packet_stats:
    get:
      tags: [Packets]
      summary: Get packet statistics
      description: Returns packet counts, types, and routing statistics
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
            minimum: 1
            maximum: 168
          description: Time range in hours (1-168 = 1 hour to 1 week)
          example: 24
      responses:
        '200':
          description: Packet statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      total_packets:
                        type: integer
                      received:
                        type: integer
                      transmitted:
                        type: integer
                      dropped:
                        type: integer
                      by_type:
                        type: object
                      by_route:
                        type: object

  /set_duty_cycle:
    post:
      tags: [System]
      summary: Set duty cycle
      description: Enable or disable duty cycle limiting
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [enabled]
              properties:
                enabled:
                  type: boolean
                  description: Enable duty cycle limiting
                  example: true
            examples:
              enable:
                value:
                  enabled: true
              disable:
                value:
                  enabled: false
      responses:
        '200':
          description: Duty cycle setting updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /update_duty_cycle_config:
    post:
      tags: [System]
      summary: Update duty cycle configuration
      description: Update detailed duty cycle timing configuration
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                on_time:
                  type: integer
                  description: ON time in seconds
                off_time:
                  type: integer
                  description: OFF time in seconds
            example:
              enabled: true
              on_time: 300
              off_time: 60
      responses:
        '200':
          description: Duty cycle config updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /update_radio_config:
    post:
      tags: [System]
      summary: Update radio configuration
      description: Update LoRa radio parameters
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Radio config updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  # ============================================================================
  # Packet Endpoints
  # ============================================================================
  /recent_packets:
    get:
      tags: [Packets]
      summary: Get recent packets
      description: Retrieve recent packet history
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 1000
          description: Maximum number of packets to return
      responses:
        '200':
          description: Recent packets
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      type: object

  /packet_by_hash:
    get:
      tags: [Packets]
      summary: Get packet by hash
      parameters:
        - name: packet_hash
          in: query
          required: true
          schema:
            type: string
          description: Packet hash to lookup
      responses:
        '200':
          description: Packet details

  /packet_type_stats:
    get:
      tags: [Packets]
      summary: Get packet type statistics
      description: Statistics broken down by packet type
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
      responses:
        '200':
          description: Packet type stats
          content:
            application/json:
              schema:
                type: object

  /route_stats:
    get:
      tags: [Packets]
      summary: Get route statistics
      description: Statistics broken down by route type
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
      responses:
        '200':
          description: Route stats
          content:
            application/json:
              schema:
                type: object

  /filtered_packets:
    get:
      tags: [Packets]
      summary: Get filtered packets
      description: Retrieve packets filtered by type, route, and timestamp
      parameters:
        - name: type
          in: query
          schema:
            type: integer
            minimum: 0
            maximum: 15
          description: Packet type filter
        - name: route
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 3
          description: Route type filter
        - name: start_timestamp
          in: query
          schema:
            type: integer
          description: Start timestamp (Unix)
        - name: end_timestamp
          in: query
          schema:
            type: integer
          description: End timestamp (Unix)
        - name: limit
          in: query
          schema:
            type: integer
            default: 1000
          description: Maximum results
      responses:
        '200':
          description: Filtered packets
          content:
            application/json:
              schema:
                type: object

  # ============================================================================
  # Charts & RRD Endpoints
  # ============================================================================
  /rrd_data:
    get:
      tags: [Charts]
      summary: Get RRD time-series data
      description: |
        Retrieve Round-Robin Database metrics for graphing.

        **Note:** This endpoint extracts parameters from the request internally.
        Parameters are handled automatically by the backend.
      responses:
        '200':
          description: RRD time-series data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      start_time:
                        type: integer
                      end_time:
                        type: integer
                      step:
                        type: integer
                      timestamps:
                        type: array
                        items:
                          type: integer
                      metrics:
                        type: object

  /packet_type_graph_data:
    get:
      tags: [Charts]
      summary: Get packet type distribution graph data
      description: Returns bar chart data showing packet counts by type
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
        - name: resolution
          in: query
          schema:
            type: string
            enum: [average, max, min]
            default: average
        - name: types
          in: query
          schema:
            type: string
            default: all
          description: Comma-separated packet types or 'all'
      responses:
        '200':
          description: Packet type graph data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      start_time:
                        type: integer
                      end_time:
                        type: integer
                      step:
                        type: integer
                      chart_type:
                        type: string
                        example: bar
                      series:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            type:
                              type: string
                            data:
                              type: array
                              items:
                                type: number

  /metrics_graph_data:
    get:
      tags: [Charts]
      summary: Get system metrics graph data
      description: |
        Returns time-series data for system metrics like packet counts, RSSI, SNR, etc.

        Available metrics:
        - rx_count: Received packets
        - tx_count: Transmitted packets
        - drop_count: Dropped packets
        - avg_rssi: Average RSSI (dBm)
        - avg_snr: Average SNR (dB)
        - avg_length: Average packet length
        - avg_score: Average score
        - neighbor_count: Neighbor count
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
        - name: resolution
          in: query
          schema:
            type: string
            enum: [average, max, min]
            default: average
        - name: metrics
          in: query
          schema:
            type: string
            default: all
          description: Comma-separated metric names or 'all'
          example: "rx_count,tx_count,avg_rssi"
      responses:
        '200':
          description: Metrics graph data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      start_time:
                        type: integer
                      end_time:
                        type: integer
                      step:
                        type: integer
                      timestamps:
                        type: array
                        items:
                          type: integer
                      series:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            type:
                              type: string
                            data:
                              type: array
                              items:
                                type: number

  /noise_floor_history:
    get:
      tags: [Noise Floor]
      summary: Get noise floor history
      description: Retrieve historical noise floor measurements
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
            minimum: 1
            maximum: 168
          description: Time range in hours
      responses:
        '200':
          description: Noise floor history
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      type: object

  /noise_floor_stats:
    get:
      tags: [Noise Floor]
      summary: Get noise floor statistics
      description: Statistical analysis of noise floor measurements
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
      responses:
        '200':
          description: Noise floor statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      min:
                        type: number
                      max:
                        type: number
                      avg:
                        type: number
                      current:
                        type: number

  /noise_floor_chart_data:
    get:
      tags: [Noise Floor]
      summary: Get noise floor chart data
      description: Formatted noise floor data for charting
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
      responses:
        '200':
          description: Chart-ready noise floor data
          content:
            application/json:
              schema:
                type: object

  # ============================================================================
  # CAD Calibration Endpoints
  # ============================================================================
  /cad_calibration_start:
    post:
      tags: [CAD Calibration]
      summary: Start CAD calibration
      description: Begin Channel Activity Detection calibration process
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                samples:
                  type: integer
                  default: 8
                  minimum: 1
                  maximum: 64
                  description: Number of samples to collect
                  example: 8
                delay:
                  type: integer
                  default: 100
                  minimum: 10
                  maximum: 1000
                  description: Delay between samples in milliseconds
                  example: 100
            examples:
              default:
                value:
                  samples: 8
                  delay: 100
              detailed:
                value:
                  samples: 16
                  delay: 50
      responses:
        '200':
          description: Calibration started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /cad_calibration_stop:
    post:
      tags: [CAD Calibration]
      summary: Stop CAD calibration
      responses:
        '200':
          description: Calibration stopped
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /save_cad_settings:
    post:
      tags: [CAD Calibration]
      summary: Save CAD settings
      description: Save calibrated CAD peak and min values to configuration
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [peak, min_val]
              properties:
                peak:
                  type: integer
                  minimum: 0
                  maximum: 255
                  description: CAD peak value
                  example: 127
                min_val:
                  type: integer
                  minimum: 0
                  maximum: 255
                  description: CAD minimum value
                  example: 64
      responses:
        '200':
          description: Settings saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /cad_calibration_stream:
    get:
      tags: [CAD Calibration]
      summary: CAD calibration SSE stream
      description: Server-Sent Events stream of calibration progress
      responses:
        '200':
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string

  # ============================================================================
  # Adverts & Contacts
  # ============================================================================
  /adverts_by_contact_type:
    get:
      tags: [Adverts]
      summary: Get adverts by contact type
      description: Retrieve advertisements filtered by contact type
      parameters:
        - name: contact_type
          in: query
          schema:
            type: integer
          description: Contact type to filter by
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
          description: Maximum number of results
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
          description: Time range in hours
      responses:
        '200':
          description: Filtered adverts
          content:
            application/json:
              schema:
                type: object

  /advert:
    get:
      tags: [Adverts]
      summary: Get specific advert
      description: Retrieve details of a specific advertisement
      parameters:
        - name: advert_id
          in: query
          required: true
          schema:
            type: integer
          description: Advert ID to retrieve
      responses:
        '200':
          description: Advert details
          content:
            application/json:
              schema:
                type: object

  # ============================================================================
  # Transport Keys
  # ============================================================================
  /transport_keys:
    get:
      tags: [Transport Keys]
      summary: List transport keys
      description: Get list of all transport encryption keys
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: List of transport keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      type: object
    post:
      tags: [Transport Keys]
      summary: Create transport key
      description: Generate a new transport encryption key
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Key name/identifier
      responses:
        '200':
          description: Key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /transport_key:
    get:
      tags: [Transport Keys]
      summary: Get transport key
      description: Retrieve specific transport key details
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: key_id
          in: query
          required: true
          schema:
            type: string
          description: Key ID to retrieve
      responses:
        '200':
          description: Key details
          content:
            application/json:
              schema:
                type: object
    delete:
      tags: [Transport Keys]
      summary: Delete transport key
      description: Remove a transport encryption key
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: key_id
          in: query
          required: true
          schema:
            type: string
          description: Key ID to delete
      responses:
        '200':
          description: Key deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  # ============================================================================
  # Network Policy
  # ============================================================================
  /global_flood_policy:
    get:
      tags: [Network Policy]
      summary: Get global flood policy
      description: Retrieve current network flood policy configuration
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: Current policy
          content:
            application/json:
              schema:
                type: object
    post:
      tags: [Network Policy]
      summary: Update global flood policy
      description: Modify network flood policy settings
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Policy updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /ping_neighbor:
    post:
      tags: [Network Policy]
      summary: Ping neighbor node
      description: Send ping to a specific neighbor node
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                node_id:
                  type: string
                  description: Target node identifier
      responses:
        '200':
          description: Ping sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  # ============================================================================
  # Identity Management
  # ============================================================================
  /create_identity:
    post:
      tags: [Identities]
      summary: Create new identity
      description: |
        Create a new repeater or room server identity.
        `name` must be non-empty after trimming leading/trailing whitespace (whitespace-only values are rejected).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 64
                  pattern: '^[a-zA-Z0-9_\-\s]+$'
                  description: |
                    Identity registration name (alphanumeric, spaces, hyphens, underscores).
                    Trimmed; must not be empty or whitespace-only.
                  example: "General Chat"
                identity_key:
                  type: string
                  pattern: '^[0-9a-fA-F]{64}$'
                  description: 32-byte hex key (64 chars). Omit for auto-generation
                  example: "abc123def456..."
                type:
                  type: string
                  enum: [repeater, room_server]
                  description: |
                    - repeater: Repeater identity (only one allowed)
                    - room_server: Room server for group chat
                  example: room_server
                settings:
                  type: object
                  description: Type-specific settings
                  properties:
                    admin_password:
                      type: string
                      minLength: 4
                      description: Admin authentication password (room_server only)
                      example: "admin123"
                    guest_password:
                      type: string
                      minLength: 4
                      description: Guest authentication password (room_server only)
                      example: "guest123"
                    max_posts:
                      type: integer
                      minimum: 1
                      maximum: 32
                      default: 32
                      description: Maximum messages to keep (hard limit 32)
                      example: 32
            examples:
              room_server:
                value:
                  name: "General Chat"
                  type: room_server
                  settings:
                    admin_password: "admin123"
                    guest_password: "guest123"
                    max_posts: 32
              repeater:
                value:
                  name: "My Repeater"
                  type: repeater
                  identity_key: "abc123def456..."
      responses:
        '200':
          description: Identity created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      name:
                        type: string
                      type:
                        type: string
                      hash:
                        type: string
                      public_key:
                        type: string
              examples:
                success:
                  value:
                    success: true
                    data:
                      name: "General Chat"
                      type: room_server
                      hash: "0x42"
                      public_key: "abc123..."
        '400':
          description: Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /identities:
    get:
      tags: [Identities]
      summary: List all identities
      description: |
        Returns configured room servers and companions plus runtime registration info.
        Companion entries with missing or blank registration names are assigned a stable
        `companion_<pubkeyPrefix>` name derived from the identity key and persisted when possible.
      responses:
        '200':
          description: List of identities
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      identities:
                        type: array
                        items:
                          $ref: '#/components/schemas/Identity'

  /identity:
    get:
      tags: [Identities]
      summary: Get specific identity
      description: Retrieve details of a specific identity by name
      parameters:
        - name: name
          in: query
          required: true
          schema:
            type: string
          description: Identity name to retrieve
      responses:
        '200':
          description: Identity details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/Identity'

  /update_identity:
    put:
      tags: [Identities]
      summary: Update identity
      description: |
        Modify an existing identity's configuration.
        For `room_server`, `name` is required to locate the identity.
        For `companion`, provide `name` OR `lookup_identity_key` OR `public_key_prefix`
        (at least 8 hex characters for prefix lookups) when the registration name is unknown.
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  enum: [room_server, companion]
                  default: room_server
                name:
                  type: string
                  description: Current identity registration name (required for room_server; optional for companion if lookup fields are set)
                lookup_identity_key:
                  type: string
                  description: |
                    Companion only: hex private identity key (full or unique prefix, min 8 hex chars)
                    to locate the companion when `name` is omitted.
                public_key_prefix:
                  type: string
                  description: |
                    Companion only: ed25519 public key hex prefix (min 8 hex chars) to locate the companion when `name` is omitted.
                new_name:
                  type: string
                  description: New identity name (optional; must be non-empty if provided)
                identity_key:
                  type: string
                  description: New identity key (optional)
                settings:
                  type: object
                  description: Updated settings
      responses:
        '200':
          description: Identity updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /delete_identity:
    delete:
      tags: [Identities]
      summary: Delete identity
      description: |
        Remove an identity from the system.
        For `room_server`, `name` is required.
        For `companion`, provide `name` OR `lookup_identity_key` OR `public_key_prefix`
        (at least 8 hex characters for prefix lookups).
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - name: name
          in: query
          required: false
          schema:
            type: string
          description: Identity registration name to delete (required for room_server)
        - name: type
          in: query
          required: false
          schema:
            type: string
            enum: [room_server, companion]
          description: Identity kind (default room_server)
        - name: lookup_identity_key
          in: query
          required: false
          schema:
            type: string
          description: |
            Companion only: hex identity key (full or unique prefix, min 8 hex chars) when `name` is omitted.
        - name: public_key_prefix
          in: query
          required: false
          schema:
            type: string
          description: |
            Companion only: public key hex prefix (min 8 hex chars) when `name` is omitted.
      responses:
        '200':
          description: Identity deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /send_room_server_advert:
    post:
      tags: [Identities]
      summary: Send room server advertisement
      description: Broadcast a room server advertisement packet
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Room server identity name
                node_name:
                  type: string
                  description: Node name for the advertisement
                latitude:
                  type: number
                  format: float
                  description: GPS latitude
                  example: 0.0
                longitude:
                  type: number
                  format: float
                  description: GPS longitude
                  example: 0.0
                disable_fwd:
                  type: boolean
                  description: Disable forwarding flag
                  default: false
      responses:
        '200':
          description: Advertisement sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  # ============================================================================
  # ACL (Access Control List)
  # ============================================================================
  /acl_info:
    get:
      tags: [ACL]
      summary: Get ACL information for all identities
      description: |
        Get ACL configuration and statistics for all registered identities.

        Returns information including:
        - Identity name, type, and hash
        - Max clients allowed
        - Number of authenticated clients
        - Password configuration status
        - Read-only access setting
      responses:
        '200':
          description: ACL information for all identities
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      acls:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                              example: "repeater"
                            type:
                              type: string
                              enum: [repeater, room_server]
                              example: "repeater"
                            hash:
                              type: string
                              pattern: '^0x[0-9a-fA-F]{2}$'
                              example: "0x42"
                            max_clients:
                              type: integer
                              example: 100
                            authenticated_clients:
                              type: integer
                              example: 5
                            has_admin_password:
                              type: boolean
                              example: true
                            has_guest_password:
                              type: boolean
                              example: true
                            allow_read_only:
                              type: boolean
                              example: true
                      total_identities:
                        type: integer
                        example: 3
                      total_authenticated_clients:
                        type: integer
                        example: 15

  /acl_clients:
    get:
      tags: [ACL]
      summary: List ACL clients
      description: Get list of authenticated clients in access control list for an identity
      parameters:
        - name: identity_hash
          in: query
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{2}$'
          description: Identity hash
          example: "0x42"
        - name: identity_name
          in: query
          schema:
            type: string
          description: Identity name
          example: "General"
      responses:
        '200':
          description: ACL clients list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      clients:
                        type: array
                        items:
                          $ref: '#/components/schemas/ACLClient'
                      count:
                        type: integer
                        description: Number of clients returned
                      filter:
                        type: string
                        nullable: true
                        description: Filter applied (if any)
              examples:
                success:
                  value:
                    success: true
                    data:
                      clients:
                        - public_key: "03ccf3bb0bed9a51...21416fff"
                          public_key_full: "03ccf3bb0bed9a5109868a1e33ed020519aab6dbb30e42df3b11a21d21416fff"
                          address: "e1"
                          permissions: "admin"
                          last_activity: 1766065148
                          last_login_success: 1766065146
                          last_timestamp: 1766065146
                          identity_name: "rrrrr"
                          identity_type: "room_server"
                          identity_hash: "0xC5"
                      count: 1
                      filter: null

  /acl_remove_client:
    post:
      tags: [ACL]
      summary: Remove client from ACL
      description: Remove a client from the access control list
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [identity_hash, client_pubkey]
              properties:
                identity_hash:
                  type: string
                  pattern: '^0x[0-9a-fA-F]{2}$'
                  description: Identity hash
                  example: "0x42"
                identity_name:
                  type: string
                  description: Identity name (alternative to hash)
                  example: "General"
                client_pubkey:
                  type: string
                  pattern: '^[0-9a-fA-F]{64}$'
                  description: Client public key to remove
                  example: "abc123def456..."
      responses:
        '200':
          description: Client removed from ACL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /acl_stats:
    get:
      tags: [ACL]
      summary: Get ACL statistics
      description: Get statistics about access control lists
      responses:
        '200':
          description: ACL statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      total_entries:
                        type: integer
                      by_identity:
                        type: object

  /room_messages:
    get:
      tags: [Room Server]
      summary: Get room messages
      description: |
        Retrieve messages from a room with pagination.

        **Max Messages Per Room**: 32 (hard limit)
        - Older messages auto-deleted every 10 minutes
        - Cannot be increased beyond 32
      parameters:
        - name: room_name
          in: query
          schema:
            type: string
          description: Name of the room (use this OR room_hash)
          example: General
        - name: room_hash
          in: query
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{2}$'
          description: Hash of room identity (use this OR room_name)
          example: "0x42"
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
          description: Max messages to return
          example: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
            minimum: 0
          description: Skip first N messages (for pagination)
          example: 0
        - name: since_timestamp
          in: query
          schema:
            type: number
            format: float
          description: Only return messages after this Unix timestamp
          example: 1734567890.5
      responses:
        '200':
          description: Messages retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      room_name:
                        type: string
                      room_hash:
                        type: string
                      messages:
                        type: array
                        items:
                          $ref: '#/components/schemas/RoomMessage'
                      count:
                        type: integer
                        description: Number of messages in this response
                      total:
                        type: integer
                        description: Total messages in room (max 32)
                      limit:
                        type: integer
                      offset:
                        type: integer
              examples:
                success:
                  value:
                    success: true
                    data:
                      room_name: "General"
                      room_hash: "0x42"
                      messages:
                        - id: 123
                          author_pubkey: "abc123def456..."
                          author_prefix: "abc123de"
                          post_timestamp: 1734567890.5
                          sender_timestamp: 1734567890
                          message_text: "Hello world"
                          txt_type: 0
                          created_at: 1734567890.5
                      count: 1
                      total: 15
                      limit: 50
                      offset: 0
        '404':
          description: Room not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /room_post_message:
    post:
      tags: [Room Server]
      summary: Post message to room
      description: |
        Add a new message to a room server. Message will be distributed to all synced clients.

        **Special author values:**
        - `"server"` or `"system"` - System message, goes to ALL clients (API only)
        - Any hex string - Normal message, NOT sent to that client

        **Security:**
        - Radio messages cannot use server key (blocked)
        - API messages can use server key (for announcements)

        **Rate Limits:**
        - 10 messages/minute per author_pubkey
        - 160 bytes max message length
        - Global 1.1s gap between transmissions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message, author_pubkey]
              properties:
                room_name:
                  type: string
                  description: Name of the room (use this OR room_hash)
                  example: General
                room_hash:
                  type: string
                  pattern: '^0x[0-9a-fA-F]{2}$'
                  description: Hash of room identity (use this OR room_name)
                  example: "0x42"
                message:
                  type: string
                  minLength: 1
                  maxLength: 160
                  description: Message text (auto-truncated at 160 bytes)
                  example: "Hello from API"
                author_pubkey:
                  type: string
                  description: |
                    Author's public key as hex string (64 chars), or special values:
                    - "server" = system message (all clients receive)
                    - "system" = alias for "server"
                    - hex string = normal message (author won't receive)
                  example: "abc123def456..."
                txt_type:
                  type: integer
                  default: 0
                  enum: [0, 2]
                  description: |
                    Message type:
                    - 0: Plain text
                    - 2: Signed plain text
                  example: 0
            examples:
              normal_message:
                summary: Normal message
                value:
                  room_name: "General"
                  message: "Hello everyone!"
                  author_pubkey: "abc123def456..."
                  txt_type: 0
              server_message:
                summary: Server announcement
                value:
                  room_name: "General"
                  message: "[SERVER] Maintenance in 10 minutes"
                  author_pubkey: "server"
              by_hash:
                summary: Post by room hash
                value:
                  room_hash: "0x42"
                  message: "Using hash instead of name"
                  author_pubkey: "abc123..."
      responses:
        '200':
          description: Message posted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      message_id:
                        type: integer
                        description: Database ID of created message
                      room_name:
                        type: string
                      room_hash:
                        type: string
                      queued_for_distribution:
                        type: boolean
                        description: Always true - distribution is async
                      is_server_message:
                        type: boolean
                        description: True if author_pubkey was "server"
                      author_filter_note:
                        type: string
                        description: Explains message distribution behavior
              examples:
                normal:
                  value:
                    success: true
                    data:
                      message_id: 124
                      room_name: "General"
                      room_hash: "0x42"
                      queued_for_distribution: true
                      is_server_message: false
                      author_filter_note: "Message will NOT be sent to author"
                server:
                  value:
                    success: true
                    data:
                      message_id: 125
                      room_name: "General"
                      room_hash: "0x42"
                      queued_for_distribution: true
                      is_server_message: true
                      author_filter_note: "Server messages go to ALL clients"
        '400':
          description: Invalid parameters or rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rate_limit:
                  value:
                    success: false
                    error: "Failed to add message (rate limit or validation error)"
                missing_field:
                  value:
                    success: false
                    error: "message is required"

  /room_stats:
    get:
      tags: [Room Server]
      summary: Get room statistics
      description: |
        Get detailed statistics for one or all room servers.

        **Room Limits:**
        - 32 messages maximum per room (hard limit)
        - Messages auto-expire every 10 minutes
        - Author filtering: messages not sent back to author
      parameters:
        - name: room_name
          in: query
          schema:
            type: string
          description: Get stats for specific room (omit for all rooms)
          example: General
        - name: room_hash
          in: query
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{2}$'
          description: Get stats by room hash (use this OR room_name)
          example: "0x42"
      responses:
        '200':
          description: Room statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      room_name:
                        type: string
                      room_hash:
                        type: string
                      total_messages:
                        type: integer
                        minimum: 0
                        maximum: 32
                        description: Current message count (max 32)
                        example: 15
                      total_clients:
                        type: integer
                        minimum: 0
                        description: Total clients that have synced
                        example: 8
                      active_clients:
                        type: integer
                        minimum: 0
                        description: Clients active within timeout window
                        example: 3
                      max_posts:
                        type: integer
                        minimum: 1
                        maximum: 32
                        description: Message limit setting (always 32)
                        example: 32
                      sync_running:
                        type: boolean
                        description: Distribution task is active
                        example: true
                      clients:
                        type: array
                        description: List of all clients with sync status
                        items:
                          $ref: '#/components/schemas/RoomClient'
              examples:
                single_room:
                  value:
                    success: true
                    data:
                      room_name: "General"
                      room_hash: "0x42"
                      total_messages: 15
                      total_clients: 8
                      active_clients: 3
                      max_posts: 32
                      sync_running: true
                      clients:
                        - pubkey: "abc123..."
                          pubkey_prefix: "abc123de"
                          sync_since: 1734567890.5
                          unsynced_count: 5
                          is_active: true
                all_rooms:
                  value:
                    success: true
                    data:
                      rooms:
                        - room_name: "General"
                          total_messages: 15
                          active_clients: 3
                        - room_name: "Tech"
                          total_messages: 8
                          active_clients: 1
        '404':
          description: Room not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /room_clients:
    get:
      tags: [Room Server]
      summary: Get room clients
      description: |
        List all clients synced to a room with their status.

        **Client Filtering:**
        - Clients only receive messages where author_pubkey ≠ client_pubkey
        - unsynced_count shows pending messages for each client
        - is_active indicates client synced within timeout window
      parameters:
        - name: room_name
          in: query
          schema:
            type: string
          description: Room name
          example: General
        - name: room_hash
          in: query
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{2}$'
          description: Room hash (use this OR room_name)
          example: "0x42"
      responses:
        '200':
          description: Client list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      room_name:
                        type: string
                      room_hash:
                        type: string
                      clients:
                        type: array
                        items:
                          $ref: '#/components/schemas/RoomClient'
              examples:
                success:
                  value:
                    success: true
                    data:
                      room_name: "General"
                      room_hash: "0x42"
                      clients:
                        - pubkey: "abc123def456..."
                          pubkey_prefix: "abc123de"
                          sync_since: 1734567890.5
                          unsynced_count: 5
                          pending_ack: false
                          push_failures: 0
                          last_activity: 1734567890.5
                          in_acl: true
                          is_active: true
        '404':
          description: Room not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /room_message:
    delete:
      tags: [Room Server]
      summary: Delete specific message
      parameters:
        - name: room_name
          in: query
          schema:
            type: string
        - name: room_hash
          in: query
          schema:
            type: string
        - name: message_id
          in: query
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Message deleted

  /room_messages_clear:
    delete:
      tags: [Room Server]
      summary: Clear all room messages
      description: ⚠️ Destructive operation - cannot be undone!
      parameters:
        - name: room_name
          in: query
          schema:
            type: string
        - name: room_hash
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Messages cleared
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      deleted_count:
                        type: integer

components:
  schemas:
    SuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          description: Response data (varies by endpoint)

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: Error message

    RoomMessage:
      type: object
      required: [id, author_pubkey, post_timestamp, message_text, txt_type]
      properties:
        id:
          type: integer
          description: Database message ID
          example: 123
        author_pubkey:
          type: string
          pattern: '^[0-9a-fA-F]{64}$|^00{64}$'
          description: |
            Author's public key (64 hex chars):
            - 32 zeros (64 '0's) = server/system message
            - Other hex = regular user message
          example: "abc123def456..."
        author_prefix:
          type: string
          pattern: '^[0-9a-fA-F]{8}$'
          description: First 8 chars of author_pubkey (for display)
          example: "abc123de"
        post_timestamp:
          type: number
          format: float
          description: Server timestamp when message was stored (Unix epoch)
          example: 1734567890.5
        sender_timestamp:
          type: integer
          description: Client-provided timestamp (may be inaccurate)
          example: 1734567890
        message_text:
          type: string
          maxLength: 160
          description: Message content (auto-truncated at 160 bytes)
          example: "Hello world"
        txt_type:
          type: integer
          enum: [0, 2]
          description: |
            Message type:
            - 0: Plain text
            - 2: Signed plain text
          example: 0
        created_at:
          type: number
          format: float
          description: Database insertion timestamp (Unix epoch)
          example: 1734567890.5

    RoomClient:
      type: object
      required: [pubkey, sync_since, unsynced_count, is_active]
      properties:
        pubkey:
          type: string
          pattern: '^[0-9a-fA-F]{64}$'
          description: Client's public key (64 hex chars)
          example: "abc123def456..."
        pubkey_prefix:
          type: string
          pattern: '^[0-9a-fA-F]{8}$'
          description: First 8 chars of pubkey (for display)
          example: "abc123de"
        sync_since:
          type: number
          format: float
          description: Last sync timestamp (Unix epoch)
          example: 1734567890.5
        unsynced_count:
          type: integer
          minimum: 0
          maximum: 32
          description: Number of new messages for this client (max 32, messages NOT sent to author)
          example: 5
        pending_ack:
          type: boolean
          description: Waiting for ACK from client
          example: false
        push_failures:
          type: integer
          minimum: 0
          description: Number of failed delivery attempts
          example: 0
        last_activity:
          type: number
          format: float
          description: Last activity timestamp (Unix epoch)
          example: 1734567890.5
        in_acl:
          type: boolean
          description: Client passes ACL check
          example: true
        is_active:
          type: boolean
          description: Client is currently active (synced within timeout period)
          example: true

    Identity:
      type: object
      required: [name, type, hash, public_key]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 64
          pattern: '^[a-zA-Z0-9_\-\s]+$'
          description: Identity name (alphanumeric, spaces, hyphens, underscores)
          example: "General Chat"
        type:
          type: string
          enum: [repeater, room_server]
          description: |
            - repeater: Repeater identity (only one allowed per system)
            - room_server: Room server for group chat
          example: room_server
        hash:
          type: string
          pattern: '^0x[0-9a-fA-F]{2}$'
          description: 1-byte identity hash (used in radio packets)
          example: "0x42"
        public_key:
          type: string
          pattern: '^[0-9a-fA-F]{64}$'
          description: Ed25519 public key (64 hex chars)
          example: "abc123def456..."
        settings:
          type: object
          description: Type-specific settings
          properties:
            admin_password:
              type: string
              minLength: 4
              description: Admin password (room_server only)
              example: "admin123"
            guest_password:
              type: string
              minLength: 4
              description: Guest password (room_server only)
              example: "guest123"
            max_posts:
              type: integer
              minimum: 1
              maximum: 32
              default: 32
              description: Maximum messages to keep (room_server only, hard limit 32)
              example: 32

    ACLClient:
      type: object
      required: [public_key, public_key_full, address, permissions]
      properties:
        public_key:
          type: string
          description: Truncated public key for display (first 24 and last 8 chars)
          example: "03ccf3bb0bed9a51...21416fff"
        public_key_full:
          type: string
          pattern: '^[0-9a-fA-F]{64}$'
          description: Full client public key (64 hex chars)
          example: "03ccf3bb0bed9a5109868a1e33ed020519aab6dbb30e42df3b11a21d21416fff"
        address:
          type: string
          description: Client address identifier
          example: "e1"
        permissions:
          type: string
          enum: [admin, guest, read_only]
          description: |
            Client permission level:
            - admin: Full access
            - guest: Limited access
            - read_only: Read-only access
          example: "admin"
        last_activity:
          type: integer
          description: Unix timestamp of last activity
          example: 1766065148
        last_login_success:
          type: integer
          description: Unix timestamp of last successful login
          example: 1766065146
        last_timestamp:
          type: integer
          description: Unix timestamp from last client message
          example: 1766065146
        identity_name:
          type: string
          description: Name of the identity this client is authenticated to
          example: "rrrrr"
        identity_type:
          type: string
          enum: [repeater, room_server]
          description: Type of identity
          example: "room_server"
        identity_hash:
          type: string
          pattern: '^0x[0-9a-fA-F]{2}$'
          description: Hash of the identity
          example: "0xC5"

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token authentication via Authorization header
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key authentication for machine-to-machine access

# Future security - currently open
# security:
#   - ApiKeyAuth: []
