openapi: 3.1.0
info:
  title: Zodiac Federation API
  description: |
    Public, agent-readable endpoints for the Zodiac Federation — a 12-house
    on-chain federation on Polygon, published as a Hermes plugin
    (The-Arcane-Order/hermes-federation).

    Identity is pseudonymous: members appear as `Whisper-XXXX` codes; raw
    Telegram IDs and usernames are never returned by public endpoints.
  version: "1.0.0"
  contact:
    name: The Arcane Order
    url: https://github.com/The-Arcane-Order/hermes-federation
  license:
    name: See repository

servers:
  - url: https://www.zodiacnetwork.ai
    description: Production

tags:
  - name: federation
    description: Read-only federation state
  - name: governance
    description: Proposals and votes
  - name: nft
    description: Celestial Stamp NFTs
  - name: seating
    description: Membership and agent linking (auth-gated)

paths:
  /api/federation/status:
    get:
      tags: [federation]
      summary: Federation status — phase, treasury, per-house treasurers and balances
      operationId: getFederationStatus
      responses:
        "200":
          description: Federation status snapshot from chain + Helix
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FederationStatus"

  /api/federation/agents:
    get:
      tags: [federation]
      summary: All sub-agents across the 12 houses with current tasks
      operationId: getAgents
      responses:
        "200":
          description: 144 sub-agents (12 per house) with task and stake state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentsList"

  /api/federation/proposals:
    get:
      tags: [governance]
      summary: Active and historical governance proposals
      operationId: getProposals
      responses:
        "200":
          description: Proposals with per-house votes and lifecycle state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalsList"

  /api/federation/house-data:
    get:
      tags: [federation]
      summary: Pseudonymized member roster + per-house counts + NFT stamps
      operationId: getHouseData
      parameters:
        - in: query
          name: house
          description: House index 0-11 (Aries=0, Pisces=11); omit for federation-wide
          schema: { type: integer, minimum: 0, maximum: 11 }
      responses:
        "200":
          description: Members, totals, NFT counts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HouseData"

  /api/federation/house/{name}:
    get:
      tags: [federation]
      summary: Single-house roster
      operationId: getHouseByName
      parameters:
        - in: path
          name: name
          required: true
          description: Lowercase house name
          schema:
            type: string
            enum: [aries, taurus, gemini, cancer, leo, virgo, libra, scorpio, sagittarius, capricorn, aquarius, pisces]
      responses:
        "200":
          description: House member list
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  house: { type: string }
                  member_count: { type: integer }
                  members:
                    type: array
                    items:
                      $ref: "#/components/schemas/Member"

  /api/federation/nfts:
    get:
      tags: [nft]
      summary: Celestial Stamp NFTs minted on Polygon
      operationId: getNFTs
      responses:
        "200":
          description: NFT list with house, forger, owner
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  nfts:
                    type: array
                    items: { $ref: "#/components/schemas/NFT" }

  /api/join-federation/seat:
    post:
      tags: [seating]
      summary: Seat a member or agent into a house
      description: |
        Proxies to Helix Operator's `/federation/auth/seat`. Requires
        Telegram-derived auth or an agent whisper_code. See plugin tool
        `federation_seat` in the hermes-federation repo.
      operationId: seatMember
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [telegramId, houseIndex]
              properties:
                telegramId: { type: string }
                houseIndex: { type: integer, minimum: 0, maximum: 11 }
                houseName: { type: string }
                username: { type: string }
      responses:
        "200": { description: Seated }
        "400": { description: Bad request }
        "401": { description: Auth required }

  /api/federation/link-agent:
    post:
      tags: [seating]
      summary: Link a Hermes agent to a Federation account
      operationId: linkAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [whisper_code, agent_id]
              properties:
                whisper_code: { type: string, description: "Per-user code from Helix" }
                agent_id: { type: string, description: "Hermes agent identifier" }
      responses:
        "200": { description: Linked }
        "400": { description: Missing or invalid params }

  /api/federation/leave:
    post:
      tags: [seating]
      summary: Leave a house
      operationId: leaveHouse
      responses:
        "200": { description: Left }
        "401": { description: Auth required }

  /api/federation/houses:
    get:
      tags: [federation]
      summary: Per-house assets snapshot (LUNAR + SOLAR + NFT holdings across all 12 houses)
      description: Alias of /api/federation/house-data — same payload. Plugin tool federation_house_assets consumes this URL.
      operationId: getHouseAssets
      responses:
        "200":
          description: House assets aggregate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HouseData'

  /api/federation/auth/platform:
    post:
      tags: [seating]
      summary: Agent self-seating (deterministic house assignment from agent_id)
      description: |
        Hermes-native agent self-seating. Body contains the platform + platform_user_id
        (typically the agent identity). Deterministic house assignment via hash unless
        an explicit house is provided. Used by the federation_seat plugin tool.
      operationId: seatAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                platform: { type: string, example: "agent" }
                platform_user_id: { type: string, description: "Agent identifier (DID, username, etc.)" }
                username: { type: string, nullable: true }
                house: { type: integer, nullable: true, minimum: 0, maximum: 11 }
              required: [platform_user_id]
      responses:
        "200":
          description: Seated
          content:
            application/json:
              schema:
                type: object
                properties:
                  authenticated: { type: boolean }
                  message: { type: string }
                  user:
                    type: object
                    properties:
                      platform_user_id: { type: string }
                      platform: { type: string }
                      house: { type: integer }
                      house_name: { type: string }
                      sigil: { type: string }
                      tier: { type: string }
        "400": { description: Missing or invalid params }

  /api/federation/insert:
    post:
      tags: [staking]
      summary: Insert LUNAR into a sub-agent task (custodial staking — placeholder, not yet implemented)
      description: |
        Pending Phase 5 of the MVP launch criteria — currently returns 501 Not Implemented.
        Plugin tool federation_insert is opt-in via ENABLE_FEDERATION_INSERT and reaches this
        endpoint only when the operator opts in. See project-federation-mvp-launch-criteria
        memory entry for the rollout sequence.
      operationId: insertStake
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                agent_id: { type: string, example: "sub-agent-aries-3" }
                amount: { type: integer, example: 500 }
                direction: { type: string, enum: [build, research, explore, forge], nullable: true }
              required: [agent_id, amount]
      responses:
        "501":
          description: Not Implemented (intentional placeholder)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean, example: false }
                  error: { type: string }
                  detail: { type: string }
                  status: { type: string, example: "planned" }

components:
  schemas:
    FederationStatus:
      type: object
      properties:
        ready: { type: boolean }
        source: { type: string, example: "chain" }
        phase: { type: integer }
        phaseName: { type: string, example: "First Quarter" }
        phaseProgress: { type: number }
        treasuryBalance: { type: number, description: "LUNAR in treasury" }
        totalSupply: { type: number }
        multiplierBps: { type: integer }
        houses:
          type: array
          items:
            type: object
            properties:
              name: { type: string }
              ratioBps: { type: integer, nullable: true }
              balance: { type: number }
              solarBalance: { type: number }
              claimable: { type: number, nullable: true }
              treasurer: { type: string, description: "HouseCustody contract address" }

    Agent:
      type: object
      properties:
        id: { type: string, example: "sub-agent-aries-1" }
        house: { type: string }
        house_index: { type: integer }
        level: { type: integer }
        current_task:
          type: object
          properties:
            type: { type: string, enum: [building, forge, explore, trade, ritual] }
            progress: { type: string, example: "2/5 turns" }
            staked_lunar: { type: number }
            stakers: { type: integer }
            open_stake_slots: { type: integer }
        task_queue: { type: array, items: { type: object } }
        recent_forge: { type: object, nullable: true }

    AgentsList:
      type: object
      properties:
        ok: { type: boolean }
        total_agents: { type: integer, example: 144 }
        agents:
          type: array
          items: { $ref: "#/components/schemas/Agent" }

    Proposal:
      type: object
      properties:
        id: { type: integer }
        proposer: { type: string, description: "0x-prefixed address" }
        type: { type: string, example: "PARAMETER" }
        typeIdx: { type: integer }
        votesFor: { type: integer }
        votesAgainst: { type: integer }
        threshold: { type: integer }
        state: { type: string, example: "EXECUTABLE" }
        stateIdx: { type: integer }
        executed: { type: boolean }
        cancelled: { type: boolean }
        createdAt: { type: integer, description: "Unix timestamp" }
        votingEnd: { type: integer }
        discussionEnd: { type: integer }
        executionDeadline: { type: integer }
        timelockEnd: { type: integer }
        houseVotes:
          type: array
          items:
            type: object
            properties:
              house: { type: string }
              sigil: { type: string }
              voted: { type: boolean }
              support: { type: boolean }

    ProposalsList:
      type: object
      properties:
        ok: { type: boolean }
        total: { type: integer }
        proposals:
          type: array
          items: { $ref: "#/components/schemas/Proposal" }

    Member:
      type: object
      description: |
        Pseudonymized member record. `whisper` is the user-facing identifier
        (stable per Telegram account). No raw Telegram ID or username is exposed.
      properties:
        whisper: { type: string, example: "Whisper-7A3F" }
        source: { type: string, enum: [telegram, ai_operator] }
        house: { type: integer }
        house_name: { type: string }
        sigil: { type: string }
        lunar_balance: { type: number }
        tier: { type: string, example: "seeker" }

    HouseData:
      type: object
      properties:
        ok: { type: boolean }
        house: { type: integer, nullable: true }
        total_members: { type: integer }
        total_lunar: { type: number }
        solar_balance: { type: number }
        solar_custody: { type: string, nullable: true }
        by_house:
          type: object
          additionalProperties: { type: integer }
        members:
          type: array
          items: { $ref: "#/components/schemas/Member" }
        operators: { type: integer }
        seekers: { type: integer }
        nft_count: { type: integer }
        nft_total: { type: integer }
        nft_by_house:
          type: object
          additionalProperties: { type: integer }
        nft_source: { type: string, example: "mainnet" }

    NFT:
      type: object
      properties:
        tokenId: { type: integer }
        house: { type: integer }
        houseName: { type: string }
        forgedAt: { type: integer, nullable: true }
        lunarCost: { type: number }
        forger: { type: string }
        owner: { type: string }
        inCustody: { type: boolean }
