openapi: 3.1.0
info:
  title: CCA Device Info API
  version: 1.0.0
  summary: Source-linked operating-system, kernel, browser, and hardware intelligence
  description: |
    Fleet-standard API for OS and kernel release chronology, browser history,
    observed lifecycle state, Apple hardware identifiers, compatibility
    evidence, and best-effort HTTP request discovery. Canonical release and
    hardware URLs use stable UUIDs, while append-only observations retain each
    distinct source claim.

    Discovery signals are unverified and spoofable. They must not be used as
    authentication, authorization, enrollment, or device-attestation evidence.
servers:
  - url: https://deviceinfo.datasourceapi.com/api/v1
  - url: http://localhost:8787/api/v1
tags:
  - name: Catalog
  - name: Discovery
  - name: Fingerprint
  - name: Operations
  - name: Admin
paths:
  /:
    get:
      tags: [Catalog]
      summary: Describe the service and endpoint map
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /platforms:
    get:
      tags: [Catalog]
      summary: List operating-system platforms
      responses:
        "200":
          description: Active platforms and catalog coverage
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
  /software/products:
    get:
      tags: [Catalog]
      summary: List operating-system and browser products
      responses:
        "200":
          description: Software products with release coverage
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/SoftwareProduct" }
  /software/releases:
    get:
      tags: [Catalog]
      summary: List generalized software release history
      parameters:
        - name: product
          in: query
          schema: { type: string, examples: [firefox] }
        - name: category
          in: query
          schema:
            type: string
            enum: [operating_system, browser]
        - name: vendor
          in: query
          schema: { type: string }
        - name: channel
          in: query
          schema: { type: string, examples: [stable] }
        - name: platform
          in: query
          schema: { type: string, examples: [windows] }
        - name: architecture
          in: query
          schema: { type: string, examples: [x86_64] }
        - name: major
          in: query
          schema: { type: string }
        - name: edition
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema:
            type: string
            enum: [supported, limited, end_of_support, unknown]
        - name: q
          in: query
          schema: { type: string }
        - name: released_after
          in: query
          schema: { type: string, format: date }
        - name: released_before
          in: query
          schema: { type: string, format: date }
        - name: current
          in: query
          schema: { type: boolean }
        - $ref: "#/components/parameters/SoftwareLimit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: Matching software releases
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/SoftwareRelease" }
  /software/releases/{product}/{version}:
    get:
      tags: [Catalog]
      summary: Get all channel, platform, architecture, and build variants for a version
      parameters:
        - name: product
          in: path
          required: true
          schema: { type: string, examples: [chrome] }
        - name: version
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Release variants
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/SoftwareRelease" }
        "404":
          $ref: "#/components/responses/NotFound"
  /releases/{uuid}:
    get:
      tags: [Catalog]
      summary: Get one canonical release by stable UUID
      description: Includes source claims, append-only observation history, and adjacent releases.
      parameters:
        - name: uuid
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Canonical release detail
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          release: { $ref: "#/components/schemas/SoftwareRelease" }
                          sources:
                            type: array
                            items: { type: object }
                          observations:
                            type: array
                            items: { $ref: "#/components/schemas/ReleaseObservation" }
                          adjacent: { type: object }
        "404":
          $ref: "#/components/responses/NotFound"
  /browser/releases:
    get:
      tags: [Catalog]
      summary: List browser release history
      description: Browser-only alias of `/software/releases`.
      parameters:
        - name: product
          in: query
          schema: { type: string, examples: [opera-gx] }
        - name: channel
          in: query
          schema: { type: string }
        - name: platform
          in: query
          schema: { type: string }
        - name: q
          in: query
          schema: { type: string }
        - $ref: "#/components/parameters/SoftwareLimit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: Matching browser releases
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/SoftwareRelease" }
  /os/releases:
    get:
      tags: [Catalog]
      summary: List operating-system releases
      parameters:
        - $ref: "#/components/parameters/Platform"
        - name: cycle
          in: query
          schema: { type: string }
        - name: channel
          in: query
          schema:
            type: string
            enum: [stable, beta, preview, lts]
        - name: status
          in: query
          schema:
            type: string
            enum: [supported, limited, end_of_support, unknown]
        - name: current
          in: query
          description: Return only the current patch observed for each major cycle.
          schema: { type: boolean }
        - name: q
          in: query
          schema: { type: string }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: Matching release records
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/Release" }
  /os/releases/{platform}/{version}:
    get:
      tags: [Catalog]
      summary: Get one release and its build variants
      parameters:
        - $ref: "#/components/parameters/PlatformPath"
        - name: version
          in: path
          required: true
          schema: { type: string, examples: ["26.6.2"] }
      responses:
        "200":
          description: Release detail
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data: { $ref: "#/components/schemas/Release" }
        "404":
          $ref: "#/components/responses/NotFound"
  /hardware/models:
    get:
      tags: [Catalog]
      summary: List hardware models and identifier aliases
      parameters:
        - name: vendor
          in: query
          schema: { type: string, examples: [Apple] }
        - name: family
          in: query
          schema: { type: string, examples: [MacBook] }
        - name: architecture
          in: query
          schema:
            type: string
            enum: [arm64, x86_64, universal, unknown]
        - name: status
          in: query
          schema:
            type: string
            enum: [current, supported, vintage, obsolete, unknown]
        - name: q
          in: query
          description: Marketing-name fragment or exact identifier.
          schema: { type: string }
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: Hardware models
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/HardwareModel" }
  /hardware/models/{identifier}:
    get:
      tags: [Catalog]
      summary: Resolve a model by UUID, catalog ID, or hardware identifier
      parameters:
        - name: identifier
          in: path
          required: true
          schema: { type: string, examples: ["Macmini9,1"] }
      responses:
        "200":
          description: Hardware model
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data: { $ref: "#/components/schemas/HardwareModel" }
        "404":
          $ref: "#/components/responses/NotFound"
  /compatibility:
    get:
      tags: [Catalog]
      summary: Check an OS release against a hardware identifier
      parameters:
        - name: model
          in: query
          required: true
          schema: { type: string, examples: ["MacBook9,1"] }
        - $ref: "#/components/parameters/Platform"
        - name: version
          in: query
          required: true
          schema: { type: string, examples: ["12.7.6"] }
      responses:
        "200":
          description: Compatibility result. `compatible` is null when evidence is insufficient.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/ValidationError"
  /discover:
    get:
      tags: [Discovery]
      summary: Discover from this HTTP request
      description: |
        Reads an allowlist of User-Agent and User-Agent Client Hint headers.
        Returns `Accept-CH` so supporting browsers can provide high-entropy
        signals on a subsequent same-origin request.
      responses:
        "200":
          description: Best-effort discovery and catalog assessment
          headers:
            Accept-CH:
              schema: { type: string }
            Cache-Control:
              schema: { type: string, const: no-store }
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data: { $ref: "#/components/schemas/Discovery" }
    post:
      tags: [Discovery]
      summary: Discover from submitted request or native-agent signals
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_agent: { type: string, maxLength: 512 }
                platform: { type: string, examples: [macOS] }
                platform_version: { type: string, examples: ["26.6.2"] }
                model: { type: string, examples: ["Macmini9,1"] }
                architecture: { type: string, examples: [arm64] }
                headers:
                  type: object
                  additionalProperties: { type: string, maxLength: 512 }
      responses:
        "200":
          description: Best-effort discovery and catalog assessment
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data: { $ref: "#/components/schemas/Discovery" }
        "400":
          $ref: "#/components/responses/ValidationError"
  /fingerprint/challenge:
    get:
      tags: [Fingerprint]
      summary: Issue a one-time first-party fingerprint challenge
      parameters:
        - name: purpose
          in: query
          schema:
            type: string
            enum: [fraud_prevention, account_security, user_diagnostic]
            default: user_diagnostic
        - name: tier
          in: query
          schema:
            type: string
            enum: [basic, enhanced]
            default: basic
      responses:
        "200":
          description: Five-minute one-time challenge and collection policy
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          challenge:
                            type: object
                            properties:
                              id: { type: string, format: uuid }
                              issued_at: { type: string, format: date-time }
                              expires_at: { type: string, format: date-time }
                              purpose: { type: string }
                              collection_tier: { type: string }
                          policy: { type: object }
  /fingerprint:
    post:
      tags: [Fingerprint]
      summary: Analyze a first-party browser observation
      description: |
        Consumes a one-time challenge, validates the submitted signal bundle,
        combines it with reduced Cloudflare request evidence, and returns
        origin/purpose/7-day-epoch-scoped HMACs. The observation is not persisted.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [challenge_id, signals]
              properties:
                challenge_id: { type: string, format: uuid }
                signals:
                  type: object
                  description: Versioned basic or enhanced browser signal bundle.
                privacy:
                  type: object
                  properties:
                    declaration:
                      type: string
                      enum: [enabled, not_declared]
                    profile:
                      type: [string, "null"]
      responses:
        "200":
          description: Scoped observation, availability summary, and HMAC digests
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data: { $ref: "#/components/schemas/FingerprintObservation" }
        "400":
          $ref: "#/components/responses/ValidationError"
  /sources:
    get:
      tags: [Operations]
      summary: List source provenance and freshness
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /status:
    get:
      tags: [Operations]
      summary: Get catalog and automation health
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /admin/refresh:
    post:
      tags: [Admin]
      summary: Schedule an immediate device-data agent run
      security:
        - AdminKey: []
      responses:
        "202":
          description: Refresh scheduled on the singleton DeviceDataAgent
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Envelope" }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "503":
          description: ADMIN_KEY is not configured
  /admin/software/releases:
    post:
      tags: [Admin]
      summary: Import a validated batch of software releases
      description: Idempotent operator and adapter write path. Limited to 200 rows and 2 MiB per request.
      security:
        - AdminKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: array
                  maxItems: 200
                  items: { $ref: "#/components/schemas/SoftwareReleaseInput" }
                - type: object
                  required: [releases]
                  properties:
                    releases:
                      type: array
                      maxItems: 200
                      items: { $ref: "#/components/schemas/SoftwareReleaseInput" }
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /admin/software/snapshot/{source}:
    post:
      tags: [Admin]
      summary: Archive a raw software-history source payload
      description: Streams a bounded source response into the immutable R2 archive before normalized import. Limited to 32 MiB.
      security:
        - AdminKey: []
      parameters:
        - name: source
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema: { type: string, format: binary }
          application/json:
            schema: { type: string, format: binary }
          text/html:
            schema: { type: string }
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /admin/software/pipelines:
    get:
      tags: [Admin]
      summary: List product-level software pipeline state
      security:
        - AdminKey: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /admin/software/pipelines/{product}:
    post:
      tags: [Admin]
      summary: Update a product pipeline cursor and health
      security:
        - AdminKey: []
      parameters:
        - name: product
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                adapter: { type: string }
                schedule_hours: { type: integer, minimum: 1, maximum: 8760 }
                status:
                  type: string
                  enum: [planned, active, degraded, blocked]
                completeness:
                  type: string
                  enum: [full, partial, current_only, unknown]
                history_started_at: { type: string, format: date }
                cursor: { type: string }
                success: { type: boolean }
                checked: { type: boolean }
                last_error: { type: string }
                notes: { type: string }
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /admin/agent:
    get:
      tags: [Admin]
      summary: Read persisted agent state
      security:
        - AdminKey: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /admin/proposals:
    get:
      tags: [Admin]
      summary: List AI and deterministic-source proposals
      security:
        - AdminKey: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, applied, rejected, invalid]
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /admin/proposals/{id}/approve:
    post:
      tags: [Admin]
      summary: Validate and apply a pending proposal
      security:
        - AdminKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /admin/proposals/{id}/reject:
    post:
      tags: [Admin]
      summary: Reject a pending proposal
      security:
        - AdminKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          $ref: "#/components/responses/Success"
