Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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/<success|empty|partial|stale|down|error>
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.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading