openapi: 3.0.3
info:
  title: VisualGrid API
  description: >
    Temporal Directed Property Graph Explorer for g3nretailstack.
    Read-only graph browser for traversing all 21 service DynamoDB tables
    as an interactive property graph with metadata, doc/MCP links,
    pagination, and single-node JSON export. Root-only access.
  version: 1.0.0
  contact:
    name: g3nretailstack
    url: https://github.com/mhsystems/g3nretailstack

servers:
  - url: https://api.g3nretailstack.com
    description: Production

paths:
  /visualgrid/root/auth:
    post:
      operationId: rootAuth
      summary: Authenticate with root secret code
      x-route-class: Tier A
      x-qps-target: 5
      x-concurrency-target: 2
      x-latency-p95-ms: 500
      description: >
        Verify the root secret code against the scrypt hash stored in DDB.
        Returns a short-lived token (1h TTL) for subsequent root-mode requests.
      tags:
        - Auth
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RootAuthRequest'
      responses:
        '200':
          description: Authentication successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RootAuthResponse'
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid secret code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /visualgrid/root/graph:
    get:
      operationId: rootGraph
      summary: Get top-level entry nodes (all tables)
      x-route-class: Tier B
      x-qps-target: 20
      x-concurrency-target: 10
      x-latency-p95-ms: 200
      description: >
        Returns entry nodes representing all 21 service DynamoDB tables.
        Each node includes table name, service code, PK prefix patterns,
        and links to service documentation.
      tags:
        - Graph
      security:
        - rootToken: []
      responses:
        '200':
          description: Entry nodes returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GraphEntryResponse'
        '401':
          description: Invalid or expired root token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      operationId: rootGraphPost
      summary: Get top-level entry nodes (all tables)
      x-route-class: Tier B
      x-qps-target: 20
      x-concurrency-target: 10
      x-latency-p95-ms: 200
      description: >
        Returns entry nodes representing all 21 service DynamoDB tables.
        Each node includes table name, service code, PK prefix patterns,
        and links to service documentation.
      tags:
        - Graph
      security:
        - rootToken: []
      responses:
        '200':
          description: Entry nodes returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GraphEntryResponse'
        '401':
          description: Invalid or expired root token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'


  /visualgrid/node/load:
    post:
      operationId: nodeLoad
      summary: Load a single node
      x-route-class: Tier C
      x-qps-target: 100
      x-concurrency-target: 50
      x-latency-p95-ms: 300
      x-latency-p99-ms: 500
      description: >
        Load a single DynamoDB item by table + PK + SK. Returns the item
        with separated DDB keys and metadata. Root-only access.
      tags:
        - Node
      security:
        - rootToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeLoadRequest'
      responses:
        '200':
          description: Node loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NodeLoadResponse'
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid auth
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Item not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /visualgrid/node/children:
    post:
      operationId: nodeChildren
      summary: Get paginated child nodes
      x-route-class: Tier C
      x-qps-target: 100
      x-concurrency-target: 50
      x-latency-p95-ms: 500
      x-latency-p99-ms: 800
      description: >
        Fetch paginated children of a node. Children share the same PK
        and have sort keys matching a given prefix. Uses DDB Query with
        KeyConditionExpression on PK and optional SK begins_with. Root-only access.
      tags:
        - Node
      security:
        - rootToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeChildrenRequest'
      responses:
        '200':
          description: Children returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NodeChildrenResponse'
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid auth
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Table not in registry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /visualgrid/node/export:
    post:
      operationId: nodeExport
      summary: Export a single node as JSON
      x-route-class: Tier C
      x-qps-target: 50
      x-concurrency-target: 20
      x-latency-p95-ms: 300
      x-latency-p99-ms: 500
      description: >
        Export a single node formatted for clipboard copy or file download.
        Same data as node/load but wrapped in an export envelope with
        timestamp. Root-only access.
      tags:
        - Node
      security:
        - rootToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeExportRequest'
      responses:
        '200':
          description: Node exported
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NodeExportResponse'
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid auth
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Item not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /visualgrid/node/history:
    post:
      operationId: nodeHistory
      summary: Get temporal history for a node
      x-route-class: Tier C
      x-qps-target: 50
      x-concurrency-target: 20
      x-latency-p95-ms: 500
      x-latency-p99-ms: 800
      description: Returns timestamped version history for a DDB item by querying audit/changelog records. Root-only access.
      tags: [Node]
      security: [{ rootToken: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [table, pk, sk]
              properties:
                table: { type: string }
                pk: { type: string }
                sk: { type: string }
                limit: { type: integer, minimum: 1, maximum: 256, default: 25 }
                next_token: { type: string, nullable: true }
      responses:
        '200': { description: History entries, content: { application/json: { schema: { type: object, properties: { ok: { type: boolean }, data: { type: object, properties: { items: { type: array, items: { type: object } }, has_more: { type: boolean }, count: { type: integer } } } } } } } }
        '401': { description: Invalid auth, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }

  /visualgrid/schema:
    get:
      operationId: schemaGraph
      summary: Get full schema registry graph
      x-route-class: Tier B
      x-qps-target: 20
      x-concurrency-target: 10
      x-latency-p95-ms: 300
      x-latency-p99-ms: 500
      description: Returns the complete service registry with all tables, entity types, edge relationships, and metadata dictionary. Root-only access.
      tags: [Graph]
      security: [{ rootToken: [] }]
      responses:
        '200': { description: Schema registry, content: { application/json: { schema: { type: object, properties: { ok: { type: boolean }, data: { type: object, properties: { services: { type: array }, edges: { type: array }, entity_types: { type: array } } } } } } } }
        '401': { description: Invalid auth, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }

  /visualgrid/ping:
    get:
      operationId: ping
      summary: Health check
      x-route-class: Tier D
      x-qps-target: 200
      x-concurrency-target: 100
      x-latency-p95-ms: 100
      x-latency-p99-ms: 200
      description: Returns service status and timestamp. No auth required.
      tags:
        - Health
      security: []
      responses:
        '200':
          description: Service healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PingResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  securitySchemes:
    rootToken:
      type: apiKey
      in: header
      name: x-vg-token
      description: Short-lived root token obtained via POST /visualgrid/root/auth

  schemas:
    # --- Request schemas ---

    RootAuthRequest:
      type: object
      required:
        - secret_code
      properties:
        secret_code:
          type: string
          description: The root secret code for VisualGrid authentication

    NodeLoadRequest:
      type: object
      required:
        - table
        - pk
        - sk
      properties:
        table:
          type: string
          description: DynamoDB table name (e.g., uas_main)
          example: uas_main
        pk:
          type: string
          description: Partition key value
          example: "USER#u_abc123"
        sk:
          type: string
          description: Sort key value
          example: "USER#META"

    NodeChildrenRequest:
      type: object
      required:
        - table
        - pk
      properties:
        table:
          type: string
          description: DynamoDB table name
          example: uas_main
        pk:
          type: string
          description: Partition key value
          example: "USER#u_abc123"
        sk_prefix:
          type: string
          description: Sort key prefix filter
          example: "EMAIL#"
        limit:
          type: integer
          minimum: 1
          maximum: 256
          default: 25
          description: Page size
        next_token:
          type: string
          nullable: true
          description: Pagination cursor from previous response

    NodeExportRequest:
      type: object
      required:
        - table
        - pk
        - sk
      properties:
        table:
          type: string
          description: DynamoDB table name
          example: scm_main
        pk:
          type: string
          description: Partition key value
          example: "ORG#myorg#ORDER"
        sk:
          type: string
          description: Sort key value
          example: "ORDER#ord_abc123"

    # --- Response schemas ---

    BuildInfo:
      type: object
      properties:
        service:
          type: string
          example: visualgrid
        version:
          type: string
          example: "1.0.0"

    RedactedNode:
      type: object
      description: >
        A single DynamoDB item with separated DDB keys and enriched metadata.
        All fields returned unredacted (root-only access).
      properties:
        ddb_keys:
          type: object
          description: >
            DynamoDB key attributes extracted from the record
            (PK, SK, GSI1PK, GSI1SK, etc.)
          additionalProperties:
            type: string
          example:
            PK: "USER#u_abc123"
            SK: "USER#META"
        properties:
          type: object
          description: >
            Record attributes. All fields returned unredacted.
          additionalProperties: true
          example:
            status: verified
            caption: "John Doe"
            passcode_hash: "...h8i9j0k1"
        metadata:
          type: object
          description: Enriched context for the node
          properties:
            service_code:
              type: string
              description: Service abbreviation (e.g., uas, scm)
              example: uas
            table:
              type: string
              description: DynamoDB table name
              example: uas_main
            record_type:
              type: string
              description: Record type derived from SK pattern
              example: user_core
            doc_url:
              type: string
              description: Path to service RECAP.md
              example: uservice/uas/RECAP.md
            mcp_url:
              type: string
              description: Path to service MCP PROTOCOL.md
              example: uservice/uas/mcp/PROTOCOL.md

    GraphEntryNode:
      type: object
      description: A top-level entry node representing a service table or org root
      properties:
        id:
          type: string
          description: Unique identifier for this entry node
          example: uas_main
        label:
          type: string
          description: Human-readable label
          example: "UAS — User Account Service"
        type:
          type: string
          description: Node type (table or org_root)
          enum:
            - table
            - org_root
          example: table
        service_code:
          type: string
          description: Service abbreviation
          example: uas
        table:
          type: string
          description: DynamoDB table name
          example: uas_main
        pk_prefix:
          type: string
          description: Common PK prefix pattern for this table
          example: "USER#"
        doc_url:
          type: string
          description: Path to service documentation
          example: uservice/uas/RECAP.md
        mcp_url:
          type: string
          description: Path to MCP protocol definition
          example: uservice/uas/mcp/PROTOCOL.md

    RootAuthResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            token:
              type: string
              description: Short-lived root token (1h TTL)
              example: "vg_a1b2c3d4e5f6..."
            expires_at:
              type: string
              format: date-time
              description: Token expiry timestamp (ISO 8601)
              example: "2026-02-20T14:00:00Z"
        build:
          $ref: '#/components/schemas/BuildInfo'

    GraphEntryResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            nodes:
              type: array
              items:
                $ref: '#/components/schemas/GraphEntryNode'
        build:
          $ref: '#/components/schemas/BuildInfo'

    NodeLoadResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            node:
              $ref: '#/components/schemas/RedactedNode'
        build:
          $ref: '#/components/schemas/BuildInfo'

    NodeChildrenResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            children:
              type: array
              items:
                $ref: '#/components/schemas/RedactedNode'
            next_token:
              type: string
              nullable: true
              description: >
                Pagination cursor. Null or absent when no more pages.
            count:
              type: integer
              description: Number of children in this page
        build:
          $ref: '#/components/schemas/BuildInfo'

    NodeExportResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            export:
              type: object
              properties:
                table:
                  type: string
                  example: scm_main
                pk:
                  type: string
                  example: "ORG#myorg#ORDER"
                sk:
                  type: string
                  example: "ORDER#ord_abc123"
                record:
                  type: object
                  description: Record properties
                  additionalProperties: true
                exported_at:
                  type: string
                  format: date-time
                  description: Export timestamp (ISO 8601)
        build:
          $ref: '#/components/schemas/BuildInfo'

    PingResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        data:
          type: object
          properties:
            status:
              type: string
              example: healthy
            service:
              type: string
              example: visualgrid
            timestamp:
              type: string
              format: date-time
        build:
          $ref: '#/components/schemas/BuildInfo'

    ErrorResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: false
        error:
          type: object
          properties:
            tag:
              type: string
              description: Machine-readable error tag
              enum:
                - invalid-input
                - unauthorized
                - forbidden
                - not-found
                - internal-error
              example: unauthorized
            message:
              type: string
              description: Human-readable error message (en_US)
              example: Invalid or expired root token
            details:
              type: object
              description: Optional additional error context
              additionalProperties: true
        build:
          $ref: '#/components/schemas/BuildInfo'

tags:
  - name: Auth
    description: Root authentication
  - name: Graph
    description: Graph entry points (root only)
  - name: Node
    description: Node operations (load, children, export)
  - name: Health
    description: Health check
