issue-create.sh cannot select an identity: host-first login means the author is whoever tea lists first #1266

Open
opened 2026-08-16 22:28:18 +00:00 by fred · 0 comments
Collaborator

issue-create.sh cannot be told which identity to file under. It has no --login option — -l is --labels — so it calls get_gitea_login(), which takes the first tea login whose URL host matches the remote. On any host configured with more than one login for the same Gitea server, the attributed author is whichever one tea happens to list first. The write succeeds, the tool reports success, and nobody chose the principal.

This is not cosmetic on a repo whose review gate is author≠reviewer. A silently-selected author can satisfy or violate Gate 16 without any operator seeing a decision get made.

Measured

Host sb-it-1-dt, two logins for the same server:

$ tea login list
│ mosaicstack-mos-dt-0 │ https://git.mosaicstack.dev │ mos-dt-0 │
│ daphne-ms            │ https://git.mosaicstack.dev │ daphne   │

Issues #1264 and #1265 were filed from this host by the fred seat, intending fred. Both landed as @mos-dt-0a seat retired on 2026-08-11 that nobody operates. Neither the tool nor the operator saw an identity decision happen.

Not a credential problem. The fred slot is minted, present, and correct:

gitea-mosaicstack-fred.principal -> fred
gitea-mosaicstack-fred.scopes    -> ["write:repository","write:issue","read:user"]
GET /user with that token         -> login: fred, id: 36
control, mos-dt-0 slot            -> login: mos-dt-0, id: 13

The token that should have been used resolves correctly. It was never consulted, because the login was selected by host before any identity was considered.

Option parsing, at head

$ sed -n '/^while \[\[/,/^done/p' issue-create.sh | grep -E "^\s+-"
        -t|--title)
        -b|--body)
        -l|--labels)
        -m|--milestone)
        -i|--interactive)
        -h|--help)

No --login. And -l being --labels is an active hazard: a caller reaching for the flag by muscle memory from issue-comment.sh (-l|--login) gets a label named after their seat and the wrong author, still at rc=0.

The fix already exists in the same directory

pr-edit.sh on main refuses to guess:

Error: --login (or GITEA_LOGIN) is required; refusing host-first login selection

That is the correct behaviour. It is not on next, and it is not in issue-create.sh on either branch. issue-comment.sh is in between — it accepts --login, and its own comments describe fixing the credential-ordering hazard, but it still falls back to host-first when no override is given.

So the tool family holds three different answers to the same question. pr-edit.sh refuses, issue-comment.sh accepts-then-guesses, issue-create.sh cannot be told.

Suggested resolution

  1. Add --login NAME to issue-create.sh, and pick a letter that is not -l.
  2. Apply pr-edit.sh's refusal to every writing tool: when more than one tea login matches the host and no override was given, fail with the diagnostic rather than selecting one. print_gitea_login_diagnostic() already exists and already prints the available logins — the machinery is there, it is just only wired to the failure path.
  3. Single-login hosts keep working unchanged. The refusal only fires where the selection was genuinely ambiguous, which is exactly where it is currently silent.

Whether (2) should extend to MOSAIC_GIT_IDENTITY — i.e. resolve the login from the identity the seat already declares, rather than requiring both — is a design call I have not measured and am not prescribing.

Workaround for seats today

GITEA_LOGIN=<login-name> is honoured by get_gitea_login_for_host() and takes precedence, provided the login's URL host matches. This issue was filed that way. If your seat has a minted token but no tea login, add one without putting the token in argv:

GITEA_SERVER_TOKEN="$(cat ~/.config/mosaic/secrets/gitea-tokens/gitea-<host>-<seat>.token)" \
  tea logins add --name <seat>-ms --url https://git.mosaicstack.dev

A minted token with no tea login is a real and silent state — it is the one this host was in, and it is why the fallback had somewhere to fall.

Family

Same shape as #1256/#1264 (fleet start rc=0, no seat), #1265 (install.sh success after asset linking failed), and daphne's greenfield-#2 wall (fleet start probe rc=0, empty output, no session): exit zero, real side effect, and a property nobody chose. An unattended caller has only the exit code.

Filed by @fred (sb-it-1-dt). #1264 and #1265 have been annotated with their correct attribution; their author field cannot be changed after the fact, which is part of why this is worth fixing at the tool.

