CQ is a full-stack Hack Club YSWS app for making ham-radio projects, documenting the work, submitting builds for review, earning hertz, and ordering radio gear. Eligible projects can be electronics hardware or software, but they must directly relate to ham radio. Studying or completing a course alone is not an eligible project.
CQ's submission gate and reviewer checklist implement the current Hack Club YSWS Project Submission Guidelines, including shipped/reproducible projects, required evidence, eligibility exceptions, auditable hour decisions, and distinct hours for project updates.
- Hack Club Auth OIDC sign-in with verified ID-token claims, state, nonce, PKCE, encrypted local server sessions, CSRF protection, and permission-based organizer roles
- Project creation and editing, Markdown devlogs timed from new linked-project Hackatime activity, drag-and-drop CDN image uploads, evidence, project updates, submission, status refresh, withdrawal, and decision history
- Hackatime OAuth account linking and automatic, selectable project discovery; access tokens remain server-side in the access-controlled data store
- Ari request signing, signed webhook verification, a five-minute replay window, idempotent delivery handling, approval ledger, and reversal handling
- A real shop with access-controlled shipping details, stock control, carts, locked balance/stock checks, hertz balances, cancellation refunds, order history, and fulfilment tracking
- Slack direct-message notifications for submission, review, requested changes, approval, denial, purchase, and order-status changes
- An on-platform review queue with claiming, private notes, participant feedback, immutable submitted snapshots, GitHub commit history, claimed-time checks, decisions, approved-time rewards, audit history, and Slack notifications
- Separate reviewer, shop editor, fulfilment, country editor, support, and admin roles
- Separate Airtable tables with readable summary columns and unencrypted JSON documents
- A centralized organizer audit log for roles, hertz, reviews, orders, shop items, projects, and country policies
Requirements: Node.js 20 or newer.
npm install
cp .env.example .env
npm run devSet DEV_AUTH_BYPASS=true only for a local preview. It exposes a development
sign-in form and is disabled automatically in production and test environments.
Run the automated suite with:
npm test- Create a Hack Club Auth app and set its callback to
https://cq.hackclub.com/auth/callback. Add the client ID, secret, and exact callback URI to the server environment. - Create a confidential Hackatime OAuth app under My OAuth Apps, set its
exact callback to
https://your-domain.example/app/hackatime/callback, and add its client ID and secret. CQ requests theprofile readscopes. - Follow the Airtable setup, then run
npm run airtable:setup. CQ creates separate readable tables and safely copies records from the older single-table layout without deleting the old data. - Ari is optional. With no Ari credentials, shipped projects enter CQ's local
review queue. If Ari is enabled later, add its program and signing secrets and
configure its outgoing webhook as
https://your-domain.example/ari/webhook. - Create a Slack app with
chat:writeandim:write, install it to the Hack Club workspace, then setSLACK_BOT_TOKEN.SLACK_ADMIN_CHANNEL_IDis optional and receives organizer-facing purchase notices. SetSLACK_SECURITY_USER_IDto your Slack member ID to receive direct security alerts for every organizer-side mutation. - Create a server API key in Hack Club CDN and set the R2 upload environment variables. CQ uses the configured R2 bucket for permanent devlog image uploads; the key is never sent to participants' browsers.
- Set
ADMIN_EMAILSto a comma-separated list of organizer Hack Club email addresses. Those accounts receive the admin role when they sign in. - Set
BASE_URL,NODE_ENV=production, and all remaining values documented in.env.example.
Hackatime compares redirect URIs exactly. For the production domain in this repository, register this URI as its own line in My OAuth Apps:
https://cq.rubensutton.hackclub.app/app/hackatime/callback
Do not add a trailing slash.
Public GitHub repositories can be inspected without another credential. Setting
the optional GITHUB_TOKEN raises the API rate limit for the reviewer dashboard.
All credentials are server-only environment variables. Do not add .env to
source control.
CQ is one server-rendered Node application, not a monorepo or a static export.
The included railpack.json installs the locked dependencies with npm ci and
starts the production server with npm start.
If the hosting dashboard overrides repository settings, use:
| Setting | Value |
|---|---|
| Root directory | Leave empty |
| Install command | npm ci |
| Build command | Leave empty |
| Output directory | Leave empty |
| Start command | npm start |
Do not use apps/web: that directory does not exist in this repository. Do not
set an output directory, because doing so changes the deployment into a static
site and prevents the Express server, authentication, webhooks, and Slack bot
from running. The host must provide PORT; CQ already listens on it.
The production Dockerfile uses Node 20, installs only locked runtime
dependencies, runs as the unprivileged node user, and checks /healthz for
container health. On a platform with a Dockerfile builder:
- select Dockerfile instead of Railpack;
- leave the root directory empty;
- leave install, build, output, and start overrides empty;
- use
Dockerfileas the Dockerfile path if the platform asks for one.
The container starts the Express server itself, so a static output directory or
dashboard start command must not be configured. Set the environment variables
from .env.example in the platform's secret/environment settings; do not copy
the local .env into the image.
For a host that supports Docker Compose, docker-compose.yml builds the same
image and passes through every documented setting. Locally, copy .env.example
to .env, fill in the required production values, then run:
docker compose up --build -d
docker compose logs -f cqStop it with docker compose down. CQ_HOST_PORT changes only the host-side
port if port 3000 is already occupied; PORT controls the container port and
defaults to 3000. Compose reads .env for substitution but Docker excludes it
from the image.
Country rules are seeded conservatively and can be edited under Admin → Countries. Each policy separately describes ownership, transmission, and fulfilment. Orders snapshot the applicable fulfilment policy so a later policy edit does not erase the decision context.
The participant interface tells makers to check current official rules before buying, importing, or transmitting. Seed text is operational guidance, not legal advice. Organizers should periodically verify it against the linked regulator.
Each Airtable entity table contains an ID, readable name/status/owner columns, an unencrypted formatted JSON document, and an update timestamp. Airtable permissions are the security boundary, so access to the base must be limited to trusted organizers. CQ role permissions separately control which organizer screens are available through the website.
DATA_ENCRYPTION_KEY is only needed for encrypted local-file storage and to read
legacy Airtable rows during the explicit migration command. The migration copies
them as readable JSON and leaves the source rows untouched. Rotate Hack Club,
Slack, Ari, and Airtable credentials through the deployment secret manager.