Commit Graph
75 Commits
Author SHA1 Message Date
kyle ac1ecacadc toolkit: shell
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-23 00:06:44 -07:00
kyle 0d444dd5bf toolkit: the tool program, read_file and write_file
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-23 00:00:03 -07:00
kyle 644fda14da Refuse grant paths that cannot be mounted
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-22 23:06:09 -07:00
kyle bf9e79ac21 Seal the fetch target: one value holds the URL and its host
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-22 23:03:49 -07:00
kyle f095cca1ee Move the tools' arguments and the host rules to proto
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-22 22:59:40 -07:00
kyleandClaude Opus 5.5 fc8befaf5b bxctl audit verify: name the log-like files it did not check
A .jsonl file whose name is not a date is ignored by brokerd and by verify;
it is now listed, so "ok" does not seem to cover it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:52:54 -07:00
kyleandClaude Opus 5.5 cfa02479c1 Test --accept-break on a real break through the brokerd binary
A plain start refuses, --accept-break records the break and serves, and the
next plain start serves. The given tests covered this only at library level.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:52:54 -07:00
kyleandClaude Opus 5.5 bb4d7c0919 bxctl: escape frame errors; say the outcome is unknown after a timeout
From the independent review of task 23. serde quotes a bad frame's text after
decoding, so a compromised peer could put escape sequences in it. A timed-out
admin request now says whether brokerd acted is unknown, since it may have.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:48:24 -07:00
kyleandClaude Opus 5.5 e08deb39a6 brokerd: recover a torn line in place, real dates only, bounded ttl, EMFILE
From the independent review of task 23. A torn last line followed by an empty
later file had its recovery written into the later file, which broke the
chain for good; the line is now ended in its own file. The log-name rule
takes months 01 to 12 and days 01 to 31 only. [approvals] ttl_ms is limited
to a day, the longest loopd waits after a pending frame. Running out of file
descriptors or memory pauses the listener instead of stopping brokerd (the
errors the previous fix skipped do not occur on Linux). args.rs's doc fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:48:24 -07:00
kyleandClaude Opus 5.5 ba369f82ba brokerd: refuse / and a symbolic link as a socket's directory
brokerd makes a socket's directory 0700. With a socket directly in / it would
chmod /, and through a symbolic link it would change the link's target. Both
are now refused at start with #brokerd-start-failed. Without the fix the link
case started and served, with the shared directory made private.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:44:29 -07:00
kyleandClaude Opus 5.5 f6841f1155 Record straylight's new slot layout: four slots over one 262144-token pool
loopd's self-test caught the change (context per slot 131072 -> 262144,
slots 2 -> 4). The device tests keep the expectation in one constant, and
the M3a script matches it; verify-device passes 6 of 6 and the M3a device
check passes. The inference contract notes which M0 findings rest on the old
layout and need re-measuring.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:11:23 -07:00
kyleandClaude Opus 5.5 a75a5453e9 bxctl: escape error details, time out on admin.sock, AdminError::Io
M3a review findings 9, 10, 12, 13. A retrying error and every error detail
can carry the inference server's body, so they are escaped like model text.
Admin requests wait at most 30 s, so a stuck brokerd cannot hang bxctl or a
chat turn. A failed write is AdminError::Io and stops handle_pending instead
of being answered with another write. The usage line says what audit verify
checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:09:53 -07:00
kyleandClaude Opus 5.5 70d582acf5 BrokerPort: cap the wait after a pending frame at a day
M3a review finding 8. The wait was expires plus the timeout with no bound,
and the "expiry too far away" guard could not fire, so a far expiry (which
brokerd produces when now + ttl_ms does not fit) parked a turn for ever.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:08:48 -07:00
kyleandClaude Opus 5.5 33994d0dc6 loopd, bxctl, inferproxy: read args_os instead of panicking
M3a review finding 7, for the other three roles. loopd keeps its config path
as a path; bxctl and inferproxy take text arguments and answer one that is
not UTF-8 with their usage.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:07:59 -07:00
kyleandClaude Opus 5.5 6af89f7c60 brokerd: runbook pointers for startup failures, no thread panics, args_os
M3a review findings 3, 5 (the cast), 6, 7 (brokerd), 11. A config, directory
or socket failure at start now ends with docs/runbook.md#brokerd-start-failed,
and losing a listener with #brokerd-listener-lost; both entries are new.
Threads start through thread::Builder, so a refused thread is reported
instead of silently killing a listener; an aborted connection no longer stops
the daemon. brokerd reads args_os and keeps the config path as a path. The
"requester went away" result is recorded at the time it happens.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:07:59 -07:00
kyleandClaude Opus 5.5 eed0a221ca Audit writer: open a record-less log, share the log-name rule, keep the lock file
M3a review findings 1, 2, 4 and part of 5. One empty log file made brokerd
panic at startup (files[len - 2]); it now opens as an empty log. brokerd's
name check tested one month digit, so a file bxctl ignored could become
brokerd's latest file; both now use proto::is_audit_log_name. Writer no
longer unlinks audit/.lock, which opened a two-writer window. No unwrap in
short_check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:05:13 -07:00
kyle 2249f7fd91 Add the end-to-end test: loopd against the real brokerd binary
Implemented-By: Grok 4.6
2026-09-22 20:25:40 -07:00
kyle f1f17a171f Show and answer approvals in bxctl chat
Implemented-By: Grok 4.6
2026-09-22 20:25:40 -07:00
kyleandClaude Opus 5.5 57dc7899a9 brokerd: close the connection after the final frame again
hold_open (d21baa2) kept each connection open for up to two seconds
after its final frame, reading and dropping anything the peer sent, to
hide a test client that set a read timeout after the handler had closed.
On macOS that call fails with EINVAL; the clients now allow for it
(00a85c1, d7009dc), so the handler goes back to closing at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 18:32:31 -07:00
kyleandClaude Opus 5.5 d7009dc488 brokerd tests: read the final frame after the handler closes, on macOS
The test client set its read timeout before every frame. On macOS that
fails with EINVAL once the handler has sent its final frame and closed,
so every admin test that reads a second frame failed there (40 runs of
40 at 2408e2c). The frame is already buffered, so the client now takes
that one refusal as the peer having closed and reads it. The plan's copy
changes with it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 18:28:18 -07:00
kyleandClaude Opus 5.5 00a85c124d loopd: read on a socket whose peer closed, instead of failing on macOS
macOS refuses every socket option with EINVAL once the peer has closed
(XNU sosetoptlock, bsd/kern/uipc_socket.c), even with unread data still
buffered. loopd set a read timeout before each read, so on macOS every
frame or response that arrived just before the peer closed was lost:
BrokerPort reported the broker unavailable with "os error 22", and the
llama client failed the same way. Twelve loopd test binaries failed on
macOS; Linux never refuses, so the gate on Talos did not see it.

