Metadata-Version: 2.4
Name: meridian-client
Version: 0.3.1
Summary: Python SDK for Meridian Governance Router
License-Expression: MIT
Project-URL: Homepage, https://github.com/knjpollard/meridian
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Dynamic: license-file

---
name: meridian-integration
description: How to call Meridian (the governance router) correctly from a new, standalone client — auth, the onboarding sequence, and what's still house/console-only.
---

# Integrating with Meridian

Meridian is a policy-governed inference router: you ask it "which model should
`agent_name` use right now", and it answers using project/agent policy
(quality, cost, risk, allowed/denied hubs, data classification) rather than
you hardcoding a model name. It knows nothing about MTA specifically — any
client that holds a valid key can use it standalone.

Full generated route-by-route reference: `docs/api/api-reference.md` in this
repo (regenerate with `python3 scripts/gen_api_docs.py` after any route
change — it reads the live FastAPI app, so it can't go stale the way a
hand-written doc can).

## Install

```bash
pip install meridian-client          # published — the normal path, or
pip install -e meridian-client/      # editable, from this repo (contributing to the SDK itself)
```

Prefer a terminal to Python? `pip install meridian-governance-cli` wraps
this same SDK as subcommands (`meridian signup`, `meridian resolve
my_agent`, `meridian keys list`, `meridian hubs discover`, ...) — installs
as the `meridian` command; see `meridian-cli/README.md` (not to be
confused with the unrelated `meridian-cli` package also on PyPI). One
exception to "just subcommands over the API": `meridian chat` drives a
governed resolve -> dispatch -> audit loop interactively or scripted
(`--one-shot`, `--daemon`), with `/code` and `/manage` modes inside the
REPL — everything else stays a thin management layer, not a clone of
`mta-cli`'s consumer-companion command set.

## Auth — read this before anything else

Meridian accepts a key two ways; it doesn't matter which:

```
Authorization: Bearer mdn_xxxxxxxx
x-api-key: mdn_xxxxxxxx
```

With no key at all: **on-prem only**, and only if that instance was started
without `MERIDIAN_AUTH_REQUIRED=true`. The request resolves to the instance's
single default org. Every cloud deployment (`dev-meridian.mytrustworthy.ai`
and any other `MERIDIAN_DEPLOYMENT=cloud` instance) requires a key — no key
gets a flat 401, not a default org.

## Getting your first key

```python
from meridian import Meridian

m = Meridian.signup("https://dev-meridian.mytrustworthy.ai",
    org_name="my-project", email="me@example.com", password="...")
# m is now an authenticated client. m.org_id and m.signup_response are set.
```

One call: creates a new org (always `free` tier — self-serve can't grant
itself more than that), its first user, and its first key, and returns a
`Meridian` already authenticated with it. This is the *only* unauthenticated
write on the whole API — everywhere else, minting a key or registering a
user needs an `org_id` that a cold caller doesn't have yet, which is exactly
the chicken-and-egg problem this one call exists to break.

