openapi: 3.1.0
info:
  title: hosting4agents control plane
  summary: Agent-facing primitives for provisioning sandboxed workloads.
  version: "0.1.0"
servers:
  - url: https://h4a.site
security:
  - bearerAuth: []
paths:
  /api/provision:
    post:
      operationId: Provision
      summary: Create or retrieve a workload by name (idempotent).
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ProvisionRequest" }
      responses:
        "200":
          description: Workload provisioned (or already existed).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ProvisionResponse" }
        default:
          description: Error.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
  /api/destroy:
    post:
      operationId: Destroy
      summary: Destroy a workload by name (idempotent).
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/DestroyRequest" }
      responses:
        "200":
          description: Destroyed, or already gone.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DestroyResponse" }
        default:
          description: Error.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
  /api/expose:
    post:
      operationId: Expose
      summary: Attach a reachable URL to a running workload.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ExposeRequest" }
      responses:
        "200":
          description: Exposed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ExposeResponse" }
        default:
          description: Error.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
  /api/deploy:
    post:
      operationId: Deploy
      summary: Deploy a public git repo to a live URL. Tier is auto-detected.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/DeployRequest" }
      responses:
        "200":
          description: Deployed (or build failed — see BuildLog).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DeployResponse" }
        default:
          description: Error.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
  /api/info:
    post:
      operationId: Info
      summary: Return the current state of a workload.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/InfoRequest" }
      responses:
        "200":
          description: Workload info.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/InfoResponse" }
        default:
          description: Error.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    SizeTier:
      type: string
      enum: [nano, small, medium, large, xlarge]
    WorkloadStatus:
      type: string
      enum: [provisioning, running, destroying, destroyed, failed]
    ProvisionRequest:
      type: object
      required: [name]
      properties:
        name: { type: string, description: "DNS-safe workload name ([a-z0-9][a-z0-9-]{0,62})" }
        tenant: { type: string, default: "default" }
        size:
          $ref: "#/components/schemas/SizeTier"
    ProvisionResponse:
      type: object
      properties:
        name: { type: string }
        tenant: { type: string }
        ipv4: { type: string }
        ssh_command: { type: string }
        status:
          $ref: "#/components/schemas/WorkloadStatus"
    DestroyRequest:
      type: object
      required: [name]
      properties:
        name: { type: string }
        tenant: { type: string, default: "default" }
        force: { type: boolean, default: false }
    DestroyResponse:
      type: object
      properties:
        name: { type: string }
        tenant: { type: string }
        soft_deleted_at: { type: string, format: date-time }
    HealthCheck:
      type: object
      properties:
        path: { type: string, default: "/", description: "HTTP path probed on port 8080 after boot" }
        expect_status: { type: integer, default: 0, description: "Exact HTTP status required; 0 accepts 2xx/3xx/4xx" }
        timeout_seconds: { type: integer, default: 0, description: "Probe budget in seconds; 0 = server default (~480)" }
    DeployRequest:
      type: object
      required: [name, repo]
      properties:
        name: { type: string, description: "DNS-safe workload name" }
        tenant: { type: string, default: "default" }
        repo: { type: string, description: "Public HTTPS git URL" }
        ref: { type: string, default: "HEAD" }
        strategy: { type: string, enum: [blue-green, in-place], default: "blue-green" }
        health_check:
          $ref: "#/components/schemas/HealthCheck"
        size:
          $ref: "#/components/schemas/SizeTier"
    DeployResponse:
      type: object
      properties:
        name: { type: string }
        tenant: { type: string }
        tier: { type: string, enum: [static, dynamic] }
        reason: { type: string, description: "Human-readable tier-decision rationale" }
        url: { type: string }
        deploy_id: { type: string }
        status: { type: string }
        strategy: { type: string, enum: [blue-green, in-place] }
        previous_deploy_id: { type: string, description: "Deploy retired as part of this blue-green swap" }
        build_log: { type: string }
    ExposeRequest:
      type: object
      required: [name]
      properties:
        name: { type: string }
        tenant: { type: string, default: "default" }
        mode: { type: string, enum: [public, private], default: "public" }
        port: { type: integer }
    ExposeResponse:
      type: object
      properties:
        name: { type: string }
        tenant: { type: string }
        mode: { type: string }
        url: { type: string, format: uri }
    InfoRequest:
      type: object
      required: [name]
      properties:
        name: { type: string }
        tenant: { type: string, default: "default" }
    InfoResponse:
      type: object
      properties:
        name: { type: string }
        tenant: { type: string }
        ipv4: { type: string }
        status:
          $ref: "#/components/schemas/WorkloadStatus"
        created_at: { type: string, format: date-time }
    Error:
      type: object
      properties:
        message: { type: string }
        docs_url: { type: string, format: uri }
