Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ jobs:
run: |
mkdir -p /tmp/sdist && tar xzf dist/*.tar.gz -C /tmp/sdist --strip-components=1
cd /tmp/sdist
pip install ".[dev]"
# With the extras: without them the gateway, ACS, elicitation and OTel modules
# skip, and this gate guards an upload to a version number that can never be
# reused. It should be no weaker than the one on main.
pip install ".[dev,gateway,otel]"
pytest -q

- name: The quick start works from the wheel
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ Everything below ships. `pip install ctrlrun` still installs nothing but `pyyaml
stable interface. The v0.1.0 schemas say otherwise, so the adapter ships. The no-claim rule
is untouched.

### Deprecated

- **`ctrlrun gateway --principal-from-client-info` — removed in 0.3. Use
`--principal-header`.** It takes the agent's name from `_meta["io.modelcontextprotocol/
clientInfo"]`, which the MCP revision says is self-reported and *"SHOULD NOT"* be relied on
for security decisions. It is offerable in 0.2 only because of a fact that stops being true:
a v0.1 policy cannot address the principal at all (`SPEC-v0.1.md` §3.2 refuses `agent_eq`
and every other reserved name at load), so an unauthenticated principal misattributes
evidence and cannot widen an outcome. v0.3's authority model makes the principal an
authorization input, at which point a self-reported name cannot be one. The flag warns at
startup and its `--help` says so.

### Removed

- `SQLiteStateStore.journal`, and the `EventLog` class behind it. `JSONLEventSink` is that
Expand Down
3 changes: 2 additions & 1 deletion src/ctrlrun/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@ def _approval_dict(record: ApprovalRecord) -> dict[str, Any]:
@click.option(
"--principal-from-client-info",
is_flag=True,
help="Take it from clientInfo, which the protocol does not verify. Removed in v0.3.",
help="DEPRECATED, removed in 0.3 — use --principal-header. Takes the agent from "
"clientInfo, which the protocol does not verify.",
)
@click.option("--user-header", default=None, help="Take principal.user from this header.")
@click.option("--environment", default="production", show_default=True)
Expand Down
6 changes: 4 additions & 2 deletions src/ctrlrun/gateway/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,8 +900,10 @@ def serve_forever(gateway: Gateway) -> None:
# address the principal at all, so an unauthenticated one misattributes evidence and
# cannot widen an outcome. It is removed in v0.3.
_LOG.warning(
"--principal-from-client-info trusts a self-reported name; it is attribution on "
"a receipt, never an authorization input, and it is removed in v0.3"
"--principal-from-client-info is DEPRECATED and will be removed in 0.3; use "
"--principal-header instead. It trusts a name the protocol does not verify, "
"which is attribution on a receipt and never an authorization input — and v0.3's "
"authority model makes the principal an authorization input (SPEC-v0.2 §6.5)"
)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
Expand Down
Loading