Skip to content

Commit dfa25f9

Browse files
author
Rajeh Taher
committed
feat: cellar — a searchable archive of Meta's shipped client source
cellar downloads a WhatsApp Web (or Messenger, Facebook, Instagram) client revision, parses it with the oxc AST, and writes one file per module named after the module — so the archive is something `grep -r` and an ordinary editor work on directly. On top of that it offers bundle management, filtered diffs between revisions, module lookup and search, and dependency/dependent graphs, from a CLI and from an MCP server sharing one operations layer. Two questions motivate it, neither answerable by grepping the bundle: - "What uses this module?" Minified call sites reference dependencies positionally (d[3]), never by name, so the only record of the reverse edge is the dependency arrays, which the indexer inverts. - "What changed since last release?" Meta serves only the current bundle and the URLs 404 once a rollout moves on. Historical revisions come from btarchive, which is what makes release-to-release diffing possible at all. Design notes, each of which is a mistake made and corrected against real bundles: - AST, not regex. Brace-counting through string, template and regex literals silently truncates modules, and a truncated module reads as a spurious diff on the next revision. Exports resolve through the factory's actual parameter binding, so a minified `q.sendStanza = …` is found where `e\.(\w+)=` is not. - Deterministic. Sorted lists, BTreeMaps, and a serial filename-assignment pass over sorted names behind the parallel parse, so re-indexing a bundle produces byte-identical JSON. - Diagnostics, not silence. Parse failures, unresolved dependencies and skipped diffs are counted in manifest.json rather than dropped, so "no such module" and "we failed to extract it" never look alike. - Sources are re-printed from the AST. A module shipped on one 200 KB line makes a greppable directory useless and a line diff meaningless; byte identity is kept separately as rawSha256 so change detection stays exact. - Variants are kept. About a quarter of modules ship more than one distinct definition; all are written out and change detection hashes the whole set, since otherwise a new build variant reads as no change and a shift in which copy won reads as a change that never happened. - btarchive needs a self-consistent browser request: Meta's edge rejects a Chrome User-Agent that omits Sec-Fetch-* with a 400. NAVIGATION_HEADERS records the measured status codes. Prior art, credited in the README: ProtoCocktail's wa-diff-analyzer, whose empirically-accumulated module filter ruleset is ported here as the `default` filter; whatspec, which solves the adjacent problem of extracting a typed protocol IR from the same bundles and whose approach shaped this one; and Meta's own meta-code-verify, which documents the btarchive endpoint in practice. 104 tests. Builds on stable; CI runs fmt, clippy -D warnings, the suite, an MSRV job, a guard that no test reaches the network, and a check that the Claude and Codex skills remain one file.
0 parents  commit dfa25f9

29 files changed

Lines changed: 10401 additions & 0 deletions

File tree

