Public: anyone can read this; the crew can change it.

Commits the forge makes for a person carry a public commit address, never the account email #672

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

Commits the forge writes for a person now use a no-reply address by default: `<username>@users.noreply.<forge host>`. The account email goes into a commit only if the person opts in.

shipped production is serving it deploy bf488b2

opened this

Commits the forge writes for a person now use a no-reply address by default: <username>@users.noreply.<forge host>. The account email goes into a commit only if the person opts in.

What changed

  • One place decides the address: src/core/commit_identity.ts holds commitIdentity(config, user) and forgeHost(config). The host is the hostname from FORGE_URL if set, otherwise host, otherwise localhost. The platform-edge sign-in in http/server.ts uses the same host instead of working it out separately.
  • The setting: migration 46 adds user.commit_email ('noreply' | 'account', default 'noreply', enforced by a CHECK). User.commitEmail reads it and updateProfile validates it.
  • Every commit the forge writes for a person uses it, as both author and committer:

- merges and squashes, including the Requested-by and Co-authored-by trailers, which also carried the raw email before - reverts - web edits, new files and deletes (editFiles and commitToBranch) - taking a parent's updates into a fork, done by hand or automatically

  • Choosing it: /settings has a "Commit email" select. It shows the actual no-reply address, selected by default, and says plainly that the other option is published in every clone. A form that leaves the field out keeps the current choice. PATCH /api/v1/user takes {"commitEmail":"noreply"|"account"} and returns 422 for any other value. GET /api/v1/user reports which option is chosen, never the address. The email field's hint no longer says it is used to attribute commits.
  • Where the email does not appear: the account email was already absent from page metadata, the Atom feed and the public user view. The new tests now check the public profile, the user API and every git object.

Tests

  • test/commit-identity.test.ts is new and added to the gate. With the default setting:

- a merge, squash, revert, web edit, new file and delete each produce author and committer emails in the no-reply form - the private email appears in no git object

  • A user who opted in gets their real email on a merge and on a web edit.
  • It also covers the settings page (default selection, opting in and out, a tampered value, a cross-site post) and the API's validation.
  • Three existing tests changed:

- told-by and edits-security asserted the account email, which is the behaviour this change replaces. - oauth-discovery assumed the OAuth migration is always the last one. That test was wrong, and it broke as soon as any migration was appended, so it now finds the migration by its contents.

  • The gate check (tsc plus test-shards --gate) passed apart from that oauth-discovery test. After fixing it I re-ran that file with the related tests and it passed, but I did not re-run the whole gate.
  • edits-security, edits and migrate are not in the gate. In this environment they time out in setup on main too, so their setup failures are not from this change.

Not done

  • Crew agents keep their …@agents.invalid address, which belongs to no one's mailbox.
  • Commits people push themselves carry whatever their own git config says.
  • History that already exists is not rewritten.

To try it: boot the forge, sign up, merge a pull or edit a file in the web UI, then run git log --format='%ae|%ce'. Switch "Commit email" in /settings and repeat.

🤖 Generated with Claude Code

Closes #623

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs