Skip to content

algal/rejectionlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rejection Counter

What's a rejection counter, and why would you want one?

I've written a blog post about my rejection counter. But basically, it's a public counter which lists how many rejections you have left to go. Why would you want this? Because everyone likes to minimize rejection, but unfortunately the easiest way to do this is never to try. What a loser mindset! Instead, I suggest, you should regard rejections as the footsteps you take on the paths toward your goal. From that point of view, you have a quota of rejections which you need to complete.

A rejection counter lets you set that goal, track your progress, and publish it to the world for a strange kind of accountability motivation.1

What this repo gives you

This repo lets you deploy your own rejection counter, as a free-to-run site.

You can decide on your rejection target. You can customize the copy. You can write a log entry for every rejection, and for any entry you can mark it as private so it is not shared with the world.

It looks like this:

Screenshot of a rejection counter showing the count 957, motivation copy, and a log of recent rejections including two redacted entries

The first time you run it, you claim the page and save a token in your browser. After that, only you can add rejections to the page.

It's a static-feeling page (Astro), edge-rendered on Cloudflare Workers, with state in Cloudflare KV. Bump from anywhere — phone shortcut, browser, curl — via a token-protected POST. Owner edits all settings (name, goal, deadline, motivation copy) through a built-in admin page, no code required.

Estimated cost of running your own counter on Cloudflare's free tier:

Scenario Daily traffic Monthly cost
Personal use, occasional visitors 10–100 page views Free
Linked from a popular blog post / one-day HN front page 10k–100k page views Free (within Workers free tier of 100k req/day)
Sustained heavy traffic / repeated viral spikes 1M+ page views/day ~$5–15/mo with caching, ~$50/mo without
Truly enormous attention 10M+ page views/day ~$50–150/mo

Storage and egress on Cloudflare are effectively free for this app's data shape — the cost is just metered Worker invocations and KV reads. With sensible cache headers on the public counter page (which changes only on bumps), even viral attention serves almost entirely from edge cache and skips the metered Worker call. For the realistic personal-counter use case you'll never leave the free tier.


Local development

Requirements: Node 20+, npm. No Cloudflare account needed for local dev.

git clone https://github.com/<you>/rejectionlog.git
cd rejectionlog
npm install
npm run dev

The dev server runs Astro with the Cloudflare adapter's platformProxy, which uses Miniflare to simulate KV bindings on disk. Data persists across restarts in .wrangler/state/. Hot reload on file changes.

Default port: 4327. Open http://localhost:4327:

  1. You'll be redirected to /claim — click Claim ownership. The server mints a 256-bit random token, displays it once, and saves it to your browser's localStorage. Copy it to your password manager.
  2. Then /admin?init=1 — fill in name, goal, deadline, motivation copy, and save.
  3. Then /bump is the page you'll use to add rejections. Description + sanitized checkbox + submit.

To wipe local state and start over: stop the dev server, rm -rf .wrangler/state, then start the dev server again. (Wiping while the server is running leaves Miniflare with stale handles and the next request will 500.)

To bump from the command line locally:

TOKEN="<the token you saved>"
curl -X POST http://localhost:4327/api/bump \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description":"test rejection"}'

Deploy to Cloudflare

Requirements: a Cloudflare account (free tier covers this indefinitely).

Deploy to Cloudflare

The button forks this repo into your GitHub account, provisions the KV namespace, and deploys. After it finishes, visit your new *.workers.dev URL → click "Claim ownership" → save the token → /admin to set name, goal, deadline.

Manual deploy

  1. Log in: npx wrangler login
  2. Create the KV namespace:
    npx wrangler kv namespace create REJECTIONS
    npx wrangler kv namespace create REJECTIONS --preview
    Paste the returned id and preview_id into wrangler.toml.
  3. Deploy:
    npm run deploy
  4. Visit your new *.workers.dev URL → you'll be redirected to /claim. Click "Claim ownership," save the displayed token to your password manager, then continue to /admin to set name/goal/deadline.

That's it. There are no env vars or secrets to configure. The bump token is generated server-side on first visit and stored in KV.

To use a custom domain (e.g. rejections.yoursite.com), add it from the Cloudflare Workers project dashboard.

Lost the token?

Open the Cloudflare dashboard → your Workers project → KV → REJECTIONS namespace → delete the auth_token key. Reload your site; the claim page reappears, and you can mint a new token.


Bumping from your phone

Easiest: an iOS / Android Shortcut that POSTs to /api/bump with the auth header.

Body shape:

{ "description": "(optional) what got rejected", "sanitized": false }

Even faster: open https://your-counter.example.com/bump in Safari → Share → Add to Home Screen. The icon launches like an app, lands directly on the bump form with the description field focused.

If sanitized: true, the description is stored but never rendered in the public HTML — visitors see only the date and a redacted placeholder.


Repo layout

src/
  pages/
    index.astro          # main counter page; reads KV per request
    claim.astro          # one-time ownership claim flow (auto-mints token)
    admin.astro          # config form (name, goal, deadline, motivation)
    bump.astro           # focused single-purpose bump form
    api/
      claim.ts           # POST: mint auth token if not already claimed
      bump.ts            # POST: increment count, optionally append log entry
      config.ts          # GET / PUT: read or replace config
  components/
    RejectionCounter/    # self-contained UI (drop into any Astro site unchanged)
  lib/
    kv.ts                # typed KV helpers (state, config, auth_token)
    auth.ts              # token generation + constant-time bearer check
    defaults.ts          # default config used before first /admin save
    types.ts             # shared types
  styles/
    global.css           # CSS variables, minimal reset, dark-mode media query
wrangler.toml            # CF Worker name + KV bindings
astro.config.mjs         # CF adapter, output: server (edge-rendered)

KV stores three keys under one namespace:

  • auth_token — the bump token (set once via /claim)
  • config — the page settings (set/edited via /admin)
  • state{count, log} (mutated via /api/bump)

The RejectionCounter component folder is intentionally self-contained: no imports from elsewhere in the project, scoped CSS only. Drop it into any Astro project unchanged.


License

MIT.

Footnotes

  1. The underlying premise isn't new — Jia Jiang's 100 Days of Rejection TED talk and book Rejection Proof crystallized this mental model years ago. What's new here is the framing as a public countdown (toward a goal, not a tally of so-far) and a free, deploy-your-own template you can host yourself.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors