openapi: 3.1.0
info:
  title: Eldric 5.0 — customer-facing API (Wave 22 surface)
  version: "5.0.0"
  summary: The 18 endpoints customers consume directly. RAG, retention loop, intelligent upload, router classification, custom classification, audit ledger, bundle trust, model conversion.
  description: |
    OpenAPI 3.1 spec for the customer-facing surface shipped in the run-up to Eldric 5.0 GA.

    This file enumerates the endpoints customers integrate against. Internal cluster-to-cluster
    routes (worker poll loops, peer aggregator internals, reindex orchestrator state,
    background ingest workers) are not in this spec.

    For the prose how-to pages, see https://eldric.ai/using-rag.html,
    https://eldric.ai/architecture-rag.html, https://eldric.ai/chunking-strategies.html,
    https://eldric.ai/rag-on-demand.html, https://eldric.ai/custom-classification.html.

    License gating: all endpoints below require the `rag` capability (Standard tier and above).
    Endpoints that mutate state require the `admin` role inside the requesting tenant.
    Tenant scope is enforced at the gateway; cross-tenant attempts return 403.
  contact:
    name: "[core] Informationstechnologie · Juergen Paulhart"
    email: office@eldric.ai
    url: https://eldric.ai
  license:
    name: Proprietary
    url: https://eldric.ai/pricing.html

servers:
  - url: https://your-cluster.example.com
    description: Customer-deployed Eldric cluster (replace with your edge URL)

security:
  - bearerAuth: []

tags:
  - name: chat
    description: Conversation lifecycle and retention loop
  - name: upload
    description: Intelligent upload — analyse, preview, commit, poll
  - name: vector
    description: Knowledge-base administration (per-namespace config, bulk ops, diagnostics)
  - name: router
    description: Query classification and custom intent taxonomy
  - name: audit
    description: Audit ledger reads (read-only, append-only chain)
  - name: bundle
    description: .nexus bundle trust management
  - name: models
    description: Model conversion (HuggingFace → GGUF)

paths:

  /api/v1/chat/feedback:
    post:
      tags: [chat]
      summary: Retention-loop accept/reject signal on an assistant turn
      description: |
        Customer's thumbs-up on a chat response triggers auto-ingest of cited sources
        into the appropriate knowledge base, feeds the dream cycle, and seeds the next
        ENRN training corpus. Thumbs-down marks the answer low-quality; cited sources
        are not auto-ingested and the next query on the same topic tries different sources.
      x-license-tier: Standard+
      x-capability: rag
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [session_id, message_id, verdict]
              properties:
                session_id: { type: string }
                message_id: { type: string }
                verdict: { type: string, enum: [accept, reject] }
                note: { type: string, description: optional free-text explanation }
      responses:
        "200":
          description: Signal recorded
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: string }
                  ingest_queued: { type: integer, description: number of cited sources queued for auto-ingest }

  /api/v1/upload/analyze:
    post:
      tags: [upload]
      summary: Inspect a file, return suggested chunking + enrichment parameters
      description: |
        Step 1 of the intelligent-upload flow. Reads the first few KB of the file,
        detects content type / language / structure, returns suggested chunking
        strategy + chunk size + enrichment defaults. No data is persisted at this stage.
      x-license-tier: Standard+
      x-capability: rag
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
      responses:
        "200":
          description: File inspected; suggestions returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  file_id: { type: string, description: ephemeral handle for the next step }
                  content_type: { type: string, example: "application/pdf" }
                  language: { type: string, example: "en" }
                  estimated_chunks: { type: integer }
                  suggested_strategy: { type: string, enum: [semantic, fixed, function-boundary, per-row, per-cluster, per-utterance, per-scene, per-window, metadata-only] }
                  suggested_chunk_size: { type: integer }
                  suggested_overlap: { type: integer }
                  suggested_enrichment: { type: object, additionalProperties: { type: boolean } }
                  extracted_keywords: { type: array, items: { type: string } }
                  topic_tags: { type: array, items: { type: string } }

  /api/v1/upload/preview-chunks:
    post:
      tags: [upload]
      summary: Preview the chunks a given strategy would produce
      description: Step 2 of the intelligent-upload flow. Returns the first 5–10 chunks without persisting.
      x-license-tier: Standard+
      x-capability: rag
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [file_id, strategy]
              properties:
                file_id: { type: string }
                strategy: { type: string }
                chunk_size: { type: integer }
                overlap: { type: integer }
      responses:
        "200":
          description: Preview returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  chunks: { type: array, items: { type: object, properties: { index: {type: integer}, text: {type: string}, tokens: {type: integer} } } }

  /api/v1/upload/commit:
    post:
      tags: [upload]
      summary: Commit the ingestion job with chosen parameters
      description: Step 3. Persists the file, runs chunking + embedding + enrichment per the chosen params, returns a job id for polling.
      x-license-tier: Standard+
      x-capability: rag
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [file_id, tenant_id, namespace_id]
              properties:
                file_id: { type: string }
                tenant_id: { type: string }
                namespace_id: { type: string }
                strategy: { type: string }
                chunk_size: { type: integer }
                overlap: { type: integer }
                enrichment: { type: object, additionalProperties: { type: boolean } }
                keywords: { type: array, items: { type: string } }
      responses:
        "202":
          description: Job accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: string }
                  estimated_duration_seconds: { type: integer }

  /api/v1/upload/jobs/{id}:
    get:
      tags: [upload]
      summary: Poll an ingest job
      x-license-tier: Standard+
      x-capability: rag
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: Job status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, enum: [queued, chunking, embedding, indexing, enriching, complete, failed] }
                  chunks_done: { type: integer }
                  chunks_total: { type: integer }
                  embedding_done: { type: integer }
                  error: { type: string }

  /api/v1/vector/namespaces/{tid}/{ns}/config:
    post:
      tags: [vector]
      summary: Set chunking strategy for a knowledge base
      description: Per-namespace chunking config (strategy + chunk_size + overlap). Uploads into this namespace skip the suggestion dialog (or pre-fill it).
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      parameters:
        - { name: tid, in: path, required: true, schema: { type: string } }
        - { name: ns,  in: path, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                strategy: { type: string }
                chunk_size: { type: integer }
                chunk_overlap: { type: integer }
      responses:
        "200": { description: Config updated }

  /api/v1/vector/namespaces/{tid}/{ns}/bulk-delete:
    post:
      tags: [vector]
      summary: Delete multiple documents from a knowledge base
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      parameters:
        - { name: tid, in: path, required: true, schema: { type: string } }
        - { name: ns,  in: path, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [doc_ids]
              properties:
                doc_ids: { type: array, items: { type: string } }
      responses:
        "200":
          description: Deletion result
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: { type: integer }
                  failed: { type: array, items: { type: object, properties: { id: { type: string }, reason: { type: string } } } }

  /api/v1/vector/namespaces/{tid}/{ns}/reindex:
    post:
      tags: [vector]
      summary: Re-embed every document in a knowledge base
      description: Used after changing the embedding model or the chunking strategy. Job runs in the background; poll the returned job_id.
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      parameters:
        - { name: tid, in: path, required: true, schema: { type: string } }
        - { name: ns,  in: path, required: true, schema: { type: string } }
      responses:
        "202":
          description: Reindex queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: string }
                  documents: { type: integer }

  /api/v1/vector/ingest-queue:
    get:
      tags: [vector]
      summary: View pending ingest jobs
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      responses:
        "200":
          description: Queue snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  pending: { type: array, items: { type: object, properties: { job_id: {type: string}, tenant_id: {type: string}, namespace_id: {type: string}, status: {type: string} } } }

  /api/v1/vector/stale-chunks:
    get:
      tags: [vector]
      summary: Identify chunks needing re-embed (model upgrade detector)
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      responses:
        "200":
          description: Stale set
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: { type: integer }
                  by_namespace: { type: object, additionalProperties: { type: integer } }

  /api/v1/vector/chunk-preview:
    post:
      tags: [vector]
      summary: Preview chunks for already-stored documents
      description: Useful for verifying the strategy chosen at ingest time; alternative entry point to the same renderer the upload preview uses.
      x-license-tier: Standard+
      x-capability: rag
      x-role: admin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tenant_id, namespace_id, doc_id]
              properties:
                tenant_id: { type: string }
                namespace_id: { type: string }
                doc_id: { type: string }
      responses:
        "200":
          description: Chunks returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  chunks: { type: array, items: { type: object } }

  /api/v1/router/classify:
    post:
      tags: [router]
      summary: Classify a query into an intent class
      description: |
        Returns the assigned class with a confidence score. Useful for debugging routing
        and surfacing classification in admin dashboards. Three-tier resolution: base
        classifier → tenant overlay → LLM fallback when confidence is below threshold.
      x-license-tier: Standard+
      x-capability: rag
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query: { type: string }
                tenant_id: { type: string, description: defaults to the requesting tenant }
      responses:
        "200":
          description: Classification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  class: { type: string, example: "ContractReview" }
                  confidence: { type: number, format: float, example: 0.91 }
                  source: { type: string, enum: [base, overlay, llm-fallback] }
                  fallback_chain: { type: array, items: { type: string } }

  /api/v1/router/custom-classes:
    post:
      tags: [router]
      summary: UPSERT a per-tenant custom intent class
      x-license-tier: Pro+
      x-capability: rag
      x-role: admin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tenant_id, class_name]
              properties:
                tenant_id: { type: string }
                class_name: { type: string, example: "ContractReview" }
                description: { type: string }
                examples: { type: array, items: { type: string }, description: labelled examples (a few hundred recommended for stable overlay training) }
      responses:
        "200": { description: Class registered (or updated) }
    get:
      tags: [router]
      summary: List a tenant's custom intent classes
      x-license-tier: Pro+
      x-capability: rag
      parameters:
        - { name: tenant_id, in: query, required: true, schema: { type: string } }
      responses:
        "200":
          description: Tenant's custom taxonomy
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    class_name: { type: string }
                    description: { type: string }
                    example_count: { type: integer }
                    overlay_accuracy: { type: number, format: float }
    delete:
      tags: [router]
      summary: Remove a custom class
      x-license-tier: Pro+
      x-capability: rag
      x-role: admin
      parameters:
        - { name: tenant_id, in: query, required: true, schema: { type: string } }
        - { name: class_name, in: query, required: true, schema: { type: string } }
      responses:
        "200": { description: Class removed }

  /api/v1/audit/events:
    get:
      tags: [audit]
      summary: List audit ledger events
      description: Hash-chained log of every AI-assisted decision and admin-side mutation. Tamper-evident; events cannot be retroactively edited.
      x-license-tier: Standard+
      x-capability: audit
      x-role: admin
      parameters:
        - { name: since, in: query, schema: { type: string, format: date-time } }
        - { name: tenant_id, in: query, schema: { type: string } }
        - { name: actor, in: query, schema: { type: string } }
      responses:
        "200":
          description: Event list
          content:
            application/json:
              schema:
                type: object
                properties:
                  events: { type: array, items: { type: object } }

  /api/v1/audit/events/{id}:
    get:
      tags: [audit]
      summary: Get a single audit event with hash-chain context
      x-license-tier: Standard+
      x-capability: audit
      x-role: admin
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: Event detail (includes prev_hash + this_hash)
          content:
            application/json:
              schema:
                type: object

  /api/v1/audit/export:
    post:
      tags: [audit]
      summary: Export a slice of the audit ledger as signed JSON
      description: For compliance handoff. Returns a signed JSON document customers hand to their auditor.
      x-license-tier: Pro+
      x-capability: audit
      x-role: admin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                since: { type: string, format: date-time }
                until: { type: string, format: date-time }
                tenant_id: { type: string }
      responses:
        "200":
          description: Signed JSON export
          content:
            application/json:
              schema:
                type: object
                properties:
                  events: { type: array, items: { type: object } }
                  signature: { type: string, description: Ed25519 signature over the events array }

  /api/v1/bundle/trust:
    post:
      tags: [bundle]
      summary: Trust a .nexus bundle signing key
      description: Adds a third-party Ed25519 public key to the trust list so .nexus bundles signed by that key can be imported.
      x-license-tier: Pro+
      x-capability: bundle
      x-role: admin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [key_id, public_key]
              properties:
                key_id: { type: string }
                public_key: { type: string, description: base64-encoded Ed25519 public key }
                label: { type: string }
      responses:
        "200": { description: Key trusted }

  /api/v1/bundle/trust/{key_id}:
    delete:
      tags: [bundle]
      summary: Revoke trust for a bundle signing key
      x-license-tier: Pro+
      x-capability: bundle
      x-role: admin
      parameters:
        - { name: key_id, in: path, required: true, schema: { type: string } }
      responses:
        "200": { description: Trust revoked }

  /api/v1/models/convert:
    post:
      tags: [models]
      summary: Convert a HuggingFace model to GGUF
      description: |
        Pulls a HuggingFace model into local storage and converts to GGUF format for
        Inferenced or llama.cpp consumption. Long-running; returns a job id for polling.
      x-license-tier: Pro+
      x-capability: training
      x-role: admin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hf_repo]
              properties:
                hf_repo: { type: string, example: "meta-llama/Llama-3.2-3B-Instruct" }
                quantization: { type: string, enum: [Q4_K_M, Q5_K_M, Q6_K, Q8_0, F16] }
                output_path: { type: string }
      responses:
        "202":
          description: Conversion job queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: string }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key (sk-…)
      description: |
        Per-tenant API key issued by the cluster admin. Cross-tenant attempts return 403.
        Endpoints flagged `x-role: admin` additionally require the admin role inside the
        requesting tenant.
