Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,67 @@ jobs:
fi
echo "All proto files match BSR."

audit:
name: Security Audit
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Install cargo-audit
run: cargo install cargo-audit

- name: Run cargo audit
run: cargo audit

coverage:
name: Coverage
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo registry and build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-

- name: Install protoc
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler

- name: Install cargo-tarpaulin
run: cargo install cargo-tarpaulin

- name: Generate coverage
run: cargo tarpaulin --all-targets --out xml
env:
MACP_MEMORY_ONLY: "1"

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: cobertura.xml
fail_ci_if_error: false

ci-pass:
name: All Checks Passed
runs-on: ubuntu-latest
needs: [check, fmt, clippy, test, build, lint-protobuf, proto-sync]
needs: [check, fmt, clippy, test, build, lint-protobuf, proto-sync, audit]

steps:
- name: Summary
Expand All @@ -222,3 +279,4 @@ jobs:
echo " - cargo build --release"
echo " - protobuf lint"
echo " - proto sync check"
echo " - cargo audit"
16 changes: 15 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: setup build test fmt clippy check sync-protos sync-protos-local check-protos
.PHONY: setup build test test-integration test-conformance test-all fmt clippy check audit coverage sync-protos sync-protos-local check-protos

SPEC_PROTO_DIR := ../multiagentcoordinationprotocol/schemas/proto
PROTO_FILES := macp/v1/envelope.proto macp/v1/core.proto macp/modes/decision/v1/decision.proto macp/modes/proposal/v1/proposal.proto macp/modes/task/v1/task.proto macp/modes/handoff/v1/handoff.proto macp/modes/quorum/v1/quorum.proto
Expand All @@ -20,6 +20,20 @@ fmt:
clippy:
cargo clippy --all-targets -- -D warnings

test-integration:
cargo test --test '*'

test-conformance:
cargo test conformance

test-all: fmt clippy test test-integration test-conformance

coverage:
cargo tarpaulin --all-targets --out html

audit:
cargo audit

check: fmt clippy test

## Pull latest proto files from BSR
Expand Down
71 changes: 59 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,27 +190,74 @@ cargo run --bin fuzz_client
| `WatchModeRegistry` | unimplemented |
| `WatchRoots` | unimplemented |

## Architecture

```
Client Request
|
[Transport/gRPC] -- server.rs, security.rs
|
[Coordination Kernel] -- runtime.rs
|
[Mode Registry] -- mode_registry.rs
| \
[Mode Logic] [Discovery]
mode/*.rs ListModes, GetManifest
|
[Storage Layer] -- storage.rs, log_store.rs
|
[Replay] -- replay.rs
```

See `docs/architecture.md` for detailed layer descriptions.

## Project structure

```text
runtime/
├── proto/ # protobuf schemas copied from the RFC/spec repository
├── proto/ # protobuf schemas copied from the RFC/spec repository
├── src/
│ ├── main.rs # server startup, TLS, persistence, auth wiring
│ ├── server.rs # gRPC adapter and request authentication
│ ├── runtime.rs # coordination kernel and mode dispatch
│ ├── security.rs # auth config, sender derivation, rate limiting
│ ├── session.rs # canonical SessionStart validation and session model
│ ├── registry.rs # session store with optional persistence
│ ├── log_store.rs # in-memory accepted-history log cache
│ ├── storage.rs # storage backend trait, FileBackend persistence, crash recovery
│ ├── replay.rs # session rebuild from append-only log
│ ├── mode/ # mode implementations
│ └── bin/ # local development example clients
│ ├── main.rs # server startup, TLS, persistence, auth wiring
│ ├── server.rs # gRPC adapter and request authentication
│ ├── runtime.rs # coordination kernel and mode dispatch
│ ├── mode_registry.rs # single source of truth for mode registration
│ ├── security.rs # auth config, sender derivation, rate limiting
│ ├── session.rs # canonical SessionStart validation and session model
│ ├── registry.rs # session store with optional persistence
│ ├── log_store.rs # in-memory accepted-history log cache
│ ├── storage.rs # storage backend trait, FileBackend, crash recovery
│ ├── replay.rs # session rebuild from append-only log
│ ├── mode/ # mode implementations
│ └── bin/ # local development example clients
├── tests/
│ ├── integration_mode_lifecycle.rs # full-stack integration tests
│ ├── replay_round_trip.rs # replay tests for all 5 modes
│ ├── conformance_loader.rs # JSON fixture runner
│ └── conformance/ # per-mode conformance fixtures
├── docs/
└── build.rs
```

