--- type: User Story title: Schedule group access in UTC description: Administrators restrict enabled groups to recurring weekly UTC windows and provide static denial-message templates. tags: [admin, groups, scheduling, velocity, templates, security] timestamp: 2026-08-02T17:42:26Z story_id: US-020 status: verified --- # User Story As an administrator, I want an enabled group to have recurring access windows, so that its members can join only during approved weekly periods and receive useful denial guidance. # Acceptance Criteria - [x] A group can have zero or more recurring weekly access windows stored and evaluated in UTC. - [x] The browser shows each UTC window's current equivalent in the administrator's local timezone while clearly identifying UTC as authoritative. - [x] The Groups table identifies unrestricted groups and the configured window count, linking each status to schedule management. - [x] Administrators can add and remove multiple windows, including windows that cross the end of the UTC week. - [x] Window starts are inclusive and window ends are exclusive. - [x] No configured windows preserve unrestricted scheduling behavior while Minecraft access is enabled. - [x] Disabled Minecraft access always denies admission, regardless of schedule. - [x] Enabled Minecraft access with configured windows allows login only inside an active window. - [x] VPN/proxy/Tor policy is evaluated only after group access and schedule policy pass. - [x] Schedule enforcement occurs at login and does not disconnect an existing session when a window ends. - [x] Schedule changes require confirmation, reauthorize the administrator, and commit atomically with an audit event. - [x] Malformed or overlapping schedule data is rejected; malformed persisted policy fails closed. - [x] Registration, group-disabled, and VPN/proxy/Tor templates support `{player}` and `{group}`. - [x] Schedule-denied templates additionally support `{next_start}` and `{next_end}` for the earliest upcoming UTC window. - [x] Unknown template variables, control characters, and invalid lengths are rejected server-side. - [x] Registration denials use `everyone` when no effective group can be resolved. # Implementation - [`packages/database/src/schema.ts`](../packages/database/src/schema.ts) - [`packages/database/drizzle/0005_young_vertigo.sql`](../packages/database/drizzle/0005_young_vertigo.sql) - [`apps/web/src/lib/group-schedule.ts`](../apps/web/src/lib/group-schedule.ts) - [`apps/web/src/lib/admission-settings.ts`](../apps/web/src/lib/admission-settings.ts) - [`apps/web/src/app/admin/(console)/groups/[groupId]/page.tsx`](../apps/web/src/app/admin/%28console%29/groups/%5BgroupId%5D/page.tsx) - [`apps/web/src/app/api/velocity/access/route.ts`](../apps/web/src/app/api/velocity/access/route.ts) # Validation UTC recurrence, multiple-window selection, local conversion, malformed schedules, template validation, policy precedence, and schedule-editor interactions are covered by automated tests. Drizzle generation, migration preflight, TypeScript, lint, build, security checks, and OKF validation must pass. # Related Stories - [Enforce registration at Velocity](us-009-velocity-admission.md) - [Operate settings and audit views](us-012-admin-operations.md) - [Control Minecraft admission with groups](us-017-group-access.md) - [Manage groups efficiently](us-019-admin-group-management.md)