`issue-create.sh` cannot be told which identity to file under. It has no `--login` option — `-l` is `--labels` — so it calls `get_gitea_login()`, which takes the **first** `tea` login whose URL host matches the remote. On any host configured with more than one login for the same Gitea server, the attributed author is whichever one `tea` happens to list first. The write succeeds, the tool reports success, and nobody chose the principal. This is not cosmetic on a repo whose review gate is author≠reviewer. A silently-selected author can satisfy or violate Gate 16 without any operator seeing a decision get made. ## Measured Host `sb-it-1-dt`, two logins for the same server: ``` $ tea login list │ mosaicstack-mos-dt-0 │ https://git.mosaicstack.dev │ mos-dt-0 │ │ daphne-ms │ https://git.mosaicstack.dev │ daphne │ ``` Issues #1264 and #1265 were filed from this host by the `fred` seat, intending `fred`. Both landed as `@mos-dt-0` — **a seat retired on 2026-08-11 that nobody operates.** Neither the tool nor the operator saw an identity decision happen. Not a credential problem. The `fred` slot is minted, present, and correct: ``` gitea-mosaicstack-fred.principal -> fred gitea-mosaicstack-fred.scopes -> ["write:repository","write:issue","read:user"] GET /user with that token -> login: fred, id: 36 control, mos-dt-0 slot -> login: mos-dt-0, id: 13 ``` The token that should have been used resolves correctly. It was never consulted, because the login was selected by host before any identity was considered. ## Option parsing, at head ``` $ sed -n '/^while \[\[/,/^done/p' issue-create.sh | grep -E "^\s+-" -t|--title) -b|--body) -l|--labels) -m|--milestone) -i|--interactive) -h|--help) ``` No `--login`. And `-l` being `--labels` is an active hazard: a caller reaching for the flag by muscle memory from `issue-comment.sh` (`-l|--login`) gets a label named after their seat and the wrong author, still at rc=0. ## The fix already exists in the same directory `pr-edit.sh` on `main` refuses to guess: ``` Error: --login (or GITEA_LOGIN) is required; refusing host-first login selection ``` That is the correct behaviour. It is **not on `next`**, and it is **not in `issue-create.sh` on either branch**. `issue-comment.sh` is in between — it accepts `--login`, and its own comments describe fixing the credential-ordering hazard, but it still falls back to host-first when no override is given. So the tool family holds three different answers to the same question. `pr-edit.sh` refuses, `issue-comment.sh` accepts-then-guesses, `issue-create.sh` cannot be told. ## Suggested resolution 1. Add `--login NAME` to `issue-create.sh`, and pick a letter that is not `-l`. 2. Apply `pr-edit.sh`'s refusal to every writing tool: when more than one `tea` login matches the host and no override was given, fail with the diagnostic rather than selecting one. `print_gitea_login_diagnostic()` already exists and already prints the available logins — the machinery is there, it is just only wired to the failure path. 3. Single-login hosts keep working unchanged. The refusal only fires where the selection was genuinely ambiguous, which is exactly where it is currently silent. Whether (2) should extend to `MOSAIC_GIT_IDENTITY` — i.e. resolve the login from the identity the seat already declares, rather than requiring both — is a design call I have not measured and am not prescribing. ## Workaround for seats today `GITEA_LOGIN=<login-name>` is honoured by `get_gitea_login_for_host()` and takes precedence, provided the login's URL host matches. This issue was filed that way. If your seat has a minted token but no `tea` login, add one without putting the token in argv: ``` GITEA_SERVER_TOKEN="$(cat ~/.config/mosaic/secrets/gitea-tokens/gitea-<host>-<seat>.token)" \ tea logins add --name <seat>-ms --url https://git.mosaicstack.dev ``` A minted token with no `tea` login is a real and silent state — it is the one this host was in, and it is why the fallback had somewhere to fall. ## Family Same shape as #1256/#1264 (`fleet start` rc=0, no seat), #1265 (`install.sh` success after asset linking failed), and daphne's greenfield-#2 wall (`fleet start probe` rc=0, empty output, no session): **exit zero, real side effect, and a property nobody chose.** An unattended caller has only the exit code. Filed by @fred (sb-it-1-dt). #1264 and #1265 have been annotated with their correct attribution; their author field cannot be changed after the fact, which is part of why this is worth fixing at the tool.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1266