Room is a self-hosted guardrail control plane for coding agents. It publishes immutable, scoped rulesets; evaluates trusted analyzer receipts; governs typed MCP identities; and records durable audit events.
- ConnectRPC and Protobuf admin/agent APIs.
- Four focused MCP tools:
room_get_rules,room_analyze_plan,room_check_diff, androom_open_policy_control. - Scoped opaque bearer credentials with separate admin and agent roles.
- A strict external-analyzer boundary: policy never classifies prompt, plan, diff, title, or display text.
- SQLite persistence for ruleset versions, MCP policy, and append-only audit events.
- A review-intelligence catalog that turns typed review claims and durable outcomes into replayed, staged policy candidates.
- Lifecycle hooks that fail closed unless
ROOM_HOOK_FAIL_OPEN=trueis explicitly set.
Create an admin credential and a repository-scoped agent credential. Tokens are shown once; the credential registry stores only SHA-256 digests.
go run ./cmd/roomctl token issue \
--id local-admin --role admin --human-operator --output .room/admin.token
go run ./cmd/roomctl token issue \
--id review-automation --role reviewer --output .room/reviewer.token
go run ./cmd/roomctl token issue \
--id local-agent --role agent \
--workspace local --repository haasonsaas/room --agent codex \
--output .room/agent.tokenConfigure a trusted analyzer executable, then start Room:
ROOM_ANALYZER_EXECUTABLE=/absolute/path/to/analyzer \
ROOM_ANALYZER_ID=company.security-analyzer \
ROOM_ANALYZER_VERSION=1 \
ROOM_ANALYZER_COVERED_SIGNALS='["SIGNAL_KIND_RUST_UNSAFE_WITHOUT_SAFETY_CONTRACT"]' \
go run ./cmd/roomdWithout an analyzer, evaluations return INDETERMINATE; enforcement callers
block that result. For local development only, authentication may be disabled on
a loopback listener with ROOM_AUTH_MODE=disabled.
Open http://127.0.0.1:8787 and paste the admin token into the in-memory token
field. The dashboard never persists tokens.
The Rules catalog includes learned candidates beside authored rules. Review
findings are ingested with a typed claim kind, source identity, confidence, and
cost metadata. Fixes, resolutions, reactions, merges, reverts, regressions, and
agent adjudications are recorded as separate durable evidence. Room can then
infer candidates, replay them over the stored corpus, tune their confidence
threshold, and advance them through draft, shadow, warn, and block stages.
All blocking policies require an authenticated human operator and explicit
approval; pause and rollback remain human-only emergency controls.
Review automation should use the least-privilege reviewer credential. It can
ingest evidence, infer, replay, tune, and advance eligible non-protected staged
policies, but it cannot directly edit or publish arbitrary rulesets, change MCP
trust policy, or exercise human-only controls.
Run the MCP sidecar separately:
ROOM_SERVER_URL=http://127.0.0.1:8787 \
ROOM_CONTROL_PLANE_URL=http://127.0.0.1:8787 \
go run ./cmd/room-mcpIt listens at http://127.0.0.1:8788/mcp by default. MCP callers must send the
same scoped agent bearer credential; the sidecar validates and forwards each
request's credential, and binds MCP sessions to that principal. room-mcp-call
reads ROOM_TOKEN_FILE for the caller.
Room uses MCP elicitation only from typed evaluation state. A blocking
evaluation with required checks, evidence, remediation, or analyzer gaps can
offer a closed-choice form (revise, run_required_checks,
provide_evidence, or open_control_plane). Allow decisions and blocking
decisions without a typed next-step contract do not elicit. Accept, decline,
cancel, unsupported-client, and error outcomes are written to the append-only
audit log and bound to the original evaluation and authenticated agent scope.
room_open_policy_control uses URL-mode elicitation for the human-only block,
pause, and rollback controls. Its inputs are a candidate ID, target rollout
stage, and the candidate's expected updated_at value. The sidecar creates the
URL only from ROOM_CONTROL_PLANE_URL, verifies candidate scope and freshness,
and audits both the offer and result. Opening the dashboard selects the
candidate and Rollout tab but never changes policy. The actual transition still
requires the dashboard's human-operator credential, confirmation, and
compare-and-swap checks. Do not put credentials in the control-plane URL.
For Codex, keep general approvals locked down while allowing the user to review MCP elicitations:
approval_policy = { granular = { sandbox_approval = false, rules = false, mcp_elicitations = true, request_permissions = false, skill_approval = false } }
approvals_reviewer = "user"For reliable native Codex tool discovery, prefer the room-mcp-stdio entrypoint
through a Codex plugin. It reads the agent token from a private
ROOM_TOKEN_FILE, so the Codex GUI process does not need to inherit a bearer
token environment variable. Because the stdio server is Codex's direct MCP
peer, Room form and URL elicitations render in the native Codex UI.
go build -o ~/.local/bin/room-mcp-stdio ./cmd/room-mcp-stdioThe plugin MCP entry should invoke that binary with ROOM_TOKEN_FILE,
ROOM_SERVER_URL, and ROOM_CONTROL_PLANE_URL in its non-secret environment.
After installing or updating the plugin, start a new Codex task so its MCP tool
catalog is rebuilt from the plugin.
ROOM_TOKEN_FILE=.room/agent.token go run ./cmd/roomctl rules
ROOM_TOKEN_FILE=.room/agent.token go run ./cmd/roomctl hook pre-tool < hook.json
ROOM_TOKEN_FILE=.room/admin.token go run ./cmd/roomctl publishThe ruleset cache is a private, scoped advisory cache. Evaluations are performed by Room and do not fall back to legacy local text heuristics when the server is unavailable.
Credential registry changes are loaded live. Existing IDs cannot be reissued
through the bootstrap command; scope changes require the authenticated,
human-confirmed dashboard workflow, which rotates the token and records the
mutation receipt atomically without restarting roomd or room-mcp.
See architecture, analyzer contract, rules, and hooks.
buf lint
buf generate
go test ./...
go test -race ./...
go vet ./...