| Thing | Where it lives | Risk if leaked | Rotate how |
|---|---|---|---|
muse.ai session cookies (hatch_sess, hatch_vml, hatch_gw, etc.) |
./storage_state.json |
Full account access. Anyone with these cookies can read/send everything your Muse account can, as you. | Log out of muse.ai in the browser, log back in, re-dump storage_state. |
| EdDSA JWT auth_token | ./session.json |
VM admission for a few hours. Lets someone open the Noise WS as your VM. | Expires naturally. Rerun npm run bootstrap to mint a new one. |
| notary_token | ./session.json |
Pairs with auth_token for the same window. | Same as above. |
| vm_id | ./session.json |
Low — identifies which VM you own. Not useful alone. | Changes when the server reassigns your VM. |
| MUSE_PROXY_KEY (optional) | env var at proxy startup | Lets anyone talk to your running proxy, which uses the above tokens. | Change the env var, restart. |
| SSH keys | ~/.ssh/* (not in this repo) |
— | Not touched by this project. |
All of the above except SSH keys are in .gitignore. Never commit them. The commit history should be auditable from a public fork.
- The 9 saved JS chunks under
recon/chunks/. These are Meta's publicly served minified JS; anyone can download them. However, redistributing them might trip DMCA notices. We ship them for reproducibility; a hygienic fork would delete them and re-fetch fromrecon/chunk_urls.txt. - The 5 extracted protobuf descriptors under
src/proto/schemas/. Also derived from the public JS. - Any TypeScript code in this repo. Original work.
- Client X25519 keypair is generated per handshake via
@noble/curvesx25519.utils.randomPrivateKey(). Nothing persistent on our end. - The 32-byte client nonce in msg1 is CSPRNG per handshake (
randomBytes(32)from Nodecrypto). Not reused. - AES-GCM nonces are counter-based per cipher state, per Noise spec. Never reuse a key between runs.
- We do not persist any session keys to disk. Only the HTTP tokens from
/api/hatch/tokenand/api/hatch/noise-notary-tokenget written tosession.json.
Our client logs server_pub_matches_notary: false on every run. This is expected:
notary_token.public_key.keyis a CA/root key.- The actual VM Noise static key arrives inside the decrypted msg2 payload, along with an attestation chain.
- Full verification would require parsing
src/proto/schemas/attestation_bundle.proto.binpband verifying signatures against the notary CA.
Not verifying leaves us open to a MITM between us and hatch.metaaivm.com. Mitigations in practice:
- TLS from the Node HTTP stack (cycletls for authenticated HTTP calls) pins the server cert chain via standard CA roots.
hatch.metaaivm.comis a Meta-controlled host; a MITM requires compromising their TLS infra, not just their Noise layer.
If you want to add attestation verification, that's a Phase 8 task.
Default: --host 127.0.0.1. Localhost only, no network exposure.
If you want to serve the proxy to other machines:
- Set
MUSE_PROXY_KEY=<long random string>so random port scanners can't hit it. - Put it behind a real TLS terminator (nginx / caddy / cloudflare tunnel).
- Consider: everyone who talks to the proxy is effectively using your muse.ai account.
| Hop | What's visible to a passive observer |
|---|---|
| Browser/Node → muse.ai | TLS. Observer sees TLS SNI, timing, byte counts. |
| Browser/Node → hatch.metaaivm.com | TLS + WebSocket. Observer sees SNI, URL path, query (auth_token + notary_token are in the query — don't paste the ws_url anywhere). |
| Noise frames (inside WS) | AES-GCM encrypted. Observer sees ciphertext lengths. |
The ws_url in session.json contains both tokens inline. Never share it.