AI Hub documentation

Connect your application to private AI.

AI Hub is a self-hosted control plane for local models. Your application sends authenticated requests to the Hub; an outbound-only worker runs the model and returns the result.

OpenAI-compatibleSchema validatedOutbound-only workers
01 · Pick the right API

Choose an integration path

Both paths use the same application token, worker network, routing, and execution history. Choose based on the contract your application needs.

Conversational

OpenAI-compatible API

Use an existing OpenAI SDK for chat, generation, and background responses.

Executor
chat_completion
Best for
Assistants, drafting, summarization
View OpenAI examples →
02 · First request

Quickstart

Create an account and application in the console, then copy the application token when it is shown. AI Hub stores only its digest, so the plaintext token cannot be recovered later.

1
Create an application

This gives your client an isolated namespace and application token.

2
Publish a task or model profile

Definitions are versioned and immutable so requests remain reproducible.

3
Connect a worker

Issue a worker credential from Hosting and run the worker beside your model.

4
Send a request

Use the OpenAI-compatible API or submit a durable native job.

Start a general model workershell
AI_HUB_URL=https://your-hub.example \
AI_HUB_WORKER_TOKEN=aih_... \
AI_MODEL_URL=http://127.0.0.1:8080/v1 \
AI_MODEL=local-model \
bundle exec ruby worker/worker.rb
03 · MCP

Configure AI Hub through your agent

Connect to the remote MCP server with scoped browser authorization. OAuth PKCE identifies the AI Hub user, agent, organization, and approved management capabilities. Application keys remain separate runtime credentials and are not needed in MCP configuration.

Codex CLI

Add the management endpoint, then sign inshell
codex mcp add ai-hub \
  --url https://aihub.mohsal.dev/mcp \
  --oauth-resource https://aihub.mohsal.dev/mcp

codex mcp login ai-hub --oauth-client-registration dcr

Claude Code CLI

Add the endpoint and trigger browser authorizationshell
claude mcp add --transport http --scope user \
  ai-hub https://aihub.mohsal.dev/mcp

claude mcp login ai-hub
The login command triggers OAuth.The client dynamically registers itself, opens AI Hub sign-in and consent, and completes authorization code + PKCE. Adding the server alone may not open the browser.
Generic OAuth-only configurationjson
{
  "mcpServers": {
    "ai-hub": {
      "url": "https://your-hub.example/mcp"
    }
  }
}
Management and runtime stay separate.OAuth authorizes the agent to create and manage applications by slug. An application key is issued only when a runtime needs to call the native or OpenAI-compatible API.

Available tools

APPScreate / update / revoke applicationOAuth-managed lifecycle
KEYrotate_application_keyOne-time runtime credential
READlist_task_definitionsApplication tasks by slug
WRITEpublish_typed_decisionPublish immutable Laya questions
READlist_workersWorker health and capabilities
RUNsubmit_job / get_jobExecute and retrieve durable work
AUDITlist_audit_eventsAgent-aware immutable history
The OAuth client is the agent identity.Audit events snapshot the human, registered agent client, organization, application, action, and outcome. Tokens, keys, prompts, questions, job inputs, and outputs are never copied into the audit trail.

Organization owners can inspect agents, scopes, grant expiry, denials, and immutable activity—or restrict an agent to selected applications and revoke its grant immediately—from Agents & audit. Each application's Settings screen also provides application-level agent access controls.

See the complete MCP authorization and tool reference.

04 · Typed decisions

Make calibrated decisions with Laya

A typed decision evaluates an input state against questions stored in the task definition. Questions cannot be replaced by the calling application, which makes the decision contract stable, auditable, and safe to reuse.

choiceSelect one option

Returns the selected label and a probability distribution over every option.

scoreRank criteria

Returns calibrated scores for the criteria defined by the task owner.

noulAnswer yes or no

Returns a binary decision with confidence for a focused proposition.

1. Publish the immutable questions

Create the task once from the console or API. The input schema validates each submitted state; the executor configuration defines the questions the Laya worker will ask.

Publish support.route@1curl
curl -X POST https://your-hub.example/api/v1/task_definitions \
  -H 'Authorization: Bearer aih_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "task_definition": {
      "key": "support.route",
      "version": 1,
      "executor": "typed_decision",
      "instructions": "Route incoming support requests.",
      "input_schema": {
        "type": "object",
        "additionalProperties": false,
        "required": ["message"],
        "properties": { "message": { "type": "string" } }
      },
      "executor_config": {
        "questions": {
          "department": {
            "type": "choice",
            "instructions": "Which team should handle this request?",
            "criteria": {
              "billing": "Payments, invoices, charges, or refunds",
              "technical": "Product errors or technical help",
              "other": "Anything else"
            }
          }
        }
      }
    }
  }'

2. Evaluate synchronously

The client supplies only the state being evaluated. AI Hub waits for the durable job to finish and returns the typed answers directly. Send an Idempotency-Key so a retry returns the same decision.

Evaluate support.route@1curl
curl -X POST https://your-hub.example/api/v1/typed-decisions/support.route@1/evaluate \
  -H 'Authorization: Bearer aih_...' \
  -H 'Idempotency-Key: ticket-4821-route-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "input": {
      "message": "Please refund the duplicate charge on my card."
    }
  }'
