proto: freeze the wire contract at 1.0.0
Schemas for the whole v1 surface: 38 methods, 9 events, 25 named types and the
JSON-RPC envelope, with x-privileged / x-transports / x-deadlineMs / x-errors
annotations that both generators emit as data rather than prose.
Four generators over one IR (contracts/codegen/schema_ir.py), so the C++ structs,
the TypeScript types and the OpenRPC document cannot disagree about what the
contract says:
gen_cpp.py -> core/generated/velox_proto.{hpp,cpp}
gen_ts.py -> extension/src/shared/protocol/
gen_openrpc.py -> contracts/openrpc.json
gen_cpp_conformance.py -> tests/conformance/cpp/fixture_dispatcher.hpp
Inbound parsing never throws: parse<T>() returns std::expected<T, ParseError> and
nlohmann's throwing ADL from_json is deliberately not emitted. Schema constraints
(minimum, maxLength, pattern, ...) become real runtime checks in both languages —
the daemon does not trust the extension and the extension does not trust the
daemon.
59 golden fixtures: a success case per method, 12 error cases, 9 events. Replayed
by tests/conformance/ against both the generated C++ and a live server over both
transports. tools/mockd serves the same fixtures with unhappy-path flags so the
GUI and EXT lanes never wait for veloxd.
run.sh also proves capture.offer fails open: with a daemon answering slower than
750 ms the client gives up and lets Firefox take the download.
core/generated/ is libveloxproto, a separate target from libveloxcore, which
still never sees JSON — see docs/adr/0009.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# tools/mockd — a fake `veloxd`
|
||||
|
||||
Serves `contracts/fixtures` over **both** transports, keeps just enough state that adding
|
||||
and pausing a download does something visible, and fakes progress events at 4 Hz.
|
||||
|
||||
The GUI and extension lanes develop against this from day one and never wait for the real
|
||||
daemon. Its unhappy-path flags exist so those lanes can test the cases that are hard to
|
||||
arrange on purpose — a slow daemon, a flaky one, a dropped socket, a refused pairing.
|
||||
|
||||
```sh
|
||||
cd tools/mockd
|
||||
npm install
|
||||
npm start -- --help
|
||||
npm start # both transports, default paths
|
||||
```
|
||||
|
||||
Defaults: `$XDG_RUNTIME_DIR/velox/velox.sock` and `ws://127.0.0.1:52000`.
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `--uds <path>` / `--no-uds` | Unix socket path, or don't listen |
|
||||
| `--ws-port <n>` / `--no-ws` | loopback WebSocket port, or don't listen |
|
||||
| `--progress-hz <n>` | progress event rate (default 4, the contract's ceiling) |
|
||||
| `--speed <bytes>` | synthetic per-task speed |
|
||||
| `--slow <ms>` | delay every reply. **Past 750 ms `capture.offer` must fail open.** |
|
||||
| `--flaky <0..1>` | answer this fraction of calls with `-32603` |
|
||||
| `--drop-connection <s>` | terminate every connection every N seconds |
|
||||
| `--refuse-pairing` | `session.pair` fails, as if the user clicked Deny |
|
||||
| `--lockout` | `session.pair` answers `-32014`, as if the brute-force lockout tripped |
|
||||
| `--allowed-root <dir>` | add a root that `download.add`'s `saveDir` may resolve inside |
|
||||
| `--allow-any-origin` | skip the `moz-extension://` Origin check (debugging only) |
|
||||
| `--no-validate` | stop validating params (to see what a client actually sends) |
|
||||
|
||||
## What is real and what is faked
|
||||
|
||||
**Real**, because a client's correctness depends on it:
|
||||
|
||||
* transport and privilege rules, taken from the *generated* `METHODS` table — so a
|
||||
privileged method is refused with `-32003` over the WebSocket exactly as `veloxd` must;
|
||||
* param validation, through the *generated* validators, including range and length checks;
|
||||
* `saveDir` canonicalization against the allowed roots, so `-32011` is reachable;
|
||||
* the `capture.offer` decision — monitored types, minimum size, excluded hosts — so both
|
||||
the take and the ignore paths get exercised;
|
||||
* pairing: `session.hello` accepts only a token this process actually issued;
|
||||
* task state, so add / pause / resume / cancel / remove do what a client expects to see.
|
||||
|
||||
**Faked**: bytes advance on a clock, not from a socket. There is no network, no disk, and
|
||||
no engine. Anything not listed above is answered from its golden fixture.
|
||||
|
||||
## Why it imports the generated protocol code
|
||||
|
||||
`mockd` uses `extension/src/shared/protocol/` — the generated TypeScript — rather than
|
||||
types of its own. A mock with hand-written types is a third source of truth, and it drifts.
|
||||
This way a schema change that breaks a client breaks `mockd` in the same commit.
|
||||
|
||||
It imports the individual generated modules (`types.js`, `methods.js`, …) rather than
|
||||
`index.js`, because the `extension/` tree has no `package.json` of its own for Node to
|
||||
resolve a star re-export through. That is a quirk of running from outside that package, not
|
||||
a problem with the generated code; the extension's own bundler is unaffected.
|
||||
Reference in New Issue
Block a user