{
  "openapi": "3.0.3",
  "info": {
    "title": "kull.games / kullgames.com - Public Surface",
    "version": "0.1.0",
    "x-generated": "2026-07-13",
    "description": "Discoverable spec for the PUBLIC surface of kull.games and kullgames.com.\n\nHONESTY NOTE: most of this surface is NOT a REST API. It is a set of statically-hosted apps (Blazor WASM game suites, a Cosmos 3D root, the KullOS desktop hub, and a WASM 3D viewer) plus static HTML pages. There are only THREE genuinely query-driven server operations:\n  1. GET /showcase              - PHP -> NativeAOT -> OcRazor per-request SSR (section/theme/count + form round-trip)\n  2. GET /objectkit/api.php?op=list    - JSON manifest of ~1,263 procedural 3D models\n  3. GET /objectkit/api.php?op=render  - parameterized 3D render (png/gif/obj/stl/ply/glb)\n\nEverything else in `paths` is a plain GET page, listed for discovery only, tagged x-kind: static-page with an x-runtimeClass. Every path is x-auth: public (no key/token/login) unless marked otherwise. Nothing here requires authentication today.\n\nSources: tools/objectkit-api/api.php + Program.cs; OBJECTKIT_API.md; Research_Agent_1/092 (showcase architecture) + ShowcasePage.razor; Research_Agent_1/900 + 901 (deployment/application inventory).",
    "contact": { "name": "KullGames", "url": "https://kull.games" }
  },
  "servers": [
    { "url": "https://kull.games", "description": "Games, Cosmos root, /showcase, /objectkit, /gamelist" },
    { "url": "https://kullgames.com", "description": "KullOS desktop hub (160-app gallery)" }
  ],
  "tags": [
    { "name": "dynamic-api", "description": "Genuinely query-driven server operations (the only real 'API')." },
    { "name": "static-app", "description": "Client-WASM apps: boot in the browser, no server API." },
    { "name": "static-page", "description": "Static HTML index/doc pages." }
  ],
  "paths": {
    "/showcase": {
      "get": {
        "tags": ["dynamic-api"],
        "summary": "Server-side-rendered HTML showcase (per-request SSR)",
        "operationId": "getShowcase",
        "x-auth": "public",
        "x-kind": "dynamic-api",
        "x-runtimeClass": "ssr-php-nativeaot-ocrazor",
        "description": "Per-request server render. The PHP shim hands the raw query string to a ~1.18 MB NativeAOT C# binary (`~/bin/showcase`) that parses it with a hand-rolled query parser and renders ONE section to HTML with the OnlyCSharp OcRazor SSR pipeline (`OcComponentHost.Mount(...).ToHtml()`). No database, no session, ~1 ms render. The `forms` section round-trips extra form fields (femail/fpass/fnum/fdate/size/fmsg exist in the form but only fname/fcolor/ffruit/frange/faccept are echoed by the current binary).",
        "parameters": [
          {
            "name": "section", "in": "query", "required": false,
            "description": "Which section to render. Default `home`.",
            "schema": {
              "type": "string", "default": "home",
              "enum": ["home", "text", "lists", "tables", "forms", "media", "semantic", "cssbox", "layout", "effects", "js", "state"]
            }
          },
          {
            "name": "theme", "in": "query", "required": false,
            "description": "Colour theme. Default `light`.",
            "schema": { "type": "string", "default": "light", "enum": ["light", "dark"] }
          },
          {
            "name": "count", "in": "query", "required": false,
            "description": "Integer counter, rendered on the `state` section (SSR state lives entirely in the URL).",
            "schema": { "type": "integer", "default": 0 }
          },
          { "name": "fname",   "in": "query", "required": false, "description": "Forms round-trip: text name (echoed).",  "schema": { "type": "string" } },
          { "name": "fcolor",  "in": "query", "required": false, "description": "Forms round-trip: colour (echoed).",      "schema": { "type": "string" } },
          { "name": "ffruit",  "in": "query", "required": false, "description": "Forms round-trip: fruit select (echoed).","schema": { "type": "string", "enum": ["apple", "banana", "cherry"] } },
          { "name": "frange",  "in": "query", "required": false, "description": "Forms round-trip: range 0-100 (echoed).",  "schema": { "type": "integer", "minimum": 0, "maximum": 100 } },
          { "name": "faccept", "in": "query", "required": false, "description": "Forms round-trip: presence => accepted.",  "schema": { "type": "string", "enum": ["yes"] } }
        ],
        "responses": {
          "200": {
            "description": "Rendered HTML page (full document with nav/CSS/JS chrome).",
            "content": { "text/html": { "schema": { "type": "string" } } }
          },
          "500": { "description": "Renderer binary missing on the host." }
        }
      }
    },
    "/objectkit/api.php": {
      "get": {
        "tags": ["dynamic-api"],
        "summary": "ObjectKit 3D model API (list manifest + parameterized render)",
        "operationId": "objectkitApi",
        "x-auth": "public",
        "x-kind": "dynamic-api",
        "x-runtimeClass": "cli-single-file-linux-x64",
        "description": "Public, keyless API over the OnlyCSharp 1.9 ObjectKit procedural-model library. The PHP shim strictly whitelists every key/value, then `passthru`s to a self-contained linux-x64 .NET CLI (`~/bin/objectkit`). CORS: `Access-Control-Allow-Origin: *`. Two operations selected by `op`:\n  - `op=list` -> JSON manifest of every renderable model (~1,263) and its numeric params.\n  - `op=render` -> raw bytes of the chosen model in the chosen format.\nUnknown query keys are dropped, never forwarded. Renders are deterministic (same params => same bytes) and cached (max-age 86400 for render, 3600 for list). `p.<name>` overrides forward any numeric/boolean builder parameter listed in the manifest (e.g. `p.size=1.5&p.seed=7`); PHP mangles `.` to `_`, so `p_size` is accepted as an alias.",
        "parameters": [
          {
            "name": "op", "in": "query", "required": false,
            "description": "Operation. `list` returns the model manifest; `render` returns model bytes. Default `list`.",
            "schema": { "type": "string", "default": "list", "enum": ["list", "render"] }
          },
          {
            "name": "id", "in": "query", "required": false,
            "description": "Model id from the manifest (required when op=render). Pattern `[a-z0-9_-]{1,80}` e.g. `animalskit__cat`.",
            "schema": { "type": "string", "pattern": "^[a-z0-9_-]{1,80}$" },
            "example": "animalskit__cat"
          },
          {
            "name": "format", "in": "query", "required": false,
            "description": "Output format (op=render). `png`/`gif` are images; `obj`/`stl`/`ply`/`glb` are mesh downloads (Content-Disposition: attachment). Default `png`.",
            "schema": { "type": "string", "default": "png", "enum": ["png", "gif", "obj", "stl", "ply", "glb"] }
          },
          { "name": "w",      "in": "query", "required": false, "description": "Image width, px. Clamped 16-1024.",  "schema": { "type": "integer", "default": 512, "minimum": 16, "maximum": 1024 } },
          { "name": "h",      "in": "query", "required": false, "description": "Image height, px. Clamped 16-1024.", "schema": { "type": "integer", "default": 512, "minimum": 16, "maximum": 1024 } },
          { "name": "yaw",    "in": "query", "required": false, "description": "Orbit yaw, degrees (png/gif).",      "schema": { "type": "number", "default": 0 } },
          { "name": "pitch",  "in": "query", "required": false, "description": "Orbit pitch, degrees (png/gif).",     "schema": { "type": "number", "default": 0 } },
          { "name": "frames", "in": "query", "required": false, "description": "Turntable frame count (gif). Clamped 2-48.", "schema": { "type": "integer", "default": 24, "minimum": 2, "maximum": 48 } },
          { "name": "fps",    "in": "query", "required": false, "description": "Turntable frames/sec (gif). Clamped 1-30.",   "schema": { "type": "number", "default": 15, "minimum": 1, "maximum": 30 } },
          {
            "name": "p.{name}", "in": "query", "required": false,
            "description": "Builder-parameter override (op=render). `{name}` is any param from the model's manifest entry `[A-Za-z0-9]{1,40}`; value must be numeric or `true`/`false`. Repeatable. Example keys: `p.size`, `p.seed`. PHP alias: `p_{name}`.",
            "schema": { "type": "string" },
            "example": "p.size=1.5"
          }
        ],
        "responses": {
          "200": {
            "description": "op=list -> JSON manifest. op=render -> raw model bytes (image or mesh).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ModelManifest" }
              },
              "image/png": { "schema": { "type": "string", "format": "binary" } },
              "image/gif": { "schema": { "type": "string", "format": "binary" } },
              "model/gltf-binary": { "schema": { "type": "string", "format": "binary" } },
              "application/octet-stream": { "schema": { "type": "string", "format": "binary" } },
              "text/plain": { "schema": { "type": "string", "description": "obj mesh text" } }
            }
          },
          "204": { "description": "CORS preflight (OPTIONS)." },
          "400": { "description": "Bad id / bad param / unknown op (plain-text reason)." },
          "500": { "description": "Renderer offline or render failed (plain-text reason)." }
        }
      }
    },

    "/": {
      "get": {
        "tags": ["static-app"],
        "summary": "Cosmos - 3D solar-system (site root)",
        "operationId": "getCosmosRoot",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "description": "Blazor WASM 3D solar system. Owns the kull.games root `/`; never overwritten by the game-publish pipeline.",
        "responses": { "200": { "description": "WASM app shell (HTML + _framework).", "content": { "text/html": { "schema": { "type": "string" } } } } }
      }
    },
    "/gpu-version/": {
      "get": { "tags": ["static-app"], "summary": "Solar Sky Walk (game suite)", "operationId": "getGpuVersion",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm-aot",
        "description": "Flagship 3D real-sky walk. Blazor WASM (AOT). Immutable per-version archives at /gpu-version/<ver>/.",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/good-ocean/": {
      "get": { "tags": ["static-app"], "summary": "PHYSICA (game / real-physics island)", "operationId": "getGoodOcean",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/good-physics-proof/": {
      "get": { "tags": ["static-app"], "summary": "SolarWorld (game / physics proof)", "operationId": "getGoodPhysicsProof",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/good-sky-reflections/": {
      "get": { "tags": ["static-app"], "summary": "PhysicsOdyssey / SkyReflections (planar-mirror/SSR showcase)", "operationId": "getGoodSkyReflections",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/gfx-showcase/": {
      "get": { "tags": ["static-app"], "summary": "Graphics Showcase (21 CPU-rendered scenes)", "operationId": "getGfxShowcase",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "description": "Blazor WASM standalone suite; one live NOAA/api.weather.gov call in the weather scene.",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/weather-scales/": {
      "get": { "tags": ["static-app"], "summary": "Weather Scales (multi-scale weather sim)", "operationId": "getWeatherScales",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "description": "Blazor WASM + live api.weather.gov. SSR-able in principle but shipped as WASM today.",
        "responses": { "200": { "description": "WASM app shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/gamelist/": {
      "get": { "tags": ["static-page"], "summary": "Games gallery index", "operationId": "getGamelist",
        "x-auth": "public", "x-kind": "static-page", "x-runtimeClass": "static-html",
        "description": "The existing games gallery: cards, per-version chips, evidence links (/gamelist/evidence/<slug>/), thumbnails (/gamelist/shots/<slug>.png). Covers the 6 game suites only.",
        "responses": { "200": { "description": "Static HTML gallery.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/objectkit/": {
      "get": { "tags": ["static-app"], "summary": "ObjectKit 3D viewer (WASM inspector)", "operationId": "getObjectkitViewer",
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "client-wasm",
        "description": "Blazor WASM in-browser 3D viewer: canvas + model list + auto-generated sliders. Front-end for the objectkit/api.php model catalog. Also see /objectkit/docs.html.",
        "responses": { "200": { "description": "WASM viewer shell.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/objectkit/docs.html": {
      "get": { "tags": ["static-page"], "summary": "ObjectKit API docs page", "operationId": "getObjectkitDocs",
        "x-auth": "public", "x-kind": "static-page", "x-runtimeClass": "static-html",
        "description": "Human-readable docs for objectkit/api.php (examples given as bare request URLs, never curl dropper commands - host Imunify360 scanner quarantines those).",
        "responses": { "200": { "description": "Static HTML docs.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },

    "/desktop/": {
      "get": { "tags": ["static-app"], "summary": "KullOS desktop shell",
        "operationId": "getKullOsDesktop",
        "servers": [ { "url": "https://kullgames.com" } ],
        "x-auth": "public", "x-kind": "static-app", "x-runtimeClass": "static-html-ssr-ocrazor",
        "description": "The KullOS desktop shell on kullgames.com - a canvas OS hosting ~160 internal mini-apps. Rendered as static OcRazor HTML + vanilla JS/CSS (no C#/IL shipped). Individual apps are deep-linked; the authoritative app list is AppManifest.cs (KullApps.ReadySlugs), not a hand-maintained JSON. The exact per-app URL scheme is not asserted in this spec (enumerate from the manifest at generate time).",
        "responses": { "200": { "description": "Desktop shell HTML.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/gallery": {
      "get": { "tags": ["static-page"], "summary": "kullgames.com hub / 160-app gallery (site root)",
        "operationId": "getKullGamesHub",
        "servers": [ { "url": "https://kullgames.com" } ],
        "x-auth": "public", "x-kind": "static-page", "x-runtimeClass": "static-html-ssr-ocrazor",
        "description": "The kullgames.com landing hub: 160 apps across 15 categories, static SSR HTML. NOTE: the real path is the site root `/` on kullgames.com; documented here as `/gallery` only to avoid colliding with the kull.games Cosmos `/` in this single spec (OpenAPI keys paths by string, not by server). Not behind Cloudflare (No-IP DNS, direct-to-origin).",
        "responses": { "200": { "description": "Hub HTML.", "content": { "text/html": { "schema": { "type": "string" } } } } } }
    }
  },
  "components": {
    "schemas": {
      "ModelManifest": {
        "type": "object",
        "description": "op=list response: every renderable ObjectKit model and its numeric params.",
        "required": ["count", "models"],
        "properties": {
          "count": { "type": "integer", "example": 1263 },
          "models": { "type": "array", "items": { "$ref": "#/components/schemas/Model" } }
        }
      },
      "Model": {
        "type": "object",
        "required": ["id", "kit", "name", "params"],
        "properties": {
          "id": { "type": "string", "example": "animalskit__cat", "description": "Render id (`kit__method`, slugged)." },
          "kit": { "type": "string", "example": "Animals" },
          "name": { "type": "string", "example": "Cat" },
          "params": { "type": "array", "items": { "$ref": "#/components/schemas/ModelParam" } }
        }
      },
      "ModelParam": {
        "type": "object",
        "required": ["name", "type", "default"],
        "properties": {
          "name": { "type": "string", "example": "size" },
          "type": { "type": "string", "enum": ["float", "double", "int", "uint", "bool"] },
          "default": { "description": "Builder default (number or boolean).", "example": 1 }
        }
      }
    }
  },
  "x-auth-legend": {
    "public": "No key, token, or login. Every documented path is public today.",
    "protected": "Would require a key/token/login. None currently exist on this surface."
  },
  "x-static-notes": [
    "central01.kull.games / central02.kull.games / central03.kull.games are three separately-hosted KullOS desktop variants (central03 is the active one; 01/02 archived). Not on the kull.games origin box; direct HTTPS, unproxied. Listed here for discovery, not as paths of these servers.",
    "kull.games is also the DNS root of an unrelated Minecraft-hosting business (mcservers/vote/hytale/... SRV records). Not part of this app surface."
  ]
}
