An Arc-to-Common-Lisp port of Arc and the News web app that powers Hacker News.
In September 2024, Hacker News migrated from Arc-on-Racket to Arc on SBCL using a compiler called Clarc that dang had been developing for years. The port lets HN run on multiple cores and was fast enough to retire pagination on long threads. See the announcement thread and Vincent Massol's write-up.
This repository is an independent open-source Arc-on-Common-Lisp
runtime in the same spirit. It boots arc0.lisp (a port of Arc's
ac.scm) under SBCL and then loads arc.arc and the rest of Arc on
top of it, so News and other Arc programs run unmodified.
You'll need SBCL installed (brew install sbcl
on macOS, apt install sbcl on Debian/Ubuntu).
./test.arc
test.arc is adapted from lumen's
test suite plus extra cases added during the port. A clean run prints
something like 193 passed, 0 failed.
mkdir -p arc
echo "myname" > arc/admins
export DEV=t # reload code changes without needing to restart
./sharc news.arc # prepend with `rlwrap` for repl history
Then go to http://localhost:8080.
Click on login and create an account called myname. You should now
be logged in as an admin.
Set DEV=t (or run (set autoreload*) in the repl) to automatically
reload code changes without restarting the server.
For production deployments, instead of autoreload, you can manually
git pull and then run (reload) in the repl to ship an update.
(You could use autoreload in production, but then each request is
slightly slower since it has to check the modification times of every
arc file.)
My daily driver for News is:
DEV=t lwrap ./sharc news.arc
lwrap is a wrapper over rlwrap that makes it nicer for lisp repl work. ("lisp-wrap.") Just put it in your PATH, install rlwrap, and you're good to go.
News sends password reset emails via Resend's SMTP relay. Ask Claude how to set it up for your own domain, or follow Resend's guides: verify a sending domain, create an API key, and send with SMTP.
Then copy smtp.example.json to smtp.json and fill in your
YOUR_RESEND_API_KEY (the username is the literal string resend).
The optional from-name and reply-to fields set the sender's display
name and a reply address.
Optionally, use ImprovMX to forward incoming
emails to a personal gmail account, then go to gmail's gear icon (upper
right) -> See All Settings -> Accounts and Import -> Send mail as ->
"Add another email address." Point that address's SMTP server at Resend
(smtp.resend.com, port 587, username resend, your API key as the
password) so replies you send from gmail are signed for your domain.
To slow down bulk signups, News can show a reCAPTCHA v2 ("I'm not a robot") challenge on account creation. It is conditional: like Hacker News, the create-account form looks normal until an IP has created a few accounts in a day, after which submitting it returns a "Validation required." page with the checkbox. When no keys are configured the feature is off entirely, so this is optional.
Create a key pair, then copy recaptcha.example.json to
recaptcha.json and fill in the site-key and secret. threshold
is how many accounts one IP may create per day before the captcha kicks
in (default 2). The file is re-read on each request, so changes take
effect without restarting the server.
Important: use a legacy key, not a Cloud/Enterprise one. News
verifies tokens against the classic endpoint
https://www.google.com/recaptcha/api/siteverify, which Google's newer
reCAPTCHA (created in the Google Cloud console) no longer supports.
Create the key from the classic admin instead:
google.com/recaptcha/admin/create
-> reCAPTCHA v2 -> "I'm not a robot" Checkbox. Add localhost and
127.0.0.1 to the key's allowed domains for local testing, plus your
real domain for production.
You can check whether a secret works with the classic endpoint without a
browser; a working legacy key rejects a bogus token with
invalid-input-response, while a migrated key returns a Migrate your key ... error:
curl -sS -X POST https://www.google.com/recaptcha/api/siteverify \
--data-urlencode "secret=YOUR_SECRET" --data-urlencode "response=test"
The keys may also be supplied via the RECAPTCHA_SITE_KEY,
RECAPTCHA_SECRET, and RECAPTCHA_THRESHOLD environment variables,
which override recaptcha.json. To exercise the flow locally without
making real accounts, start the server with RECAPTCHA_THRESHOLD=0 so
every signup attempt is challenged.
Change the variables at the top of news.arc.
There's a built-in scraper that fetches the current Hacker News front
page (and its comment trees, including flagged / dead / collapsed
comments the official API doesn't expose) and imports it into your
local News. See scrape.md for the full how-to.
You'll need a Hacker News account to log in to HN with -- the scraper
needs a session to see flagged/dead content. An ordinary user account
is fine, but create a fresh one for this purpose rather than using
your real account, and turn showdead on in its preferences (so the
HTML the scraper fetches includes dead comments). Put the username
into scrape.json (copied from scrape.example.json on first run);
the password is read at login time from HN_SCRAPER_PASSWORD, the
password field of scrape.json, or an interactive prompt.
(= static-max-age* 7200) ; browsers can cache static files for 7200 sec
(= autoreload* t) ; reload code changes without restarting
(declare 'explicit-flush t) ; you take responsibility for flushing output
; (all existing news code already does)
arc0.lisp — Arc runtime for Common Lisp (port of ac.scm)boot.lisp — script entry point loaded via sbcl --script; loads
arc0.lisp, then either runs each given Arc file and exits, or
drops into the Arc REPL when no files are given (analogue of
arc3.2/as.scm)sharc — thin shell wrapper: exec sbcl --script boot.lisp "$@"arc.arc, libs.arc, strings.arc, code.arc, html.arc,
pprint.arc, srv.arc, app.arc, prompt.arc — Arc itself,
built on top of arc0news.arc, blog.arc — the News and Blog applicationsscrape.arc, json.arc — HN front-page scraper and JSON support
(see scrape.md)static/ — static assets served by srv.arctest.arc — Arc test suiteThe port was built incrementally; each step is recorded as a
handoff note in
docs/agents/handoff/, starting with
2026-04-25-001-arc0-port.md.
Read those in order if you want to see how arc0 was bootstrapped, what
broke along the way, and how each fix was reasoned through.
Copyright (c) Paul Graham and Robert Morris. Released under the MIT License with Paul Graham's permission. See copyright.
Thanks to Daniel Gackle aka "dang" for coming up with the name Sharc, and for answering dozens of emails over many months regarding HN and Clarc.
1,181 commits
Common Lisp
86.0%
Arc
12.2%
An Arc-to-Common-Lisp port of Arc and the News web app that powers Hacker News.
In September 2024, Hacker News migrated from Arc-on-Racket to Arc on SBCL using a compiler called Clarc that dang had been developing for years. The port lets HN run on multiple cores and was fast enough to retire pagination on long threads. See the announcement thread and Vincent Massol's write-up.
This repository is an independent open-source Arc-on-Common-Lisp
runtime in the same spirit. It boots arc0.lisp (a port of Arc's
ac.scm) under SBCL and then loads arc.arc and the rest of Arc on
top of it, so News and other Arc programs run unmodified.
You'll need SBCL installed (brew install sbcl
on macOS, apt install sbcl on Debian/Ubuntu).
./test.arc
test.arc is adapted from lumen's
test suite plus extra cases added during the port. A clean run prints
something like 193 passed, 0 failed.
mkdir -p arc
echo "myname" > arc/admins
export DEV=t # reload code changes without needing to restart
./sharc news.arc # prepend with `rlwrap` for repl history
Then go to http://localhost:8080.
Click on login and create an account called myname. You should now
be logged in as an admin.
Set DEV=t (or run (set autoreload*) in the repl) to automatically
reload code changes without restarting the server.
For production deployments, instead of autoreload, you can manually
git pull and then run (reload) in the repl to ship an update.
(You could use autoreload in production, but then each request is
slightly slower since it has to check the modification times of every
arc file.)
My daily driver for News is:
DEV=t lwrap ./sharc news.arc
lwrap is a wrapper over rlwrap that makes it nicer for lisp repl work. ("lisp-wrap.") Just put it in your PATH, install rlwrap, and you're good to go.
News sends password reset emails via Resend's SMTP relay. Ask Claude how to set it up for your own domain, or follow Resend's guides: verify a sending domain, create an API key, and send with SMTP.
Then copy smtp.example.json to smtp.json and fill in your
YOUR_RESEND_API_KEY (the username is the literal string resend).
The optional from-name and reply-to fields set the sender's display
name and a reply address.
Optionally, use ImprovMX to forward incoming
emails to a personal gmail account, then go to gmail's gear icon (upper
right) -> See All Settings -> Accounts and Import -> Send mail as ->
"Add another email address." Point that address's SMTP server at Resend
(smtp.resend.com, port 587, username resend, your API key as the
password) so replies you send from gmail are signed for your domain.
To slow down bulk signups, News can show a reCAPTCHA v2 ("I'm not a robot") challenge on account creation. It is conditional: like Hacker News, the create-account form looks normal until an IP has created a few accounts in a day, after which submitting it returns a "Validation required." page with the checkbox. When no keys are configured the feature is off entirely, so this is optional.
Create a key pair, then copy recaptcha.example.json to
recaptcha.json and fill in the site-key and secret. threshold
is how many accounts one IP may create per day before the captcha kicks
in (default 2). The file is re-read on each request, so changes take
effect without restarting the server.
Important: use a legacy key, not a Cloud/Enterprise one. News
verifies tokens against the classic endpoint
https://www.google.com/recaptcha/api/siteverify, which Google's newer
reCAPTCHA (created in the Google Cloud console) no longer supports.
Create the key from the classic admin instead:
google.com/recaptcha/admin/create
-> reCAPTCHA v2 -> "I'm not a robot" Checkbox. Add localhost and
127.0.0.1 to the key's allowed domains for local testing, plus your
real domain for production.
You can check whether a secret works with the classic endpoint without a
browser; a working legacy key rejects a bogus token with
invalid-input-response, while a migrated key returns a Migrate your key ... error:
curl -sS -X POST https://www.google.com/recaptcha/api/siteverify \
--data-urlencode "secret=YOUR_SECRET" --data-urlencode "response=test"
The keys may also be supplied via the RECAPTCHA_SITE_KEY,
RECAPTCHA_SECRET, and RECAPTCHA_THRESHOLD environment variables,
which override recaptcha.json. To exercise the flow locally without
making real accounts, start the server with RECAPTCHA_THRESHOLD=0 so
every signup attempt is challenged.
Change the variables at the top of news.arc.
There's a built-in scraper that fetches the current Hacker News front
page (and its comment trees, including flagged / dead / collapsed
comments the official API doesn't expose) and imports it into your
local News. See scrape.md for the full how-to.
You'll need a Hacker News account to log in to HN with -- the scraper
needs a session to see flagged/dead content. An ordinary user account
is fine, but create a fresh one for this purpose rather than using
your real account, and turn showdead on in its preferences (so the
HTML the scraper fetches includes dead comments). Put the username
into scrape.json (copied from scrape.example.json on first run);
the password is read at login time from HN_SCRAPER_PASSWORD, the
password field of scrape.json, or an interactive prompt.
(= static-max-age* 7200) ; browsers can cache static files for 7200 sec
(= autoreload* t) ; reload code changes without restarting
(declare 'explicit-flush t) ; you take responsibility for flushing output
; (all existing news code already does)
arc0.lisp — Arc runtime for Common Lisp (port of ac.scm)boot.lisp — script entry point loaded via sbcl --script; loads
arc0.lisp, then either runs each given Arc file and exits, or
drops into the Arc REPL when no files are given (analogue of
arc3.2/as.scm)sharc — thin shell wrapper: exec sbcl --script boot.lisp "$@"arc.arc, libs.arc, strings.arc, code.arc, html.arc,
pprint.arc, srv.arc, app.arc, prompt.arc — Arc itself,
built on top of arc0news.arc, blog.arc — the News and Blog applicationsscrape.arc, json.arc — HN front-page scraper and JSON support
(see scrape.md)static/ — static assets served by srv.arctest.arc — Arc test suiteThe port was built incrementally; each step is recorded as a
handoff note in
docs/agents/handoff/, starting with
2026-04-25-001-arc0-port.md.
Read those in order if you want to see how arc0 was bootstrapped, what
broke along the way, and how each fix was reasoned through.
Copyright (c) Paul Graham and Robert Morris. Released under the MIT License with Paul Graham's permission. See copyright.
Thanks to Daniel Gackle aka "dang" for coming up with the name Sharc, and for answering dozens of emails over many months regarding HN and Clarc.
1,181 commits
Common Lisp
86.0%
Arc
12.2%