openapi: 3.0.3
info:
  title: XSOC P2PE API
  description: |
    The XSOC validated P2PE solution. A live API surface that demonstrates the
    architectural property that distinguishes the XSOC solution from every current
    PCI P2PE listing: a single POI device can derive session keys for multiple
    decryption environments without sharing a root key across them. Route selection
    happens BEFORE encryption, not after. Revocation propagates across three channels
    in real time.

    Authentication. Public properties (one through three) are open to any client.
    Admin endpoints (revoke, gateway enroll, session list) require a ScopedActionToken
    issued by the XSOC NIE-Go gateway at nie.xsoccorp.com, encoded as a Bearer token
    in the Authorization header. The token MUST carry the action and resource scope
    matching the operation being authorized.

    Trade secret notice. DSKAG, QSIG, and SP-VERSA are stubbed in this reference
    using HKDF-SHA256, HMAC-SHA256, and AES-256-GCM. The API contract and protocol
    flow are identical between reference and production.
  version: 1.0.0
  contact:
    name: XSOC Corp
    url: https://xsoccorp.com
    email: ssaddigh@xsoccorp.com
  license:
    name: Apache 2.0
servers:
  - url: https://p2pe.xsoccorp.com
    description: XSOC validated P2PE service
  - url: http://localhost:8080
    description: Local development
tags:
  - name: health
  - name: poi
  - name: gateway
  - name: route
  - name: session
  - name: transaction
  - name: admin

paths:
  /health:
    get:
      tags: [health]
      summary: Liveness check
      responses:
        "200":
          description: Service is up

  /version:
    get:
      tags: [health]
      summary: Service identity, version, auth model
      responses:
        "200":
          description: Identity returned

  /p2pe/v1/epoch:
    get:
      tags: [health]
      summary: Current X-ARC lineage epoch
      description: Tokens issued under epoch N become invalid when the epoch increments.
      responses:
        "200": { description: Epoch returned }

  /p2pe/v1/poi/enroll:
    post:
      tags: [poi]
      summary: Enroll a POI device
      description: |
        Generates an ML-KEM-768 keypair and an INDEPENDENT enrollment secret per
        currently enrolled gateway. No shared root key is created. Each per-gateway
        secret is rolled fresh.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/POIEnrollRequest"
      responses:
        "201":
          description: POI enrolled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/POIEnrollResponse"

  /p2pe/v1/poi/{id}:
    get:
      tags: [poi]
      summary: Read POI status
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200": { description: POI returned }
        "404": { description: Not found }

  /p2pe/v1/poi/{id}/revoke:
    post:
      tags: [poi, admin]
      summary: Revoke a POI
      description: |
        Increments the X-ARC epoch and propagates the event across three revocation
        channels (in-process cache, Redis, Event Hubs).

        Requires a ScopedActionToken from the NIE-Go gateway with:
          action   = p2pe:admin:revoke
          resource = {id}
      security:
        - nieScopedToken: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200": { description: Revoked }
        "401": { description: Bearer token missing or invalid }
        "403": { description: Token scope does not match operation }

  /p2pe/v1/gateway/enroll:
    post:
      tags: [gateway, admin]
      summary: Enroll a decryption environment
      description: |
        Requires a ScopedActionToken from the NIE-Go gateway with:
          action   = p2pe:admin:enroll-gateway
          resource = *
      security:
        - nieScopedToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GatewayEnrollRequest"
      responses:
        "201": { description: Gateway enrolled }
        "401": { description: Bearer token missing or invalid }

  /p2pe/v1/gateway:
    get:
      tags: [gateway]
      summary: List enrolled gateways (includes pre-enrolled gw_alpha and gw_beta)
      responses:
        "200": { description: Gateways returned }

  /p2pe/v1/route/select:
    post:
      tags: [route]
      summary: Select a gateway BEFORE encryption
      description: |
        The selector returns the gateway ID before the POI derives a session key.
        The session key is then bound cryptographically to that one gateway's
        enrollment secret.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RouteSelectRequest"
      responses:
        "200": { description: Gateway selected }
        "422": { description: No active gateways }

  /p2pe/v1/session/derive:
    post:
      tags: [session]
      summary: Derive a session key for one transaction
      description: |
        DSKAG produces a session key bound to the (POI, gateway, action, epoch,
        nonce) tuple. The same POI deriving keys for two different gateways
        produces two cryptographically distinct keys.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionDeriveRequest"
      responses:
        "200":
          description: Session derived
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionDeriveResponse"

  /p2pe/v1/encrypt:
    post:
      tags: [transaction]
      summary: Test harness - encrypt PAN at POI
      description: REFERENCE TEST HARNESS ONLY. Production POI encryption never accepts cleartext PAN over HTTP.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EncryptRequest"
      responses:
        "201":
          description: Ciphertext returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EncryptResponse"

  /p2pe/v1/decrypt:
    post:
      tags: [transaction]
      summary: Test harness - decrypt at gateway
      description: |
        Verifies the session ScopedActionToken (signature, expiry, X-ARC epoch),
        confirms Subject and Resource bind to the claimed POI and gateway, derives
        the matching session key from the gateway's enrollment secret, and unwraps
        the envelope. Returns 403 on any mismatch.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DecryptRequest"
      responses:
        "200": { description: Cleartext returned }
        "403": { description: Token rejected, wrong gateway, or revoked session }

  /p2pe/v1/sessions:
    get:
      tags: [admin]
      summary: List session log
      description: |
        Requires a ScopedActionToken from the NIE-Go gateway with:
          action   = p2pe:admin:list-sessions
          resource = *
      security:
        - nieScopedToken: []
      parameters:
        - in: query
          name: poi_id
          schema: { type: string }
      responses:
        "200": { description: Sessions returned }
        "401": { description: Bearer token missing or invalid }

