commit signing hell
This commit is contained in:
+8
-7
@@ -1829,16 +1829,17 @@ components:
|
|||||||
ClientRequest:
|
ClientRequest:
|
||||||
type: object
|
type: object
|
||||||
required: [id, op]
|
required: [id, op]
|
||||||
description: Work a task agent asks a connected Mac to do, sent over /api/clients/connect.
|
description: Work a task agent, or a merge, asks a connected Mac to do, sent over /api/clients/connect.
|
||||||
properties:
|
properties:
|
||||||
id: { type: string }
|
id: { type: string }
|
||||||
op: { type: string, enum: [bash, upload, download, read_file, write_file, hands] }
|
op: { type: string, enum: [bash, upload, download, read_file, write_file, hands, sign] }
|
||||||
command: { type: string, description: "bash: run with zsh as a login shell in the home directory, in its own process group." }
|
command: { type: string, description: "bash: run with zsh as a login shell in the home directory, in its own process group." }
|
||||||
timeout_ms: { type: integer, description: "bash: kill the process group after this long." }
|
timeout_ms: { type: integer, description: "bash, sign: kill the process group after this long." }
|
||||||
|
ident: { type: string, description: "sign: the committer, as \"Name <email>\", to sign as when the Mac's git config has no user.signingkey." }
|
||||||
transfer: { type: string, description: "upload: the transfer id to get from /api/clients/transfers/{transferId}. download: the transfer id to put the archive to." }
|
transfer: { type: string, description: "upload: the transfer id to get from /api/clients/transfers/{transferId}. download: the transfer id to put the archive to." }
|
||||||
dest: { type: string, description: "upload: where the archive's one entry ends up, absolute or starting with ~/; whatever is there is replaced." }
|
dest: { type: string, description: "upload: where the archive's one entry ends up, absolute or starting with ~/; whatever is there is replaced." }
|
||||||
path: { type: string, description: "download, read_file, write_file: the file (or, for download, file or directory) on the client, absolute or starting with ~/." }
|
path: { type: string, description: "download, read_file, write_file: the file (or, for download, file or directory) on the client, absolute or starting with ~/." }
|
||||||
data: { type: string, format: byte, description: "write_file: the file's new contents; parent directories are created." }
|
data: { type: string, format: byte, description: "write_file: the file's new contents; parent directories are created. sign: the unsigned commit object, which the Mac signs with the program its git config names for its gpg.format (openpgp or x509), called as git calls it." }
|
||||||
tool: { type: string, description: "hands: the byre-hands tool to call." }
|
tool: { type: string, description: "hands: the byre-hands tool to call." }
|
||||||
arguments:
|
arguments:
|
||||||
type: object
|
type: object
|
||||||
@@ -1851,9 +1852,9 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
id: { type: string, description: The request's id. }
|
id: { type: string, description: The request's id. }
|
||||||
error: { type: string, description: Set when the request couldn't be carried out. }
|
error: { type: string, description: Set when the request couldn't be carried out. }
|
||||||
exit_code: { type: integer, description: "bash: the exit status, -1 when it was killed by its timeout." }
|
exit_code: { type: integer, description: "bash, sign: the exit status, -1 when it was killed by its timeout." }
|
||||||
output: { type: string, description: "bash: stdout and stderr together, at most their last 64 KiB." }
|
output: { type: string, description: "bash: stdout and stderr together, at most their last 64 KiB. sign: the signing program's stderr, where it writes its GnuPG status lines, and the login shell's output." }
|
||||||
data: { type: string, format: byte, description: "read_file: the file's contents." }
|
data: { type: string, format: byte, description: "read_file: the file's contents. sign: the detached signature." }
|
||||||
result:
|
result:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
|
|||||||
+5
-1
@@ -75,7 +75,7 @@ A task's `merge_title` is the title of the squash commit Merge makes. It is the
|
|||||||
|
|
||||||
A task's participants are the users who put a message bubble with their name in its transcript, derived from its events (`Store.Participants`): the actors of its `user.message` events, joined to `users` so `byre` and `agent` drop out. That covers messages sent to the agent and inbox answers, whose delivery message carries the name of the first responder in it (`Deliver` in `internal/agents/inbox.go`); dismissing an item puts nothing in the transcript and doesn't count. A fork's participants also include the user who forked it, the actor of its `task.created` event (whose payload carries `forked_from`), whose "forked this task" note opens its transcript. They are ordered by their first such event. `GET /api/tasks/{id}/co-authors` returns the participants other than the caller, with their git name and email from `PUT /api/me` (empty for a user who hasn't set one). The Merge confirm fetches it and lists `Name <email>` for each co-author, and separately those with no git identity, who get no trailer.
|
A task's participants are the users who put a message bubble with their name in its transcript, derived from its events (`Store.Participants`): the actors of its `user.message` events, joined to `users` so `byre` and `agent` drop out. That covers messages sent to the agent and inbox answers, whose delivery message carries the name of the first responder in it (`Deliver` in `internal/agents/inbox.go`); dismissing an item puts nothing in the transcript and doesn't count. A fork's participants also include the user who forked it, the actor of its `task.created` event (whose payload carries `forked_from`), whose "forked this task" note opens its transcript. They are ordered by their first such event. `GET /api/tasks/{id}/co-authors` returns the participants other than the caller, with their git name and email from `PUT /api/me` (empty for a user who hasn't set one). The Merge confirm fetches it and lists `Name <email>` for each co-author, and separately those with no git identity, who get no trailer.
|
||||||
|
|
||||||
`POST /api/tasks/{id}/merge` starts the same turn, with the prompt's closing invitation to revise the title left out since the human just confirmed it and a request to call `set_task_summary` in its place, and records in `tasks.merging_by` that the task is to be merged by the requesting user (emitting `task.merge_started` with that user as actor, not drawn), who must have a git identity (400 otherwise). Every turn end of the task's session reads and clears it. It survives a byre restart during the turn: startup resumes the session with the restart notice, and the merge happens when that resumed turn ends. When that turn ends normally (not interrupted, not an error), the transaction that ends it (`Store.EndTurn`) moves the login from `merging_by` to `tasks.finalizing`, so the task stays **Merging** while byre squashes, pushes and closes it. While `finalizing` is set, the task's implementer session can't start a turn (a message or Sync is a 409; the store refuses the `running` transition in `transitionSession`), Discard and merging into a parent are refused as merging, `set_merge_title` is refused, and the task can't get new subtasks (`ErrParentNotOpen`). Closing the task clears it; a failed squash or push clears it (emitting `task.merge_ended`) before filing the error, so the queued inbox responses can be delivered. Just before pushing, byre records the squash commit and its notes commit in `tasks.finalizing_commit` and `tasks.finalizing_notes` (`Store.SetFinalizingCommit`); when the merge changes nothing, it records that in `tasks.finalizing_unchanged` (`Store.SetFinalizingUnchanged`) before removing the worktree. A push the remote rejected as non-fast-forward clears the marker as a failed push. Any other push error is ambiguous, since the remote may have taken the push before the connection dropped, so byre runs `git fetch origin` and checks whether the commit is an ancestor of `origin/<default branch>`: when it is, it finishes as if the push had succeeded; when it isn't, the push failed; when the check itself fails, the marker stays. A failure after the push was accepted (updating the checkout's notes, fast-forwarding it, removing the worktree, closing the task), or while closing an unchanged merge, keeps the marker and files an error saying byre finishes the merge when it next starts. At startup, `recoverFinalizing` (`internal/agents/mergein.go`) takes each task with `finalizing` set: an unchanged merge is finished by removing the worktree and closing the task with no commit; with a recorded commit, it runs `git fetch origin` and, when the commit is an ancestor of `origin/<default branch>`, finishes the merge (`closePushed`: advance the checkout's `refs/notes/byre` to the notes commit unless it already contains it, `git merge --ff-only`, remove the worktree, close the task), each step a no-op when already done. When the commit is not on the remote, or none was recorded, it clears the marker and files an error on the task saying to merge again; when the fetch, the ancestry check or a finishing step fails, it keeps the marker for the next start and files the error. `sqlite3 byre.db "SELECT id, finalizing, finalizing_commit FROM tasks WHERE finalizing != ''"` lists merges being finished. Then byre squashes the worktree, now containing the default branch, onto it with `git merge-tree --merge-base`, makes one commit titled with the task's `merge_title` with `git commit-tree` (passing `-S` when the checkout's `commit.gpgsign` is true, since `commit-tree` ignores that setting), authored and committed by the merging user with no agent attribution, its body one `Co-authored-by: Name <email>` trailer per co-author with a git identity (`squashMessage` in `internal/agents/mergein.go`; the co-authors are computed when the squash runs, relative to the user who clicked Merge), and pushes that commit to the default branch on the `origin` remote of the project's checkout together with its note (below), in one atomic push (`git push --atomic --porcelain origin <commit>:refs/heads/<default branch> <notes commit>:refs/notes/byre`, run in the checkout), so either both land or neither does. Only once the push is accepted does it point the checkout's `refs/notes/byre` at the notes commit, run `git merge --ff-only` in the project's checkout (which must have the default branch checked out) and close the task; the `task.merged` event carries the commit and `pushed` (`origin/<default branch>`), and the transcript shows both. When the merge changes nothing, no commit is made and nothing is pushed.
|
`POST /api/tasks/{id}/merge` starts the same turn, with the prompt's closing invitation to revise the title left out since the human just confirmed it and a request to call `set_task_summary` in its place, and records in `tasks.merging_by` that the task is to be merged by the requesting user (emitting `task.merge_started` with that user as actor, not drawn), who must have a git identity (400 otherwise). Every turn end of the task's session reads and clears it. It survives a byre restart during the turn: startup resumes the session with the restart notice, and the merge happens when that resumed turn ends. When that turn ends normally (not interrupted, not an error), the transaction that ends it (`Store.EndTurn`) moves the login from `merging_by` to `tasks.finalizing`, so the task stays **Merging** while byre squashes, pushes and closes it. While `finalizing` is set, the task's implementer session can't start a turn (a message or Sync is a 409; the store refuses the `running` transition in `transitionSession`), Discard and merging into a parent are refused as merging, `set_merge_title` is refused, and the task can't get new subtasks (`ErrParentNotOpen`). Closing the task clears it; a failed squash or push clears it (emitting `task.merge_ended`) before filing the error, so the queued inbox responses can be delivered. Just before pushing, byre records the squash commit and its notes commit in `tasks.finalizing_commit` and `tasks.finalizing_notes` (`Store.SetFinalizingCommit`); when the merge changes nothing, it records that in `tasks.finalizing_unchanged` (`Store.SetFinalizingUnchanged`) before removing the worktree. A push the remote rejected as non-fast-forward clears the marker as a failed push. Any other push error is ambiguous, since the remote may have taken the push before the connection dropped, so byre runs `git fetch origin` and checks whether the commit is an ancestor of `origin/<default branch>`: when it is, it finishes as if the push had succeeded; when it isn't, the push failed; when the check itself fails, the marker stays. A failure after the push was accepted (updating the checkout's notes, fast-forwarding it, removing the worktree, closing the task), or while closing an unchanged merge, keeps the marker and files an error saying byre finishes the merge when it next starts. At startup, `recoverFinalizing` (`internal/agents/mergein.go`) takes each task with `finalizing` set: an unchanged merge is finished by removing the worktree and closing the task with no commit; with a recorded commit, it runs `git fetch origin` and, when the commit is an ancestor of `origin/<default branch>`, finishes the merge (`closePushed`: advance the checkout's `refs/notes/byre` to the notes commit unless it already contains it, `git merge --ff-only`, remove the worktree, close the task), each step a no-op when already done. When the commit is not on the remote, or none was recorded, it clears the marker and files an error on the task saying to merge again; when the fetch, the ancestry check or a finishing step fails, it keeps the marker for the next start and files the error. `sqlite3 byre.db "SELECT id, finalizing, finalizing_commit FROM tasks WHERE finalizing != ''"` lists merges being finished. Then byre squashes the worktree, now containing the default branch, onto it with `git merge-tree --merge-base`, makes one commit titled with the task's `merge_title` with `git commit-tree` (signed on the merging user's Mac when the checkout's `commit.gpgsign` is true; see "Signing merges"), authored and committed by the merging user with no agent attribution, its body one `Co-authored-by: Name <email>` trailer per co-author with a git identity (`squashMessage` in `internal/agents/mergein.go`; the co-authors are computed when the squash runs, relative to the user who clicked Merge), and pushes that commit to the default branch on the `origin` remote of the project's checkout together with its note (below), in one atomic push (`git push --atomic --porcelain origin <commit>:refs/heads/<default branch> <notes commit>:refs/notes/byre`, run in the checkout), so either both land or neither does. Only once the push is accepted does it point the checkout's `refs/notes/byre` at the notes commit, run `git merge --ff-only` in the project's checkout (which must have the default branch checked out) and close the task; the `task.merged` event carries the commit and `pushed` (`origin/<default branch>`), and the transcript shows both. When the merge changes nothing, no commit is made and nothing is pushed.
|
||||||
|
|
||||||
Before the agent gets the prompt, Merge runs `git fetch origin` (every configured refspec, so the notes too) and `git merge --ff-only origin/<default branch>` in the checkout, so the agent merges in what the remote has. The request doesn't wait for it: it sets `merging_by`, moves the session to `running` and returns 202, and a goroutine (`launchMerge` in `internal/agents/mergein.go`) starts the agent process if needed, emits the prompt's `user.message` so the transcript shows it at once, then fetches and only then sends the prompt to the agent. A failure there (the process, or the fetch: network, or a checkout with commits the remote lacks) puts the session back to `idle` with the error, which the transcript shows as a `session idle: …` note, and abandons the merge, clearing `merging_by` and releasing the project's merge. Interrupt during the fetch (`live.fetching` set) records the interrupt without calling the vendor, since no vendor turn exists yet; the fetch runs to its end, and then the prompt isn't sent and the merge is abandoned the same way, with the error `interrupted before the merge started`. If the push is still refused, for example because someone pushed during the turn, or fails for any other reason, byre records the error in the transcript and the task stays active; clicking Merge again fetches and goes round once more. A refused push leaves the checkout's default branch where it was, since the squash commit was never moved onto it. If the squash fails, for example because the default branch moved again during the turn, byre also files an error and the task stays active. The agent calls `stop_merge` (`internal/agents/mergein.go`) with a `reason` when its conflict resolution took substantive changes: it clears `merging_by` and releases the project's merge at once, and records a `task.merge_stopped` event carrying the reason, which the transcript shows as a note. The turn then ends without a squash, the task stays active, its snapshot is reviewed, and the human clicks Merge again when satisfied. Called with no merge pending (a Sync turn, say), it changes nothing and tells the agent so. An interrupted or failed turn, an agent process that exits mid-turn, or a session that can't be resumed after a restart clears `merging_by` and leaves the task active without squashing; a turn that ends interrupted or with an empty final message also records an error saying the merge didn't happen. A Claude `result` with `num_turns` 0, no `result` text and no error, outside an interrupt, isn't a turn end: the CLI emits one on its own, for example after a resume when it reports a background shell left over from the previous process, possibly after byre has already sent the next message. byre logs `ignored result with no model turns`, records it as an `agent.vendor` event, and keeps the turn running, so it can't take `merging_by` from the Merge turn that follows. Merge (either target, see "Subtasks") and Discard are refused (409, naming them) while the task has active subtasks, whoever asks: the button, the lead or an agent.
|
Before the agent gets the prompt, Merge runs `git fetch origin` (every configured refspec, so the notes too) and `git merge --ff-only origin/<default branch>` in the checkout, so the agent merges in what the remote has. The request doesn't wait for it: it sets `merging_by`, moves the session to `running` and returns 202, and a goroutine (`launchMerge` in `internal/agents/mergein.go`) starts the agent process if needed, emits the prompt's `user.message` so the transcript shows it at once, then fetches and only then sends the prompt to the agent. A failure there (the process, or the fetch: network, or a checkout with commits the remote lacks) puts the session back to `idle` with the error, which the transcript shows as a `session idle: …` note, and abandons the merge, clearing `merging_by` and releasing the project's merge. Interrupt during the fetch (`live.fetching` set) records the interrupt without calling the vendor, since no vendor turn exists yet; the fetch runs to its end, and then the prompt isn't sent and the merge is abandoned the same way, with the error `interrupted before the merge started`. If the push is still refused, for example because someone pushed during the turn, or fails for any other reason, byre records the error in the transcript and the task stays active; clicking Merge again fetches and goes round once more. A refused push leaves the checkout's default branch where it was, since the squash commit was never moved onto it. If the squash fails, for example because the default branch moved again during the turn, byre also files an error and the task stays active. The agent calls `stop_merge` (`internal/agents/mergein.go`) with a `reason` when its conflict resolution took substantive changes: it clears `merging_by` and releases the project's merge at once, and records a `task.merge_stopped` event carrying the reason, which the transcript shows as a note. The turn then ends without a squash, the task stays active, its snapshot is reviewed, and the human clicks Merge again when satisfied. Called with no merge pending (a Sync turn, say), it changes nothing and tells the agent so. An interrupted or failed turn, an agent process that exits mid-turn, or a session that can't be resumed after a restart clears `merging_by` and leaves the task active without squashing; a turn that ends interrupted or with an empty final message also records an error saying the merge didn't happen. A Claude `result` with `num_turns` 0, no `result` text and no error, outside an interrupt, isn't a turn end: the CLI emits one on its own, for example after a resume when it reports a background shell left over from the previous process, possibly after byre has already sent the next message. byre logs `ignored result with no model turns`, records it as an `agent.vendor` event, and keeps the turn running, so it can't take `merging_by` from the Merge turn that follows. Merge (either target, see "Subtasks") and Discard are refused (409, naming them) while the task has active subtasks, whoever asks: the button, the lead or an agent.
|
||||||
|
|
||||||
@@ -358,6 +358,10 @@ Implementer sessions get eight MCP tools for it (`internal/agents/clients.go`).
|
|||||||
|
|
||||||
A byre restart drops every connection; each app reconnects 2s later, and calls in flight at the restart fail as disconnected. Updating a client's app goes through the tools themselves: `client_upload` the new `Byre.app`, then `client_bash` a command that quits the old app and opens the new one, which outlives the app and reconnects. An app that doesn't know the `upload` op can't be updated this way, so its update is by hand.
|
A byre restart drops every connection; each app reconnects 2s later, and calls in flight at the restart fail as disconnected. Updating a client's app goes through the tools themselves: `client_upload` the new `Byre.app`, then `client_bash` a command that quits the old app and opens the new one, which outlives the app and reconnects. An app that doesn't know the `upload` op can't be updated this way, so its update is by hand.
|
||||||
|
|
||||||
|
### Signing merges
|
||||||
|
|
||||||
|
byre never signs with a key on the machine it runs on. When a project checkout's `commit.gpgsign` is true (`git -C <checkout> config --type=bool --get commit.gpgsign` prints it), the squash commit of a Merge or `merge_to_main` is signed on the merging user's Mac, so a key that never leaves that Mac's Secure Enclave and needs a fingerprint works. `SquashCommit` (`internal/git/git.go`) makes the unsigned commit with `git commit-tree`, reads its object with `git cat-file commit`, and hands those bytes to the signer (`internal/agents/sign.go`). The signer sends `op: sign` with the bytes in `data`, `ident` (`Name <email>` from the user's git identity) and `timeout_ms` 120000 to the user's most recently active connected client, the same order `list_clients` uses, and fails the merge when the user has none connected. The app writes the bytes to a temporary file and runs a script under `/bin/zsh -l` in the home directory that reads that Mac's own git config: `gpg.format` (default `openpgp`), its program (`gpg.openpgp.program` or `gpg.program`, default `gpg`, for `openpgp`; `gpg.x509.program`, default `gpgsm`, for `x509`; any other format fails), and `user.signingkey`, or `ident` when unset. It runs the program as git does, `<program> --status-fd=2 -bsau <key>` with the commit on stdin, with the signature written to a second temporary file, since login shell startup files can print to stdout. The reply carries the exit status, the program's stderr in `output` and the signature in `data`. byre fails the merge unless the status is 0 and `output` has a `[GNUPG:] SIG_CREATED ` line, as git requires, and otherwise inserts the signature as a `gpgsig` header before the commit's blank line, each line after the first indented by one space, and writes the signed object with `git hash-object -t commit -w --stdin`. The merge waits up to 2 minutes 30 seconds for the reply, and a Touch ID prompt the human doesn't answer within the 2 minutes fails it. The notes commit is not signed.
|
||||||
|
|
||||||
`log show --info --last 10m --predicate 'subsystem == "run.fc.byre.mac"'` on a client shows `client request <id>: <op>` lines and `byre-hands` starts and exits, which are info level and kept only in memory, so they cover the last few minutes at most; connection drops (`client connection closed`, `client connection got no pong`) are notices, which macOS keeps on disk, so `log show --start` reaches much further back for them; byre's log shows `client connected` and `client disconnected`.
|
`log show --info --last 10m --predicate 'subsystem == "run.fc.byre.mac"'` on a client shows `client request <id>: <op>` lines and `byre-hands` starts and exits, which are info level and kept only in memory, so they cover the last few minutes at most; connection drops (`client connection closed`, `client connection got no pong`) are notices, which macOS keeps on disk, so `log show --start` reaches much further back for them; byre's log shows `client connected` and `client disconnected`.
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ func (m *Manager) squashAndClose(ctx context.Context, taskID, login string) erro
|
|||||||
"GIT_COMMITTER_NAME=" + user.GitName,
|
"GIT_COMMITTER_NAME=" + user.GitName,
|
||||||
"GIT_COMMITTER_EMAIL=" + user.GitEmail,
|
"GIT_COMMITTER_EMAIL=" + user.GitEmail,
|
||||||
}
|
}
|
||||||
commit, err := git.SquashCommit(project.RepoPath, task.WorktreePath, project.DefaultBranch, squashMessage(task.MergeTitle, coAuthors), env)
|
commit, err := git.SquashCommit(project.RepoPath, task.WorktreePath, project.DefaultBranch, squashMessage(task.MergeTitle, coAuthors), env, m.signer(ctx, user))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func (m *Manager) pushToMain(ctx context.Context, l *live, task api.Task, projec
|
|||||||
"GIT_COMMITTER_NAME=" + user.GitName,
|
"GIT_COMMITTER_NAME=" + user.GitName,
|
||||||
"GIT_COMMITTER_EMAIL=" + user.GitEmail,
|
"GIT_COMMITTER_EMAIL=" + user.GitEmail,
|
||||||
}
|
}
|
||||||
commit, err := git.SquashCommit(project.RepoPath, l.dir, project.DefaultBranch, squashMessage(title, coAuthors), env)
|
commit, err := git.SquashCommit(project.RepoPath, l.dir, project.DefaultBranch, squashMessage(title, coAuthors), env, m.signer(ctx, user))
|
||||||
conflict := &git.ConflictError{}
|
conflict := &git.ConflictError{}
|
||||||
if errors.As(err, &conflict) {
|
if errors.As(err, &conflict) {
|
||||||
return "", fmt.Errorf("%w; run `git merge --no-ff --no-commit %s`, resolve them, and call merge_to_main again", err, project.DefaultBranch)
|
return "", fmt.Errorf("%w; run `git merge --no-ff --no-commit %s`, resolve them, and call merge_to_main again", err, project.DefaultBranch)
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package agents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"g.fc.run/ai/byre/internal/api"
|
||||||
|
"g.fc.run/ai/byre/internal/clients"
|
||||||
|
)
|
||||||
|
|
||||||
|
const signTimeout = 2 * time.Minute
|
||||||
|
|
||||||
|
func (m *Manager) signer(ctx context.Context, user api.User) func(payload []byte) ([]byte, error) {
|
||||||
|
return func(payload []byte) ([]byte, error) {
|
||||||
|
mine := []clients.Client{}
|
||||||
|
for _, c := range m.clients.List() {
|
||||||
|
if c.Login == user.Login {
|
||||||
|
mine = append(mine, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mine) == 0 {
|
||||||
|
return nil, fmt.Errorf("commits are signed on %s's Mac, and none of theirs is connected", user.Login)
|
||||||
|
}
|
||||||
|
sort.SliceStable(mine, func(i, j int) bool {
|
||||||
|
a, b := mine[i].LastActionAt, mine[j].LastActionAt
|
||||||
|
return a != nil && (b == nil || a.After(*b))
|
||||||
|
})
|
||||||
|
client := mine[0]
|
||||||
|
|
||||||
|
ident := fmt.Sprintf("%s <%s>", user.GitName, user.GitEmail)
|
||||||
|
ms := int(signTimeout.Milliseconds())
|
||||||
|
cctx, cancel := context.WithTimeout(ctx, signTimeout+replyGrace)
|
||||||
|
defer cancel()
|
||||||
|
reply, err := m.clients.Call(cctx, client.ID, api.ClientRequest{Op: api.Sign, Data: &payload, Ident: &ident, TimeoutMs: &ms})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if reply.ExitCode == nil || reply.Output == nil || reply.Data == nil {
|
||||||
|
return nil, fmt.Errorf("%s's reply has no exit_code, output or data", client.Host)
|
||||||
|
}
|
||||||
|
if *reply.ExitCode == -1 {
|
||||||
|
return nil, fmt.Errorf("%s didn't sign within %s", client.Host, signTimeout)
|
||||||
|
}
|
||||||
|
if *reply.ExitCode != 0 || !strings.Contains(*reply.Output, "[GNUPG:] SIG_CREATED ") {
|
||||||
|
return nil, fmt.Errorf("signing on %s failed with exit status %d: %s", client.Host, *reply.ExitCode, strings.TrimSpace(*reply.Output))
|
||||||
|
}
|
||||||
|
return *reply.Data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
+271
-262
@@ -144,6 +144,7 @@ const (
|
|||||||
Download ClientRequestOp = "download"
|
Download ClientRequestOp = "download"
|
||||||
Hands ClientRequestOp = "hands"
|
Hands ClientRequestOp = "hands"
|
||||||
ReadFile ClientRequestOp = "read_file"
|
ReadFile ClientRequestOp = "read_file"
|
||||||
|
Sign ClientRequestOp = "sign"
|
||||||
Upload ClientRequestOp = "upload"
|
Upload ClientRequestOp = "upload"
|
||||||
WriteFile ClientRequestOp = "write_file"
|
WriteFile ClientRequestOp = "write_file"
|
||||||
)
|
)
|
||||||
@@ -159,6 +160,8 @@ func (e ClientRequestOp) Valid() bool {
|
|||||||
return true
|
return true
|
||||||
case ReadFile:
|
case ReadFile:
|
||||||
return true
|
return true
|
||||||
|
case Sign:
|
||||||
|
return true
|
||||||
case Upload:
|
case Upload:
|
||||||
return true
|
return true
|
||||||
case WriteFile:
|
case WriteFile:
|
||||||
@@ -677,26 +680,26 @@ type BoardPostCreate struct {
|
|||||||
|
|
||||||
// ClientReply defines model for ClientReply.
|
// ClientReply defines model for ClientReply.
|
||||||
type ClientReply struct {
|
type ClientReply struct {
|
||||||
// Data read_file: the file's contents.
|
// Data read_file: the file's contents. sign: the detached signature.
|
||||||
Data *[]byte `json:"data,omitempty"`
|
Data *[]byte `json:"data,omitempty"`
|
||||||
|
|
||||||
// Error Set when the request couldn't be carried out.
|
// Error Set when the request couldn't be carried out.
|
||||||
Error *string `json:"error,omitempty"`
|
Error *string `json:"error,omitempty"`
|
||||||
|
|
||||||
// ExitCode bash: the exit status, -1 when it was killed by its timeout.
|
// ExitCode bash, sign: the exit status, -1 when it was killed by its timeout.
|
||||||
ExitCode *int `json:"exit_code,omitempty"`
|
ExitCode *int `json:"exit_code,omitempty"`
|
||||||
|
|
||||||
// Id The request's id.
|
// Id The request's id.
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
|
||||||
// Output bash: stdout and stderr together, at most their last 64 KiB.
|
// Output bash: stdout and stderr together, at most their last 64 KiB. sign: the signing program's stderr, where it writes its GnuPG status lines, and the login shell's output.
|
||||||
Output *string `json:"output,omitempty"`
|
Output *string `json:"output,omitempty"`
|
||||||
|
|
||||||
// Result hands: the tool's MCP CallToolResult.
|
// Result hands: the tool's MCP CallToolResult.
|
||||||
Result *map[string]interface{} `json:"result,omitempty"`
|
Result *map[string]interface{} `json:"result,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientRequest Work a task agent asks a connected Mac to do, sent over /api/clients/connect.
|
// ClientRequest Work a task agent, or a merge, asks a connected Mac to do, sent over /api/clients/connect.
|
||||||
type ClientRequest struct {
|
type ClientRequest struct {
|
||||||
// Arguments hands: the tool's arguments.
|
// Arguments hands: the tool's arguments.
|
||||||
Arguments *map[string]interface{} `json:"arguments,omitempty"`
|
Arguments *map[string]interface{} `json:"arguments,omitempty"`
|
||||||
@@ -704,18 +707,21 @@ type ClientRequest struct {
|
|||||||
// Command bash: run with zsh as a login shell in the home directory, in its own process group.
|
// Command bash: run with zsh as a login shell in the home directory, in its own process group.
|
||||||
Command *string `json:"command,omitempty"`
|
Command *string `json:"command,omitempty"`
|
||||||
|
|
||||||
// Data write_file: the file's new contents; parent directories are created.
|
// Data write_file: the file's new contents; parent directories are created. sign: the unsigned commit object, which the Mac signs with the program its git config names for its gpg.format (openpgp or x509), called as git calls it.
|
||||||
Data *[]byte `json:"data,omitempty"`
|
Data *[]byte `json:"data,omitempty"`
|
||||||
|
|
||||||
// Dest upload: where the archive's one entry ends up, absolute or starting with ~/; whatever is there is replaced.
|
// Dest upload: where the archive's one entry ends up, absolute or starting with ~/; whatever is there is replaced.
|
||||||
Dest *string `json:"dest,omitempty"`
|
Dest *string `json:"dest,omitempty"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Op ClientRequestOp `json:"op"`
|
|
||||||
|
// Ident sign: the committer, as "Name <email>", to sign as when the Mac's git config has no user.signingkey.
|
||||||
|
Ident *string `json:"ident,omitempty"`
|
||||||
|
Op ClientRequestOp `json:"op"`
|
||||||
|
|
||||||
// Path download, read_file, write_file: the file (or, for download, file or directory) on the client, absolute or starting with ~/.
|
// Path download, read_file, write_file: the file (or, for download, file or directory) on the client, absolute or starting with ~/.
|
||||||
Path *string `json:"path,omitempty"`
|
Path *string `json:"path,omitempty"`
|
||||||
|
|
||||||
// TimeoutMs bash: kill the process group after this long.
|
// TimeoutMs bash, sign: kill the process group after this long.
|
||||||
TimeoutMs *int `json:"timeout_ms,omitempty"`
|
TimeoutMs *int `json:"timeout_ms,omitempty"`
|
||||||
|
|
||||||
// Tool hands: the byre-hands tool to call.
|
// Tool hands: the byre-hands tool to call.
|
||||||
@@ -3841,260 +3847,263 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
|
|||||||
// const string: with thousands of chunks the chained `+` fold is several
|
// const string: with thousands of chunks the chained `+` fold is several
|
||||||
// times slower for the Go compiler than parsing a slice literal.
|
// times slower for the Go compiler than parsing a slice literal.
|
||||||
var swaggerSpec = []string{
|
var swaggerSpec = []string{
|
||||||
"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",
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||||
|
|||||||
+29
-6
@@ -177,7 +177,7 @@ func UnmergedChanges(worktree, target string) (Unmerged, error) {
|
|||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SquashCommit(repo, worktree, target, message string, env []string) (string, error) {
|
func SquashCommit(repo, worktree, target, message string, env []string, sign func(payload []byte) ([]byte, error)) (string, error) {
|
||||||
checkedOut, err := run(repo, "symbolic-ref", "--short", "HEAD")
|
checkedOut, err := run(repo, "symbolic-ref", "--short", "HEAD")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -226,18 +226,41 @@ func SquashCommit(repo, worktree, target, message string, env []string) (string,
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"commit-tree", lines[0], "-p", head, "-m", message}
|
commit, err := runEnv(repo, env, "commit-tree", lines[0], "-p", head, "-m", message)
|
||||||
sign, stderr, code, err := runCode(repo, nil, "config", "--type=bool", "--get", "commit.gpgsign")
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
signing, stderr, code, err := runCode(repo, nil, "config", "--type=bool", "--get", "commit.gpgsign")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if code != 0 && code != 1 {
|
if code != 0 && code != 1 {
|
||||||
return "", fmt.Errorf("git config --get commit.gpgsign: exit status %d: %s", code, stderr)
|
return "", fmt.Errorf("git config --get commit.gpgsign: exit status %d: %s", code, stderr)
|
||||||
}
|
}
|
||||||
if sign == "true" {
|
if signing != "true" {
|
||||||
args = append(args, "-S")
|
return commit, nil
|
||||||
}
|
}
|
||||||
return runEnv(repo, env, args...)
|
return signCommit(repo, commit, sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
func signCommit(repo, commit string, sign func(payload []byte) ([]byte, error)) (string, error) {
|
||||||
|
cmd := exec.Command("git", "cat-file", "commit", commit)
|
||||||
|
cmd.Dir = repo
|
||||||
|
payload, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("git cat-file commit %s: %w", commit, err)
|
||||||
|
}
|
||||||
|
sig, err := sign(payload)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("signing the commit: %w", err)
|
||||||
|
}
|
||||||
|
eoh := strings.Index(string(payload), "\n\n")
|
||||||
|
if eoh < 0 {
|
||||||
|
return "", fmt.Errorf("commit %s has no message", commit)
|
||||||
|
}
|
||||||
|
header := "gpgsig " + strings.ReplaceAll(strings.TrimSuffix(string(sig), "\n"), "\n", "\n ") + "\n"
|
||||||
|
signed := string(payload[:eoh+1]) + header + string(payload[eoh+1:])
|
||||||
|
return runInput(repo, nil, signed, "hash-object", "-t", "commit", "-w", "--stdin")
|
||||||
}
|
}
|
||||||
|
|
||||||
const Remote = "origin"
|
const Remote = "origin"
|
||||||
|
|||||||
@@ -14,6 +14,17 @@ private let maxMessage = 256 << 20
|
|||||||
private let maxRead = 64 << 20
|
private let maxRead = 64 << 20
|
||||||
private let copyTimeoutMs = 10 * 60 * 1000
|
private let copyTimeoutMs = 10 * 60 * 1000
|
||||||
|
|
||||||
|
// The signature goes to a file, since a login shell's startup files can print to stdout.
|
||||||
|
private let signScript = """
|
||||||
|
format=$(git config gpg.format || echo openpgp)
|
||||||
|
case $format in
|
||||||
|
openpgp) program=$(git config gpg.openpgp.program || git config gpg.program || echo gpg) ;;
|
||||||
|
x509) program=$(git config gpg.x509.program || echo gpgsm) ;;
|
||||||
|
*) echo "byre can't sign with gpg.format $format" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
exec "$program" --status-fd=2 -bsau "$(git config user.signingkey || echo "$1")" >"$2"
|
||||||
|
"""
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class Remote {
|
final class Remote {
|
||||||
let api: API
|
let api: API
|
||||||
@@ -158,9 +169,31 @@ final class Remote {
|
|||||||
case .hands:
|
case .hands:
|
||||||
let result = try await hands.call(req.tool ?? "", req.arguments?.additionalProperties ?? OpenAPIObjectContainer())
|
let result = try await hands.call(req.tool ?? "", req.arguments?.additionalProperties ?? OpenAPIObjectContainer())
|
||||||
return ClientReply(id: req.id, result: .init(additionalProperties: result))
|
return ClientReply(id: req.id, result: .init(additionalProperties: result))
|
||||||
|
case .sign:
|
||||||
|
let (code, status, signature) = try await sign(Data(req.data?.data ?? []), as: req.ident ?? "", timeoutMs: req.timeoutMs ?? 120_000)
|
||||||
|
return ClientReply(id: req.id, exitCode: code, output: status, data: Base64EncodedData(signature))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func sign(_ payload: Data, as ident: String, timeoutMs: Int) async throws -> (Int, String, Data) {
|
||||||
|
let fm = FileManager.default
|
||||||
|
let dir = fm.temporaryDirectory.appending(path: "byre-sign-\(UUID().uuidString)", directoryHint: .isDirectory)
|
||||||
|
try fm.createDirectory(at: dir, withIntermediateDirectories: false, attributes: [.posixPermissions: 0o700])
|
||||||
|
defer {
|
||||||
|
try? fm.removeItem(at: dir)
|
||||||
|
}
|
||||||
|
let input = dir.appending(path: "commit")
|
||||||
|
let output = dir.appending(path: "signature")
|
||||||
|
try payload.write(to: input)
|
||||||
|
let stdin = try FileHandle(forReadingFrom: input)
|
||||||
|
defer {
|
||||||
|
try? stdin.close()
|
||||||
|
}
|
||||||
|
let home = fm.homeDirectoryForCurrentUser.path
|
||||||
|
let (code, status) = try await spawn(["/bin/zsh", "-l", "-c", signScript, "byre-sign", ident, output.path], in: home, timeoutMs: timeoutMs, input: stdin)
|
||||||
|
return (code, status, (try? Data(contentsOf: output)) ?? Data())
|
||||||
|
}
|
||||||
|
|
||||||
private func download(_ path: String, transfer: String) async throws {
|
private func download(_ path: String, transfer: String) async throws {
|
||||||
let source = expand(path)
|
let source = expand(path)
|
||||||
let archive = FileManager.default.temporaryDirectory.appending(path: "byre-download-\(UUID().uuidString).tgz")
|
let archive = FileManager.default.temporaryDirectory.appending(path: "byre-download-\(UUID().uuidString).tgz")
|
||||||
@@ -258,7 +291,7 @@ private func start(_ p: Process) throws -> AsyncStream<Void> {
|
|||||||
return exit
|
return exit
|
||||||
}
|
}
|
||||||
|
|
||||||
private func spawn(_ argv: [String], in dir: String, timeoutMs: Int) async throws -> (Int, String) {
|
private func spawn(_ argv: [String], in dir: String, timeoutMs: Int, input: FileHandle = .nullDevice) async throws -> (Int, String) {
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
let out = fm.temporaryDirectory.appending(path: "byre-\(UUID().uuidString).log")
|
let out = fm.temporaryDirectory.appending(path: "byre-\(UUID().uuidString).log")
|
||||||
defer {
|
defer {
|
||||||
@@ -274,7 +307,7 @@ private func spawn(_ argv: [String], in dir: String, timeoutMs: Int) async throw
|
|||||||
p.executableURL = URL(filePath: argv[0])
|
p.executableURL = URL(filePath: argv[0])
|
||||||
p.arguments = Array(argv.dropFirst())
|
p.arguments = Array(argv.dropFirst())
|
||||||
p.currentDirectoryURL = URL(filePath: dir)
|
p.currentDirectoryURL = URL(filePath: dir)
|
||||||
p.standardInput = FileHandle.nullDevice
|
p.standardInput = input
|
||||||
p.standardOutput = sink
|
p.standardOutput = sink
|
||||||
p.standardError = sink
|
p.standardError = sink
|
||||||
let exit: AsyncStream<Void>
|
let exit: AsyncStream<Void>
|
||||||
|
|||||||
Reference in New Issue
Block a user