{
  "openapi": "3.1.0",
  "info": {
    "title": "Touchstone API",
    "version": "0.1.0",
    "description": "Tamper-evident, externally-anchored audit log for AI agents. Agents append Ed25519-signed events to a recorder; anyone can verify a disclosure without trusting Touchstone. There is also a remote MCP server at /mcp. See https://touchstone.cv/developers.",
    "contact": { "url": "https://touchstone.cv/developers" },
    "license": { "name": "Proprietary" }
  },
  "servers": [{ "url": "https://touchstone.cv" }],
  "externalDocs": { "description": "Developer guide", "url": "https://touchstone.cv/developers" },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tsk_",
        "description": "A Touchstone API key (tsk_...), minted on a recorder."
      },
      "colonyToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your Colony token (POST https://thecolony.cc/api/v1/auth/token { api_key }). Used on the agent API to onboard as your Colony identity, no browser."
      }
    },
    "schemas": {
      "AppendRequest": {
        "type": "object",
        "required": ["event_type", "payload_hash", "actor_sig"],
        "properties": {
          "event_type": { "type": "string", "example": "tool_call" },
          "payload_hash": { "type": "string", "description": "sha256 hex of JCS-canonical payload", "pattern": "^[0-9a-f]{64}$" },
          "actor_sig": { "type": "string", "description": "base64 Ed25519 detached signature over the canonical signed_content" },
          "counterparty_sub": { "type": "string", "nullable": true },
          "client_ts": { "type": "string", "format": "date-time", "nullable": true },
          "body_enc": { "type": "string", "nullable": true, "description": "optional stored payload body" }
        }
      },
      "AppendResponse": {
        "type": "object",
        "properties": {
          "seq": { "type": "integer" },
          "prev_hash": { "type": "string" },
          "entry_hash": { "type": "string" },
          "server_ts": { "type": "string", "format": "date-time" }
        }
      },
      "Error": { "type": "object", "properties": { "error": { "type": "string" } } }
    }
  },
  "paths": {
    "/auth/colony/agent": {
      "post": {
        "operationId": "agentLogin",
        "summary": "Agent-native login: exchange your Colony token for a Touchstone session (RFC 8693, no browser)",
        "security": [{ "colonyToken": [] }],
        "responses": {
          "200": { "description": "Session established; returns the resolved Colony identity" },
          "401": { "description": "Missing Colony token" },
          "502": { "description": "Token exchange failed" }
        }
      }
    },
    "/agent/me": {
      "get": {
        "operationId": "agentMe",
        "summary": "The Colony identity of the presented agent token",
        "security": [{ "colonyToken": [] }],
        "responses": { "200": { "description": "Identity" }, "401": { "description": "Missing/invalid Colony token" } }
      }
    },
    "/agent/recorders": {
      "get": {
        "operationId": "agentListRecorders",
        "summary": "List recorders you operate or are the subject of",
        "security": [{ "colonyToken": [] }],
        "responses": { "200": { "description": "Recorders" } }
      },
      "post": {
        "operationId": "agentCreateRecorder",
        "summary": "Self-provision a recorder about yourself (subject forced to your Colony identity; debug tier)",
        "security": [{ "colonyToken": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": {
          "type": "object",
          "required": ["name", "signing_pubkey", "pop_signature"],
          "properties": {
            "name": { "type": "string" },
            "signing_pubkey": { "type": "string", "description": "your base64 Ed25519 public key" },
            "pop_signature": { "type": "string", "description": "base64 Ed25519 over touchstone-pop:v1:<your-colony-sub>:<pubkey>" },
            "retention_days": { "type": "integer", "default": 90 }
          }
        } } } },
        "responses": {
          "201": { "description": "Created (public_id, trust_tier, self_operated)" },
          "422": { "description": "pop_signature did not verify" }
        }
      }
    },
    "/agent/recorders/{publicId}/keys": {
      "post": {
        "operationId": "agentMintKey",
        "summary": "Mint a tsk_ API key on a recorder you own",
        "security": [{ "colonyToken": [] }],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "201": { "description": "api_key (shown once)" }, "403": { "description": "not your recorder" } }
      }
    },
    "/agent/recorders/{publicId}/disclosures": {
      "post": {
        "operationId": "agentCreateDisclosure",
        "summary": "Create a verifiable disclosure on a recorder you own",
        "security": [{ "colonyToken": [] }],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "201": { "description": "share_token + url" }, "403": { "description": "not your recorder" } }
      }
    },
    "/agent/recorders/{publicId}/rotate-key": {
      "post": {
        "operationId": "agentRotateKey",
        "summary": "Rotate the subject signing key (signed by the current key; carries the new key's proof-of-possession)",
        "security": [{ "colonyToken": [] }],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": {
          "type": "object",
          "required": ["new_signing_pubkey", "new_key_pop", "rotation_sig"],
          "properties": {
            "new_signing_pubkey": { "type": "string" },
            "new_key_pop": { "type": "string", "description": "new key signs touchstone-rotate:v1:<recorder>:<new_pubkey>" },
            "rotation_sig": { "type": "string", "description": "current key signs the key_rotation entry's canonical content" }
          }
        } } } },
        "responses": { "201": { "description": "rotation_seq + effective_from_seq" }, "422": { "description": "signature did not verify" } }
      }
    },
    "/agent/recorders/{publicId}/keys/list": {
      "get": {
        "operationId": "agentListKeys",
        "summary": "List API keys on a recorder you own (prefixes only) — GET /agent/recorders/{publicId}/keys",
        "security": [{ "colonyToken": [] }],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "keys" } }
      }
    },
    "/agent/keys/{keyId}/revoke": {
      "post": {
        "operationId": "agentRevokeKey",
        "summary": "Revoke an API key on a recorder you own",
        "security": [{ "colonyToken": [] }],
        "parameters": [{ "name": "keyId", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "revoked" }, "403": { "description": "not your key" }, "409": { "description": "already revoked" } }
      }
    },
    "/checkpoint/{id}/cosign": {
      "post": {
        "operationId": "checkpointCosign",
        "summary": "Independent witness co-signs a checkpoint (split-view resistance)",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": {
          "type": "object",
          "required": ["witness_sub", "witness_pubkey", "witness_sig"],
          "properties": {
            "witness_sub": { "type": "string" },
            "witness_pubkey": { "type": "string" },
            "witness_sig": { "type": "string", "description": "signs touchstone-cp-witness:v1:<recorder>:<cpId>:<root>:<head>" }
          }
        } } } },
        "responses": { "201": { "description": "co-signed (grade verified|claimed)" }, "409": { "description": "already co-signed" } }
      }
    },
    "/.well-known/touchstone/checkpoints/{publicId}": {
      "get": {
        "operationId": "checkpointFeed",
        "summary": "Public append-only feed of a recorder's full checkpoint chain (mirror it to detect split views)",
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "touchstone-checkpoint-feed/1" } }
      }
    },
    "/api/v1/recorders/{publicId}/entries": {
      "post": {
        "operationId": "appendEntry",
        "summary": "Append a signed event to a recorder's hash chain",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AppendRequest" } } } },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AppendResponse" } } } },
          "401": { "description": "Missing/invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Key not valid for this recorder or lacks scope", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "Signature did not verify", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/cosign": {
      "post": {
        "operationId": "cosign",
        "summary": "Counterparty co-signs a commitment (non-repudiation)",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "required": ["recorder_id", "seq", "counterparty_pubkey", "counterparty_sig"],
            "properties": {
              "recorder_id": { "type": "string" },
              "seq": { "type": "integer" },
              "counterparty_pubkey": { "type": "string", "description": "base64 Ed25519 public key" },
              "counterparty_sig": { "type": "string", "description": "base64 Ed25519 signature over the same signed_content" }
            }
          } } }
        },
        "responses": {
          "200": { "description": "Co-signed", "content": { "application/json": { "schema": { "type": "object", "properties": { "grade": { "type": "string", "enum": ["verified", "claimed"] } } } } } },
          "409": { "description": "Already co-signed" }
        }
      }
    },
    "/d/{token}": {
      "get": {
        "operationId": "getDisclosure",
        "summary": "Fetch a disclosure bundle (touchstone-disclosure/1) for independent verification",
        "parameters": [{ "name": "token", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Disclosure bundle", "content": { "application/json": { "schema": { "type": "object" } } } },
          "404": { "description": "Not found or expired" }
        }
      }
    },
    "/.well-known/touchstone/pubkeys/{sub}": {
      "get": {
        "operationId": "getPubkeys",
        "summary": "Public keys bound to a subject (agent)",
        "parameters": [{ "name": "sub", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Bindings", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "summary": "Remote MCP server (JSON-RPC 2.0, Streamable HTTP). See /.well-known/mcp.json.",
        "security": [{ "apiKey": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": { "200": { "description": "JSON-RPC response" }, "401": { "description": "Missing/invalid API key" } }
      }
    }
  }
}
