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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ bun run photos:env

# build the JPEG thumbnail encoder (zenc = zenjpeg hybrid+scan). the pipeline
# scripts auto-build it on first run; this is the explicit form.
cargo build --release --manifest-path tools/photos/zenc/Cargo.toml
cargo build --release --locked --manifest-path tools/photos/zenc/Cargo.toml

# bust caches via wrangler (RN_KV namespace ID hardcoded in scripts).
# NB: the photo manifest is NOT a cache anymore — the worker bundles
Expand Down
6 changes: 3 additions & 3 deletions config/tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@
"match": "cargo (\\d+\\.\\d+\\.\\d+)"
},
"bytes": false,
"bytes_why": "builds zenc, and zenjpeg's encode is deterministic for a given pin, which dependabot owns in tools/photos/zenc/Cargo.toml. A compiler bump does not move a JPEG"
"bytes_why": "builds zenc, and zenjpeg's encode is deterministic for a given pin, which dependabot owns in tools/photos/zenc/Cargo.toml. A compiler bump does not move a JPEG, and it does not move a HISTOGRAM either, which is the half that was doubted: `bun run zenc:reproducible` bakes all 165 stems under three rustc versions and diffs them"
},
{
"bin": "zenc",
"path": "tools/photos/zenc/target/release/zenc",
"install": "cargo build --release --manifest-path tools/photos/zenc/Cargo.toml",
"install": "cargo build --release --locked --manifest-path tools/photos/zenc/Cargo.toml",
"optional": true,
"why": "the JPEG encoder for every shipped tier, plus the square/resize geometry and the histogram bake. It decides the bytes of the JPG half of /i/ exactly the way avifenc decides the AVIF half, and it was the one encoder here with no entry: `cargo` was declared as its BUILD prerequisite, which is a different question from which encoder baked the library",
"required_by": [
Expand All @@ -165,7 +165,7 @@
"tools/photos/onestep-probe.sh",
"tools/photos/reencode-thumbnails.sh"
],
"optional_why": "`optional` here means BUILT ON DEMAND rather than degrades: every caller runs `cargo build --release` when the binary is missing, so an absent zenc on a fresh checkout is a state the pipeline repairs itself rather than a prerequisite anybody has to go install. A missing `cargo` is the blocking failure, and cargo is declared above",
"optional_why": "`optional` here means BUILT ON DEMAND rather than degrades: every caller runs `cargo build --release --locked` when the binary is missing, so an absent zenc on a fresh checkout is a state the pipeline repairs itself rather than a prerequisite anybody has to go install. A missing `cargo` is the blocking failure, and cargo is declared above",
"version": {
"flag": "--version",
"match": "zenc (?<zenc>\\d+\\.\\d+\\.\\d+) \\(zenjpeg (?<zenjpeg>\\d+\\.\\d+\\.\\d+)\\)"
Expand Down
2 changes: 1 addition & 1 deletion docs/MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@ brew install cmake ninja # for the pinned avifenc below
./tools/photos/libavif/build.sh
brew install uv && bun run photos:env # Pillow, for gen-pixel-peeper.py only (brew's python3 is PEP 668; pip into it fails)
# the JPEG encoder (zenc) builds itself on first pipeline run; needs rust (rustup.rs)
cargo build --release --manifest-path tools/photos/zenc/Cargo.toml
cargo build --release --locked --manifest-path tools/photos/zenc/Cargo.toml
bun run wrangler login # Cloudflare auth (deploys + KV + R2 all use it)

# the study pages, which are NOT needed to add a photo
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"checkpoints:check": "bun tools/check-checkpoints.ts",
"checkpoints:sync": "bun tools/check-checkpoints.ts --sync",
"inp": "bun tools/inp-lab.ts",
"release": "bun tools/release-status.ts"
"release": "bun tools/release-status.ts",
"zenc:reproducible": "bun tools/photos/zenc-reproducible.ts"
},
"comment:check-fast": "Local feedback only: lint, typecheck and test are independent read-only gates, so Bun can run them concurrently. CI keeps them as named sequential steps for legible failures. Do not add build, routes, Lens or perf commands here: several share .build or intentionally run under Node.",
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions public/_headers
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
Cache-Control: public, max-age=31536000, immutable

# OG / Twitter card images (public/og/<section>-<name>.png), rendered from each
# garage + lwe demo by scripts/gen-og-cards.mjs. The URL is stable and the bytes
# garage + lwe demo by tools/photos/gen-og-cards.ts. The URL is stable and the bytes
# change only when a demo's look changes (rare), so NOT immutable — a 30-day
# cache clears Lighthouse's "efficient cache lifetimes" bar while a regen + deploy
# purges the edge so a refreshed card lands. Social crawlers cache on their own
Expand Down Expand Up @@ -409,7 +409,7 @@
Cache-Control: public, max-age=0, must-revalidate, s-maxage=60
Service-Worker-Allowed: /

# RSS feeds are build output (scripts/gen-feeds.mjs), regenerated on every deploy
# RSS feeds are build output (tools/gen-feeds.ts), regenerated on every deploy
# from site-manifest.json + sitemap lastmod + posts.json. Served as
# application/rss+xml rather than the extension's default application/xml so a
# reader recognizes the document without sniffing it. Cached like the other
Expand Down
2 changes: 1 addition & 1 deletion src/content/md/terminal.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ So `/radar` does not sense anything. **You bring the signal; it brings
the display.** POST readings you have already measured and it draws them:

```
node tools/photos/radar-sample.mjs --at https://aadhar.sh --anonymize
node tools/photos/radar-sample.ts --at https://aadhar.sh --anonymize
curl -X POST aadhar.sh/radar -d '{"samples":[{"name":"AP","rssi":-58}]}'
```

Expand Down
8 changes: 4 additions & 4 deletions src/pages/garage/workers.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ <h2>The real unlock: configuration as code</h2>
with <code class="m">!</code> carves that path back out to serve straight from the edge:</p>
<pre class="cf-code">{
"name": "aadhar-sh",
"main": "www/_worker.js/index.js",
"main": ".build/src/worker/index.ts",
"assets": {
"directory": "www",
"directory": ".build/public",
"binding": "ASSETS",
"run_worker_first": ["/*", "!/nav.js", "!/garage/*", "!/images/*.avif", ...]
},
Expand Down Expand Up @@ -266,7 +266,7 @@ <h2>2 &middot; Durable Objects, in-house (already shipped)</h2>
odometer at the bottom of the front page) had to reach <em>across</em> to a separate
Worker, <code class="m">cf-garage</code>, and borrow its <code class="m">Counter</code>
class by cross-script binding. On Workers the class lives right here, in
<code class="m">www/_worker.js/counter.js</code>, exported from the entry, with its
<code class="m">src/worker/counter.ts</code>, exported from the entry, with its
migration in the same config. To keep the number honest across the move, the new object
self-seeds once from the count it had on the old one (about 2,375) instead of resetting to
zero. Same odometer, now wired into its own dashboard.</p>
Expand Down Expand Up @@ -462,7 +462,7 @@ <h2>The honest scorecard</h2>
{
"t": "Its Durable Object class moved in-house, exported from this worker's entry with its migration in the same config.",
"ok": true,
"why": "Right. Pages could host no Durable Object, so the counter borrowed the Counter class from the separate cf-garage worker by cross-script binding. Now it lives in www/_worker.js/counter.js."
"why": "Right. Pages could host no Durable Object, so the counter borrowed the Counter class from the separate cf-garage worker by cross-script binding. Now it lives in src/worker/counter.ts."
},
{
"t": "It moved out of a Durable Object into KV, which Workers ships built in.",
Expand Down
16 changes: 8 additions & 8 deletions src/pages/lwe/vigenere.html
Original file line number Diff line number Diff line change
Expand Up @@ -547,13 +547,13 @@

<script>
// ── Demo D: the cracker ─────────────────────────────────────────────────────
// A JS port of the periodic-cipher attack in buttcrack (MIT, © 2026 diid),
// src/buttcrack/ciphers/_periodic.py: seed each period column by χ², then
// hill-climb every key position against the quadgram score of the WHOLE
// plaintext, because cross-column context is what recovers the last letters
// that per-column analysis alone always gets wrong. Candidates dedupe by
// resulting plaintext keeping the shortest key, so a 5-letter key and its
// 10-letter repeat collapse instead of competing.
// A JS port of the periodic-cipher attack in buttcrack (MIT, © 2026 diid,
// github.com/0xdiid/buttcrack), from src/buttcrack/ciphers/_periodic.py there:
// seed each period column by χ², then hill-climb every key position against
// the quadgram score of the WHOLE plaintext, because cross-column context is
// what recovers the last letters that per-column analysis alone always gets
// wrong. Candidates dedupe by resulting plaintext keeping the shortest key,
// so a 5-letter key and its 10-letter repeat collapse instead of competing.
(function () {
var V = document.getElementById("demo-solve"); if (!V) return;
var $ = function (id) { return document.getElementById(id); };
Expand All @@ -565,7 +565,7 @@
// is derived from RANK on a Zipf curve. Only the ORDER of scores matters to a
// hill-climb, and dropping the counts is 60% of the bytes. TOP is log10 of
// the share "THAT" holds in the source corpus, FLOOR an unseen quadgram.
// Both printed by scripts/gen-quadgram-table.mjs, which builds the table.
// Both printed by tools/photos/gen-quadgram-table.ts, which builds the table.
var TOP = -2.4773, FLOOR = -8.7282, ZIPF = 0.75;
var MAX_KEY = 12, MAX_LETTERS = 900, PSEUDO = 40;
var LOGP = null, HI = 0, model = null;
Expand Down
4 changes: 2 additions & 2 deletions src/worker/lib/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ export async function servePrecompressedShell(request, env) {

// ── dcz: the delta path ────────────────────────────────────────────────────────
// A Chromium client that accepted our Use-As-Dictionary offer sends back the SHA-256
// of the shell bytes it already holds. scripts/gen-shell-deltas.mjs has precomputed
// zstd-against-that-dictionary offline, so serving the diff is a filename lookup:
// of the shell bytes it already holds. tools/build.ts step 8 has precomputed
// zstd-against-that-dictionary at deploy time, so serving the diff is a filename lookup:
// /ad/<base>.<hash8>.<dicttag>.dcz. The first real one: luna.css in 115 bytes instead
// of 7,615, a 98.5% cut, from an ordinary CSS change.
//
Expand Down
2 changes: 1 addition & 1 deletion src/worker/lib/photo-grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function renderPhotoSlots(pick, altMap = {}, { deferred = true, histogram
// late for hover number one.
//
// One character per bin in ASCII 63..126, packed by
// scripts/build-histogram-index.mjs, which carries the reasoning. 256 chars a
// tools/photos/build-histogram-index.ts, which carries the reasoning. 256 chars a
// tile against base64's 344, and 36% smaller after brotli because base64
// destroys the byte alignment brotli exploits on smooth data.
//
Expand Down
4 changes: 2 additions & 2 deletions src/worker/photos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export async function getAltMap(env) {
return _altMap;
}

// content-hash map {stem: {a,j,s}} written by scripts/hash-thumbnails.sh at
// content-hash map {stem: {a,j,s}} written by tools/photos/hash-thumbnails.sh at
// photo-add time. The pool bakes these into /i/ URLs, so a thumbnail URL
// is born with its bytes: no global ?v= bump, and a cached 404 can never
// shadow real bytes (a new encode IS a new URL). Module-cached like _altMap.
Expand All @@ -265,7 +265,7 @@ export async function getAltMap(env) {
// are not hot, and keeping them on ASSETS keeps them stubbable in
// contract-tests. Both readers see the same committed file per deploy.
// Packed histograms {stem: base64(4 channels x 64 bins, one byte each)}, written
// by scripts/build-histogram-index.mjs. Module-cached like _altMap, and that memo
// by tools/photos/build-histogram-index.ts. Module-cached like _altMap, and that memo
// is the whole reason this is an ASSETS read rather than a bundled import: the
// fragment draws a random twelve per request so all 158 must be reachable, and
// bundling them costs 22.9 KiB gzip (83% of the bundle's remaining headroom)
Expand Down
2 changes: 1 addition & 1 deletion src/worker/radar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// modelled on — is a native macOS CLI rather than a web page.
//
// So this is not a hosted radar. It is a hosted INSTRUMENT: the sensing happens
// where the antenna is (tools/photos/radar-sample.mjs, or any agent with a
// where the antenna is (tools/photos/radar-sample.ts, or any agent with a
// shell), and the readings are POSTed here to be drawn. The client brings the
// signal, the site brings the display. That is the only honest way to host this,
// and it happens to be the version an agent can actually use.
Expand Down
2 changes: 1 addition & 1 deletion src/worker/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ function radarIdleFrame() {
body: rows(
...wrap("A server has no antenna and neither does an agent, so this half does not sense anything. POST signal readings and it draws them: concentric bands by strength, a meter and a trend per source.", INNER).map((row) => [s(row)]),
blank(),
[s(" node tools/photos/radar-sample.mjs --at https://aadhar.sh", "accent")],
[s(" node tools/photos/radar-sample.ts --at https://aadhar.sh", "accent")],
[s(" curl -X POST aadhar.sh/radar -d '{\"samples\":[{\"name\":\"AP\",\"rssi\":-58}]}'", "accent")],
blank(),
rule(INNER, "the shape"),
Expand Down
2 changes: 1 addition & 1 deletion src/worker/updates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function readCheckpoints(env) {

export async function renderWindowsUpdate(cp) {
// Single source of truth: the same D1 `checkpoints` table that backs /restore.
// One row per logged deploy (scripts/bump-version.sh, which now derives the
// One row per logged deploy (tools/photos/bump-version.sh, which now derives the
// next vnum from this table; the retired service worker used to carry it as
// CACHE_VERSION). The newest row is the running build and the recent rows ARE
// the changelog, so /updates and /restore cannot drift apart. Degrades
Expand Down
2 changes: 1 addition & 1 deletion src/worker/webmention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const EXCERPT_MAX = 320;
// DB: social content is third-party, moderated, and mutable, while the
// checkpoints table is the site's own append-only history. Created lazily like
// census.js and around.js do, so a fresh binding self-provisions instead of
// requiring a migration step before the route works. migrations/0001_init.sql
// requiring a migration step before the route works. migrations/0001_webmentions.sql
// carries the same DDL for anyone who prefers to apply it up front.
const TABLE = "webmentions";
let ensured = false;
Expand Down
Loading
Loading