Completed synchronous responsejson
{
  "id": "job_...",
  "status": "completed",
  "task": "support.route@1",
  "decisions": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": {
        "billing": 0.94,
        "technical": 0.03,
        "other": 0.03
      }
    }
  },
  "usage": {
    "input_tokens": 51,
    "output_tokens": 0,
    "total_tokens": 51
  }
}
Slow work remains recoverable.If the worker does not finish within the synchronous timeout, AI Hub returns 202 Accepted with the durable job ID, a Location header, and retrieve_url. Poll that URL with the same application key.

3. Use a durable job when you do not want to wait

The native job endpoint returns immediately. Put the idempotency key in the request body and poll the returned job.

Create a durable decision jobcurl
curl -X POST https://your-hub.example/api/v1/jobs \
  -H 'Authorization: Bearer aih_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "task": "support.route@1",
    "idempotency_key": "ticket-4821-route-v1",
    "input": {
      "message": "Please refund the duplicate charge on my card."
    }
  }'

4. Poll for the durable result

A submitted job moves from queued to leased, then to completed. Poll the returned job ID until it reaches a terminal state.

Retrieve the jobcurl
curl https://your-hub.example/api/v1/jobs/job_... \
  -H 'Authorization: Bearer aih_...'
Completed responsejson
{
  "id": "job_...",
  "status": "completed",
  "task": "support.route@1",
  "output": {
    "answers": {
      "department": {
        "type": "choice",
        "choice": "billing",
        "probabilities": {
          "billing": 0.94,
          "technical": 0.03,
          "other": 0.03
        }
      }
    }
  }
}
Treat confidence as product input.Use the selected value for normal routing, but define a review threshold for low-confidence decisions. Publish a new task version whenever questions, criteria, or schemas change.
04 · Model profiles

Give chat models a stable contract

A model profile is an immutable chat_completion definition. Its key and version become the public model ID, such as assistant.general@1. The profile controls system instructions; the worker’s AI_MODEL selects the underlying model.

Version deliberately.Publish version 2 when instructions change. Existing integrations and audit history continue to reference version 1.
05 · OpenAI-compatible API

Use the SDK you already know

Use the application token as the API key and append /v1 to the Hub origin.

Python Responses APIpython
from openai import OpenAI

client = OpenAI(
    api_key="aih_...",
    base_url="https://your-hub.example/v1",
)

response = client.responses.create(
    model="assistant.general@1",
    input="Summarize this report",
)
print(response.output_text)

Available endpoints

GET/v1/modelsList model profiles
POST/v1/responsesCreate a response
GET/v1/responses/:idRetrieve a background response
POST/v1/chat/completionsCreate a chat completion

Run longer work in the background

Set background: true when work may outlive one HTTP request. The create call returns a durable job ID immediately.

Create and retrieve a background responsecurl
curl -X POST https://your-hub.example/v1/responses \
  -H 'Authorization: Bearer aih_...' \
  -H 'Content-Type: application/json' \
  -d '{"model":"assistant.general@1","input":"Write a brief","background":true}'

curl https://your-hub.example/v1/responses/job_... \
  -H 'Authorization: Bearer aih_...'
06 · Native job API

Run durable, schema-validated jobs

Use native jobs for structured output, retries, idempotency, explicit versions, and asynchronous execution.

GET/api/v1/task_definitionsList application definitions
POST/api/v1/task_definitionsPublish an immutable definition
POST/api/v1/jobsSubmit an idempotent job
GET/api/v1/jobs/:idRetrieve status and output

Job lifecycle

queued→leased→completed

Retryable failures and expired leases return a job to the queue until its attempt budget is exhausted. Permanent failures end in failed; exhausted jobs end in dead.

07 · Workers

Keep models private and outbound-only

Workers advertise executor capabilities and claim only compatible jobs. The official worker creates an Ed25519 device key on first boot and enrolls its public identity with a one-time owner-issued grant.

No inbound model port is required.The worker polls the Hub over HTTPS, then calls the model on its own private network. Every later Hub request is signed and bound to its method, path, timestamp, nonce, and body.

Read the identity protocol, threat model, and Laya MLX setup.

08 · Operations

Meter every execution attempt

Every claim creates a separate execution record, including retries. Workers report bounded token counts, model identity, and inference duration. AI Hub measures elapsed Hub time and finalizes every record once.

Accounting evidence, not billing.Consumer run details hide provider worker and model identity. Hosting shows provider contribution without consumer payloads. Prompts, inputs, outputs, and instructions are not duplicated into metering rows.
09 · Reference

Current limits

  • OpenAI surfaceText inputs and outputs only; no streaming, tools, images, files, audio, or embeddings yet.
  • Payload sizeNative job inputs and results are limited to 256 KiB.
  • SQLiteRun one Hub replica and keep all database files on durable storage.
  • SecretsUse HTTPS and keep application and worker tokens in a secret manager.

For the full wire contract and operational details, see the OpenAI integration guide and protocol reference.