Quick Start

Get Admina running in under 5 minutes. The default workflow is zero-Docker β€” a single uvicorn process serves both the proxy API and the bundled dashboard on :3000 (auto-fallback to the next free port if it is taken). Docker Compose remains available for the full stack with ClickHouse, Grafana and OTEL collector.

Prerequisites
  • Python 3.11+ (pinned in .python-version)
  • Platforms β€” Linux x86_64 (Ubuntu 20.04+, Debian 11+, Fedora 38+, RHEL 9+) and macOS arm64 (12 Monterey+); both tested in CI
  • Optional: Docker 24+ with Docker Compose v2 for the full stack
  • 4 GB RAM available (full stack runs 8 containers)

Step 1 β€” Install Admina

# Recommended β€” proxy + infrastructure deps (what makes `admina dev` work)
pip install "admina-framework[proxy]"

# Broad install β€” [full] is [proxy,nlp,telemetry] and does not include Presidio;
# the complete roll-up is [all] = [proxy,nlp,telemetry,adapters,presidio] since v0.11.0
pip install "admina-framework[full]"

# Optional: Rust-accelerated engine (opt-in since v0.9.4)
pip install "admina-framework[rust]"

# Optional: Microsoft Presidio as a selectable PII engine (new in v0.11.0 β€”
# the default engine stays spacy-regex until you set ADMINA_PII_ENGINE=presidio)
pip install "admina-framework[presidio]"

# Advanced β€” SDK only (embed governance in your own app; no local dev server)
pip install admina-framework

The default install runs the pure-Python governance engines. The Rust accelerator is opt-in via the [rust] extra β€” it pulls the admina-core wheel from PyPI, and ADMINA_ENGINE=auto (the default) uses it for the firewall + loop breaker while keeping PII on Python for full recall. The two engines are not equivalent β€” the Python engines carry broader detection coverage, and the gap is measured and tracked by the shipped red-team baseline rather than assumed. See the Rust engine guide.

Extra step for the NLP and Presidio extras

The [nlp], [full], [presidio] and [all] extras require spaCy models, which PyPI does not allow as direct-URL dependencies. Presidio supports English and Italian, and each language is active only if its model is installed. After install, run:

python -m spacy download en_core_web_sm it_core_news_sm

it_core_news_sm is only needed for the Presidio engine's Italian support β€” the default spacy-regex engine uses en_core_web_sm.

[proxy] boots without spaCy

admina dev boots with the [proxy] extra only. spaCy is imported lazily β€” without the [nlp] extra, PII redaction runs in regex-only mode, still covering email, phone, SSN, IBAN, IP address, credit card and EU national IDs. Add [nlp] when you also want NER-based entities (person, organisation, location).

Step 2 β€” Initialize a project

admina init my-project
cd my-project
# Scaffolds admina.yaml, docker-compose.yml, .env, and a runnable main.py

Step 3 β€” Start the proxy + dashboard

admina dev has three execution modes:

# 1. Default β€” zero-Docker local mode (one uvicorn serves API + dashboard)
admina dev

# 2. Full Compose stack (proxy + dashboard + redis + clickhouse + otel + grafana)
admina dev --stack

# 3. Stack + local AI infra (ollama + chromadb + open-webui)
admina dev --with-llm

# Expose on the LAN instead of localhost:
admina dev --public                # or --host 0.0.0.0
Without the CLI? Docker-only workflow

If you cloned the repo directly and prefer Docker Compose without the Python CLI, bootstrap a .env with random credentials first:

git clone https://github.com/admina-org/admina.git
cd admina
./scripts/bootstrap-secrets.sh   # writes + prints random API key and dashboard password (.env)
docker compose up --build     # startup banner masks the key β€” re-read it with `cat .env`

Step 4 β€” Verify governance is active

In local mode everything β€” API, dashboard and Swagger UI β€” is on :3000. Under --stack the proxy is a separate container on :8080, so swap the port in the commands below.

