Commit Graph
2 Commits
Author SHA1 Message Date
vhaudiquet f96f8c4c1d feat(tools): the ingest credential — the event door, write-only
The agent token is deliberately barred from /v1/ingest: the events
are the inbound channel, and the model must not feed it itself. But
the same bar closes the door on every honest integration too — a
sidecar poller with events to hand in had to hold the human token,
which is wider than its job. This adds the missing class: a third
bearer, the ingest token, answering the event door and nothing else.

`ingest_allowed` is the whole credential in one function — one
route row, `POST /v1/ingest` — symmetric with `agent_allowed` and
suite-covered the same way: the mock and the daemon's real router
both answer the new checks (a push succeeds with the contract header;
a watch read and a consent answer are 403). The middleware classifier
gains the third arm, boot refuses an ingest token equal to either
other credential — one value playing two roles silently widens the
scoped one — and the daemon provisions it to tool shells as
`RHEMA_INGEST_TOKEN`, so a tool that only pushes events never sees
the wider agent token at all.

The door is the first half of the extensions story (rhema-tools spec
§8): a self-timed sidecar is now a tier-1 tool with a credential
narrow enough to hold.
2026-10-24 01:17:43 +02:00
vhaudiquet 75585f2dd3 feat(tools): the contract crate — allowlist, header, conformance suite
The client tools need one shared definition of what a tool may do,
and the daemon needs to enforce exactly that definition, not a
hand-copied copy of it. This crate is that definition, pure: no HTTP
client, no process — the allowlist as data, the version header as
constants, the request/response and error types, and the conformance
suite that drives any transport through the checks the contract
promises (the bearer gate, the version handshake, the allowlist's
denials, round-trips through every allowed route, one error shape).

`agent_allowed` is the whole security surface in one function:
method and path segments with `*` and `**`, exact otherwise — a
daemon's middleware asks it and nothing else, so the claw's scoping
and the tools' expectations cannot drift apart. The suite runs
against a mock router in this crate's tests and against the daemon's
real axum router in the claw's; same results is the definition of
implementing the contract.
2026-10-24 01:02:28 +02:00