# Server

A template's sign-in page for the demo: the kit's SignIn, against the template's demo session route (`demoSessionRoutes` in ./candor-server.ts). It says plainly that no email is sent and which code to type, and after signing in it goes to `next`. In production the demo is off unless the app sets `CANDOR_DEMO_AUTH=1` (`demoAuthAllowed()` in ./candor-server.ts), and the page says signing in isn't set up.

Replace it with your auth's sign-in page, or keep SignIn and point its `onSendCode` and `onVerifyCode` at your auth.

- Registry name: `server` (`@candor/server`)
- Tier: Premium. Installing it needs a Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/server

## Install

```bash
npx candor-ui add server
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/server`.
It brings in: locale, sign-in, tokens.
npm packages: drizzle-orm@^0.45.4, postgres@^4.0.0.

## <DemoSignIn>

Props:

- `api: string`: The template's session route, such as "/approvals-inbox/api/session".
- `next: string`: Where to go once signed in.
- `brand?: React.ReactNode | undefined`: The product's name or logo, shown above the form.

## Functions

### setDatabase

```ts
setDatabase(db: Database)
```

Use your app's own Drizzle database for every block. Call it once at startup, before the first request.

### database

```ts
database()
```

The shared database: whatever setDatabase() was given, else a pool on DATABASE_URL, opened on first use.

### demoViewer

```ts
demoViewer(request: Request)
```

The demo's viewer: whoever the `candor_demo_user` cookie names. With no cookie it's Riley, so a block works from a clean download; after signing out (the cookie is empty) it's nobody. Not authentication: anyone can set a cookie.

### setViewerSource

```ts
setViewerSource(source: ViewerSource)
```

Replace the demo cookie with your auth. Call it once at startup, before the first request.

### demoAuthAllowed

```ts
demoAuthAllowed()
```

Whether the demo cookie may say who is signed in: always outside production, and in production (`NODE_ENV` is "production") only when the app opts in with `CANDOR_DEMO_AUTH=1`. So a production deploy that never called setViewerSource() signs nobody in, rather than every visitor as the demo's first person.

### getViewer

```ts
getViewer(request: Request)
```

Who sent `request`, by your auth; null when nobody is signed in. Until you set a source it's the demo cookie, except in production without `CANDOR_DEMO_AUTH=1`, where it's null for everyone (and the server log says why, once).

### viewerName

```ts
viewerName(request: Request)
```

The signed-in person's name, which the blocks record as who did what; null when nobody is signed in.

### signedOut

```ts
signedOut()
```

What a route answers when nobody is signed in.

### demoSessionRoutes

```ts
demoSessionRoutes(people: DemoPerson[])
```

A template's sign-in route for the demo (`api/session`): GET says who is signed in and lists the demo's people, POST `{ email, code }` signs that person in, DELETE signs out. Nothing is sent and nothing is checked beyond the code above; it sets the cookie demoViewer() reads (not HttpOnly, so the blocks' demo "signed in as" switches can still change it). In production without `CANDOR_DEMO_AUTH=1` every method answers 403 with `{ error, demo: false }` and lists nobody.

Replace it with your auth's own sign-in (its route, or its hosted page) and call setViewerSource() with how your auth reads a request. The template's pages then need nothing else changed: they ask getViewer() through the API, and a 401 sends the person to the sign-in page.

### approvalSigning

```ts
approvalSigning()
```

The AI SDK's approval signing, spread into `streamText()`: with CANDOR_APPROVAL_SECRET set, each approval request is signed, so a browser can't approve a call the server never asked about. Without it, nothing is signed.

## Strings (`demoSignIn`)

The fixed words, English by default. Override any of them under `demoSignIn` in `<LocaleProvider strings={{ demoSignIn: { … } }}>`; the table is exported as `demoSignInStrings`.

- `hint`: This is a demo with no real accounts. Sign in as {emails}.
- `codeSent`: This demo sends no email. Type {code} to sign in as {email}.
- `unreachable`: The sign-in couldn't be reached. Check your connection and try again.
- `failed`: That didn't work. Try again.
- `off`: Signing in isn't set up here. The demo sign-in is off in production, and this app hasn't connected its own sign-in yet.

## Types

### Database

Any Drizzle Postgres database, whatever its driver; each block's tables are its own, so the handle has no schema.

```ts
PgDatabase<PgQueryResultHKT, any>
```

### Viewer

A person who is signed in, as your auth knows them. `name` is what the blocks record as who did what.

```ts
{ id: string; name: string }
```

### ViewerSource

Your auth's answer to "who sent this request?": the person, or null when nobody is signed in.

```ts
(request: Request) => Viewer | null | Promise<Viewer | null>
```

### DemoPerson

One of the demo's people, as its sign-in page lists them.

```ts
{ name: string; email: string }
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