components:
  securitySchemes:
    nieScopedToken:
      type: http
      scheme: bearer
      bearerFormat: NIE-ScopedActionToken
      description: |
        Base64-encoded JSON ScopedActionToken issued by the XSOC NIE-Go gateway at
        nie.xsoccorp.com. Token shape: {sub, res, act, iat, exp, epoch, nonce, iss, sig}.
        Signature is HMAC-SHA256 over the canonical serialization, keyed with the
        shared NIE_SECRET.

  schemas:
    POIEnrollRequest:
      type: object
      properties:
        poi_id:
          type: string
          description: Optional. Server generates one if omitted.
        metadata:
          type: object
          additionalProperties: { type: string }

    POIEnrollResponse:
      type: object
      properties:
        poi:
          type: object
          properties:
            id: { type: string }
            enrolled_at: { type: string, format: date-time }
            status: { type: string, example: active }
            revocation_epoch: { type: integer, format: int64 }
            metadata:
              type: object
              additionalProperties: { type: string }
        attestation: { type: string }
        mlkem_public_key_b64: { type: string }
        note: { type: string }

    GatewayEnrollRequest:
      type: object
      properties:
        gateway_id: { type: string }
        hsm_profile:
          type: string
          enum: [reference, thales, futurex, aws-cloudhsm, azure-dhsm]
        endpoint: { type: string }
        operator_id: { type: string }

    RouteSelectRequest:
      type: object
      properties:
        policy:
          type: object
          properties:
            preferred:
              type: array
              items: { type: string }
            currency: { type: string }
            region: { type: string }

    SessionDeriveRequest:
      type: object
      required: [poi_id, gateway_id]
      properties:
        poi_id: { type: string }
        gateway_id: { type: string }
        action:
          type: string
          example: "p2pe:session:decrypt"

    ScopedActionToken:
      type: object
      properties:
        sub: { type: string, description: "Subject (POI ID for session, counterparty ID for admin)" }
        res: { type: string, description: "Resource (Gateway ID for session, target ID for admin)" }
        act: { type: string, description: "Namespaced action verb" }
        iat: { type: integer, format: int64 }
        exp: { type: integer, format: int64 }
        epoch: { type: integer, format: int64 }
        nonce: { type: string }
        iss: { type: string, description: "Issuer (e.g. nie-go for admin tokens)" }
        sig: { type: string, description: "Base64 HMAC-SHA256 signature over canonical serialization" }

    SessionDeriveResponse:
      type: object
      properties:
        session_key_b64: { type: string }
        token: { $ref: "#/components/schemas/ScopedActionToken" }
        epoch_unix: { type: integer, format: int64 }
        nonce: { type: string }
        note: { type: string }

    EncryptRequest:
      type: object
      required: [poi_id, gateway_id, pan]
      properties:
        poi_id: { type: string }
        gateway_id: { type: string }
        pan: { type: string }

    EncryptResponse:
      type: object
      properties:
        ciphertext_b64: { type: string }
        token: { $ref: "#/components/schemas/ScopedActionToken" }
        gateway_id: { type: string }
        note: { type: string }

    DecryptRequest:
      type: object
      required: [poi_id, gateway_id, ciphertext_b64, nonce, token]
      properties:
        poi_id: { type: string }
        gateway_id: { type: string }
        ciphertext_b64: { type: string }
        nonce: { type: string }
        token: { $ref: "#/components/schemas/ScopedActionToken" }
