From af4103127fadc9dc86118f86e1d01d2f21a6b665 Mon Sep 17 00:00:00 2001 From: 1mb-dev <1mb@1mb.dev> Date: Fri, 9 Oct 2026 08:44:14 +0530 Subject: [PATCH 1/2] docs: README for the first release --- README.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b60e72..df701b8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,46 @@ # firsthour -Fresh remote roles from Hacker News and the companies hiring there, while they are still new. +Fresh remote roles from Hacker News and the companies hiring there, while they are still new: https://firsthour.1mb.dev -Work in progress toward v0.1.0. Sources: the HN "Who is hiring?" thread, HN job stories, and the public job boards (Ashby, Greenhouse, Lever) of companies that posted in recent threads. +The monthly "Ask HN: Who is hiring?" thread is a burst. Most remote postings land in its first day, and applying early matters. firsthour lists remote roles from the last seven days, grouped by age (first hour, last 24 hours, this week), and marks the ones that arrived since your last visit. + +## Sources + +- **HN thread:** top-level postings in the current "Who is hiring?" thread. +- **HN jobs:** job stories posted on Hacker News. +- **Boards:** the Ashby, Greenhouse and Lever job boards of companies that posted in the last three threads. + +Thread postings need a `Company | Role | …` header. Every role must pass a remote gate and a deny list and be under seven days old. Titles and bodies are untrusted: the page renders text only, and every link is built from a validated id. + +## How it runs + +``` +snapshots.yml (GitHub Actions, every 2h; hourly on days 1-5) + builds boards.json + hn.json -> GitHub Pages (data only) + +Cloudflare Worker (Workers Free) + cron */15 Pages -> validate -> KV + /api/posts KV snapshots + live HN delta -> select -> cached 5 min, last good kept 24h + / static page (public/) +``` + +The scheduled job holds no Cloudflare credential; the Worker pulls. Two things break the pipeline silently: + +- **GitHub Pages carries data only.** Deploying anything else there, or adding a custom domain, removes or redirects `boards.json` and `hn.json`, and the refresh cron fails. +- **GitHub disables scheduled workflows** on a public repository after 60 days without activity. The page then shows HN and Boards as stale; re-enable `snapshots.yml` from the Actions tab. + +## Develop + +Node 22.18+. ```bash make install && make setup -make check +make check # type-check + tests +make mock # Worker on fixture data; /__mock/ +make dev # Worker against live sources (seed the local KV with make snapshots) +node scripts/probe.mjs https://firsthour.1mb.dev # live check of a running Worker ``` +Tests run on recorded, anonymized fixtures; CI never calls live sources. The design language is in [docs/design.md](docs/design.md). + MIT licensed. From 526a1c05fb4d25ed8ce5fe8b30fd25baf77d313b Mon Sep 17 00:00:00 2001 From: 1mb-dev <1mb@1mb.dev> Date: Fri, 9 Oct 2026 08:44:14 +0530 Subject: [PATCH 2/2] chore(release): 0.1.0 --- CHANGELOG.md | 18 ++++++++++++++++-- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 616d47b..81ce6ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,21 @@ All notable changes to this project are documented here. Format follows [Keep a ## [Unreleased] +## [0.1.0] - 2026-10-11 + +First release, live at https://firsthour.1mb.dev. + ### Added -- Source adapters for the HN "Who is hiring?" thread, HN job stories, and the job boards (Ashby, Greenhouse, Lever) of companies posting in recent threads. -- Selection rules: posting shape, deny list, remote gate, 7-day window, newest first. +- Sources: the HN "Who is hiring?" thread, HN job stories, and the Ashby, Greenhouse and Lever boards of companies that posted in the last three threads. +- Selection: posting shape, deny list, remote gate, seven-day window, newest first. +- Board roles that ATS data marks remote but list by city read `Remote (New York)`. +- Page: groups by age (first hour, last 24 hours, this week), a filter mirrored in `?q=`, new-since-last-visit ticks, dimmed rows once read, auto/light/dark theme, and the expected time of the next thread. Job links open in a new tab. +- Logbook design language with a self-hosted font and one token block; see `docs/design.md`. +- Scheduled job (every 2 hours, hourly on days 1-5) that publishes the boards snapshot and the HN baseline to GitHub Pages; a Worker cron copies them into KV every 15 minutes. +- `/api/posts` cached for 5 minutes with a 24-hour last-good fallback, and an HN path that serves the baseline plus postings since its watermark. +- A strict CSP (same-origin only, plus a per-response nonce for the scripts Cloudflare injects). +- Deploy by workflow with a check that the active Worker deployment is this commit; `scripts/probe.mjs` for live checks. + +[Unreleased]: https://github.com/1mb-dev/firsthour/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/1mb-dev/firsthour/releases/tag/v0.1.0 diff --git a/package-lock.json b/package-lock.json index 26be98a..f5e1df1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firsthour", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firsthour", - "version": "0.0.0", + "version": "0.1.0", "license": "MIT", "devDependencies": { "@types/node": "^22.20.5", diff --git a/package.json b/package.json index 2cec47e..fab58b2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "firsthour", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "description": "Fresh remote roles from Hacker News and the companies hiring there, while they are still new", "homepage": "https://firsthour.1mb.dev",