## Troubleshooting

**TLS required error on startup**
Set `MACP_ALLOW_INSECURE=1` for local development, or provide `MACP_TLS_CERT_PATH` and `MACP_TLS_KEY_PATH` for production.

**`InvalidSessionId` error**
Session IDs must be UUID v4/v7 in hyphenated lowercase form (36 chars) or base64url tokens (22+ chars). Short or human-readable IDs like `"s1"` or `"my-session"` are rejected.

**`InvalidPayload` on `SessionStart`**
For standards-track modes, `SessionStartPayload` must include non-empty `participants`, `mode_version`, `configuration_version`, and a positive `ttl_ms`. Empty payloads are rejected.

**`Forbidden` error**
Check that the sender identity matches the session's participant list. For `Commitment` messages, only the session initiator is authorized. Verify your bearer token maps to the correct sender.

**`StorageFailed` error**
The runtime requires write access to `MACP_DATA_DIR`. Check directory permissions. Log append failures are fatal — the runtime will not acknowledge a message without a durable record.

**Proto drift / `make check-protos` failure**
Run `make sync-protos` to update local proto files from BSR.

## Development notes

- The RFC/spec repository remains the normative source for protocol semantics.
Expand Down
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ export MACP_MEMORY_ONLY=1
- `../README.md` — root-level quick start and configuration reference
- `examples.md` — updated local-development examples and canonical message patterns
- `protocol.md` — implementation notes and protocol surface summary
- `architecture.md` — runtime component layout
- `architecture.md` — runtime component layout and mode registry design
- `deployment.md` — production deployment guide, container notes, and environment reference
56 changes: 47 additions & 9 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,26 @@ Responsibilities:
- enforce lazy TTL expiry on reads and writes
- persist updated session snapshots

## 3. Mode layer (`src/mode/*`)
## 3. Mode Registry (`src/mode_registry.rs`)

The `ModeRegistry` is the single source of truth for mode dispatch, replay, and discovery. It eliminates the previous pattern of hardcoded mode maps in `runtime.rs`, `main.rs`, and `replay.rs`.

Responsibilities:

- register all mode implementations (standards-track and experimental)
- provide mode lookup for dispatch and replay
- provide standards-track mode names for `ListModes`
- provide mode descriptors for `ListModes` and `GetManifest`
- classify modes as standards-track or experimental

Key methods:

- `build_default()` — constructs the canonical mode set
- `get_mode(name)` — mode lookup for dispatch
- `standard_mode_names()` — drives `ListModes` and `GetManifest`
- `standard_mode_descriptors()` — drives `ListModes` response

## 4. Mode layer (`src/mode/*`)

Responsibilities:

Expand All @@ -34,14 +53,14 @@ Responsibilities:

Implemented modes:

- Decision
- Proposal
- Task
- Handoff
- Quorum
- MultiRound (experimental)
- Decision — enforced phase transitions (Proposal -> Evaluation -> Voting -> Committed)
- Proposal — negotiation with counterproposals, acceptance convergence, terminal rejections
- Task — delegated task with serial assignment, progress tracking, terminal reports
- Handoff — serial handoff offers with accept/decline disposition
- Quorum — threshold approval with ballots
- MultiRound (experimental) — iterative value convergence

## 4. Storage layer
## 5. Storage layer

### Storage backend (`src/storage.rs`)

Expand Down Expand Up @@ -71,7 +90,7 @@ In-memory cache of accepted-history logs. Stores:

On-disk persistence is handled by `FileBackend`, not by LogStore.

## 5. Security layer (`src/security.rs`)
## 6. Security layer (`src/security.rs`)

Responsibilities:

Expand All @@ -81,6 +100,25 @@ Responsibilities:
- enforce session-start policy
- enforce per-sender rate limits

## Architecture diagram