components:
  securitySchemes:
    AdminKey:
      type: apiKey
      in: header
      name: X-Admin-Key
  parameters:
    Platform:
      name: platform
      in: query
      schema: { type: string, examples: [macos] }
    PlatformPath:
      name: platform
      in: path
      required: true
      schema: { type: string, examples: [macos] }
    Limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
    SoftwareLimit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 250, default: 50 }
    Offset:
      name: offset
      in: query
      schema: { type: integer, minimum: 0, default: 0 }
  responses:
    Success:
      description: Fleet-standard success envelope
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Envelope" }
    ValidationError:
      description: Invalid input
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    NotFound:
      description: Resource is not in the mirror
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    Unauthorized:
      description: A valid X-Admin-Key is required
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
  schemas:
    Envelope:
      type: object
      required: [request_status, message, data, meta]
      properties:
        request_status: { type: string, const: success }
        message: { type: string }
        data: {}
        meta:
          type: object
          required: [sources, cache, upstream, as_of, response_time_ms]
          properties:
            sources:
              type: array
              items: { type: string }
            cache:
              type: object
              properties:
                status: { type: string }
                layer: { type: string }
            upstream:
              type: object
              properties:
                status: { type: string }
            as_of: { type: string, format: date-time }
            response_time_ms: { type: integer }
            pagination:
              type: object
              properties:
                limit: { type: integer }
                offset: { type: integer }
                count: { type: integer }
    ErrorEnvelope:
      type: object
      required: [request_status, error_code, message, data, meta]
      properties:
        request_status: { type: string, const: error }
        error_code: { type: string }
        message: { type: string }
        data: {}
        meta: { type: object }
    SoftwareProduct:
      type: object
      required: [id, vendor, name, category, release_count]
      properties:
        id: { type: string }
        vendor: { type: string }
        name: { type: string }
        category:
          type: string
          enum: [operating_system, browser]
        engine: { type: [string, "null"] }
        homepage_url: { type: [string, "null"], format: uri }
        discontinued: { type: boolean }
        release_count: { type: integer }
        latest_version: { type: [string, "null"] }
        latest_release_date: { type: [string, "null"], format: date }
        pipeline:
          type: object
          properties:
            adapter: { type: string }
            status:
              type: string
              enum: [planned, active, degraded, blocked]
            completeness:
              type: string
              enum: [full, partial, current_only, unknown]
            history_started_at: { type: [string, "null"] }
            last_success_at: { type: [string, "null"], format: date-time }
            notes: { type: [string, "null"] }
    SoftwareRelease:
      type: object
      required: [id, uuid, permalink, api_url, product, version, channel, platform, architecture, support]
      properties:
        id: { type: string }
        uuid: { type: string, format: uuid }
        permalink: { type: string }
        api_url: { type: string }
        product:
          type: object
          required: [id, name, vendor, category]
          properties:
            id: { type: string }
            name: { type: string }
            vendor: { type: string }
            category:
              type: string
              enum: [operating_system, browser]
            engine: { type: [string, "null"] }
        version: { type: string }
        major_version: { type: [string, "null"] }
        channel: { type: string }
        platform: { type: string }
        architecture: { type: string }
        build: { type: [string, "null"] }
        edition: { type: [string, "null"] }
        codename: { type: [string, "null"] }
        release_date: { type: [string, "null"], format: date }
        release_timestamp: { type: [string, "null"], format: date-time }
        release_date_precision:
          type: string
          enum: [day, month, year, unknown]
        age_days: { type: [integer, "null"] }
        support:
          type: object
          properties:
            status:
              type: string
              enum: [supported, limited, end_of_support, unknown]
            basis:
              type: string
              enum: [vendor_release_observed, published_eol, inferred, unknown]
            ended_at: { type: [string, "null"] }
        is_current: { type: boolean }
        external_id: { type: [string, "null"] }
        metadata: {}
        provenance: { type: object }
    ReleaseObservation:
      type: object
      required: [uuid, source_id, claim_hash, observed_at, claim]
      properties:
        uuid: { type: string, format: uuid }
        source_id: { type: string }
        source_external_id: { type: [string, "null"] }
        claim_hash: { type: [string, "null"] }
        observed_at: { type: string, format: date-time }
        claim: { type: object }
    SoftwareReleaseInput:
      type: object
      required: [product_id, version, source_id, source_url]
      properties:
        product_id: { type: string, examples: [opera-gx] }
        version: { type: string }
        major_version: { type: [string, "null"] }
        channel: { type: string, default: stable }
        platform: { type: string, default: all }
        architecture: { type: string, default: all }
        build: { type: [string, "null"] }
        edition: { type: [string, "null"] }
        codename: { type: [string, "null"] }
        release_date: { type: [string, "null"], format: date }
        release_timestamp: { type: [string, "null"], format: date-time }
        release_date_precision:
          type: string
          enum: [day, month, year, unknown]
        support_ended_at: { type: [string, "null"], format: date }
        support_status:
          type: string
          enum: [supported, limited, end_of_support, unknown]
        support_basis:
          type: string
          enum: [vendor_release_observed, published_eol, inferred, unknown]
        is_current: { type: boolean }
        source_id: { type: string }
        source_url: { type: string, format: uri }
        external_id: { type: [string, "null"] }
        metadata: { type: [object, "null"] }
    Release:
      type: object
      required: [id, platform_id, version, release_date, age_days, support]
      properties:
        id: { type: string }
        platform_id: { type: string }
        platform_name: { type: string }
        vendor: { type: string }
        cycle: { type: string }
        version: { type: string }
        release_name: { type: [string, "null"] }
        channel: { type: string }
        release_date: { type: string, format: date }
        age_days: { type: integer }
        support:
          type: object
          properties:
            status:
              type: string
              enum: [supported, limited, end_of_support, unknown]
            basis:
              type: string
              enum: [vendor_release_observed, published_eol, inferred, unknown]
            ended_at: { type: [string, "null"], format: date }
        is_current_patch: { type: boolean }
        builds:
          type: array
          items:
            type: object
            properties:
              build: { type: string }
              asset_posted_at: { type: [string, "null"], format: date }
              installable_until: { type: [string, "null"], format: date }
              supported_device_count: { type: integer }
        provenance: { type: object }
    HardwareModel:
      type: object
      required: [id, uuid, permalink, api_url, vendor, family, marketing_name, identifiers]
      properties:
        id: { type: string }
        uuid: { type: string, format: uuid }
        permalink: { type: string }
        api_url: { type: string }
        vendor: { type: string }
        family: { type: string }
        marketing_name: { type: string }
        introduced_at: { type: [string, "null"], format: date }
        discontinued_at: { type: [string, "null"], format: date }
        architecture:
          type: [string, "null"]
          enum: [arm64, x86_64, universal, unknown, null]
        processor: { type: [string, "null"] }
        service_status:
          type: string
          enum: [current, supported, vintage, obsolete, unknown]
        identifiers:
          type: array
          items:
            type: object
            properties:
              scheme: { type: string }
              value: { type: string }
        os_compatibility:
          type: object
          properties:
            minimum_version: { type: [string, "null"] }
            maximum_version: { type: [string, "null"] }
        provenance: { type: object }
    Discovery:
      type: object
      required: [detected, assessment, software_intelligence]
      properties:
        detected:
          type: object
          properties:
            platform_id: { type: [string, "null"] }
            platform_name: { type: [string, "null"] }
            version: { type: [string, "null"] }
            device_class:
              type: string
              enum: [desktop, mobile, tablet, unknown]
            model: { type: [string, "null"] }
            architecture: { type: [string, "null"] }
            browser: { type: [string, "null"] }
            browser_version: { type: [string, "null"] }
            confidence: { type: number, minimum: 0, maximum: 1 }
            parse_confidence:
              type: string
              enum: [high, medium, low, none]
            trust:
              type: string
              enum: [self_asserted, browser_reported, heuristic, none]
            assurance:
              type: object
              properties:
                authenticity: { type: string, const: unverified }
                identity: { type: string, const: none }
                management: { type: string, const: unknown }
                posture: { type: string, const: unknown }
            evidence:
              type: array
              items:
                type: object
                properties:
                  field: { type: string }
                  value: { type: string }
                  source: { type: string }
            warnings:
              type: array
              items: { type: string }
        assessment:
          type: object
          properties:
            state:
              type: string
              enum: [current, update_available, unsupported, unrecognized]
            update_available: { type: [boolean, "null"] }
            release_age_days: { type: [integer, "null"] }
            versions_behind: { type: [string, "null"] }
            reason: { type: string }
        matched_release: { type: [object, "null"] }
        current_release: { type: [object, "null"] }
        matched_hardware: { type: [object, "null"] }
        software_intelligence:
          type: object
          required: [os, browser]
          properties:
            os: { $ref: "#/components/schemas/DiscoverySoftwarePair" }
            browser: { $ref: "#/components/schemas/DiscoverySoftwarePair" }
    DiscoverySoftwarePair:
      type: object
      required: [matched, current, assessment]
      properties:
        matched:
          oneOf:
            - $ref: "#/components/schemas/SoftwareRelease"
            - type: "null"
        current:
          oneOf:
            - $ref: "#/components/schemas/SoftwareRelease"
            - type: "null"
        assessment:
          type: [object, "null"]
          properties:
            state:
              type: string
              enum: [current, update_available, unsupported, unrecognized]
            update_available: { type: [boolean, "null"] }
            release_age_days: { type: [integer, "null"] }
            versions_behind: { type: [string, "null"] }
            reason: { type: string }
    FingerprintObservation:
      type: object
      required:
        - schema_version
        - observed_at
        - purpose
        - collection_tier
        - signals
        - server
        - availability
        - digests
        - assurance
        - retention
      properties:
        schema_version: { type: string, const: device-observation/1 }
        observed_at: { type: string, format: date-time }
        purpose:
          type: string
          enum: [fraud_prevention, account_security, user_diagnostic]
        collection_tier:
          type: string
          enum: [basic, enhanced, minimal]
        privacy:
          type: object
          properties:
            declaration:
              type: string
              enum: [enabled, not_declared]
            profile: { type: [string, "null"] }
            included_in_fingerprint: { type: boolean, const: false }
            included_in_risk_score: { type: boolean, const: false }
            persisted: { type: boolean, const: false }
        signals: { type: object }
        server: { type: object }
        availability:
          type: object
          properties:
            level:
              type: string
              enum: [normal, reduced, minimal]
            blocked_count: { type: integer }
            unsupported_count: { type: integer }
            optional_probes_skipped: { type: integer }
        digests:
          type: object
          properties:
            scope: { type: string, const: origin-purpose-7-day-epoch }
            epoch: { type: string }
            algorithm: { type: string, const: HMAC-SHA-256 }
            core_hmac: { type: string }
            rendering_hmac: { type: [string, "null"] }
            request_hmac: { type: string }
        assurance:
          type: object
          properties:
            authenticity: { type: string, const: unverified }
            identity: { type: string, const: probabilistic_only }
            same_digest_means_same_device: { type: boolean, const: false }
            different_digest_means_different_device: { type: boolean, const: false }
        retention:
          type: object
          properties:
            observation_persisted: { type: boolean, const: false }
            challenge_expires_at: { type: string, format: date-time }
