Automattic / Automattic/mShots
Add optional Web Bot Auth request signing
- Dominant language
- JavaScript
- Stars
- 118
- Forks
- 22
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
Add optional [Web Bot Auth](https://datatracker.ietf.org/doc/draft-meunier-web-bot-auth-architecture/) signing to outbound browser requests made by mShots.
This would let websites verify that requests came from an mShots deployment. They would not need to rely only on the user agent or source network.
The feature should be disabled by default.
## Proposed implementation
mShots already intercepts browser requests in `lib/snapshot.js`.
The successful branch currently calls `request.continue()` after `allowHost(request.url())` succeeds. Extend this branch to:
1. Check whether WBA is enabled.
2. Sign the current request URL and method.
3. Merge the generated headers with `request.headers()`.
4. Continue the request with the merged headers.
The signer should add the current WBA headers:
- `Signature-Agent`
- `Signature-Input`
- `Signature`
A fresh signature must be generated for each request. Redirect hops must use their current URL and authority.
The blacklist and DNS checks must run before signing.
## Configuration
Suggested configuration:
```text
MSHOTS_WBA_ENABLED=true
MSHOTS_WBA_PRIVATE_KEY_FILE=/run/secrets/mshots-wba-key.json
MSHOTS_WBA_SIGNATURE_AGENT=https://example.com/.well-known/http-message-signatures-directory
```
The exact names are open for discussion.
The private Ed25519 key should be loaded once during worker startup. It should not be stored in the repository, image, queue, cache key, or logs.
The public key directory is hosted by the mShots operator. Hosting or managing that directory is outside this repository.
## Request scope
The initial implementation could sign every allowed HTTPS request observed by the existing Puppeteer `Page` request interceptor.
This includes:
- Main navigation
- Redirect hops
- Frames
- Stylesheets, scripts, images, and fonts
- Page `fetch` and XHR requests observed by the page interceptor
Requests that the current page interceptor does not observe are outside the initial scope.
## Failure behavior
When WBA is disabled, behavior must remain unchanged.
When WBA is enabled but its key or configuration is invalid, the worker should fail readiness with a clear error.
A per-request signing error must not leave the Puppeteer request paused. The implementation should either continue unsigned or abort according to a documented policy.
It must never send a partial WBA header set.
## Acceptance criteria
- WBA is disabled by default.
- Existing behavior is unchanged while disabled.
- Allowed HTTPS requests receive valid WBA headers while enabled.
- Existing browser headers are preserved.
- Redirects receive fresh signatures for their actual authority.
- Denied hosts are aborted before signing.
- Existing WBA header names are handled without duplicate case variants.
- A test verifies captured signatures using the corresponding public JWK.
- Tests cover an initial navigation, a redirect, a subresource, and a signing failure.
- Documentation explains configuration and private-key provisioning.
## Open questions
1. Should the initial scope include every request observed by the page, or only main-frame navigation and redirects?
2. Should a request continue unsigned or abort after a signing failure?
3. Which maintained WBA implementation supports the Node version used in production?
4. Should workers reload the private key, or should rotation restart workers?
## References
- [Web Bot Auth architecture draft](https://datatracker.ietf.org/doc/draft-meunier-web-bot-auth-architecture/)
- [HTTP Message Signatures, RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html)
- Existing hook: `lib/snapshot.js`, after the successful `allowHost()` result and before `request.continue()`
Contributor guide
No contributing guide indexed for this repository
Research direction
Read lib/snapshot.js at the existing allowHost(request.url()) and request.continue() hook, then trace worker startup and configuration handling. Review the WBA and RFC 9421 references before deciding how the open questions affect the design. Done means the acceptance criteria are covered, including captured-signature tests for navigation, redirects, subresources, failures, and documentation for configuration and key provisioning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100