The tasks build the inference path: emsha-backed SHA-256, inferproxy, config, a hand-written HTTP and SSE client, request building, delta assembly, the chat state machine, the thinking cap, the slot gate with retry, the startup self-test and on-device verification. Everything the tasks copy in was checked against a private reference implementation: the gate passes after each task in order, the timing tests pass repeatedly under CPU load, and the reference passes the self-test and all four device checks on straylight. Expected results for the recorded streams were derived by a separate script. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
104 lines
3.9 KiB
Markdown
104 lines
3.9 KiB
Markdown
# M2a task 01: SHA-256 in `proto`
|
|
|
|
**Branch:** `m2a` (create it from `master`: `git switch master && git switch -c m2a`)
|
|
**Commit subject:** `Add SHA-256 to proto, wrapping the emsha crate`
|
|
|
|
## Goal
|
|
|
|
Give the workspace one SHA-256 function. It wraps the `emsha` crate, which the project's owner
|
|
wrote. Nothing else in the workspace may use `emsha` directly, so that the choice of implementation
|
|
stays in one file.
|
|
|
|
## Context
|
|
|
|
The hash is used to check that the inference server runs the chat template we expect, and later
|
|
for the audit log. An earlier release of `emsha` hashed every message of 63, 127, 191, … bytes
|
|
wrongly, because of a mistake at a padding boundary. That is fixed in 1.0.4, and it is why the test
|
|
file checks lengths on both sides of every boundary and feeds each message in two pieces.
|
|
|
|
## Files
|
|
|
|
- Copy: `crates/proto/tests/hash.rs`
|
|
- Create: `crates/proto/src/hash.rs`
|
|
- Modify: `Cargo.toml`, `crates/proto/Cargo.toml`, `crates/proto/src/lib.rs`,
|
|
`docs/dependencies.md`, `docs/implementer-log.md`, `Cargo.lock` (generated)
|
|
|
|
## Interfaces
|
|
|
|
Consumes: `proto::Hash32` (`Hash32::from_bytes([u8; 32])`).
|
|
|
|
Produces, in `crates/proto/src/hash.rs`, re-exported from the crate root:
|
|
|
|
```rust
|
|
pub struct HashError; // Debug, Clone, Copy, PartialEq, Eq; Display; std::error::Error
|
|
|
|
pub struct Sha256(/* private */);
|
|
impl Sha256 {
|
|
pub fn new() -> Self;
|
|
pub fn update(&mut self, data: &[u8]) -> Result<(), HashError>; // may be called many times
|
|
pub fn finish(self) -> Result<Hash32, HashError>;
|
|
}
|
|
impl Default for Sha256 { /* same as new() */ }
|
|
|
|
pub fn sha256(data: &[u8]) -> Result<Hash32, HashError>; // new, update, finish
|
|
```
|
|
|
|
Every `emsha` error becomes `HashError`. No `unwrap` or `expect`.
|
|
|
|
## API notes (`emsha` 1.0.4, read from its source on 2026-09-17)
|
|
|
|
```rust
|
|
use emsha::Hash; // the trait that has update and finalize
|
|
let mut h = emsha::sha256::SHA256::new();
|
|
h.update(b"bytes")?; // fn update(&mut self, msg: &[u8]) -> emsha::Result<()>
|
|
let mut out = [0u8; emsha::sha256::SIZE]; // SIZE is 32
|
|
h.finalize(&mut out)?; // fn finalize(&mut self, digest: &mut [u8]) -> emsha::Result<()>
|
|
```
|
|
|
|
`emsha::Result<T>` is `Result<T, emsha::Error>`. The crate has no dependencies and is `no_std`.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Branch, copy the test, add the dependency.**
|
|
|
|
```sh
|
|
git switch master && git switch -c m2a
|
|
cp docs/plans/M2a/files/crates/proto/tests/hash.rs crates/proto/tests/
|
|
```
|
|
|
|
Add to `[workspace.dependencies]` in the root `Cargo.toml`: `emsha = "1.0.4"`.
|
|
Add to `[dependencies]` in `crates/proto/Cargo.toml`: `emsha.workspace = true`.
|
|
Add this row to `docs/dependencies.md`:
|
|
|
|
```markdown
|
|
| `emsha` | 1.0.4 | `proto` | SHA-256. Written by the owner; no dependencies, no `unsafe`. Checked against `sha256sum` on 3,204 inputs. |
|
|
```
|
|
|
|
- [ ] **2. See the test fail.** `cargo test -p proto --test hash`. Expected: it does not compile.
|
|
|
|
- [ ] **3. Write `hash.rs`,** and in `lib.rs` add `pub mod hash;` and
|
|
`pub use hash::{HashError, Sha256, sha256};`. Run `cargo fmt --all`.
|
|
|
|
- [ ] **4. See the test pass.** `cargo test -p proto --test hash`. Expected: `4 passed`.
|
|
|
|
- [ ] **5. Run the gate.** `cargo build`, then `make gate`. Expected last line: `gate: ok`.
|
|
`cargo deny` prints a `no-license-field` warning for `emsha`; that is expected, because the
|
|
crate's licence is a file and not an SPDX name. Do not edit `deny.toml`.
|
|
|
|
- [ ] **6. Log and commit.**
|
|
|
|
```sh
|
|
git add Cargo.toml Cargo.lock crates/proto docs/dependencies.md docs/implementer-log.md
|
|
git commit
|
|
```
|
|
|
|
## Done when
|
|
|
|
- `cargo test -p proto --test hash` reports 4 passed; `make gate` prints `gate: ok`.
|
|
- `grep -rn emsha crates/*/src` shows `crates/proto/src/hash.rs` only.
|
|
|
|
## Stop and report if
|
|
|
|
- A vector in `hash.rs` fails. Do not change the vector: it would mean `emsha` is wrong again.
|
|
- `cargo deny` reports an error, not a warning, for `emsha`.
|