openapi: 3.0.3
info:
  title: NodelistDB API
  description: |
    NodelistDB is a comprehensive API for accessing historical FidoNet nodelist data.
    
    This API provides access to millions of FidoNet node records spanning multiple years,
    with powerful search and filtering capabilities. All nodelist data is preserved with
    full historical context, including conflict resolution for duplicate entries.
    
    ## Features
    - **Node Search**: Find nodes by zone, net, node number, system name, location, or sysop
    - **Historical Data**: Access complete history for any node
    - **Statistics**: Network statistics and trends over time
    - **Flag Documentation**: Complete reference for FidoNet node flags
    - **Database Export**: Download the complete database for offline analysis
    
    ## Data Sources
    Data is sourced from official FidoNet nodelist distribution points and updated regularly.
    
    ## Rate Limiting
    This API currently has no rate limiting, but please be respectful with your requests.
    
  version: 1.0.0
  contact:
    name: NodelistDB Support
    url: https://github.com/nodelistdb/nodelistdb
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: /api
    description: NodelistDB API Server

paths:
  /api/health:
    get:
      summary: Health Check
      description: Returns the health status of the API server
      operationId: getHealth
      tags:
        - System
      responses:
        '200':
          description: Server is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  time:
                    type: string
                    format: date-time
                    example: "2025-07-29T14:30:00Z"

  /api/nodes:
    get:
      summary: Search Nodes
      description: |
        Search for FidoNet nodes using various filter criteria.
        
        Returns paginated results with detailed node information including
        flags, internet connectivity, and historical metadata.
      operationId: searchNodes
      tags:
        - Nodes
      parameters:
        - name: zone
          in: query
          description: FidoNet zone number (1-6)
          schema:
            type: integer
            minimum: 1
            maximum: 6
            example: 2
        - name: net
          in: query
          description: Network number within the zone
          schema:
            type: integer
            minimum: 0
            maximum: 65535
            example: 5001
        - name: node
          in: query
          description: Node number within the network
          schema:
            type: integer
            minimum: 0
            maximum: 65535
            example: 100
        - name: system_name
          in: query
          description: System name (partial match, case-insensitive)
          schema:
            type: string
            example: "Linux BBS"
        - name: location
          in: query
          description: Location (partial match, case-insensitive)
          schema:
            type: string
            example: "Moscow"
        - name: sysop_name
          in: query
          description: Sysop name (partial match, case-insensitive)
          schema:
            type: string
            example: "John Doe"
        - name: node_type
          in: query
          description: Node type (Zone, Region, Host, Hub, Pvt, Down, Hold)
          schema:
            type: string
            example: "Hub"
        - name: is_cm
          in: query
          description: Filter by CM (Continuous Mail) flag
          schema:
            type: boolean
            example: true
        - name: is_active
          in: query
          description: Filter by active status (true=in latest nodelist, false=historical only)
          schema:
            type: boolean
            example: true
        - name: date_from
          in: query
          description: Start date for historical data (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2024-01-01"
        - name: date_to
          in: query
          description: End date for historical data (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2024-12-31"
        - name: latest_only
          in: query
          description: Return only the most recent entry for each node
          schema:
            type: boolean
            default: false
            example: true
        - name: limit
          in: query
          description: Maximum number of results to return
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
            example: 50
        - name: offset
          in: query
          description: Number of results to skip (for pagination)
          schema:
            type: integer
            minimum: 0
            default: 0
            example: 0
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  nodes:
                    type: array
                    items:
                      $ref: '#/components/schemas/Node'
                  count:
                    type: integer
                    description: Number of nodes returned
                    example: 25
                  total:
                    type: integer
                    description: Total number of matching nodes
                    example: 1250
                  filter:
                    $ref: '#/components/schemas/NodeFilter'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/nodes/{zone}/{net}/{node}:
    get:
      summary: Get Specific Node
      description: Get detailed information for a specific node by its address
      operationId: getNode
      tags:
        - Nodes
      parameters:
        - name: zone
          in: path
          required: true
          description: FidoNet zone number
          schema:
            type: integer
            minimum: 1
            maximum: 6
        - name: net
          in: path
          required: true
          description: Network number
          schema:
            type: integer
            minimum: 0
            maximum: 65535
        - name: node
          in: path
          required: true
          description: Node number
          schema:
            type: integer
            minimum: 0
            maximum: 65535
        - name: latest_only
          in: query
          description: Return only the most recent entry
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: Node found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Node'
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/nodes/{zone}/{net}/{node}/history:
    get:
      summary: Get Node History
      description: Get complete historical timeline for a specific node
      operationId: getNodeHistory
      tags:
        - Nodes
      parameters:
        - name: zone
          in: path
          required: true
          description: FidoNet zone number
          schema:
            type: integer
            minimum: 1
            maximum: 6
        - name: net
          in: path
          required: true
          description: Network number
          schema:
            type: integer
            minimum: 0
            maximum: 65535
        - name: node
          in: path
          required: true
          description: Node number
          schema:
            type: integer
            minimum: 0
            maximum: 65535
      responses:
        '200':
          description: Node history retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  history:
                    type: array
                    items:
                      $ref: '#/components/schemas/Node'
                  count:
                    type: integer
                    example: 52
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/nodes/{zone}/{net}/{node}/changes:
    get:
      summary: Get Node Changes
      description: |
        Get a summary of all changes to a node over time.
      operationId: getNodeChanges
      tags:
        - Nodes
      parameters:
        - name: zone
          in: path
          required: true
          description: FidoNet zone number
          schema:
            type: integer
        - name: net
          in: path
          required: true
          description: Network number
          schema:
            type: integer
        - name: node
          in: path
          required: true
          description: Node number
          schema:
            type: integer
      responses:
        '200':
          description: Node changes retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  changes:
                    type: array
                    items:
                      $ref: '#/components/schemas/NodeChange'
        '404':
          $ref: '#/components/responses/NotFound'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/nodes/{zone}/{net}/{node}/timeline:
    get:
      summary: Get Node Timeline
      description: Get a visual timeline representation of node changes
      operationId: getNodeTimeline
      tags:
        - Nodes
      parameters:
        - name: zone
          in: path
          required: true
          description: FidoNet zone number
          schema:
            type: integer
        - name: net
          in: path
          required: true
          description: Network number
          schema:
            type: integer
        - name: node
          in: path
          required: true
          description: Node number
          schema:
            type: integer
      responses:
        '200':
          description: Node timeline retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  timeline:
                    type: array
                    items:
                      $ref: '#/components/schemas/TimelineEvent'


  /api/sysops:
    get:
      summary: List Sysops
      description: |
        Get a list of unique sysops with their node counts and activity statistics.

        Results can be filtered by name and paginated for large datasets.
      operationId: listSysops
      tags:
        - Sysops
      parameters:
        - name: name
          in: query
          description: Filter by sysop name (partial match, case-insensitive)
          schema:
            type: string
            example: "John"
        - name: limit
          in: query
          description: Maximum number of results
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: offset
          in: query
          description: Number of results to skip
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  sysops:
                    type: array
                    items:
                      $ref: '#/components/schemas/SysopInfo'
                  count:
                    type: integer
                    description: Number of sysops returned
                    example: 50
                  filter:
                    type: object
                    properties:
                      name:
                        type: string
                        nullable: true
                      limit:
                        type: integer
                      offset:
                        type: integer
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/sysops/{name}/nodes:
    get:
      summary: Get Nodes by Sysop
      description: |
        Get all nodes operated by a specific sysop.
        
        The sysop name must be URL-encoded if it contains special characters.
      operationId: getNodesBySysop
      tags:
        - Sysops
      parameters:
        - name: name
          in: path
          required: true
          description: Sysop name (exact match, URL-encoded)
          schema:
            type: string
            example: "John_Doe"
        - name: limit
          in: query
          description: Maximum number of results
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  sysop_name:
                    type: string
                    description: The sysop name
                    example: "John_Doe"
                  nodes:
                    type: array
                    items:
                      $ref: '#/components/schemas/Node'
                  count:
                    type: integer
                    description: Number of nodes returned
                    example: 25
                  limit:
                    type: integer
                    description: The limit applied
                    example: 100
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/stats:
    get:
      summary: Get Network Statistics
      description: Get comprehensive FidoNet network statistics
      operationId: getStats
      tags:
        - Statistics
      responses:
        '200':
          description: Network statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NetworkStats'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/stats/dates:
    get:
      summary: Get Available Dates
      description: Get list of all dates for which nodelist data is available
      operationId: getAvailableDates
      tags:
        - Statistics
      responses:
        '200':
          description: Available dates
          content:
            application/json:
              schema:
                type: object
                properties:
                  dates:
                    type: array
                    items:
                      type: string
                      format: date
                    example: ["2024-01-01", "2024-01-08", "2024-01-15"]
                  count:
                    type: integer
                    example: 52

  /api/flags:
    get:
      summary: Get Flags Documentation
      description: Get comprehensive documentation for all FidoNet node flags
      operationId: getFlagsDocumentation
      tags:
        - Reference
      responses:
        '200':
          description: Flags documentation
          content:
            application/json:
              schema:
                type: object
                properties:
                  flags:
                    type: object
                    additionalProperties:
                      $ref: '#/components/schemas/FlagDefinition'

  /api/software/binkp:
    get:
      summary: Get BinkP Software Distribution
      description: |
        Get statistical distribution of BinkP software implementations detected across the network.

        Returns aggregated data showing which BinkP software versions are in use.
      operationId: getBinkPSoftwareStats
      tags:
        - Software Analytics
      parameters:
        - name: days
          in: query
          description: Number of days to include in analysis
          schema:
            type: integer
            minimum: 1
            default: 365
            example: 365
      responses:
        '200':
          description: BinkP software distribution statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SoftwareDistribution'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/software/ifcico:
    get:
      summary: Get IFCico Software Distribution
      description: |
        Get statistical distribution of IFCico/Telnet software implementations detected across the network.

        Returns aggregated data showing which IFCico software versions are in use.
      operationId: getIFCICOSoftwareStats
      tags:
        - Software Analytics
      parameters:
        - name: days
          in: query
          description: Number of days to include in analysis
          schema:
            type: integer
            minimum: 1
            default: 365
            example: 365
      responses:
        '200':
          description: IFCico software distribution statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SoftwareDistribution'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/software/binkd:
    get:
      summary: Get Binkd Detailed Statistics
      description: |
        Get detailed statistics for Binkd software implementations, including version breakdowns and trends.
      operationId: getBinkdDetailedStats
      tags:
        - Software Analytics
      parameters:
        - name: days
          in: query
          description: Number of days to include in analysis
          schema:
            type: integer
            minimum: 1
            default: 365
            example: 365
      responses:
        '200':
          description: Detailed Binkd statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BinkdDetailedStats'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/software/trends:
    get:
      summary: Get Software Trends
      description: |
        Get software usage trends over time.

        **Note:** This endpoint is currently a stub and returns empty data. Implementation pending.
      operationId: getSoftwareTrends
      tags:
        - Software Analytics
      responses:
        '200':
          description: Software trends (currently empty)
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/analytics/geo-hosting:
    get:
      summary: Get Geographic Hosting Distribution
      description: |
        Get geographic distribution statistics of node hosting by country and region.

        Returns data showing where FidoNet nodes are physically hosted based on IP geolocation.
      operationId: getGeoHostingStats
      tags:
        - Geographic Analytics
      parameters:
        - name: days
          in: query
          description: Number of days to include in analysis
          schema:
            type: integer
            minimum: 1
            default: 365
            example: 365
      responses:
        '200':
          description: Geographic hosting distribution statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GeoDistribution'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/nodelist/latest:
    get:
      summary: Get Latest Nodelist
      description: Get information about the latest available nodelist
      operationId: getLatestNodelist
      tags:
        - Nodelists
      responses:
        '200':
          description: Latest nodelist information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NodelistInfo'

  /api/openapi.yaml:
    get:
      summary: OpenAPI Specification
      description: Get the OpenAPI specification for this API
      operationId: getOpenAPISpec
      tags:
        - Documentation
      responses:
        '200':
          description: OpenAPI specification
          content:
            application/x-yaml:
              schema:
                type: string

  /api/docs:
    get:
      summary: API Documentation
      description: Interactive Swagger UI documentation for this API
      operationId: getAPIDocs
      tags:
        - Documentation
      responses:
        '200':
          description: Swagger UI HTML page
          content:
            text/html:
              schema:
                type: string

components:
  schemas:
    Node:
      type: object
      description: Complete FidoNet node information
      properties:
        zone:
          type: integer
          description: FidoNet zone number (1-6)
          example: 2
        net:
          type: integer
          description: Network number within the zone
          example: 5001
        node:
          type: integer
          description: Node number within the network
          example: 100
        nodelist_date:
          type: string
          format: date-time
          description: Date this nodelist entry was published
          example: "2024-07-29T00:00:00Z"
        day_number:
          type: integer
          description: Day number of the year (1-366)
          example: 211
        system_name:
          type: string
          description: Name of the BBS/system
          example: "Linux Node"
        location:
          type: string
          description: Geographic location
          example: "Moscow, Russia"
        sysop_name:
          type: string
          description: System operator name
          example: "John Doe"
        phone:
          type: string
          description: Phone number or connection method
          example: "000-000-0000"
        node_type:
          type: string
          description: Type of node (Zone, Region, Host, Hub, Pvt, Down, Hold)
          example: "Pvt"
        region:
          type: integer
          nullable: true
          description: Region number (if applicable)
          example: 50
        max_speed:
          type: string
          description: Maximum connection speed
          example: "33600"
        is_cm:
          type: boolean
          description: Continuous Mail capability
          example: true
        is_mo:
          type: boolean
          description: Mail Only node
          example: false
        has_binkp:
          type: boolean
          description: Supports Binkp protocol
          example: true
        has_inet:
          type: boolean
          description: Has internet connectivity
          example: true
        has_telnet:
          type: boolean
          description: Supports Telnet connections
          example: true
        is_down:
          type: boolean
          description: Node is currently down
          example: false
        is_hold:
          type: boolean
          description: Node is on hold
          example: false
        is_pvt:
          type: boolean
          description: Private node
          example: true
        is_active:
          type: boolean
          description: Node is currently active
          example: true
        flags:
          type: array
          items:
            type: string
          description: Raw nodelist flags
          example: ["CM", "IBN", "INA:fidonet.example.com"]
        modem_flags:
          type: array
          items:
            type: string
          description: Modem-related flags
          example: ["V32B", "V42B"]
        # Internet array fields have been removed - data is now in internet_config JSON field
        internet_config:
          type: object
          description: Parsed internet configuration
          additionalProperties: true
        conflict_sequence:
          type: integer
          description: Sequence number for duplicate entries (0 = original)
          example: 0
        has_conflict:
          type: boolean
          description: Whether this node has duplicate entries
          example: false

    NodeSummary:
      type: object
      description: Simplified node information for search results
      properties:
        zone:
          type: integer
          example: 2
        net:
          type: integer
          example: 5001
        node:
          type: integer
          example: 100
        system_name:
          type: string
          example: "Linux Node"
        location:
          type: string
          example: "Moscow, Russia"
        sysop_name:
          type: string
          example: "John Doe"
        first_date:
          type: string
          format: date-time
          example: "2024-01-01T00:00:00Z"
        last_date:
          type: string
          format: date-time
          example: "2024-07-29T00:00:00Z"
        currently_active:
          type: boolean
          example: true

    SysopInfo:
      type: object
      description: Information about a sysop
      properties:
        name:
          type: string
          description: Sysop name
          example: "John_Doe"
        node_count:
          type: integer
          description: Total number of unique nodes operated
          example: 15
        active_nodes:
          type: integer
          description: Number of currently active nodes
          example: 12
        first_seen:
          type: string
          format: date-time
          description: First appearance in nodelist
          example: "2020-01-15T00:00:00Z"
        last_seen:
          type: string
          format: date-time
          description: Most recent appearance in nodelist
          example: "2024-07-29T00:00:00Z"
        zones:
          type: array
          items:
            type: integer
          description: List of zones where sysop has nodes
          example: [1, 2, 3]

    NodeFilter:
      type: object
      description: Applied search filters
      properties:
        zone:
          type: integer
          nullable: true
        net:
          type: integer
          nullable: true
        node:
          type: integer
          nullable: true
        system_name:
          type: string
          nullable: true
        location:
          type: string
          nullable: true
        sysop_name:
          type: string
          nullable: true
        has_binkp:
          type: boolean
          nullable: true
        has_telnet:
          type: boolean
          nullable: true
        has_inet:
          type: boolean
          nullable: true
        is_cm:
          type: boolean
          nullable: true
        is_active:
          type: boolean
          nullable: true
        date_from:
          type: string
          format: date
          nullable: true
        date_to:
          type: string
          format: date
          nullable: true
        latest_only:
          type: boolean
          nullable: true
        limit:
          type: integer
        offset:
          type: integer

    NodeChange:
      type: object
      description: A change to a node over time
      properties:
        date:
          type: string
          format: date-time
        field:
          type: string
          description: Field that changed
          example: "location"
        old_value:
          type: string
          nullable: true
          example: "Old Location"
        new_value:
          type: string
          nullable: true
          example: "New Location"
        change_type:
          type: string
          enum: [added, modified, removed]
          example: "modified"

    TimelineEvent:
      type: object
      description: A timeline event for a node
      properties:
        date:
          type: string
          format: date-time
        event_type:
          type: string
          enum: [first_seen, last_seen, major_change, flag_change]
        description:
          type: string
          example: "Node first appeared in nodelist"
        details:
          type: object
          additionalProperties: true

    NetworkStats:
      type: object
      description: FidoNet network statistics
      properties:
        total_nodes:
          type: integer
          description: Total number of nodes in database
          example: 30933920
        active_nodes:
          type: integer
          description: Currently active nodes
          example: 2547
        zones:
          type: array
          items:
            $ref: '#/components/schemas/ZoneStats'
        date_range:
          type: object
          properties:
            first_date:
              type: string
              format: date
            last_date:
              type: string
              format: date
        update_frequency:
          type: string
          example: "Weekly"

    ZoneStats:
      type: object
      description: Statistics for a specific zone
      properties:
        zone:
          type: integer
          example: 2
        name:
          type: string
          example: "Europe, former Soviet Union, and Africa"
        total_nodes:
          type: integer
          example: 1250
        active_nodes:
          type: integer
          example: 945
        regions:
          type: array
          items:
            $ref: '#/components/schemas/RegionStats'

    RegionStats:
      type: object
      description: Statistics for a region within a zone
      properties:
        region:
          type: integer
          example: 50
        name:
          type: string
          example: "Moscow"
        node_count:
          type: integer
          example: 125

    FlagDefinition:
      type: object
      description: Definition of a FidoNet flag
      properties:
        name:
          type: string
          example: "CM"
        description:
          type: string
          example: "Continuous Mail - accepts mail 24 hours a day"
        category:
          type: string
          example: "Operation"
        deprecated:
          type: boolean
          example: false
        syntax:
          type: string
          nullable: true
          example: "CM"

    NodelistInfo:
      type: object
      description: Information about a nodelist file
      properties:
        name:
          type: string
          example: "nodelist.211"
        date:
          type: string
          format: date-time
          example: "2024-07-29T00:00:00Z"
        day_number:
          type: integer
          example: 211
        year:
          type: string
          example: "2024"
        download_url:
          type: string
          example: "/download/nodelist/2024/nodelist.211"

    SoftwareDistribution:
      type: object
      description: Software distribution statistics
      properties:
        protocol:
          type: string
          description: Protocol type (binkp, ifcico, telnet)
          example: "binkp"
        total_nodes:
          type: integer
          description: Total number of nodes tested
          example: 1250
        software_counts:
          type: array
          items:
            type: object
            properties:
              software:
                type: string
                description: Software name and version
                example: "binkd/1.1a-111"
              count:
                type: integer
                description: Number of nodes using this software
                example: 450
              percentage:
                type: number
                format: float
                description: Percentage of total
                example: 36.0
        period_days:
          type: integer
          description: Number of days included in the analysis
          example: 365

    BinkdDetailedStats:
      type: object
      description: Detailed Binkd software statistics
      properties:
        total_binkd_nodes:
          type: integer
          description: Total number of nodes running Binkd
          example: 850
        versions:
          type: array
          items:
            type: object
            properties:
              version:
                type: string
                description: Binkd version
                example: "1.1a-111"
              count:
                type: integer
                description: Number of nodes
                example: 450
              percentage:
                type: number
                format: float
                example: 52.9
        period_days:
          type: integer
          description: Number of days included in the analysis
          example: 365

    GeoDistribution:
      type: object
      description: Geographic distribution statistics
      properties:
        total_nodes:
          type: integer
          description: Total number of nodes with geolocation data
          example: 2450
        countries:
          type: array
          items:
            type: object
            properties:
              country_code:
                type: string
                description: ISO 3166-1 alpha-2 country code
                example: "US"
              country_name:
                type: string
                description: Country name
                example: "United States"
              node_count:
                type: integer
                description: Number of nodes hosted in this country
                example: 450
              percentage:
                type: number
                format: float
                description: Percentage of total
                example: 18.4
        period_days:
          type: integer
          description: Number of days included in the analysis
          example: 365

    Error:
      type: object
      description: Standard error response
      properties:
        error:
          type: string
          description: Error message
          example: "Invalid zone number"
        code:
          type: string
          description: Error code
          example: "INVALID_PARAMETER"
        details:
          type: object
          additionalProperties: true
          description: Additional error details

  responses:
    BadRequest:
      description: Bad request - invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: "Invalid zone number"
            code: "INVALID_PARAMETER"

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: "Node not found"
            code: "NOT_FOUND"

    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: "Database connection failed"
            code: "INTERNAL_ERROR"

  parameters:
    Zone:
      name: zone
      in: path
      required: true
      description: FidoNet zone number (1-6)
      schema:
        type: integer
        minimum: 1
        maximum: 6

    Net:
      name: net
      in: path
      required: true
      description: Network number (0-65535)
      schema:
        type: integer
        minimum: 0
        maximum: 65535

    Node:
      name: node
      in: path
      required: true
      description: Node number (0-65535)
      schema:
        type: integer
        minimum: 0
        maximum: 65535

    Limit:
      name: limit
      in: query
      description: Maximum number of results to return
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 100

    Offset:
      name: offset
      in: query
      description: Number of results to skip (for pagination)
      schema:
        type: integer
        minimum: 0
        default: 0

tags:
  - name: System
    description: System health and status endpoints
  - name: Nodes
    description: FidoNet node search and information
  - name: Sysops
    description: System operator (sysop) information and queries
  - name: Statistics
    description: Network statistics and analytics
  - name: Software Analytics
    description: Software distribution and usage statistics from node testing
  - name: Geographic Analytics
    description: Geographic distribution and hosting statistics
  - name: Reference
    description: Reference documentation and definitions
  - name: Nodelists
    description: Nodelist file information and metadata
  - name: Documentation
    description: API documentation and specifications

externalDocs:
  description: NodelistDB Documentation
  url: https://github.com/nodelistdb/nodelistdb/wiki