---ci--- phase: 5 milestone: v0.1 status: complete requirements: covered: [REQ-01, REQ-02, REQ-03, REQ-04, REQ-05, REQ-06, REQ-07, REQ-08, REQ-09, REQ-10, REQ-11, REQ-12, REQ-13, REQ-14, REQ-15, REQ-16, REQ-17, REQ-18, REQ-19, REQ-20] partial: [] ---/ci---
9.2 KiB
CoreCI Broker
A single-file credential broker for autonomous agent tools on the Open WebUI
host. Tools authenticate with derived tokens instead of shared secrets, the
CEO gates access through UI-managed allowlists of app UUIDs and user IDs,
every read lands in an append-only audit log, and secret reads are path-scoped
to coreci/<agent_id> — one Python file, one SQLite DB, a four-page admin UI,
zero pip installs, no build step.
Quickstart (standalone)
python3 coreci_broker.py
- Stdlib-only runtime, Python 3.11+. Zero pip installs: bcrypt when importable, stdlib scrypt otherwise (format-prefixed hashes).
- First boot generates
.env: a 32-byte base64 pepper plus a 32+-char admin password. The password is printed to the startup log asBROKER_ADMIN_PASS=…exactly once — save it immediately; it is never printed again. Lost pass: removeBROKER_ADMIN_PASSfrom.envand reboot — a new one prints once. - The server binds
127.0.0.1:8765. Openhttp://127.0.0.1:8765/ui/loginand log in with the printed pass. - Stop with Ctrl+C. Durable state is exactly three files:
broker.db,broker.audit.log,.env— back them up.
Configuration
Read on boot from the .env at BROKER_ENV_PATH. Precedence:
process env → .env → defaults.
| Variable | Default | Purpose |
|---|---|---|
BROKER_BIND |
127.0.0.1:8765 |
Listen address; the bind controls reachability |
BROKER_ADMIN_USER |
admin |
Admin login user name |
BROKER_ADMIN_PASS |
generated first boot | Admin login password (32+ random chars) |
BROKER_INSTANCE_PEPPER |
generated first boot | 32-byte base64 pepper; token derivation input, synced to SQLite |
BROKER_ENV_PATH |
.env |
Path of the .env file the broker reads/writes |
BROKER_TOKEN_TTL |
86400 |
Tool token lifetime, seconds (24 h) |
BROKER_SESSION_TTL |
300 |
Admin UI session cookie lifetime, seconds |
BROKER_DB_PATH |
broker.db |
SQLite database (mode 0600) |
BROKER_AUDIT_LOG_PATH |
broker.audit.log |
Append-only JSONL audit log (mode 0600) |
BROKER_CREDS_SOURCE |
file |
Credential source adapter: file (dev/tests) or owui (production) |
BROKER_CREDS_FILE |
data/creds.json |
FileCredsSource JSON document: paths → secret objects |
BROKER_OWUI_BASE_URL |
— | Open WebUI base URL for the OWUI creds source |
BROKER_OWUI_API_KEY |
— | Open WebUI API key (Bearer) for KB read access |
BROKER_AGENT_STATE_BACKEND |
internal |
Agent-state storage: internal (rows in broker.db) or owui |
BROKER_MAX_AGENTS_PER_APP |
50 |
Cap on agent registrations per app UUID |
BROKER_UI_ROOT |
coreci_broker_ui |
Directory served for /ui/* pages |
BROKER_STATIC_ROOT |
. |
Directory served for /static/* (client module) |
Production: BROKER_CREDS_SOURCE=owui with BROKER_OWUI_BASE_URL +
BROKER_OWUI_API_KEY (0600 .env discipline). Keep BROKER_BIND on
loopback unless you deliberately widen reachability.
Running as an Open WebUI workspace tool
Paste coreci_broker.py into Open WebUI as a workspace Tool (frontmatter
requirements: stays empty — the runtime already ships everything). Notes
that matter:
- The server starts in
Tools.__init__behind a once-per-process guard; the OWUI loader has no activate/deactivate hook, and disabling a tool does not stop a previously started server. - After editing the broker tool source, restart Open WebUI. The loader re-execs the module on source change, but the previously loaded server keeps its port — the new instance fails loud with a bind-error message telling you to restart, rather than silently serving old code.
- Paths (DB, audit log,
.env, UI roots) resolve from environment config, never from__file__— the OWUI loader deletes the temp file it execs. - Durable state:
broker.db+broker.audit.log+.env— back those three up; SQLite is the source of truth, no HA, restore is a file copy. Tools.status()reports the serving address; call it from a chat to verify the mount, or hit/healthzon the bind.
Admin UI
Four pages + dashboard, server-rendered on the same port as the API:
/ui/login— the CEO's bookmark. First-boot pass discipline: the printed pass is shown once; record it in your secret store on first login./ui/app_registrations— allowlist of tool app UUIDs: add, revoke, reinstate./ui/user_allowlist— allowlist of user IDs (optional email): same actions./ui/audit— filterable, read-only view of the audit log (no export in v0.1)./ui/dashboard— landing page with persistent nav header and Logout.
Allowlist semantics: revoke keeps existing tokens working until TTL expiry —
by design. Revocation immediately blocks new registrations; already-issued
tokens live at most one BROKER_TOKEN_TTL (24 h by default).
Tool authors
Fetch the public client module — unauthenticated, from
http://<broker>/static/coreci_broker_client.py — into your workspace and
import it. Flow: ensure_registered(state) → put_state(state) →
read_secret("coreci/<agent_id>", state). Stdlib-urllib-only (no httpx, no
bcrypt), so any tool can import it; on a 401 it re-registers and retries
once. State endpoints are the tool's only persistence channel — keep state
there, not in local files. See reference_tools/ for the proxmox-style §11
flow and a minimal auth-then-use showcase; downstream calls default to a
safe dry-run.
Security model
- Derived token:
base64(sha256(pepper:UUID:user_id:agent_id:sorted_tags))— 44 ASCII chars, no prefix, no challenge protocol. Stored only as a slow hash ($2b$…/scrypt$…), never in plaintext. - Allowlists are the gate: unknown or revoked app UUIDs and unknown users get 403 at registration; the token is the consequence, not the authority.
- Every secret-read attempt is audited (ok/denied/error) with user, UUID, agent, and path.
- Secret reads are path-scoped: a registration reads exactly
coreci/<agent_id>— cross-path → 403, absent → 404. - The broker never writes to
creds-coreci-coo; the CEO maintains that KB through the Open WebUI UI. broker.db,broker.audit.log, and.envare mode 0600.- Binds
127.0.0.1by default; trusted-network posture — no rate limiting by design (spec §14).
Documented spec deviations
Honest list; full detail in .ciagent/PROJECT.md:
- Line-cap amendments (D-016/D-017/D-018): the single-file constraint grew 800 → 1000 → 1200 → 1400 lines via the owner-approved §12.14 amendment pattern (readability over dense cramming). Current source: 1390 lines; AC14 enforces the cap.
- Dual-mode hashing (D-001): spec §6 mandates bcrypt; the runtime is stdlib-only, so scrypt fallback with format-prefixed hashes.
- Token verification (D-004): spec §7.5's in-memory
token_hashindex is impossible with salted slow hashes; the broker scans rows scoped to the presented UUID and keeps a non-authoritative verified-token cache. - Pepper rule (D-005): the pepper is returned only on row creation or
tokenless registration (the KB-loss recovery path); spec §9's snippet would
raise
KeyErroron re-registration. - Spec defects resolved: §7.5 index, §6-vs-§9 (the broker derives
authoritatively; the tool stores), §9's
KeyError— see PROJECT.md §Context. BROKER_ADMIN_USERdefaults toadmin(spec silent on a default).- Register cap
BROKER_MAX_AGENTS_PER_APP(50) — a documented addition. - Reinstate actions audit as
add_app/add_user(no separate audit action). - In-scope but absent secrets audit as
readwithresult: error(404s are audited, not silent).
Post-ship live-OWUI smoke checklist (owner-run)
Before migrating any real tool (G-005; deployed host pinned v0.11.3, G-006):
- Confirm the deployed Open WebUI version (expected v0.11.3) and that the
KB read endpoints match the researched surface:
/api/v1/knowledge/{id}and/api/v1/files/{id}/data/content. - Add the broker as a workspace tool; check
/healthzon the bind. - CEO first-boot login at
/ui/loginwith the printed pass. - Add one app UUID and one user to the allowlists via the UI.
- In a scratch tool: import the client module from
/static/→ensure_registered→ readcoreci/proxmoxvia the OWUI creds source. - Verify every step in
/ui/audit. - Rollback = disable the tool + restore the three state files.
No real tool migrates before this passes; the 90-day adoption criterion (proxmox tool on the client, direct KB reads removed) is due 2027-01-09.
Tests
timeout 300 python3 -m unittest discover -s tests
295 tests, stdlib unittest only (~2 min; one skip where bcrypt is not importable). The AC1–AC14 acceptance matrix is green.
Repository layout
| Path | What it is |
|---|---|
coreci_broker.py |
The broker: single file — server, store, audit, auth, UI serving |
coreci_broker_client.py |
Public stdlib-only client module (served at /static/) |
coreci_broker_ui/ |
Server-rendered HTML for the four pages + dashboard |
reference_tools/ |
Tool-author templates (proxmox-style §11 flow + minimal showcase) |
tests/ |
Stdlib-unittest suite incl. the AC1–AC14 acceptance matrix |
.ciagent/ |
CI pipeline artifacts (project, requirements, roadmap, decisions) |