feat: implement project bus MVP
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# Epimonos Project Bus
|
||||
|
||||
Epimonos Project Bus is a small, generic coordination service for auditable
|
||||
human/agent projects. It exposes a stateless MCP Streamable HTTP endpoint and
|
||||
keeps an ordered, append-only event trail alongside relational projections.
|
||||
|
||||
The core has no Sandbox, Epimonos product, billing, identity-provider, or
|
||||
backoffice domain logic. A project supplies its own actors, role assignments,
|
||||
work-package contents, decisions, evidence URIs, and repository references.
|
||||
|
||||
## MVP capabilities
|
||||
|
||||
- isolated projects and actors with one formal role per project;
|
||||
- issued, atomically claimed, and evidenced work packages;
|
||||
- independent review requests and verdicts;
|
||||
- product, architecture, operational, and gate decisions;
|
||||
- escalations and formal resolution;
|
||||
- commit and artifact references;
|
||||
- PM/PO gate closure and baseline-release events;
|
||||
- ordered cursor-based event synchronization;
|
||||
- idempotent mutations and concurrency-safe local writes;
|
||||
- transport-derived identity and role-based authorization;
|
||||
- database-level protection against event update and deletion.
|
||||
|
||||
## Governance invariants
|
||||
|
||||
The service enforces these minimum separations:
|
||||
|
||||
- a PA cannot record product, architecture, operational, or gate decisions;
|
||||
- only a PO can record a product decision;
|
||||
- only a PM or PO can close a gate or release a baseline;
|
||||
- the actor who claimed/submitted a result cannot review it;
|
||||
- a gate cannot accept a result without an independent `APPROVE` review;
|
||||
- an actor who submitted a result cannot accept it;
|
||||
- a baseline can reference only accepted work packages.
|
||||
|
||||
These are technical guardrails, not a replacement for the project role
|
||||
contracts or the PM's tolerance and escalation policy.
|
||||
|
||||
## Quick start
|
||||
|
||||
Python 3.12 is the only runtime dependency.
|
||||
|
||||
```bash
|
||||
export PYTHONPATH=src
|
||||
export PROJECT_BUS_BOOTSTRAP_TOKEN='replace-this-with-long-secret'
|
||||
python -m project_bus --database ./project-bus.db --host 127.0.0.1 --port 8080
|
||||
```
|
||||
|
||||
Health endpoint:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/healthz
|
||||
```
|
||||
|
||||
Initialize MCP:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/mcp \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
|
||||
```
|
||||
|
||||
Create a project:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/mcp \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer replace-this-with-long-secret' \
|
||||
--data '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"create_project","arguments":{"project_id":"example","name":"Example","idempotency_key":"bootstrap-example-v1"}}}'
|
||||
```
|
||||
|
||||
Use `register_actor` with the bootstrap identity to appoint a PO. Configure
|
||||
the actor's bearer credential through `PROJECT_BUS_TOKENS_JSON`, then let the
|
||||
PO/PM register the remaining members. Actor identity is always taken from the
|
||||
authenticated bearer credential; callers cannot supply an acting identity in
|
||||
tool arguments.
|
||||
|
||||
Example credential configuration:
|
||||
|
||||
```bash
|
||||
export PROJECT_BUS_TOKENS_JSON='{
|
||||
"long-random-po-token": {
|
||||
"actor_id": "po-1",
|
||||
"auth_subject": "po-1",
|
||||
"display_name": "Product Owner"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
The identity must match an actor registered in the target project. Environment
|
||||
changes require a process restart in this MVP.
|
||||
|
||||
## MCP client configuration
|
||||
|
||||
For Claude Code:
|
||||
|
||||
```bash
|
||||
claude mcp add \
|
||||
--scope project \
|
||||
--transport http \
|
||||
epimonos-project-bus \
|
||||
https://project-bus.example/mcp
|
||||
```
|
||||
|
||||
For a local-only development server, use the local HTTP URL. Credentials
|
||||
belong in the client/user secret store, never in `.mcp.json` or Git.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---:|---|
|
||||
| `PROJECT_BUS_DATABASE` | `project-bus.db` | SQLite database path |
|
||||
| `PROJECT_BUS_MIGRATIONS` | source-tree discovery | Migration directory |
|
||||
| `PROJECT_BUS_HOST` | `127.0.0.1` | Bind address |
|
||||
| `PROJECT_BUS_PORT` | `8080` | HTTP port |
|
||||
| `PROJECT_BUS_BOOTSTRAP_TOKEN` | required, minimum 24 characters | SYSTEM bearer token |
|
||||
| `PROJECT_BUS_TOKENS_JSON` | empty | Static bearer-token identity map |
|
||||
| `PROJECT_BUS_ALLOWED_ORIGINS` | empty | Comma-separated exact browser origins |
|
||||
| `PROJECT_BUS_LOG_LEVEL` | `INFO` | Python log level |
|
||||
|
||||
Never expose the bootstrap token. On an internet-facing deployment, terminate
|
||||
TLS at a trusted reverse proxy and set a strict origin allowlist.
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python -m unittest discover -v
|
||||
```
|
||||
|
||||
The suite uses only temporary databases and includes concurrent writer tests.
|
||||
|
||||
## Local persistence and production profile
|
||||
|
||||
SQLite runs in WAL mode and every mutation uses `BEGIN IMMEDIATE`. This gives
|
||||
a dependable single-process/single-node MVP and prevents claim and invariant
|
||||
check races. The append-only event table is protected with `UPDATE` and
|
||||
`DELETE` rejection triggers.
|
||||
|
||||
Production differences are intentionally explicit:
|
||||
|
||||
| Concern | MVP | Production target |
|
||||
|---|---|---|
|
||||
| Database | SQLite/WAL, one node | PostgreSQL 16+, transactions and row locks |
|
||||
| Identity | static bearer map | OIDC/JWT or mTLS workload identity |
|
||||
| Secrets | environment | secret manager, rotation, revocation |
|
||||
| Transport | plain HTTP capable | TLS-only reverse proxy/service mesh |
|
||||
| Scaling | one server process | multiple stateless MCP instances |
|
||||
| Audit durability | local DB/backup | PITR, replicas, external immutable export |
|
||||
| Availability | process supervisor | orchestrator probes, SLOs, alerting |
|
||||
| Abuse control | body cap/origin policy | edge rate limits, quotas, WAF where useful |
|
||||
|
||||
The persistence boundary is deliberately small (`Database` plus SQL in the
|
||||
service), but this release does **not** contain a PostgreSQL adapter. SQLite
|
||||
must not be mounted on NFS or used for active-active replicas.
|
||||
|
||||
## Repository guide
|
||||
|
||||
- `src/project_bus/` — domain service, auth abstraction, MCP and HTTP adapter;
|
||||
- `migrations/` — ordered relational schema;
|
||||
- `tests/` — service, concurrency, governance, and HTTP contract tests;
|
||||
- `docs/` — architecture, tool contract, threat model, production notes;
|
||||
- `deploy/` — container and Compose example;
|
||||
- `evidence/` — WP-PA-001 verification and independent-review handoff.
|
||||
|
||||
See [docs/TOOL_CONTRACT.md](docs/TOOL_CONTRACT.md) before integrating a new
|
||||
actor and [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md) before deployment.
|
||||
Reference in New Issue
Block a user