{
  "name": "tuna",
  "version": 1,
  "philosophy": "The tree calculus is the API. Compile an s-expression to a program hash, POST a run, and read the journal; every firing is a step and every run is replayable from its journal.",
  "base_url": "https://club.pricklypear.club",
  "auth": {
    "scheme": "Bearer",
    "realm": "tuna identities table (bearer token, sha256 at rest)",
    "how_to_get_credentials": "A human supplies the bearer token once (boot prints root's, or an admin mints one at /identities); store it mode 600.  There are no credentials in this document.",
    "session_cookie": "Browser clients may use cookie tuna_session (HttpOnly, SameSite=Lax) after POST /login; agents use Bearer.",
    "login_url": "/login",
    "notes": [
      "Bearer is the agent tier; sessions are the browser tier.",
      "No OAuth/JWT in v1.", "Never log or commit the token."
    ]
  },
  "endpoints": {
    "programs_post": {
      "method": "POST",
      "path": "/api/programs",
      "auth": "required",
      "request": { "source": "(\\f. \\x. f x)" },
      "response_ok": { "hash": "sha256-hex", "ternary": "…" }
    },
    "program_get": {
      "method": "GET",
      "path": "/api/programs/<hash>",
      "auth": "required"
    },
    "runs_post": {
      "method": "POST",
      "path": "/api/runs",
      "auth": "required",
      "request": {
        "program": "<hash>",
        "inputs": [],
        "grants": [],
        "fuel": 10000,
        "size_cap": 10000
      }
    },
    "runs_get": {
      "method": "GET",
      "path": "/api/runs/<id>",
      "auth": "required"
    },
    "run_trace": {
      "method": "GET",
      "path": "/api/runs/<id>/trace",
      "auth": "required",
      "returns": "the firing trace (steps, prims, step counts)"
    },
    "run_verify": {
      "method": "POST",
      "path": "/api/runs/<id>/verify",
      "auth": "required",
      "returns": "replay verdict: verified | failed"
    },
    "journal": {
      "method": "GET",
      "path": "/api/journals/<run_id>",
      "auth": "required",
      "returns": "the run's prim-call journal rows (the replay source of truth)"
    },
    "grants_post": {
      "method": "POST",
      "path": "/api/grants",
      "auth": "required",
      "request": {
        "prim": "*",
        "args_attenuation": {},
        "path_prefix": "route/"
      }
    },
    "identities_post": {
      "method": "POST",
      "path": "/api/identities",
      "auth": "required (admin)",
      "request": {
        "name": "agent-label",
        "password": "(optional) initial browser password"
      },
      "response_ok": {
        "token": "returned ONCE, only sha256 stored",
        "is_admin": false
      }
    },
    "route_put": {
      "method": "POST",
      "path": "/api/route/put",
      "auth": "required (covering grant)",
      "request": {
        "path": "hello",
        "record": {
          "method": "GET",
          "template": "<hash>",
          "content_type": "text/plain"
        }
      }
    },
    "health": { "method": "GET", "path": "/health", "auth": "none" },
    "discovery_json": {
      "method": "GET",
      "path": "/.well-known/agent.json",
      "auth": "none"
    },
    "discovery_text": {
      "method": "GET",
      "path": "/agent.txt",
      "auth": "none"
    }
  },
  "bootstrap": {
    "first_calls": [
      "curl -sS -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"source\":\"(\\\\f. \\\\x. f x)\"}' $BASE/api/programs",
      "curl -sS -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"program\":\"<hash>\",\"inputs\":[]}' $BASE/api/runs",
      "curl -sS -H 'Authorization: Bearer $TOKEN' $BASE/api/runs/<id>/trace",
      "curl -sS $BASE/code"
    ],
    "rules": [
      "Authenticate with Authorization: Bearer <identity token> — the agent tier.  Browser clients may instead carry the tuna_session cookie (minted at /login); agents never need it.",
      "POST /api/programs compiles an s-expression to a content-addressed program hash.  POST /api/runs executes one with inputs and grants.",
      "Grants are arguments, not ambient: a prim call must be covered by a live grant row, checked by the host at the boundary.  Denial is a journaled error result, never an exception.",
      "A step is one triage-rule firing; the two wrapper applications are application, not steps.  Step counts are an invariant of the calculus, so they are stable across engines.",
      "Replay determinism: GET /api/runs/:id/trace and POST /api/runs/:id/verify re-execute program+inputs with prim calls answered sequentially from the run's journal.  Replay never touches the live host.",
      "Fuel and size caps are ordinary results (fuel_exhausted / size_exhausted), never timeouts; a wall-clock cap only finalizes a run as deadline_exceeded at the operator boundary.",
      "Publishing endpoints is a store write: POST /api/route/put writes a route/<site-path> record (method, template/program hash, content_type, grant_prefix) served data-first.  Do not hardcode endpoints.",
      "Discovery: /code is the read-only program+run browser; /.well-known/agent.json is this contract; /agent.txt is prose."
    ]
  },
  "examples": {
    "curl": "curl -sS -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' \\\n  -d '{\"source\":\"(\\\\f. \\\\x. f x)\"}' https://club.pricklypear.club/api/programs"
  },
  "related": {
    "welcome": "/welcome",
    "source_browser": "/code",
    "source_tarball": "/src.tgz",
    "book": "tuna.borg + borg/*.borg in the source tarball",
    "login": "/login"
  }
}