Both places now go through socket::set_read_timeout, which on Apple
targets takes that one refusal as success: a socket shut in both
directions returns its data or the end at once and cannot block. A
zero timeout is still an error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 18:27:31 -07:00
kyle d21baa2954 Debug collection: hold_open fix, grants.rs refactor, and DEBUG-HANDOFF findings
Debug collection commit for the brokerd admin-test hang investigation (M3a
task 19). Contains:

- crates/brokerd/src/broker.rs: hold_open helper (HOLD_OPEN = 2s read-timeout
  loop) applied after forbid and after the final send in broker::handle
- crates/brokerd/src/admin.rs: hold_open applied after forbid and after the
  final send in admin::handle
- crates/brokerd/src/grants.rs: check_tool_constraints refactor (match guard
  instead of nested if)
- docs/M3a/DEBUG-HANDOFF.md: investigation results added (310 runs, zero
  hangs reproduced; stalled fsync cannot be fixed without dropping durability)

The implementer log row was committed separately (a6d81d9).

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-22 15:39:20 -07:00
kyle 2408e2c622 Add bxctl audit verify
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-21 01:35:08 -07:00
kyle 2d94067b52 Add bxctl approvals, approve, refuse and grants check
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 23:25:05 -07:00
kyle 469be2c0a1 Add BrokerPort: loopd asks brokerd for every tool call
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 19:40:35 -07:00
kyle 1ceaa36b9b Give loopd's tool port approvals, its own clock and plain denials
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 17:43:05 -07:00
kyle 3ecaef3c8b Add brokerd serve: startup, both sockets, and the expiry thread
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 17:24:57 -07:00
kyle d250678355 Handle approvals, refusals and grant checks on admin.sock
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 13:54:01 -07:00
kyle e68e626cd7 Handle a tool request from decision to answer
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 12:59:42 -07:00
kyle cff22ce579 Add the ledger: the audit writer and session state behind one lock
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-20 03:30:37 -07:00
kyle caf8fd6eca Add the table of pending approvals
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 16:48:09 -07:00
kyle 57734ebb9a Replace the runner stub with the Runtime seam and RunSpec
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 16:43:56 -07:00
kyle ded7eb8c50 Add the audit writer with its startup check
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 16:32:37 -07:00
kyle 726ce1f766 Keep each session's taint and untrusted flag in a file
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 03:08:46 -07:00
kyle e1e6c7a338 Decide tool calls against grants, taint and time
Implemented decide and redecide in crates/brokerd/src/policy.rs:
SessionState, Label, Denial, private Matched, and Decision/Ask (private
fields, Debug only, nine getters each) with the Outcome enum. decide
rejects an unknown tool (args not parsed) and malformed arguments before
matching, then runs the M1-M5 matching pass in id order and returns
Allowed/Ask/Denied by the winner's mode; redecide re-runs matching now
and rebuilds the Decision from the Ask. Seven doctests (six compile_fail,
one compiling) guard the two facts. policy 7, policy_matching 10,
policy_redecide 7, policy_property 4, doc 7 all pass; make gate ok.

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 03:05:01 -07:00
kyle e2ab29aa15 Load grant files, failing closed on any invalid file
crates/brokerd/src/grants.rs reads grants/*.toml into a GrantSet: load reports every problem in every file and returns either a complete valid set or the full problem list, never a partial one; from_grants sorts by id and collects every rule-2..9 problem; render prints each problem then the runbook pointer. All 17 grants tests pass; make gate prints gate: ok.

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 02:50:26 -07:00
kyle 9150effc8e Add typed tool arguments and the form checks for paths, hosts and URLs
Implemented brokerd::args: a pure module (no I/O, no clock) that parses tool
arguments into a typed ToolArgs and checks the form of paths, hosts and URLs.
Four private deny_unknown_fields structs drive parse and canonical_json; path,
cwd and url are validated as written and never normalised. All 13 args tests
pass and make gate prints gate: ok.

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 02:37:31 -07:00
kyle 5502de1c90 Add brokerd's configuration
Implemented crates/brokerd/src/config.rs: typed Paths, Sockets, Approvals and
Config with serde(deny_unknown_fields, default) on every struct, hand-written
ConfigError (Read/Parse) with Display and std::error::Error, and the parse/load/
broker_socket/admin_socket/audit_dir/state_dir methods. Added serde, serde_json
and toml to crates/brokerd/Cargo.toml, registered pub mod config; in lib.rs,
added brokerd to the serde and serde_json Used-by cells in docs/dependencies.md,
and copied the given test and six fixtures byte-identical. 7 config tests pass;
make gate prints gate: ok.

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 02:24:15 -07:00
kyle d01b2ef2d9 Add the audit chain verifier
Implemented proto::ChainVerifier as a pure line-holding state machine:
each line is judged only once the next has arrived, so a Recovery record
can mark the line before it not-a-record. Adds ChainFailure, TornTail,
ChainReport, Location and ChainVerifier, wired through lib.rs and the
matching re-export in audit.rs. 13 chain tests pass; make gate prints
gate: ok.

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 02:19:45 -07:00
kyle e8568edf7e Add the admin messages, approval ids as numbers, and two turn events
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 00:28:46 -07:00
kyle c6395b16f4 Replace the audit record with chained audit events
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-19 00:22:25 -07:00
kyle c060c80c7e Fix four review findings: busy release, poison recovery, core.md errors, chat loop
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 21:06:58 -07:00
kyle 34951084cc Extend on-device verification to the agent loop
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 20:38:56 -07:00
kyle 06cda69509 Add bxctl chat
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 20:24:26 -07:00
kyle eb8fc5f920 Add the loopd serve command
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 19:50:32 -07:00
kyle 8bc835f623 Add the channel server on loop.sock
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 19:43:12 -07:00
kyle 9063cf958e Add the turn loop with its limits
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 19:14:52 -07:00
kyle 9704a64507 Add the session store: baseline file and append-only log
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 18:42:00 -07:00
kyle b152ba3cd3 Add the baseline and the log replay function
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 18:31:33 -07:00
kyle fd7ade9689 Add the tool port, registry, dispatch and the fake tools
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
2026-09-18 18:12:45 -07:00