Registration, login, access codes and the waitlist.
Every schema below is exported from @novaterra/contracts and validated on both sides of the wire. Source: packages/contracts/src/auth.ts.
RegisterRequest
| Field | Type | Notes |
|---|
email | string (email) | |
password | string (min 8) | |
handle | string (min 2, max 32, regex) | |
displayName | string (min 1, max 80) | |
accessCode | string (min 4) | |
LoginRequest
| Field | Type | Notes |
|---|
email | string (email) | |
password | string | |
AuthResponse
| Field | Type | Notes |
|---|
being | Being | |
isOwner | boolean | |
AccessCode
| Field | Type | Notes |
|---|
code | string | |
createdBy | Id | |
usedBy | Id | null | |
note | string | optional · default '' |
createdAt | ISODate | |
usedAt | ISODate | null | |
WaitlistRequest
| Field | Type | Notes |
|---|
email | string (email) | |
name | string (max 80) | optional |
why | string (max 500) | optional |
EmailStatus
What the signed-in person’s own address looks like from here. emailConfigured is deliberately part of the response rather than something the UI guesses: a node with no RESEND_API_KEY cannot send anything, and a button offering to “send a link” that silently goes nowhere is worse than no button. It is a capability flag, not a secret — it says whether this node has a mail provider, not who it is or what its key is.
| Field | Type | Notes |
|---|
email | string (email) | |
verified | boolean | |
verifiedAt | ISODate | null | |
pending | boolean | |
emailConfigured | boolean | |
VerifyEmailRequest
| Field | Type | Notes |
|---|
token | string (min 16, max 256) | |
PasswordResetRequest
“Send me a way back in.” Answered identically whether or not the address is known here.
| Field | Type | Notes |
|---|
email | string (email) | |
SetPasswordRequest
The other half: the token from the link, and the password to put in its place.
| Field | Type | Notes |
|---|
token | string (min 16, max 256) | |
password | string (min 8) | |
InviteEmailRequest
Owner only: mint a code and send it to an address, rather than reading it off a screen.
| Field | Type | Notes |
|---|
email | string (email) | |
note | string (max 200) | optional · default '' |
InviteEmail
One invitation, as the owner’s own screen shows it back. codeHint is a MASK (NOVA-••••-7F3K), not the code. An access code is a single-use bearer credential: whoever reads it can claim a place in this world, and this list is the one screen that will hold dozens of them at once. Settings once printed 27 live codes in plaintext and that was the finding; a list whose job is “who did I invite, and did they arrive” does not need to carry the credential to do it. The owner who needs the literal code still has GET /api/auth/codes.
| Field | Type | Notes |
|---|
id | Id | |
email | string (email) | |
codeHint | string | |
note | string | optional · default '' |
sentBy | Id | |
delivered | boolean | |
reason | string | optional · default '' |
used | boolean | |
createdAt | ISODate | |