openapi: 3.1.0
info:
  title: GovSign Digital Signature API
  description: |
    Enterprise digital signature API for the Government of Nepal.
    Provides eIDAS-compliant PAdES, XAdES, CAdES, and ASiC-E signing,
    multi-format validation, document encryption, and multi-tenant gateway.
  version: 1.0.0
  contact:
    name: GovSign Support
    email: govsign@nepal.gov.np
  license:
    name: Apache 2.0

servers:
  - url: http://localhost:9090
    description: Signing Agent (local)
  - url: http://localhost:8443
    description: Enterprise Gateway (local)

tags:
  - name: Health
    description: Service health and status
  - name: Signing
    description: Document signing operations
  - name: Validation
    description: Signature validation
  - name: Trust
    description: Trust infrastructure status
  - name: Encryption
    description: Document encryption/decryption
  - name: Gateway
    description: Enterprise Gateway operations
  - name: Admin
    description: Gateway administration

paths:
  /health:
    get:
      tags: [Health]
      summary: Health check
      operationId: getHealth
      responses:
        '200':
          description: Service status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /api/v1/sign/pdf:
    post:
      tags: [Signing]
      summary: Sign PDF (PAdES-B)
      operationId: signPdf
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PdfSignRequest'
      responses:
        '200':
          description: Signed PDF
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignResponse'

  /api/v1/sign/pades:
    post:
      tags: [Signing]
      summary: Sign PDF with PAdES (B/T/LT/LTA)
      operationId: signPades
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvancedSignRequest'
      responses:
        '200':
          description: Signed document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvancedSignResponse'

  /api/v1/sign/xades:
    post:
      tags: [Signing]
      summary: Sign with XAdES
      operationId: signXades
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvancedSignRequest'
      responses:
        '200':
          description: Signed XML
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvancedSignResponse'

  /api/v1/sign/cades:
    post:
      tags: [Signing]
      summary: Sign with CAdES
      operationId: signCades
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvancedSignRequest'
      responses:
        '200':
          description: CMS signature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvancedSignResponse'

  /api/v1/sign/asice:
    post:
      tags: [Signing]
      summary: Create ASiC-E container
      operationId: signAsice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AsiceSignRequest'
      responses:
        '200':
          description: Signed container
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvancedSignResponse'

  /api/v1/validate/pdf:
    post:
      tags: [Validation]
      summary: Validate PDF signatures
      operationId: validatePdf
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationReport'

  /api/v1/validate/asic:
    post:
      tags: [Validation]
      summary: Validate ASiC-E container
      operationId: validateAsic
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationReport'

  /api/v1/validate/xml:
    post:
      tags: [Validation]
      summary: Validate XML/XAdES signatures
      operationId: validateXml
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationReport'

  /api/v1/validate/cms:
    post:
      tags: [Validation]
      summary: Validate CMS/CAdES signatures
      operationId: validateCms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationReport'

  /api/v1/trust/status:
    get:
      tags: [Trust]
      summary: Trust infrastructure status
      operationId: getTrustStatus
      responses:
        '200':
          description: Trust status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrustStatus'

  /api/v1/sign/bdoc:
    post:
      tags: [Signing]
      summary: Sign a single file as a BDOC container (Estonia ASiC-E variant)
      operationId: signBdoc
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, p12Path, p12Password]
              properties:
                file: { type: string, format: binary }
                p12Path: { type: string }
                p12Password: { type: string, format: password }
                level: { type: string, enum: [B, T, LT, LTA], default: LT }
      responses:
        '200': { description: BDOC container, content: { application/json: { schema: { type: object } } } }

  /api/v1/sign/hashcode:
    post:
      tags: [Signing]
      summary: Sign a precomputed digest (XAdES hashcode form)
      description: |
        The relying party hashes the data file locally and submits only the digest.
        Returns a XAdES detached signature without the agent ever holding plaintext.
      operationId: signHashcode
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [fileName, digestBase64, p12Path, p12Password]
              properties:
                fileName: { type: string }
                digestBase64: { type: string }
                digestAlgorithm: { type: string, enum: [SHA256, SHA384, SHA512], default: SHA256 }
                level: { type: string, enum: [B, T, LT, LTA], default: LT }
                p12Path: { type: string }
                p12Password: { type: string, format: password }
      responses:
        '200': { description: XAdES signature, content: { application/json: { schema: { type: object } } } }

  /api/v1/validate/bdoc:
    post:
      tags: [Validation]
      summary: Validate a BDOC container
      operationId: validateBdoc
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
      responses:
        '200': { description: Validation report, content: { application/json: { schema: { type: object } } } }

  /api/v1/validate/ddoc:
    post:
      tags: [Validation]
      summary: Validate a legacy DDOC XML container
      operationId: validateDdoc
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
      responses:
        '200': { description: Validation report, content: { application/json: { schema: { type: object } } } }

  /api/v1/validate/auto:
    post:
      tags: [Validation]
      summary: Auto-detect format and dispatch to the correct validator
      operationId: validateAuto
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
      responses:
        '200': { description: Validation report with detected format, content: { application/json: { schema: { type: object } } } }

  /api/v1/validate/report:
    post:
      tags: [Validation]
      summary: Build DSS-style SimpleReport / DetailedReport / DiagnosticData (XML)
      operationId: validationReport
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
                reportType:
                  type: string
                  enum: [SIMPLE, DETAILED, DIAGNOSTIC, ALL]
                  default: ALL
      responses:
        '200': { description: DSS reports, content: { application/json: { schema: { type: object } } } }

  /api/v1/validate/signed-report:
    post:
      tags: [Validation]
      summary: Produce a PAdES-LT signed validation report PDF (offline-verifiable evidence)
      operationId: signedValidationReport
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, p12Path, p12Password]
              properties:
                file: { type: string, format: binary }
                p12Path: { type: string }
                p12Password: { type: string, format: password }
      responses:
        '200':
          description: Signed PDF
          content:
            application/pdf:
              schema: { type: string, format: binary }

  /api/v1/encrypt:
    post:
      tags: [Encryption]
      summary: Encrypt document
      operationId: encryptDocument
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EncryptRequest'
      responses:
        '200':
          description: Encrypted document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EncryptResponse'

  /api/v1/decrypt:
    post:
      tags: [Encryption]
      summary: Decrypt document
      operationId: decryptDocument
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecryptRequest'
      responses:
        '200':
          description: Decrypted document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecryptResponse'

  /api/v1/containers:
    post:
      tags: [Gateway]
      summary: Create signing session
      operationId: createContainer
      security:
        - hmacAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContainerRequest'
      responses:
        '200':
          description: Session created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContainerSession'

  /admin/clients:
    post:
      tags: [Admin]
      summary: Register service client
      operationId: registerClient
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterClientRequest'
      responses:
        '200':
          description: Client registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServiceClient'
    get:
      tags: [Admin]
      summary: List service clients
      operationId: listClients
      responses:
        '200':
          description: Client list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ServiceClient'

  # ── SDK-facing endpoints (stable contract for npm / NuGet / PyPI / Maven / Packagist SDKs) ──

  /api/v1/sign/pdf-citizen:
    post:
      tags: [SDK]
      summary: Open a citizen-confirm PDF signing session
      description: |
        The SDK hashes the PDF locally and submits the digest + base64 PDF here.
        The server opens a session, routes it to the citizen's preferred channel
        (phone push / QR Connect / SIM applet / USB token bridge) and returns a
        sessionId + 4-digit verification code for visual confirmation.
      operationId: openCitizenSign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CitizenSignRequest'
      responses:
        '200':
          description: Session opened
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CitizenSignSession'
        '409':
          description: Idempotency conflict — same key still in flight

  /api/v1/sign/pdf-citizen/{sessionId}:
    get:
      tags: [SDK]
      summary: Poll a citizen-sign session
      operationId: pollCitizenSign
      parameters:
        - in: path
          name: sessionId
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: Session state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CitizenSignState'
        '404':
          description: No such session

  /api/v1/sign/pdf-citizen/{sessionId}/complete:
    post:
      tags: [SDK]
      summary: Mobile/bridge callback — citizen has signed
      description: Called by the GovSign mobile app or desktop bridge after the citizen confirms.
      operationId: completeCitizenSign
      parameters:
        - in: path
          name: sessionId
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [signedPdfB64]
              properties:
                signedPdfB64: { type: string, format: byte }
      responses:
        '200':
          description: Session marked SIGNED

  /api/v1/sign/batch-citizen:
    post:
      tags: [SDK]
      summary: Open a multi-PDF citizen-sign session (one tap signs many)
      operationId: openBatchCitizenSign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchCitizenSignRequest'
      responses:
        '200':
          description: Batch session opened
          content:
            application/json:
              schema:
                type: object
                properties:
                  sessionId: { type: string, format: uuid }

  /api/v1/sign/batch-citizen/{sessionId}:
    get:
      tags: [SDK]
      summary: Poll a batch citizen-sign session
      operationId: pollBatchCitizenSign
      parameters:
        - in: path
          name: sessionId
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: Batch session state

  /api/v1/sign/batch-citizen/{sessionId}/complete:
    post:
      tags: [SDK]
      summary: Batch mobile/bridge callback
      operationId: completeBatchCitizenSign
      parameters:
        - in: path
          name: sessionId
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: Batch session marked SIGNED

  /api/v1/sign/bulk-institutional:
    post:
      tags: [SDK]
      summary: Server-side bulk signing with a registered department certificate
      description: |
        For batches signed under a department cert (no citizen). The SDK provides
        a `certReference` resolved server-side from `GOVSIGN_INSTITUTIONAL_CERTS`
        and a list of PDFs. Each item is wrapped via the AdvancedSigner PAdES-LT
        pipeline.
      operationId: bulkInstitutional
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkInstitutionalRequest'
      responses:
        '200':
          description: Items signed
          content:
            application/json:
              schema:
                type: object
                properties:
                  signedItems:
                    type: array
                    items:
                      type: object
                      properties:
                        filename:     { type: string }
                        signedPdfB64: { type: string, format: byte }
                        format:       { type: string }
                        level:        { type: string }
                        signer:       { type: string }

  /api/v1/citizens/{citizenId}:
    get:
      tags: [SDK]
      summary: Lookup citizen enrolment / preferred signing channel
      operationId: getCitizen
      parameters:
        - in: path
          name: citizenId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Citizen view
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CitizenView'

  # ── Transparency log (Sigstore-Rekor compatible) ──

  /api/v1/log/entries:
    post:
      tags: [TransparencyLog]
      summary: Append an entry to the transparency log
      operationId: appendLogEntry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string, description: "Opaque signed artifact (typically JSON or DER)" }
      responses:
        '201':
          description: Entry integrated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogEntry'

  /api/v1/log/entries/{index}:
    get:
      tags: [TransparencyLog]
      summary: Get a log entry by index
      operationId: getLogEntry
      parameters:
        - in: path
          name: index
          required: true
          schema: { type: integer, minimum: 0 }
      responses:
        '200':
          description: Log entry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogEntry'

  /api/v1/log/proof/{index}:
    get:
      tags: [TransparencyLog]
      summary: Inclusion proof against the current STH
      operationId: getInclusionProof
      parameters:
        - in: path
          name: index
          required: true
          schema: { type: integer, minimum: 0 }
      responses:
        '200':
          description: Inclusion proof + current STH
          content:
            application/json:
              schema:
                type: object
                properties:
                  logIndex:       { type: integer }
                  treeSize:       { type: integer }
                  rootHashBase64: { type: string }
                  hashes:
                    type: array
                    items: { type: string, format: byte }
                  sth: { $ref: '#/components/schemas/SignedTreeHead' }

  /api/v1/log/sth:
    get:
      tags: [TransparencyLog]
      summary: Current Signed Tree Head
      operationId: getSth
      responses:
        '200':
          description: STH
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedTreeHead'

  /api/v1/log/publicKey:
    get:
      tags: [TransparencyLog]
      summary: Ed25519 public key for STH verification
      operationId: getLogPublicKey
      responses:
        '200':
          description: Public key
          content:
            application/json:
              schema:
                type: object
                properties:
                  logId:           { type: string }
                  algorithm:       { type: string, example: "Ed25519" }
                  publicKeyBase64: { type: string, format: byte }

