Skip to content

Epic: passcode-gated DJ self-signup for holiday breaks (server) #2365

Description

@jakebromberg

Why

During holiday breaks WXYC suspends the regular schedule and fills shows by signup. An inactive DJ who walks into the control room with no account cannot log their show at all — account creation is admin-only end to end, and no manager is on site.

This epic adds a passcode-gated self-signup path: a short code on a sticky note in the control room, which a DJ enters to create their own account at dj role. Every account made this way is flagged for manager review.

The governing constraint: availability, not secrecy

This feature runs during weeks when nobody is watching. Every control must fail toward "wait a few minutes," never "the control room is locked out until someone drives in."

That constraint is load-bearing and easy to violate. Three separate mechanisms had to be redesigned during planning because each converted a legitimate user's mistake into an outage:

  • a per-IP rate limiter — every DJ shares one IP, the control-room computer, so three fumbled codes would lock the room out for 15 minutes
  • an auto-revoking failure counter — a failed attempt matches no passcode row, so the only implementable version revoked every active code on a few wrong guesses from anywhere on the internet
  • a use cap that counted typos — validation ran after the use was claimed, so 25 fumbled usernames would burn the code

Check any new counter or limit against this before adding one.

Design decisions worth knowing before you start

  • The passcode is encrypted, not hashed (AES-256-GCM), because it is meant to be read back: a manager reveals the current code and phones it to a stranded DJ rather than rotating, since rotating under the two-active-row cap can invalidate the note the rest of the room is using. The key sits beside DB_PASSWORD in the EC2 .env, so this protects dumps and snapshots, not host compromise.
  • disableSignUp: true stays on for both emailAndPassword and emailOTP. This is a separate endpoint that happens to create users, not better-auth's signup.
  • Self-signed DJs get dj immediately. member has flowsheet: ['read'] only, so a lower role could not do the thing they walked in to do. The review flag, not a reduced role, is what bounds the risk — and between signup and review, the passcode is sufficient to obtain flowsheet write access. That is the deliberate trade.
  • Review state is three auth_user columns with no derived boolean, and the predicate is exported once and shared between the roster filter and the badge, so the two cannot drift.

Sub-issues

Ship in dependency order. The first two define the schema and the module surface everything else consumes, so they land sequentially before anything fans out.

dj-site side

The frontend half is tracked at WXYC/dj-site#1356. Cross-repo dependencies are wired natively: dj-site's signup form is blocked by the endpoint issue here, its roster queue by the schema issue, its manager panel by the admin API issue.

Plan

Full plan: ~/Downloads/wxyc-station-passcode-signup-plan.md — to move into docs/plans/ once the first PR lands. It carries the reasoning behind each decision, the rejected alternatives, and the rollout stance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestepicEpic-level tracking issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions