Signing was account-wide (gpg.format/user.signingkey/commit.gpgsign in
~/.gitconfig), so every repository was signed. Now Settings → Git →
Commit signing lists the user's workspaces, each with its own key picker
("Don't sign" by default). PUT /api/me/git/signing/lane {laneId, key}
writes a small file of ours (lane-<id>.gitconfig beside the account's
keys; a member's through write-as) and includes it from ~/.gitconfig
with includeIf "gitdir:<the workspace's git dir>" and
"gitdir:<common dir>/worktrees/ah-<id>-*" (its sessions' worktrees) —
only that workspace signs, from the terminal, the Changes panel or an
agent, and nothing is written into the repository. The git dirs come
from git run in the workspace's folder as its account; a workspace that
runs as another account is EFOREIGN, a folder that is not a repo
ENOREPO. Stopping needs no workspace, so one removed from the dashboard
while it signed is still listed and can be turned off.
The card now lists the keys that can sign, each with Delete (confirm;
deleting a key stops every workspace signing with it), a way out of the
old account-wide setting, and Remove for a signing setup the user made
themselves (PUT /signing {clearForeign: true}: the three settings only,
never their key file). Importing a key no longer turns on signing.
Verified with real signatures: the picked workspace's commits are G,
another repo's and an unrelated worktree's N, a session worktree's G.
Settings → Git gains Commit signing: one picker signs every commit
(terminal, Changes panel, agents) with one of the account's keys —
gpg.format ssh, user.signingkey = the key's private file, commit.gpgsign
true in its own ~/.gitconfig; a signing key the user set up themselves is
replaced only after a confirm (EFOREIGNSIGN without `replace`). Import a key
you have takes a key file or paste (OpenSSH or PEM, ed25519 or RSA >= 2048)
into ~/.config/agenthub/ssh/ as a named key, written like a generated one
(member: write-as, 0600), and by default signs with it in the same step.
A passphrase key, a .pub, an ecdsa key, a mismatched paste and the same key
twice are refused; only the public half ever comes back. Deleting the
signing key stops signing so commits keep working.
The SSH tab is renamed "Git (GitHub, GitLab, Gitea, Backlog…)" with the
Gitea and Backlog steps, and the File Explorer's ESECRET toast now says
that top-level dot folders of a home are edited in a terminal, and where
the SSH keys live instead.
Both clone POSTs take background: true: every check answers synchronously,
then 202 {job}. git runs in a detached runner (scripts/clone-runner.js, its
own process group, stderr to a file — no SIGPIPE when the server restarts);
the server tails it for progress and runs the after-step (workspace / extra
folder) once when it exits. Jobs persist in <data dir>/runtime/clone-jobs.json
(0600, atomic, throttled progress writes); on start a live runner is adopted
(counted against the clone limit), an ended one finished, a queued one
requeued (interrupted if it carried a typed login), the rest interrupted.
GET /clone/jobs, DELETE /clone/jobs/:id, POST /clone/jobs/:id/cancel (queued:
dequeued; running: SIGTERM to the runner's group -> cancelled, no after-step)
and /retry (new job from the stored request, never a credential). The
held-open path is unchanged; worktree.gitCommand builds the runner's argv
and git-identity.cloneFailure the shared error classification.
Settings → Git → AWS CodeCommit asks for the key ID only: the default key's
login is written for every CodeCommit region at once (allRegions), shown as
one row, with Use in every region for an older one-region save. A clone of
a CodeCommit URL with no user tries every saved CodeCommit login — the
region's own first, then other regions' and each named key's — so a second
AWS account clones from the plain AWS URL with no ssh://<alias>/ rewrite.
An IAM SSH key ID logs in to every region of its AWS account, but a login
was saved per host, so a repo in a region with no key ID of its own was
refused. Clone now tries the key IDs saved for the other CodeCommit regions
(each with its own key; the first ls-remote accepts, or the only one) and
keeps that user in the clone's origin.
Files → New → Clone repository steps aside for its progress chip as soon
as the clone starts; a failure asking for a login reopens the form.
- Chat: an Agent step shows its task, status and tool calls and opens its conversation in place; Windows paths (E:\…, C:/…) in replies open as files
- Prompt: attach a file on this computer by path (native picker), clearer too-large messages
- Artifacts: edit title/description and a skill's files (new snapshot), Reinstall, install history, Hub-only rows, skills installed into ~/.claude/skills and ~/.agents/skills
- Files: batch upload of small files in one extract
- AI Tools: npm install falls back to --prefix ~/.local when the global prefix is not writable
Whole tree from parallel sessions, plus the version bump:
- Clone: a queue with live progress (Add workspace and extra folders),
clones run under nice/ionice so the machine stays responsive.
- Settings → AI tools → Session defaults: a new session's default
permission mode / bypass per account.
- Chat lists the background subagents that have not reported back yet.
- A file pasted or dropped into a claude pane goes in as an @ mention.
- Changes panel: say why amend/undo is refused on a pushed commit.
- openapi.yaml regenerated (Artifacts, Host, resumeById).
- Files: right-click a sidebar workspace (Open, Copy path) or home (Open,
Open Trash), a path crumb (Open, Download as zip, Paste into, Copy path),
a Trash row (Restore, Copy path, Delete forever), the Trash area (Refresh,
Empty Trash) and an admin's per-user Trash folder (Open, Empty Trash).
- Users: row menu (Edit + Open home in Files for an admin, Copy username/email).
- Artifacts: card menu repeating its buttons, plus Copy link for a page.
- Git identity: named SSH keys beside the default ones (id_<type>_<name>,
max 10, DELETE ?id=); SSH logins per alias (`ssh://<alias>/` →
`ssh://<user>@<host>/`, so several AWS CodeCommit accounts share a host),
each optionally tied to one key (`agenthub.<from>.sshKey`) — a clone of
that URL offers only that key and persists it. Terminal sshCommand follows
the keys. Settings → Git: "More SSH keys" + account name/key in the AWS and
other-host forms. Docs/API.md, OpenAPI updated.
- Files → Trash: ONE sidebar entry. An admin's opens on a folder per user's
Trash (My Trash first, item counts from GET /api/homes/trash, an unreadable
one showing its error); a folder opens that user's Trash (path bar
Trash › user, Up back to the folders). An admin may restore/purge in
another user's Trash (audited); purging asks first. The merged "All users"
list is gone from the UI.
- A home's Trash is its user's whole recycle bin (home store + host store,
items tagged `store`).
- `agenthub server setup` installs one Linux user per account on Linux by
default (`--no-system` opts out); install-system messages drop `sudo`.
- Settings → Git: the author email suggests <username>@<AGENTHUB_EMAIL_DOMAIN>.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AWS CodeCommit logs an SSH key in by its IAM SSH key ID (APKA…), so a
plain `git clone ssh://git-codecommit.<region>.amazonaws.com/v1/repos/x`
was refused as the Linux account name. The account can now save an SSH
user per host — PUT/DELETE /api/me/git/ssh-user, listed as `sshUsers` —
written as `url."ssh://<user>@<host>/".insteadOf "ssh://<host>/"` in its
own ~/.gitconfig (as the member under S3), so the plain URL works in a
pane, in Clone and in the git bar. Only rules of exactly that shape are
listed or removed; any other insteadOf is the user's and left alone.
Settings → Git → Your git identity now shows the SSH keys one section
per kind of host, each with only its own numbered steps: GitHub · GitLab
· Gitea · Bitbucket (ED25519) and AWS CodeCommit (RSA: copy → upload in
IAM → paste the key ID and pick the region, then a "Ready" clone line),
plus a collapsed "Other SSH hosts (advanced)". The shared key-type
picker is gone: each section has its own Generate/Replace/Delete.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Stop (a tab's or pane's X, Close tab) now archives the session it held
(DELETE /api/run/:id?archive=1); the new Sleep (moon left of the X, and in
the tab, pane and session right-click menus) stops the pane but keeps the
session listed to resume later. Shared endRuns in client/src/lib/stopSession.ts.
- Git identity (#82): an account keeps an ED25519 AND an RSA key (AWS
CodeCommit takes only RSA), every ssh git offers both, and a member's
~/.gitconfig gets core.sshCommand so a git typed in a terminal uses them.
- Update dialog: Auto fix starts a Claude Code session on the install
folder with the failed update's error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Artifacts (/artifacts): claude.ai pages sessions publish (captured from the
Artifact tool's hook), pinned workspace files, skills and plugins, each
kept as a snapshot; shared ones appear in a Hub and a skill or plugin
installs into the viewer's own account (server/lib/artifacts.js,
/api/artifacts, docs/plans/artifacts/).
- Workspace extra folders: clone one or more related repos beside the
workspace and add them in one step (POST /api/lanes/:id/sources/clone).
- A pane that ended by itself while tmux kept running is no longer offered
for resume (tmux.serverStartedAt in reconcileOrphans).
- Changes panel: a folder that is not a git repo opens on Files, and its
files open from there.
- Plus the parallel sessions' work in this tree: File Explorer button in the
panel header, user delete, RSA SSH keys, chat/terminal and pet fixes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gathered from the parallel sessions' uncommitted work on main, at the
owner's request (the session that wrote #78/#79/#80 was stopped mid-task):
- #78/#79/#80: Files tab upload/download (GET /:id/git/download, POST
/:id/git/upload, server/lib/lane-file-transfer.js) and a non-git tree
(git-diff.js walkFolder); pinned sessions (sessions.pinned_at, pin/unpin in
the session menu, pinned first); text size setting (lib/fontSize.ts,
Appearance).
- Git identity and clone over HTTPS/SSH (server/lib/git-identity.js,
routes/git-identity.js, GitIdentitySettings, AddLaneModal clone).
- pets.js FEATURED list and view().
Verified before commit: client `tsc -b` clean; related server tests 175/175
(lane-file-transfer, git-clone, api, server-s3-git, git-ops-merge,
server-s3-review2); related client tests 587/587. Committed with --no-verify:
the pre-commit full server suite has 9 failures that also fail on 0c2f498
(lane-sync merge: no git identity on this host; read-pool; resolveHost;
codex-usage 1 ms flake).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>