# Health check (always public)
curl http://localhost:3000/health
# {
#   "status": "healthy",
#   "service": "admina-proxy",
#   "version": "0.11.1",
#   "engine": {
#     "engine": "python",
#     "rust_available": false,
#     "rust_version": null,
#     "selection": "auto",
#     "active": "python",
#     "pii_active": "python"
#   },
#   "timestamp": "2026-01-01T12:00:00+00:00"
# }

# Check stats (auth required when ADMINA_API_KEY is set)
curl http://localhost:3000/api/stats -H "X-API-Key: $ADMINA_API_KEY"

engine is an object, not a string. With the [rust] extra installed it reports "active": "rust" while "pii_active" stays "python" under the default ADMINA_ENGINE=auto, which keeps full PII recall.

Step 5 β€” Explore the dashboard

In local mode the dashboard opens without a login β€” the proxy hands the browser a signed, expiring session cookie so the SPA can call the API. HTTP Basic auth is a --stack feature: the dashboard container puts it in front of nginx when ADMINA_DASHBOARD_PASSWORD is set, using the credentials printed by admina dev or bootstrap-secrets.sh.

Local mode β€” admina dev

http://localhost:3000 Governance dashboard β€” Admina Score, OISG adequacy, live event feed, EU AI Act gaps
http://localhost:3000/docs Interactive API documentation (Swagger UI) β€” same process, same port

Full stack β€” admina dev --stack

http://localhost:3000 Governance dashboard β€” served by nginx in the dashboard container
http://localhost:8080 Proxy container β€” MCP proxy, admin API, /v1 gateway and /docs
http://localhost:3001 Grafana β€” OTEL metrics and traces
http://127.0.0.1:8123 ClickHouse HTTP interface β€” bound to localhost only
http://127.0.0.1:4317 OTEL collector, OTLP gRPC (4318 HTTP, 8888/8889 Prometheus) β€” localhost only

Step 6 β€” Send your first governed request

Route your AI agent's MCP calls through Admina instead of directly to the MCP server. In local mode the proxy listens on port 3000 (8080 under --stack). /mcp forwards to UPSTREAM_MCP_URL, which local mode leaves empty β€” point it at your MCP server before this step, or run admina dev --stack, which wires the bundled mock MCP server for you. /mcp is also authenticated, unlike /health.

# Send a test MCP call through the governance proxy
curl -X POST http://localhost:3000/mcp \
  -H "X-API-Key: $ADMINA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "read_file",
      "arguments": {"path": "/etc/hosts"}
    },
    "id": 1
  }'

# The response includes governance metadata:
# X-Admina-Governance-Action: ALLOW
# X-Admina-Latency-Us: 6.25
# X-Admina-Event-Id: evt_01J...
# X-Admina-Forensic-Hash: a3f8c9...

Step 7 β€” Point any OpenAI client at Admina

Since v0.11.0 the same proxy port also serves an OpenAI-compatible surface β€” POST /v1/chat/completions and GET /v1/models β€” so any client that speaks the OpenAI chat-completions API can be governed without a code change. Point Admina at your model server first β€” these go in .env or the shell that launches admina dev, since settings are read once at start-up:

# Upstream OpenAI-compatible API Admina forwards to β€” must include the /v1 base path.
# Default: http://localhost:11434/v1 (a local Ollama)
export ADMINA_GATEWAY_UPSTREAM=http://localhost:11434/v1

# Optional β€” comma-separated allow-list applied to GET /v1/models (default: empty = passthrough)
export ADMINA_GATEWAY_MODELS_ALLOWLIST=llama3.1:8b

# Optional β€” text returned to the caller when governance blocks a request
# (default: "This request was blocked by the Admina governance policy.")
export ADMINA_GATEWAY_BLOCK_MESSAGE="Blocked by policy."

