Repository navigation
v1.7.0
Stories implemented since v1.6.0:
instance-git-identity: an instance mapping may namegit_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 ofconductor <conductor@localhost>or adaemon.kubernetes.enventry 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 namemodel_token_kind:api_key(the default, as before) orsubscription_token. Forsubscription_tokenthe runtime reads the value from its secrets source at each session's start and sets it asCLAUDE_CODE_OAUTH_TOKENin the environment of the harness's process; the session's settings name noapiKeyHelper. 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 untilPOST /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
201ofPOST /v1/instances/{name}/workstarts 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, areview <url>report, never merge). - The row's member
startsays why nothing started:ControllerLimit,AlreadyRunningorStartFailed. The call still answers201; 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'slinksthen holds the URL andlast_reportthe report's first line. Only anhttp://orhttps://URL is recorded, and only by areviewreport. A row with a review link is not finished: it readsDoneonce the session reportsdone. - The work call and
dispatch send-dispatchrefuse a brief with a NUL or a run of more than 255 backticks (InvalidBrief), and anissueorstart_refwith 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.0tov1.6.0every process a runtime started, an agent session among them, inherited the variableCONDUCTOR_API_TOKENwhere 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.0and later). Fromv1.7.0no process a runtime starts gets the variable. - Closed: the token as a file in a pod. From
v1.4.0tov1.6.0the token was also a file of the instance's Secret as the pod mounts it. Fromv1.7.0the 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,cacheandcheckouts; among the keys it changes aresources,placement,authority, andforge_tokenandmodel_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_keyreaches the harness through a helper command the session's settings name, and a session can run that command. Asubscription_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 aforge_token,model_token,sourcesorplacementyou 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