Files
hack-house/hh/Cargo.toml
T
leetcrypt 7de0f57fa6 R5: consent-gated recruitment + per-hop X25519 sealed credentials
Add the in-band, signature-gated consent handshake that governs whether a
node may be recruited into a measured SOR circuit, plus a per-recipient
sealed box so a hop credential decrypts only with the host's X25519 key.

- crypto.rs: seal_to_pubkey/open_sealed (ephemeral X25519 -> HKDF-SHA256 ->
  Fernet AEAD), reusing the existing fernet crate (no new symmetric primitive).
- sor/consent.rs: signed ConsentRequest (Ed25519 persona), node_evaluate
  (reject unless the signature verifies), CircuitBuilder that recruits a hop
  only on an accept it can open; parse_sor_frame with never-panic proptests.
- net.rs: {"_sor":...} control frames are live-only and classified out-of-band
  by the SOR layer (parse_sor), never surfaced as chat/app events.
- cmd_chat/sor/consent.py + bridge _sor case: bit-compatible Python mirror;
  the agent only observes/classifies consent frames — it never auto-accepts
  and stands up no forwarder (forwarding is R4, isolated-engine-only).

Acceptance check (roadmap R5) green both languages: host recruits a hop only
after an explicit accept; a reject leaves no entry; the credential opens only
with the host key (third party cannot); unsigned/forged request -> rejected.
Cross-language KDF parity pinned by a shared known-answer vector.
Rust 75 passed; Python SOR suite 37 passed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-19 16:35:11 -07:00

58 lines
1.5 KiB
TOML

[package]
name = "hack-house"
version = "0.1.0"
edition = "2021"
description = "hack-house — encrypted collaborative sessions with a summoned sandbox."
license = "MIT"
[[bin]]
name = "hack-house"
path = "src/main.rs"
[dependencies]
# crypto
num-bigint = "0.4"
num-traits = "0.2"
sha2 = "0.10"
hkdf = "0.12"
fernet = "0.2"
base64 = "0.22"
rand = "0.8"
hex = "0.4"
# pseudonymous attribution (persona signing keys, ESA-style)
ed25519-dalek = "2"
# net
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] }
rustls = "0.23"
url = "2"
# sandbox (P3): PTY + terminal emulation
portable-pty = "0.8"
vt100 = "0.15"
# file/dir transfer (P5)
tar = "0.4"
# async + tui
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "io-util", "sync", "time", "signal"] }
tokio-tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] }
futures-util = "0.3"
ratatui = { version = "0.29", features = ["serde", "unstable-rendered-line-info"] }
crossterm = { version = "0.28", features = ["event-stream"] }
toml = "0.8"
# data
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# cli / errors
clap = { version = "4", features = ["derive"] }
anyhow = "1"
x25519-dalek = { version = "3.0.0", features = ["static_secrets"] }
[dev-dependencies]
# property-based fuzzing of the frame parsers (attacker-controlled JSON)
proptest = "1"