If you already have a key (an account team provisioned you directly, or
you're adding a second client to an org that already signed up):

```python
m = Meridian("https://dev-meridian.mytrustworthy.ai", api_key="mdn_...")
```

## Three tiers, one engine

There's one governance engine underneath all of this — the tiers below are
just how much of it you choose to use, not three different products:

- **Individual / fast start:** pin a model and go. `pinned_model` skips
  scoring entirely (`resolver.py`'s "Step 3: Pinned model — skip scoring") —
  deterministic, zero policy to configure.
- **Business / small team:** the quality/cost/risk sliders. Same `create_agent`
  call, just set overrides instead of (or in addition to) a pin; invite
  teammates into the same org via `POST /api/v1/auth/register` once you're
  authenticated.
- **Enterprise / custom governance:** change-requests and approval
  (`create_change_request` → `approve_change_request`), hub allow/denylists,
  data classification enforcement (`pii` hard-forces risk to 1, on-prem only).
  All already built; nothing here is Enterprise-only code, just the parts a
  fast-start integration doesn't need to touch.

## The onboarding sequence

A tenant key can do all of this itself — no console needed past signup:

```python
# 1. A project: your governance boundary. One org can have many.
project = m.create_project(
    org_id=m.org_id,
    name="my-standalone-agent",
    quality=7, cost=5, risk=5,          # 0-10 sliders, project-level defaults
    classification="general",            # "pii" forces risk=1 regardless of the slider
)

# 2. An agent inside it. This is the identity you resolve against — not a
#    user, not an API key, an AGENT. One project can have many, each with its
#    own overrides.
agent = m.create_agent(
    project.id, "my_agent",
    quality_override=9,                  # omit to inherit the project default
    allowed_hubs=["anthropic-direct", "bedrock"],  # omit for no hub restriction
)

# 3. Resolve. Call this every time you're about to make an inference call —
#    it's cheap, and policy can change between calls.
decision = m.resolve("my_agent", project_id=project.id)
print(decision.model, decision.hub)      # e.g. "claude-opus-5" "anthropic-direct"

# 4. After the real inference call, report usage so cost tracking means
#    something.
m.audit(decision.routing_log_id, input_tokens=512, output_tokens=128,
         latency_ms=840)
```

That's the whole golden path. `m.explain(decision.routing_log_id)` gets you
the full candidate breakdown (every model considered, why each one was
eliminated or scored where it scored) if you need to show your work.

## Without the SDK (raw HTTP)

```bash
curl -X POST https://dev-meridian.mytrustworthy.ai/api/v1/resolve \
  -H "Authorization: Bearer mdn_..." -H "Content-Type: application/json" \
  -d '{"agent_name": "my_agent", "project_id": 123}'
```

Every route in `docs/api/api-reference.md` works the same way — method, path,
JSON body of the fields listed, same two auth headers.

## What a tenant key cannot do (yet)

`docs/api/api-reference.md` marks every route **tenant key: yes/no**. The
"no" routes answer `403 "This route is not yet tenant-scoped; refused for
tenant keys"` regardless of whether your key is valid — it's a blanket
refusal by path, not a permission check. As of this writing that's mainly:
hub/model/deployment registration and edits, org creation and edits, and most
cost/governance console views. Those need a house key or the console. Project
and agent create/update/delete, resolve, audit, explain, simulate, and
change-requests are all tenant-reachable.

## Gotchas

- **`PUT /api/v1/projects/{id}/lifecycle` is a stub.** It validates the
  request and echoes the current row back — it does not change anything.
  Don't build on it (`meridian#7`'s predecessor finding).
- **The SDK's response objects have an `.extra` dict.** Every typed field the
  SDK knows about is a real attribute (`agent.allowed_hubs`); anything the
  server returns that this SDK version doesn't model yet lands in
  `agent.extra` instead of silently vanishing. If a field you expect is
  `None`, check `.extra` before assuming the server didn't send it.
- **`resolve_failover` only writes an audit record if you pass
  `agent_name`.** Without it you get a plain hub-allowlist fallback with no
  governance and no audit trail — fine for "just get me anything that
  works", wrong for anything that needs to explain itself later.
- **Tool-access governance does not exist yet.** Meridian governs *which
  model* an agent may use. It does not yet govern *which tools* an agent may
  call — there's no route for it, and the schema slots for it
  (`agent.agent_tools`, `agent.skill_ids`) are unpopulated. If your new agent
  needs Meridian to gate tool access rather than just model choice, that's a
  real gap, not a documentation gap — see
  `obsidian-vault-kb/relays/meridian/2026-10-12-should-meridian-govern-tools-and-what-that-costs-in-apis.md`
  for the live design discussion.
- **Forgot a built-in-auth password? `POST /auth/forgot-password` /
  `POST /auth/reset-password`** (meridian#10), also reachable from the
  console's "Forgot password?" link. Requires `RESEND_API_KEY` to actually
  be configured on the instance — without it the request fails loudly
  rather than handing the token back in the response. That's deliberate:
  an earlier version of this capability did exactly that and was removed on
  2026-09-12 because it let anyone who knew an email address take over that
  account. There is no magic-link *login* — only password reset; you still
  need to know a password to sign in, the email only proves you own the
  inbox.
