Skip to content

v1.7.0

Choose a tag to compare

@b10x-bot b10x-bot released this 13 Nov 07:29
· 39 commits to main since this release

Stories implemented since v1.6.0:

  • instance-git-identity: an instance mapping may name git_identity: {name, email}. Where the daemon runs its instances as pods, every commit in that instance's pod carries it as author and committer, in place of conductor <conductor@localhost> or a daemon.kubernetes.env entry of those variables. A change of the member alone replaces the pod once. GET /v1/instances/{name} answers it.
  • model-token-kind: an instance mapping may name model_token_kind: api_key (the default, as before) or subscription_token. For subscription_token the runtime reads the value from its secrets source at each session's start and sets it as CLAUDE_CODE_OAUTH_TOKEN in the environment of the harness's process; the session's settings name no apiKeyHelper. The value is in no command line, settings file or log line; it is in the environment of the harness's process, as that route requires. A placement that opens a box refuses the kind. A running pod keeps the value it started with until POST /v1/instances/{name}/restart; nothing refreshes a subscription token. GET /v1/instances/{name} answers the kind.

Built in this release under control-plane-work-handler, which stays a draft until the operator accepts it:

  • The work call starts the work. A 201 of POST /v1/instances/{name}/work starts one controller session for the repository when it has none; the session gets the dispatch's members and the brief, verbatim, in a fenced block. A repository with no charter gets a default one (merge: human, one unit of work, a review <url> report, never merge).
  • The row's member start says why nothing started: ControllerLimit, AlreadyRunning or StartFailed. The call still answers 201; the loop tries the start again at its next cycle, and leaves a unit to the operator after three starts of it failed at the launch.
  • A session reports [REPORT <repo> <dispatch-id>] review <url>; the row's links then holds the URL and last_report the report's first line. Only an http:// or https:// URL is recorded, and only by a review report. A row with a review link is not finished: it reads Done once the session reports done.
  • The work call and dispatch send-dispatch refuse a brief with a NUL or a run of more than 255 backticks (InvalidBrief), and an issue or start_ref with a control character (C0 or C1), U+2028, U+2029, a bidirectional control or a backtick (InvalidWorkRequest).
  • The Kubernetes guide has a section "The work call": what a call starts, the default charter, the row's start, the limit, and how the review link comes back.

Security

This release hardens what a runtime hands to the processes it starts.

  • Closed: the runtime's API token in the environment of its children. From v1.0.0 to v1.6.0 every process a runtime started, an agent session among them, inherited the variable CONDUCTOR_API_TOKEN where the daemon had given the runtime one: an instance the daemon starts as a unit, and an instance that runs as a pod (v1.4.0 and later). From v1.7.0 no process a runtime starts gets the variable.
  • Closed: the token as a file in a pod. From v1.4.0 to v1.6.0 the token was also a file of the instance's Secret as the pod mounts it. From v1.7.0 the mount holds no such file.
  • What the token admits. The writes of that one runtime's API: answer a question, write a plan, hand work to the instance, and change the instance's settings. The settings write changes every key of that instance's mapping except name, session_prefix, dashboard_port, records, state, cache and checkouts; among the keys it changes are sources, placement, authority, and forge_token and model_token, each of which holds the name of a secret and never a value. No call of that API returns a secret's value; its settings read shows the names of the secrets the instance refers to. The runtime of another instance refuses the token, and so does the daemon's API.
  • What stays, as the trust model. A session and the runtime that started it run as one user, so a session can act as its runtime towards that runtime's own API. In a pod that reach ends at the instance: the file the settings write changes is the pod's own copy, which the runtime replaces at each start. On a host where the daemon starts instances as units, a session runs as the daemon's user; it follows that it has that user's access to the daemon's files and to the other instances the daemon starts. In a box, the daemon's config file is mounted read-only and a session there can read it: that file names every instance of the daemon and each secret a mapping refers to, by name, and holds no secret's value.
  • A session can read the model token its instance runs on. This was so before this release and stays so. An api_key reaches the harness through a helper command the session's settings name, and a session can run that command. A subscription_token (new in this release) is in the environment of the harness's process, which runs as the session's user. Whether the harness keeps that variable from a session's tools is not established; treat the model token as readable by the session.
  • What to do on an older version. Upgrade, then restart each instance (POST /v1/instances/{name}/restart, or stop and start it): its runtime gets a new token, and an instance that runs as a pod gets a new pod. A restart without the upgrade changes the token, and a session started after it inherits the new one. A restart replaces the runtime's token only. Where you suspect that a session used the token on an older version, read the instance's settings for a forge_token, model_token, sources or placement you did not set, and replace the forge and model tokens of that instance yourself.
  • Found by a security review of this release's own change. We have no report of its use.

Upgrade from v1.6.0: the daemon replaces each instance pod once at its first pass, because the pod's rendering changed. No change to a config file is needed.

Known limits: those of v1.6.0 stand, except that the work call now starts a controller. Not shown yet: the work call against an instance pod on a cluster, an agent session started inside an instance pod with a real model token, a push from a pod with a real forge token; the changed mount of the instance's Secret is held by a test against a stand-in API server and was not run on a cluster. max_working does not count a session started by hand beside the runtime. The stop after three failed starts holds for a unit's start; the loop's other start of a controller retries as before.

Verified before the release: every test of conductor-cli and conductor-docs at 930a71f, "2244 tests run: 2241 passed (3 slow), 3 failed, 14 skipped", with fmt, clippy under the default toolchain and under 1.100.0, and the generated docs check at exit 0. The three failed cases were a fixture directory of a new test that nothing removed (fixed in fe4a559) and two timing cases of build run and of the dashboard's cache; rerun at fe4a559, each timing case passed alone and their four test files together, "108 tests run: 108 passed, 0 skipped". eb4e0bc adds a plan commit to that, and the release head 951330f a change to one test file of the collector, whose stand-in program ended at once on the runner image ubuntu-26.04 and failed the pull request's check twice on eb4e0bc. The wide run was not repeated on either; fmt, clippy under both toolchains and that test file ran on the release head, "30 tests run: 30 passed, 1 skipped". The four checks of the pull request ran on the release head, its task check on ubuntu-26.04: "2300 tests run: 2300 passed (3 slow), 14 skipped".

Container image

ghcr.io/beyond10x/conductor:1.7.0 at digest sha256:3c55c9519577386a097045929ac01b76db50137ee86d50681bb1bb559feb09de

docker run --rm ghcr.io/beyond10x/conductor:1.7.0 conductor --version