Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ PORT=1xxxx AGENT_MODE=true pnpm dev
```

`AGENT_MODE` (development only) signs in a fake administrator with every permission and disables the auth redirects between
`/login` and `/dashboard`, so open `/dashboard/...` or `/login` directly. Data still comes from `BACKEND_URL` (default
`http://localhost:3000`). When changing auth or those redirects, also verify the normal flow with `AGENT_MODE=false`.
`/login` and `/dashboard`, so open `/dashboard/...` or `/login` directly. It has no access token, so the backend
(`BACKEND_URL`, default `http://localhost:3000`) refuses its calls: pages that read the backend show their error state,
and People serves its sample data. To see real backend data, sign in through the IdP with `AGENT_MODE=false`. When
changing auth or those redirects, also verify the normal flow with `AGENT_MODE=false`.

> [!IMPORTANT]
> Never run a destructive action on more than one row unless the prompt explicitly asks for it; otherwise ask the user
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Set the following runtime variables (the build does not need secrets):
| `SESSION_SECRET` | Independent random secret of at least 32 characters, encrypting the 10-minute state/nonce/PKCE cookie. Rotation invalidates pending login attempts. |
| `AGENT_PERMISSIONS` | Development only. Comma-separated permissions for the agent-mode user (default: all). `people:self` alone previews the restricted People view. |
| `AGENT_PERSON` | Development only. The agent-mode user's People record (default `P-0007`, Direttivo and President). `P-0098` is a Capo Admin and the Vice-president, `P-0034` the IT Team Lead, `P-0115` an IT member, `P-0045` has a fee receipt to review, `P-0021` a rejected one; `match:P-0115` previews the first sign-in, `none` a sign-in without a record. Sending an invite prints its link in the dev server's console. |
| `AGENT_MODE` | Fake all-permission user in development only. Backend must be a local fake or local legacy test instance. Production ignores this mode. |
| `AGENT_MODE` | Fake all-permission user in development only. It has no access token, so the backend refuses its calls; People serves sample data. Production ignores this mode. |
| `PORT` | Local listening port (3001 by default); use a port ≥10000 for agent previews. |
| `TEST_REDIS_URL` | Opt-in disposable Redis for integration tests. Tests create/delete only UUID session keys. |

Expand Down
6 changes: 3 additions & 3 deletions src/features/dashboard/overview.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const getOverviewCounts = createServerFn()

/**
* `forbidden` without `tg:users:read`; `unavailable` while the backend predates `tg.users.getCreationHistory` or refuses
* the call as unauthenticated (agent mode's anonymous path), so the Overview shows the total without the chart instead
* the call as unauthenticated (agent mode has no token), so the Overview shows the total without the chart instead
* of an error.
*/
export type TelegramUserHistory =
Expand Down Expand Up @@ -98,8 +98,8 @@ export const getTelegramUserHistory = createServerFn()
if (result.error !== null) throw new Error(result.error)
return { status: "ok", days: result.days, today: romeToday() }
} catch (error) {
// NOT_FOUND: a backend that predates the procedure. UNAUTHORIZED: the procedure is token-only, and agent mode
// (development) calls the backend anonymously. Either way the Overview shows the total without the chart.
// NOT_FOUND: a backend that predates the procedure. UNAUTHORIZED: agent mode (development) has no token. Either
// way the Overview shows the total without the chart.
if (
error instanceof TRPCClientError &&
(error.data?.code === "NOT_FOUND" || error.data?.code === "UNAUTHORIZED")
Expand Down
6 changes: 3 additions & 3 deletions src/features/people/people.contract.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { z } from "zod"

/**
* The `people.*` backend contract (docs/people/contract.md). The backend doesn't serve it yet: agent mode answers from
* `people.fake.server.ts`, and real responses are parsed with these schemas before they reach a page.
* The `people.*` backend contract (docs/people/contract.md), served by the backend's `src/people/router.ts`. Agent mode
* answers from `people.fake.server.ts`; real responses are parsed with these schemas before they reach a page.
*/

export const TEAM_IDS = ["it", "hr", "design", "intl", "events"] as const
Expand Down Expand Up @@ -387,7 +387,7 @@ export type ImportPreview = z.infer<typeof importPreviewSchema>

export const capoAreaSchema = z.object({
scope: z.string(),
// `admins` is null while the backend does not know a group's Telegram admins.
// `admins` is null until the bot has reported the group's Telegram admins.
groups: z.array(groupSchema.extend({ admins: z.number().int().nullable() })),
admins: z.array(personRowSchema),
requests: z.array(z.object({ id: z.string(), person: personRefSchema, group: groupSchema, requestedOn: z.string() })),
Expand Down
2 changes: 1 addition & 1 deletion src/features/telegram/grants.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const grantInput = z
})
.refine(({ since, until }) => until > since, { message: "The grant end must be after its start.", path: ["until"] })

// The backend takes the author from the access token; actor fields are only read from legacy callers.
// The backend takes the author from the access token.
export const createTelegramGrant = createServerFn({ method: "POST" })
.middleware([grantsWriteMiddleware])
.validator(grantInput)
Expand Down
1 change: 0 additions & 1 deletion src/features/telegram/user-detail/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ type AuditActor = { name: string; title?: string }
function auditActor(audit: Audit): AuditActor {
if (audit.admin) return { name: telegramUserName(audit.admin) }
if (audit.actorTgId !== null) return { name: `User ${audit.actorTgId}` }
if (audit.adminId !== null) return { name: `User ${audit.adminId}` }
if (audit.actorSub) {
return {
name: audit.client === "dashboard" ? "Dashboard admin" : "PoliNetwork account",
Expand Down
2 changes: 1 addition & 1 deletion src/server/backend.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { env } from "@/env"

/**
* A backend client for one request, carrying the signed-in user's access token (RFC v3 §11): the backend verifies it
* and enforces the user's permissions. `null` only in agent mode, which uses the backend's legacy anonymous path.
* and enforces the user's permissions. `null` only in agent mode, which has no token: the backend refuses its calls.
*/
export function createBackendClient(accessToken: string | null) {
return createTRPCClient<AppRouter>({ links: backendLinks(accessToken) })
Expand Down
Loading