‎.claude/skills/cellar/SKILL.md‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
name: cellar
3+
description: Look things up in Meta's shipped client source (WhatsApp Web, Messenger, Facebook, Instagram) — find where a feature is implemented, discover unreleased features by diffing two client revisions, read any module's source, and trace what depends on what. Use when asked about WhatsApp Web internals, a WA protocol detail, what changed between two WhatsApp releases, where some behaviour lives in the client, or any question answerable from Meta's minified bundle. Triggers on "WhatsApp Web source", "what changed in this revision", "how does WhatsApp implement X", "which module does Y", "btarchive", "bundle diff", "WAWeb...", "WASmax...", "WAM event".
4+
---
5+
6+
# cellar
7+
8+
`cellar` keeps an archive of Meta's shipped client bundles, one directory per
9+
client revision, exploded so that **every module is its own ordinary file**. That
10+
last part is the point: once a query points you at a module, you can read and grep
11+
those files with your normal tools instead of going back through the CLI.
12+
13+
## Before anything else
14+
15+
```bash
16+
cellar bundle list
17+
```
18+
19+
Nothing in the archive means nothing to search. Adding a bundle takes several
20+
minutes and gigabytes — say so before starting one, and never start two at once.
21+
22+
```bash
23+
cellar bundle add --rev latest # fetch and index the current revision
24+
cellar bundle add --rev 1030882912 # a specific historical revision
25+
```
26+
27+
Meta serves historical revisions from `btarchive`, which is what makes
28+
release-to-release diffing possible at all — the live bundle URLs 404 the moment a
29+
rollout moves on.
30+
31+
## Pick the right question
32+
33+
**"Where is X implemented?"** — search, then read the files.
34+
35+
```bash
36+
cellar module search --source 'addon_type|addonType' --name '^WAWeb' -C 2
37+
cellar grep 'disappearing_mode' --filter default
38+
```
39+
40+
Narrow with `--name` whenever you can. It cuts the candidate set in the index
41+
before any file is opened, which is the difference between opening forty files and
42+
a hundred thousand. Then read the `path` each hit reports with your own file tools.
43+
44+
**"What changed in this release?"** — diff, overview first.
45+
46+
```bash
47+
cellar diff --no-hunks # the two newest stored revisions
48+
cellar diff whatsapp-1030882912 whatsapp-1030947950 --format json
49+
```
50+
51+
Read the summary before the changes. `added` and `removed` modules are where new
52+
features appear; `modified` with new dependencies or new exports is where existing
53+
ones grow. Changes that are pure transpiler churn are counted as `noiseOnly` and
54+
withheld — pass `--include-noise` only if you suspect the noise rules are wrong.
55+
56+
**"What uses this?"** — the graph, not grep.
57+
58+
```bash
59+
cellar graph WAWebSendMsgStanza --direction dependents --depth 2
60+
cellar graph --match '^WAWebNewsletter' --direction deps --depth 1 --format mermaid
61+
```
62+
63+
Grep genuinely cannot answer this. Minified call sites reference modules
64+
positionally (`d[3]`), never by name, so the only record of "who uses this" is the
65+
dependency arrays the indexer inverted.
66+
67+
**"Show me this module."**
68+
69+
```bash
70+
cellar module show WAWebSendMsgStanza
71+
cellar module show WAWebSendMsgStanza --path-only # then read it directly
72+
```
73+
74+
## Filters decide what you are looking at
75+
76+
A bundle holds ~170k modules, most of them React components and code for the other
77+
Meta products that share the bundle. Every operation takes `--filter`:
78+
79+
| filter | keeps | use for |
80+
| --- | --- | --- |
81+
| `default` | ~11% — the protocol surface | diffing releases; the default |
82+
| `all` | everything | when you suspect the filter hid your answer |
83+
| `protocol` | only positively-identified protocol modules | a tight, high-signal diff |
84+
| `schemas` | `.pb` / `.proto` / `.graphql` | new protobuf fields, new persisted queries |
85+
| `wam` | WAM analytics events and enums | earliest traces of unreleased features |
86+
87+
`default` **hard-excludes** `.pb` and `.graphql` modules. If you are looking for a
88+
new protobuf field, use `--filter schemas` — otherwise you will conclude, wrongly,
89+
that nothing changed.
90+
91+
Check an unfamiliar filter before trusting it:
92+
93+
```bash
94+
cellar filter test default --bundle latest
95+
```
96+
97+
Built-ins are read-only. To adjust one, fork it:
98+
99+
```bash
100+
cellar filter fork default my-filter
101+
cellar filter show my-filter > /tmp/f.json # edit, then:
102+
cellar filter set my-filter --from /tmp/f.json
103+
```
104+
105+
Precedence is `hardExclude`, then `include`, then `exclude`; anything unmatched
106+
takes `defaultVerdict`. With `excludeDependentsOfExcluded`, a module that depends
107+
on an excluded one is excluded too — unless `include` claimed it.
108+
109+
## Reading results honestly
110+
111+
- **Diagnostics are not decoration.** `cellar bundle info <bundle>` reports parse
112+
failures and unresolved dependencies. A surprising diff with non-zero
113+
`chunkParseFailures` means modules are genuinely missing from the index, not that
114+
they were removed from the client.
115+
- **Absent is not zero.** Truncated results say `truncated: true`; a skipped diff
116+
says `hunksOmitted` and why. Never read a short list as a complete one.
117+
- **Variants are real.** About a quarter of modules ship in more than one form.
118+
`variants` lists the others, written beside the primary as
119+
`<name>~alt-<hash>.js`. A variant appearing or disappearing between revisions is
120+
itself a rollout signal.
121+
- **Stored sources are re-printed, not byte-exact.** Modules are pretty-printed so
122+
grep and diff work line by line. Identity is tracked separately by `rawSha256`,
123+
so change detection is still exact.
124+
125+
## MCP
126+
127+
The same operations are available as MCP tools (`cellar mcp`), named
128+
`bundle_*`, `filter_*`, `module_get`, `module_search`, `diff`, `graph`. Prefer them
129+
when available; fall back to the CLI otherwise. Both return identical JSON.
130+
131+
## Reporting findings
132+
133+
Cite the module name and its path, and quote the lines you are relying on. "New
134+
`WAWebFooBar` module, `modules/WAWebFooBar.js:41`, sends `<iq type='set'
135+
xmlns='w:foo'>`" is a finding. "It looks like they added foo support" is a guess —
136+
if that is all you have, say so.