Then talk to http://localhost:3000/v1 instead of the upstream (http://localhost:8080/v1 under --stack). The API key is your ADMINA_API_KEY β€” the proxy accepts it as Authorization: Bearer or X-API-Key, so an OpenAI SDK's api_key parameter works as-is.

curl -X POST http://localhost:3000/v1/chat/completions \
  -H "Authorization: Bearer $ADMINA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llama3.1:8b",
    "messages": [{"role": "user", "content": "Summarise this clause…"}]
  }'
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:3000/v1",   # Admina, not the provider
    api_key="<your ADMINA_API_KEY>",
)

resp = client.chat.completions.create(
    model="llama3.1:8b",
    messages=[{"role": "user", "content": "Summarise this clause…"}],
)
print(resp.choices[0].message.content)

Streaming works the same way β€” Admina re-emits the upstream as Server-Sent Events, redacting PII across delta boundaries, and always terminates the stream with data: [DONE]:

for chunk in client.chat.completions.create(
    model="llama3.1:8b",
    messages=[{"role": "user", "content": "Summarise this clause…"}],
    stream=True,
):
    print(chunk.choices[0].delta.content or "", end="")
Blocks are HTTP 200 β€” check finish_reason

When governance blocks a prompt, the gateway returns HTTP 200 with a synthetic completion whose finish_reason is content_filter (streaming: one chunk carrying the block message with the same content_filter, then data: [DONE]). This is deliberate β€” OpenAI-compatible UIs render the refusal instead of erroring β€” so your client must detect blocks by finish_reason, never by status code. Admina also forwards no credentials upstream, so ADMINA_GATEWAY_UPSTREAM must point at a keyless server such as Ollama, a local vLLM, or LM Studio.

What just happened?

Every call you made was:

  • Scanned for PII in both directions (Data Sovereignty)
  • Checked for prompt injection patterns (Agent Security β€” Firewall)
  • Checked for infinite loop signatures (Agent Security β€” Loop Breaker)
  • Added to the SHA-256 hash chain audit log (Compliance β€” Forensic)
  • Classified for EU AI Act risk level (Compliance)
  • Recorded as an OpenTelemetry span (Compliance β€” OTEL) β€” only with the [telemetry] extra installed and OTEL_ENDPOINT set, which --stack does for you; bare admina dev clears it
  • Counted into the Admina Score at /api/dashboard/score β€” the live runtime composite (residency, audit coverage, EU AI Act coverage, recent attacks, forensic chain)

Two exceptions on the /v1 gateway from Step 7: loop detection does not run on that surface, and a gateway request writes its forensic record without emitting an OpenTelemetry span. Everything else in the list applies.

OISG measures capability, not traffic

Your OISG score at /api/dashboard/oisg is a static capability assessment β€” 4 pillars Γ— 5 criteria Γ— 5 points, each criterion satisfied when the corresponding subsystem is present and configured. It does not move with traffic: a stock instance reads the same after one request as after a million. Nor does it start at the top β€” a bare admina dev leaves five criteria unsatisfied (o1 and i3 want AI infra + RAG enabled, o5 and s5 want the Rust engine, g3 wants a governance-guard plugin), capping it at 75 in the Good coverage band until you enable them. See OISG Adequacy for the full paradigm.

Next steps

Troubleshooting

First stop: admina doctor

The CLI ships with a diagnostic command that inspects your environment and reports which checks pass or fail β€” Python version, Rust engine availability, plugin discovery, admina.yaml validity, and infrastructure reachability.

admina doctor

Containers fail to start

Check that all required ports are free. The stack binds 3000, 3001 and 8080 on all interfaces, plus 8123, 4317, 4318, 8888 and 8889 on localhost only. Run docker compose logs proxy for detailed error output.

Rust engine not detected

Admina automatically falls back to the Python implementation if the Rust binary is not available. The health endpoint's engine object will show "rust_available": false and "active": "python". To enable Rust, ensure the admina_core binary is compiled for your platform (see Rust engine guide).

Need help?

Open an issue on GitHub or start a discussion in GitHub Discussions.