```
Client Request
|
[Transport/gRPC] -- server.rs, security.rs
|
[Coordination Kernel] -- runtime.rs
|
[Mode Registry] -- mode_registry.rs
| \
[Mode Logic] [Discovery]
mode/*.rs ListModes, GetManifest
|
[Storage Layer] -- storage.rs, log_store.rs
|
[Replay] -- replay.rs
```

## Request path summary

1. gRPC request arrives in `MacpServer`
Expand Down
76 changes: 76 additions & 0 deletions docs/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Deployment Guide

## Production checklist

1. **TLS certificates** — Set `MACP_TLS_CERT_PATH` and `MACP_TLS_KEY_PATH` to valid PEM files
2. **Auth tokens** — Create a `tokens.json` file mapping bearer tokens to agent identities and set `MACP_AUTH_TOKENS_FILE`
3. **Data directory** — Ensure `MACP_DATA_DIR` points to a directory with write permissions
4. **Bind address** — Set `MACP_BIND_ADDR` to the desired listen address (default: `127.0.0.1:50051`)

## Environment variable reference

| Variable | Required | Default | Description |
|---|---|---|---|
| `MACP_BIND_ADDR` | No | `127.0.0.1:50051` | gRPC listen address |
| `MACP_TLS_CERT_PATH` | Yes* | — | Path to TLS certificate PEM |
| `MACP_TLS_KEY_PATH` | Yes* | — | Path to TLS private key PEM |
| `MACP_AUTH_TOKENS_FILE` | No | — | Path to `tokens.json` for bearer token auth |
| `MACP_DATA_DIR` | No | `.macp-data` | Directory for session persistence |
| `MACP_MEMORY_ONLY` | No | — | Set to `1` to disable persistence |
| `MACP_ALLOW_INSECURE` | No | — | Set to `1` to allow plaintext (dev only) |
| `MACP_ALLOW_DEV_SENDER_HEADER` | No | — | Set to `1` to trust `x-macp-sender` header (dev only) |
| `MACP_MAX_OPEN_SESSIONS` | No | — | Per-initiator open session limit |
| `MACP_MAX_PAYLOAD_BYTES` | No | `1048576` | Maximum envelope payload size |

*TLS is required unless `MACP_ALLOW_INSECURE=1`.

## Persistence and crash recovery

When `MACP_MEMORY_ONLY` is not set:

- Each session gets a directory under `MACP_DATA_DIR/sessions/<session_id>/`
- An append-only `log.jsonl` records every accepted message and internal event
- A `session.json` snapshot is written on each state change (best-effort)
- On startup, all sessions are rebuilt from their `log.jsonl` files via `replay_session()`
- Log append failures are **fatal** — the runtime rejects the message rather than acknowledging without a durable record
- Atomic writes (tmp file + rename) prevent partial-write corruption

## Monitoring

- **stderr warnings** — Failed persistence operations, replay errors, and recovered session counts are logged to stderr
- **Session counts** — On startup, the runtime prints the number of replayed sessions
- **TTL expiry** — Sessions are lazily expired on next read or write; no background reaper
- **Rate limiting** — Per-sender rate limits for `SessionStart` and in-session messages

## Container deployment

```dockerfile
FROM rust:1.85 AS builder
WORKDIR /app
COPY . .
RUN apt-get update && apt-get install -y protobuf-compiler
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/macp-runtime /usr/local/bin/
EXPOSE 50051
VOLUME /data
ENV MACP_DATA_DIR=/data
CMD ["macp-runtime"]
```

Key considerations:

- Mount a persistent volume at `MACP_DATA_DIR` for session durability
- Expose port 50051 (or the configured `MACP_BIND_ADDR` port)
- Provide TLS certificates via mounted secrets
- Set `MACP_AUTH_TOKENS_FILE` to a mounted secrets file for production auth

## Dev tool prerequisites

For development, install these additional tools:

- `cargo-tarpaulin` — Coverage reporting (`cargo install cargo-tarpaulin`)
- `cargo-audit` — Dependency security auditing (`cargo install cargo-audit`)
- `buf` — Protocol buffer tooling (for proto sync and lint)
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod quorum_pb {
pub mod error;
pub mod log_store;
pub mod mode;
pub mod mode_registry;
pub mod registry;
pub mod replay;
pub mod runtime;
Expand Down
Loading
Loading