CIRWEL Research

The working path

Build on it.

Four steps: run the server, point a client at it, run the check-in loop, and decide what to do with what comes back. Run it on hardware you control, with the Homebrew preview for Apple Silicon Macs or Docker Compose.

The home page says what UNITARES is and How UNITARES fits together shows how it is put together. Here is the part you type. Every measured result's status is in the server's evidence ledger.


What you need

Server
cirwel/unitares · Apache 2.0 · Homebrew preview or Docker Compose
Endpoint
http://localhost:8767/mcp/ · Streamable HTTP · REST at /v1/tools/call
Contract
unitares-sdk · for agents you write yourself
Runtime
Python 3.14+ (SDK 3.12+) · PostgreSQL with AGE and pgvector · Redis · optional Elixir/OTP coordination
Suited to
operators running several long-lived agents on infrastructure they control

§ IRun the server

Choose your installation.

Self-hosted by design. Identity, telemetry, evidence, and policy history never leave it.

Apple Silicon Mac · Homebrew preview

Install Homebrew and Apple's Command Line Tools, then run these commands as your regular macOS user, without sudo. The preview builds from source and packages the server, PostgreSQL 17 with AGE and pgvector, Redis, and the coordination service.

brew update
brew tap cirwel/unitares
brew trust cirwel/unitares
brew install cirwel/unitares/unitares
brew pin unitares unitares-lease-plane

Next, follow the tap's first-time setup: create a dedicated database, configure UNITARES, bootstrap the new database, then verify and start the services. Package installation alone does not create the database or start the server.

Keep the packages pinned until you are ready for a backed-up upgrade. Database migrations are explicit; unitares update handles Docker Compose installations only. Native user services run in the Homebrew owner's logged-in session.

Choose one primary installation per Mac. If Docker already runs UNITARES, follow the migration guidance before starting native services; installing the package does not move existing data or clients. Intel Macs and Linux can use the Docker path below.

Docker Compose

$ v=$(curl -fsSL https://raw.githubusercontent.com/cirwel/unitares/master/PUBLISHED_VERSION) && git clone --branch "v$v" --depth 1 https://github.com/cirwel/unitares.git && cd unitares && docker compose up -d --wait

That brings up PostgreSQL with AGE and pgvector, Redis, the lease plane that decides which agent holds a shared surface, and the server on loopback. MCP clients connect at localhost:8767/mcp/, the self-hosted dashboard is at /dashboard, and /health answers for readiness checks.

That is the server README's own command: it clones the latest verified release tag rather than the default branch, which can run ahead of it.

Optional models and search

The core record works without a model connection. Configure an endpoint and model explicitly when you want model-backed consultation. Ollama is optional, and fallback is an explicit choice. The Homebrew preview includes database-backed text search; semantic embeddings need a separately managed installation. Docker's full search overlay adds optional semantic search and hybrid ranking.

Try the demos from a source checkout

$ make demo onboards a process, sends six check-ins
$ make coordination-demo two agents contend for one surface and hand it off

The second one is the identity guarantee biting rather than being described: governance exchanges each agent's continuity credential for a single-use, request-bound attestation, refuses one agent's attestation when it claims the other's identifier, refuses a captured attestation on replay, and passes the contested surface through an identity-checked handoff before releasing it.

§ IIConnect a client

Client-neutral at the boundary.

Any client that speaks Streamable HTTP MCP can connect. Nothing about the server assumes a model vendor.

The same server answers Codex, Claude Code, Cursor, Goose, Hermes, hosted connectors, and custom hosts. Add it the way that client adds any HTTP MCP server:

{
  "mcpServers": {
    "unitares": {
      "type": "http",
      "url": "http://localhost:8767/mcp/"
    }
  }
}

A client without native HTTP support bridges through stdio. Claude Desktop is the usual case:

{
  "mcpServers": {
    "unitares": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:8767/mcp/"]
    }
  }
}

No agent-name header is configured anywhere. The agent identifies itself in its first call, which is the point: a name in a config file is a label, and a label is not an identity.

Non-MCP callers post to /v1/tools/call instead. For Codex and Claude Code, session lifecycle and hooks are packaged separately as unitares-governance-plugin.

§ IIIRun the loop

Four calls, and a decision that stays yours.

session = start_session(force_new=True)
sid = session["client_session_id"]

result = sync_state(
    response_text=output,
    complexity=0.6,
    confidence=0.8,
    client_session_id=sid,
)
if result.get("state_summary", {}).get("action") == "pause":
    return_to_operator(result.get("next_action"))

record_result(
    outcome_type="test_passed" if tests_passed else "test_failed",
    prediction_id=result.get("prediction_id"),
    client_session_id=sid,
)
state = check_working_state(client_session_id=sid)
  1. Identify

    start_session

    Binds every later write to this process instance. A fresh process mints fresh identity; continuity across a restart is declared, never inferred from a reused display name.

  2. Claim

    sync_state

    Records what the process says it did, and how confident it is. Returns a policy action, proceed, guide, or pause, with a named reason and a next step.

  3. Ground

    record_result

    Attaches the real outcome to the claim that predicted it, through the check-in's own prediction_id. Without it, the state estimate rests on the agent's own report.

  4. Read

    check_working_state

    Reads the current policy action without writing anything.

Treat the action, the reason, and the next step as the stable contract; the enforcement record rides alongside them. When the server pauses an agent, it holds that agent's governed writes until recovery, review, the operator, or expiry lifts the pause. Anything the agent does outside the server is the host's to enforce.

The complete tool catalog is callable on every transport. The initial listing is deliberately small: use list_tools to discover names, describe_tool to inspect parameters, and use_tool to call a tool omitted from the initial listing. The checkpoint loop, shared memory, structured review, advisory consultation, and recovery are reachable from the same catalog.

§ IVBuild a long-running agent

The contract is a package, not the server repository.

$ pip install unitares-sdk

For an agent that runs for weeks rather than minutes, the SDK handles connection, identity, check-ins, heartbeats, and knowledge participation. Preserve its identity anchor across restarts with identity(agent_uuid=...) rather than minting a new one each run, or its history restarts with it.

A workload registers its own progress source through the unitares.resident_progress_sources entry-point group, with no change to the server's repository at all. A workload never has to touch the server's code.

The lowercase agents in the server repository are reference clients and operational examples. They are built on it and are explicitly not part of it, so read them as worked examples rather than a framework to subclass. cirwel/unitares-resident is the first-party userland skeleton on the same public contract.

§ VWhere it fits

Where it sits beside evals and guardrails.

UNITARES keeps the record. Judgments of correctness and ethics stay with evals, reviewers, and independent evidence. The scope and threat model covers agents that conceal or game signals.

It runs beside what you already have:

Evals

between runs

Is this model good enough for a defined task?

Guardrails

per action

Is this action allowed, and is it contained?

UNITARES

continuously

What has this running process been doing, what evidence supports its claims, who challenged them, and what happened?

Every measured result and pre-registered read is in the evidence ledger, versioned with the code. The research page lists the papers.