‎.codex/skills/cellar‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../.claude/skills/cellar

‎.github/workflows/ci.yml‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
concurrency:
9+
group: ci-${{ github.ref }}
10+
cancel-in-progress: true
11+
12+
env:
13+
CARGO_TERM_COLOR: always
14+
CARGO_INCREMENTAL: 0
15+
16+
jobs:
17+
check:
18+
name: fmt + clippy + test
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 30
21+
# Read-only: the job builds and tests, and `actions/checkout` writes the token
22+
# into .git/config where every dependency build script could read it.
23+
permissions:
24+
contents: read
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: actions-rust-lang/setup-rust-toolchain@v1
29+
with:
30+
# Stable, deliberately: nothing here needs nightly, and requiring it
31+
# would be an adoption barrier for a library other people build on.
32+
toolchain: stable
33+
components: rustfmt, clippy
34+
35+
- uses: Swatinem/rust-cache@v2
36+
37+
- run: cargo fmt --all -- --check
38+
39+
- run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
40+
41+
- run: cargo test --workspace --locked
42+
43+
# No test may reach the network: `bundle add` is the only operation that
44+
# does, and it is exercised against local fixtures. This asserts the
45+
# endpoints stay confined to cellar-fetch, so CI can never start depending
46+
# on Meta's uptime.
47+
- name: no network outside cellar-fetch
48+
run: |
49+
if grep -rn 'facebook\.com\|web\.whatsapp\.com' \
50+
--include='*.rs' crates/*/src \
51+
| grep -v '^crates/cellar-fetch/src/lib.rs' \
52+
| grep -v 'archive_url_is_revision_and_platform'; then
53+
echo "network endpoints must stay inside cellar-fetch"
54+
exit 1
55+
fi
56+
57+
# The two agent skills are one file behind a symlink; a copy would drift.
58+
- name: skill is a single source of truth
59+
run: |
60+
test -L .codex/skills/cellar
61+
diff -q .claude/skills/cellar/SKILL.md .codex/skills/cellar/SKILL.md
62+
63+
msrv:
64+
name: msrv (1.95)
65+
runs-on: ubuntu-latest
66+
timeout-minutes: 30
67+
permissions:
68+
contents: read
69+
steps:
70+
- uses: actions/checkout@v4
71+
- uses: actions-rust-lang/setup-rust-toolchain@v1
72+
with:
73+
toolchain: "1.95"
74+
- uses: Swatinem/rust-cache@v2
75+
with:
76+
shared-key: msrv
77+
# The declared floor is a promise; check it instead of hoping.
78+
- run: cargo check --workspace --all-targets --locked

‎.gitignore‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Build artifacts
2+
target/
3+
**/*.rs.bk
4+
*.pdb
5+
6+
# A local archive, if someone points CELLAR_HOME at the checkout. Bundles run to
7+
# gigabytes and must never be committed.
8+
.cellar/
9+
bundles/
10+
11+
# Profiling
12+
*.profraw
13+
*.profdata
14+
flamegraph.svg
15+
perf.data*
16+
17+
# IDE / editor
18+
.idea/
19+
.vscode/
20+
*.swp
21+
*.swo
22+
23+
# OS
24+
.DS_Store
25+
Thumbs.db

0 commit comments

Comments
 (0)