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

Invites, part 2b: the terminal's create-account screen asks for an invite when signup = invite #2572

Merged opened this

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

Merged

With `signup = invite`, the terminal's create-account screen now asks for the invite token and redeems it the way the web form does. I ran `tui-signup-invite`, `tui-can`, `tui-screens`, `tui-signup-throttle` and `invite-redeem` plus `tsc --noEmit`. Apart from the two new tests, which failed on their first run (below), the tests passed and `tsc` was clean. I didn't run the full suite.

shipped production is serving it deploy 1f09d72

opened this

With signup = invite, the terminal's create-account screen now asks for the invite token and redeems it the way the web form does. I ran tui-signup-invite, tui-can, tui-screens, tui-signup-throttle and invite-redeem plus tsc --noEmit. Apart from the two new tests, which failed on their first run (below), the tests passed and tsc was clean. I didn't run the full suite.

  • Screen: in invite mode the screen keeps its "Create account" mode and adds a fourth field, "Invite", which is masked. The token goes through the same signUp(…, { invite }) call as the web door, so it is checked and spent in the same transaction as the account, and the sign-up window and audit event apply as before. Under closed the screen is unchanged: no create mode.
  • Refusal: a bad, used, revoked or expired token gets one plain line, the same text /signup shows. I moved that text, INVITE_SPENT_MESSAGE, from site.ts into actions/users.ts so both doors share it.
  • Tests: test/tui-signup-invite.test.ts has two cases. A bad token makes no account and shows the refusal. A good token makes the account and is spent, and reusing it is refused. Both failed on their first run because my test read the screen before it had redrawn after Ctrl+T; I fixed the test, not the code.
  • tui-can.test.ts: unchanged. That screen's list didn't change, so there was no line to alter.
  • Left out: the passkey and Turnstile paths. The terminal still can't do those, as before. The token is typed into the field, not read from a link, because the terminal has no URL to carry it.

To try it, set signup to invite, mint a link under Invites, then ssh in as a guest, press s, then Ctrl+T, and fill in the form including the token.

Closes #2418

commented

With signup = invite, the terminal's create-account screen now asks for the invite token and redeems it the way the web form does. I ran tui-signup-invite, tui-can, tui-screens, tui-signup-throttle and invite-redeem plus tsc --noEmit. Apart from the two new tests, which failed on their first run (below), the tests passed and tsc was clean. I didn't run the full suite.

  • Screen: in invite mode the screen keeps its "Create account" mode and adds a fourth field, "Invite", which is masked. The token goes through the same signUp(…, { invite }) call as the web door, so it is checked and spent in the same transaction as the account, and the sign-up window and audit event apply as before. Under closed the screen is unchanged: no create mode.
  • Refusal: a bad, used, revoked or expired token gets one plain line, the same text /signup shows. I moved that text, INVITE_SPENT_MESSAGE, from site.ts into actions/users.ts so both doors share it.
  • Tests: test/tui-signup-invite.test.ts has two cases. A bad token makes no account and shows the refusal. A good token makes the account and is spent, and reusing it is refused. Both failed on their first run because my test read the screen before it had redrawn after Ctrl+T; I fixed the test, not the code.
  • tui-can.test.ts: unchanged. That screen's list didn't change, so there was no line to alter.
  • Left out: the passkey and Turnstile paths. The terminal still can't do those, as before. The token is typed into the field, not read from a link, because the terminal has no URL to carry it.

To try it, set signup to invite, mint a link under Invites, then ssh in as a guest, press s, then Ctrl+T, and fill in the form including the token.

Changes requested — before the latest push

The change does what the issue asks and the tests cover it, but two things miss the bar.

Fix first: the README does not say the terminal takes an invite. README.md:888 (Invites) and :1489 (signup setting) still say the terminal 'offers no Create account' for closed and describe invite links only for /signup?invite= and POST /api/v1/users. Add that with signup = invite the terminal's create screen has a fourth masked 'Invite' field, takes the token (not the link), and refuses a bad one with the same sentence as /signup.

Second: the mode is read once when the screen is built (invited/closed in signin.ts), but the README promises the setting 'takes effect on the next request'. An admin switching signup while a guest sits on this screen leaves the screen stale: it asks for an invite after signup opens, or offers no token field after it moves to invite. The server stays correct because signUp re-checks. Still, read getSignup inside fields() and submit, or say in the pull that the screen is fixed per session.

Smaller: an empty token in invite mode goes out with no invite and shows 'used, revoked or expired', which is wrong for a field left blank. Ask for the token ('Paste the token from your invite link.') before calling signUp. Add a test for it.

commented

With signup = invite, the terminal's create-account screen now asks for the invite token and redeems it the same way the web form does. This push addresses all three points from the review. bun test on tui-signup-invite, tui-screens and tui-can and tsc --noEmit pass; I did not run the whole suite.

  • Create screen: with signup = invite there is a fourth, masked "Invite" field. A bad, used, revoked or expired token gets the same sentence as /signup. test/tui-can.test.ts is unchanged, because the "What I can do" screen's list did not change.
  • Live setting: the screen no longer reads the signup mode once when it is built. It reads it on every key press, redraw and submit (invited() and closed() in src/tui/screens/signin.ts). A guest on the screen now sees the Invite field appear or go when an admin changes signup.
  • Blank token: in invite mode a blank token is caught before signUp is called. The screen shows "Paste the token from your invite link." and puts the cursor on the Invite field.
  • README: the Invites paragraph and the signup setting paragraph now say the terminal takes the token (not the link) in a fourth masked field, asks for a blank one, and refuses a bad one with the same sentence as /signup.
  • Tests: two new cases in test/tui-signup-invite.test.ts. One checks the blank-token prompt. The other changes the setting from invite to open while the screen is up and checks that the Invite field goes away.

What I left out: the passkey and Turnstile paths are still web-only. The token is typed in, not read from a link, because the terminal has no URL to carry it.

Try it: set signup to invite, mint a link under Invite links, then ssh in as a guest. Press s, then Ctrl+T, and fill in the form including the token.

Approved

Approved. The terminal create-account screen now has a masked Invite field while signup = invite. It redeems through the same signUp(..., {invite}) call as the web door, so the check and the spend are one write. The sign-up window still throttles by socket address. A blank token is asked for before any slot is taken. A bad token gets the shared INVITE_SPENT_MESSAGE, which moved to actions/users.ts so the two doors cannot drift. The screen reads the live setting on every call, and the README covers both paragraphs. The tests cover bad, blank, live-change and good-then-reused tokens. I read the diff and did not open the preview.

Two things I would do next:

  1. In signin.ts, clamp focus when the setting flips from invite to open while the cursor is on the Invite field (index 3). The field list shrinks to three, so focus can point past the end.
  2. Mapping every signup_closed to the spent-link sentence is right under invite. A person who pastes a token with stray characters or the whole link gets the same line. Consider a hint that the screen wants the token, not the link, as the README already says.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs