|

Sports Agents architecture and workflows

Current Keystone models, access rules, dashboard workflows, and named GraphQL operations.

Everything on this page is starter scaffolding. There are no sports-agents models or workflows in the current source.

Application surfaces

SurfaceLocationNotes
Landing pageapp/page.tsxStarter welcome page
Dashboard auth pagesapp/dashboard/{init,signin,signup,reset,no-access}, features/dashboard/screensFirst user, sign-in, optional sign-up, reset, no access
Generic model pagesapp/dashboard/(admin)/[listKey]/**, features/dashboardList, create, and item pages for each Keystone list
Dashboard homeapp/dashboard/(admin)/page.tsxHomePage screen with list counts
GraphQLpages/api/graphql.ts → /api/graphqlGraphQL Yoga over the Keystone schema, with multipart uploads
AI chatapp/api/completion/route.tsOpenRouter streaming with MCP tools
MCPapp/api/mcp-transport/[transport]/route.tsJSON-RPC tools over the GraphQL API
Route guardproxy.ts, features/dashboard/middleware.tsRedirects for /dashboard routes

Keystone config lives in features/keystone/index.ts and is re-exported from keystone.ts. Sessions are stateless signed cookies with a maximum age of 360 days.

Models

User (features/keystone/models/User.ts): name, email (unique), password, role (to Role.assignedTo), tasks (to Todo.assignedTo). Password is never readable. A migration also adds password reset token fields used by Keystone auth.

Role (Role.ts): name, assignedTo, and seven checkbox flags:

FlagWhat it allows
canAccessDashboardUse /dashboard and the shared AI chat
canManagePeopleCreate and delete users, set roles, change other users' passwords
canSeeOtherPeopleRead other users
canEditOtherPeopleUpdate other users
canManageRolesCreate, update, and delete roles (any signed-in user can read roles)
canCreateTodosCreate todos and create, update, or delete todo images
canManageAllTodosRead and change todos assigned to other users, and set assignedTo

Todo (Todo.ts): example model. label, description (document), grouped status fields (isComplete, status with todo / in_progress / done / blocked, priority 1 to 5, tags multiselect), planning fields (dueDate, weight, budget), advanced fields (isPrivate, largeNumber, metadata, secretNote), coverImage, todoImages, and assignedTo. On create, assignedTo defaults to the signed-in user.

TodoImage (TodoImage.ts): image, imagePath, altText, todos, metadata.

Access rules

Rules live in features/keystone/access.ts.

  • All lists require a signed-in user for most operations.
  • User create is open to anyone only when PUBLIC_SIGNUPS_ALLOWED=true; otherwise it needs canManagePeople.
  • rules.canReadPeople and rules.canUpdatePeople limit users without the matching flag to their own record.
  • rules.canReadTodos limits users without canManageAllTodos to todos assigned to them. With the flag, a user sees their own todos, unassigned private todos, and all non-private todos.
  • rules.canManageTodos limits update and delete the same way.
  • The Keystone ui.isAccessAllowed check and the route guard both require canAccessDashboard.

Workflow: first run

  1. proxy.ts calls getAuthenticatedUser, which runs the authenticatedItem query and redirectToInit.
  2. If redirectToInit is true (no users), every /dashboard route redirects to /dashboard/init.
  3. The init form calls createInitialUser. Keystone creates the user and an Admin role with every flag on, then signs them in.

Workflow: sign-in, sign-up, reset

  • Sign-in calls authenticateUserWithPassword and sets the session cookie. The from redirect is sanitized to same-site dashboard paths.
  • Sign-up at /dashboard/signup only works when PUBLIC_SIGNUPS_ALLOWED=true. It calls createUser with no role, so the new user lands on /dashboard/no-access until an admin gives them a role.
  • Reset calls sendUserPasswordResetLink, then redeemUserPasswordResetToken from the emailed link. Without SMTP_HOST the reset page says email is not available.
  • Sign-out calls endSession.

Workflow: manage records

The generic pages under /dashboard/<list-path> (for example /dashboard/todos, /dashboard/users, /dashboard/roles, /dashboard/todo-images) read Keystone admin meta and build list, filter, sort, create, and edit screens from it. Keystone ui options hide create and delete buttons when the role lacks the flag. The server still enforces access on every operation.

Workflow: AI chat

  1. A dashboard user opens the right sidebar chat.
  2. The client posts to /api/completion in shared mode (server keys) or local mode (keys from the browser).
  3. In shared mode the route checks the session and canAccessDashboard, and needs OPENROUTER_API_KEY and OPENROUTER_MODEL.
  4. The route connects to /api/mcp-transport/http with the caller's cookie and gives the model those tools for up to 10 steps.
  5. The model can query, create, update, and delete records the user is allowed to change.

GraphQL operations

Custom operations added in features/keystone/mutations/index.ts:

OperationTypePurpose
redirectToInitQueryReturns true when there are no users

Keystone auth operations used by the dashboard:

OperationUsed by
authenticatedItemRoute guard, AuthenticatedUser query in features/dashboard/actions/auth.ts
authenticateUserWithPasswordSign-in
createInitialUserFirst-user setup
createUserSign-up
sendUserPasswordResetLink, validateUserPasswordResetToken, redeemUserPasswordResetTokenPassword reset
endSessionSign-out

Named dashboard queries: KsFetchAdminMeta (getAdminMetaAction), KsFetchItem (getItemAction), GetListItems (getListItemsAction). Item create, update, and delete use the generated create<List>, update<List>, delete<List>, and delete<Lists> mutations from features/dashboard/actions/item-actions.ts.

Generated CRUD exists for each list: users, user, usersCount, createUser, updateUser, deleteUser, and the same shape for Role, Todo, and TodoImage. See schema.graphql for the full schema.

On this page