components:
  securitySchemes:
    hmacAuth:
      type: apiKey
      in: header
      name: X-Authorization
      description: HMAC-SHA256 signature — format "HMAC clientId:timestamp:signature"

  schemas:
    HealthResponse:
      type: object
      properties:
        status: { type: string, example: "UP" }
        version: { type: string, example: "1.0.0" }
        platform: { type: string, example: "GovSign" }

    PdfSignRequest:
      type: object
      required: [pdf, p12, password]
      properties:
        pdf: { type: string, format: byte, description: "PDF as Base64" }
        p12: { type: string, format: byte, description: "P12 certificate as Base64" }
        password: { type: string }
        reason: { type: string, default: "Digital Signature" }
        location: { type: string, default: "Nepal" }
        visible: { type: boolean, default: false }

    AdvancedSignRequest:
      type: object
      required: [file, p12, password]
      properties:
        file: { type: string, format: byte }
        p12: { type: string, format: byte }
        password: { type: string }
        level: { type: string, enum: [B, T, LT, LTA], default: "LTA" }
        fileName: { type: string }

    AsiceSignRequest:
      type: object
      required: [files, p12, password]
      properties:
        files:
          type: object
          additionalProperties: { type: string, format: byte }
        p12: { type: string, format: byte }
        password: { type: string }
        level: { type: string, enum: [B, T, LT, LTA], default: "LTA" }

    SignResponse:
      type: object
      properties:
        status: { type: string }
        signedPdf: { type: string, format: byte }
        signer: { type: string }
        pages: { type: integer }

    AdvancedSignResponse:
      type: object
      properties:
        status: { type: string }
        signedDocument: { type: string, format: byte }
        format: { type: string }
        level: { type: string }
        signer: { type: string }
        elapsedMs: { type: integer }

    ValidateRequest:
      type: object
      required: [file]
      properties:
        file: { type: string, format: byte }

    ValidationReport:
      type: object
      properties:
        valid: { type: boolean }
        totalSignatures: { type: integer }
        validSignatures: { type: integer }
        invalidSignatures: { type: integer }
        format: { type: string }
        signatures:
          type: array
          items:
            $ref: '#/components/schemas/SignatureInfo'
        error: { type: string, nullable: true }

    SignatureInfo:
      type: object
      properties:
        signer: { type: string }
        issuer: { type: string }
        signatureFormat: { type: string }
        indication: { type: string, enum: [TOTAL_PASSED, TOTAL_FAILED, INDETERMINATE] }
        hasTimestamp: { type: boolean }
        certificateExpired: { type: boolean }
        ocspStatus: { type: string }
        chainTrusted: { type: boolean }

    TrustStatus:
      type: object
      properties:
        status: { type: string }
        ocspCacheSize: { type: integer }
        primaryTsaUrl: { type: string }
        tsaCount: { type: integer }
        trustedCACount: { type: integer }

    EncryptRequest:
      type: object
      required: [file, password]
      properties:
        file: { type: string, format: byte }
        password: { type: string }

    EncryptResponse:
      type: object
      properties:
        encrypted: { type: string, format: byte }
        algorithm: { type: string }
        originalSize: { type: integer }
        encryptedSize: { type: integer }

    DecryptRequest:
      type: object
      required: [file, password]
      properties:
        file: { type: string, format: byte }
        password: { type: string }

    DecryptResponse:
      type: object
      properties:
        decrypted: { type: string, format: byte }
        originalSize: { type: integer }

    CreateContainerRequest:
      type: object
      properties:
        containerType: { type: string, enum: [ASICE, ASICS, PDF], default: "ASICE" }

    ContainerSession:
      type: object
      properties:
        sessionId: { type: string, format: uuid }
        containerType: { type: string }
        state: { type: string }

    RegisterClientRequest:
      type: object
      required: [serviceName]
      properties:
        serviceName: { type: string }
        contactEmail: { type: string }
        rateLimit: { type: integer, default: 100 }

    ServiceClient:
      type: object
      properties:
        clientId: { type: string }
        serviceName: { type: string }
        hmacSecret: { type: string }
        active: { type: boolean }
        rateLimit: { type: integer }

    # ── SDK contract schemas ──

    CitizenSignRequest:
      type: object
      required: [idempotencyKey, citizenId, title, relyingParty, documentDigestB64, pdfB64]
      properties:
        idempotencyKey:    { type: string, description: "Stripe-style. Same key → same session." }
        citizenId:         { type: string, example: "12-34-56-7890" }
        title:             { type: string, example: "Tax Filing 2026" }
        relyingParty:      { type: string, example: "IRD" }
        documentDigestB64: { type: string, format: byte, description: "SHA-256 of the PDF" }
        pdfB64:            { type: string, format: byte }
        level:             { type: string, enum: [B, T, LT, LTA], default: LT }
        reason:            { type: string }
        location:          { type: string }
        stampTemplate:     { type: object, description: "Optional stamp render template" }

    CitizenSignSession:
      type: object
      properties:
        sessionId:        { type: string, format: uuid }
        verificationCode: { type: string, description: "4-digit code shown on both phone and SDK caller" }

    CitizenSignState:
      type: object
      properties:
        state:        { type: string, enum: [WAITING, SIGNED, EXPIRED, CANCELLED] }
        signedPdfB64: { type: string, format: byte, nullable: true }
        completedAt:  { type: string, format: date-time, nullable: true }

    BatchCitizenSignRequest:
      type: object
      required: [citizenId, title, relyingParty, items]
      properties:
        citizenId:    { type: string }
        title:        { type: string }
        relyingParty: { type: string }
        items:
          type: array
          items:
            type: object
            required: [pdfB64]
            properties:
              pdfB64: { type: string, format: byte }

    BulkInstitutionalRequest:
      type: object
      required: [certReference, items]
      properties:
        certReference: { type: string, description: "Logical name resolved via GOVSIGN_INSTITUTIONAL_CERTS" }
        level:         { type: string, enum: [B, T, LT, LTA], default: LT }
        reason:        { type: string }
        location:      { type: string }
        items:
          type: array
          items:
            type: object
            required: [pdfB64]
            properties:
              pdfB64: { type: string, format: byte }

    CitizenView:
      type: object
      properties:
        citizenId:        { type: string }
        commonName:       { type: string }
        enrolled:         { type: boolean }
        preferredChannel: { type: string, enum: [PHONE_PUSH, QR_CONNECT, SIM_APPLET, USB_TOKEN] }

    # ── Transparency log schemas ──

    LogEntry:
      type: object
      properties:
        index:          { type: integer }
        leafHashBase64: { type: string, format: byte }
        logId:          { type: string }
        integratedAt:   { type: string, format: date-time }
        body:           { type: string }

    SignedTreeHead:
      type: object
      properties:
        treeSize:        { type: integer }
        rootHashBase64:  { type: string, format: byte }
        timestampMillis: { type: integer, format: int64 }
        signatureBase64: { type: string, format: byte, description: "Ed25519 over BE-uint64(size) || BE-uint64(ts) || rootHash" }
        logId:           { type: string }
