Repository navigation
Socket admission: identity binding, chi-scoped delivery, and revocation #66
Description
Follow-up to #65, which enforced the protoVersion half of the socket admission contract. The rest is unwritten.
Why this exists
A bee reaches humd over $XDG_RUNTIME_DIR/hum/thrum.sock. After #65 it must declare a compatible protocol version. That is the whole of what is checked.
Everything below the version line is absent.
1. Declared chis filter nothing
A hello's chis list is parsed into manifest.chis, gossiped to the ensemble as advertised vocabulary, and then never read again. Delivery to a connected client is not filtered by it.
So a bee declaring three chis receives every tone the nest sends it. And a bee declaring none is treated identically. The field is descriptive metadata pretending to be a capability statement.
Wanted: chis becomes an admission scope. A client receives only tones whose chi it declared. Open question — what a bee must declare to receive a chunk in a session it never opened, and whether an empty chis means "nothing" or "everything".
Blocker to resolve first: Sim::inject_tone bypasses the socket and exists to drive shapes directly. Synthetic clients must be distinguishable from real ones before any delivery gate can be added, or the sim suite loses the ability to inject tones without a hello.
2. Identity is self-asserted
HumdId on the socket path is whatever the hello claims. There is no challenge, no signature, no proof of possession.
Any local process can claim to be any bee. For a bee holding a key that authorises chain writes, or that receives settlement tones, that is a confused-deputy problem, not a theoretical one.
The primitives already exist for the peer path and should be reused rather than reinvented — HumdKey around ed25519_dalek::SigningKey, HumdId = sha256(pubkey), domain-separated handshake_message with a signed timestamp and skew tolerance, HelloParse::{Verified, Unsigned, Invalid}, and Ensemble::with_strict_auth. The socket does not use any of them.
Wanted: the socket admits on a signature, not a claim.
3. Nothing is revocable
HumdId is a hash of a public key. That is fine for pseudonymity and useless for accountability: there is no name, no authority, and no way to withdraw a peer that behaved badly. HumdRegistry exists on-chain (ensemble/src/onchain.rs, IHumdRegistry) and is currently used for optional manifest anchoring.
Wanted: a revocation root for bee identity. Note this is the primitive the README's "zero-trust access control" row already claims and does not have.
4. The 60-second skew window is a replay window
The peer handshake accepts signed_at within 60s. Over an unauthenticated socket that is a comfortable replay budget. Constrain it once the socket has a key to bind it to.
Explicitly out of scope
Routing integrity. find-the-worker selects a worker from gossiped manifests with no distance proof, so a peer that gossips a false manifest influences where prompts are sent. That belongs to routing, not admission, and needs its own issue.
Ordering
1 is independent and can land on its own. 2 is the one that matters — it is what makes the socket safe for a bee that holds authority. 3 and 4 follow from 2.
Activity
adiled commentedon Oct 4, 2026
Revising this after reading the code properly rather than grepping it. Two corrections, one addition, and this issue is now partly wrong about scope.
Correction: routing is no longer out of scope
The "Explicitly out of scope" section is stale. #67 fixed part of it, and the rest is a bigger deal than I implied there.
find-the-workerroutes on a self-declared gossiped claim. #67 adds a provenance gate (an announce must claim the same Hid as its tone origin) and makes remote routing opt-in viaHUM_TRUST_REMOTE_WORKERS, default off. That stops impersonation and stops a claim alone from moving prompt content — it does not solve capability routing.The real finding is a structural split:
kad_findatensemble/src/lib.rs:524, full iterative lookup with alpha batches andLookupShortlist. Peers here come from signedHumdAddr. Has no notion of a model or capability claim (grepformodels/workerinkad.rsreturns nothing).pick_remote_worker(humd/src/lib.rs:527) reads the second.kad_findhas no caller outsideensemble/tests/kad_integration.rs. Both key onHid, so they collide in name only.Worse, one layer up:
handshake_messageisdomain:hid:signed_at. The signature covers identity only —nests,hosts,can_relay,free_slots, andheadroomare parsed after verification and never covered. So any peer completing a signed handshake can over-claim relay rights or capacity, and those feed real decisions. Model claims are the same defect in the layer above.Kademlia proves location, not capability —
closest_to("who has model X")is still a self-declared claim. Proof-of-possession or stake is required and does not exist in any form.Correction:
strict_authhas a default-off postureSection 2 says the socket uses none of the signing primitives. True, but the peer path is not uniformly protected either —
simspawns withwith_strict_auth(id, false), and production does too unless configured. Under lax auth,Hidis self-declared with no signature.The cheap next win
Extend
handshake_messageto covernests/can_relay/free_slots, then have announces carry that signature. Closes relay and capacity over-claiming for a small diff. Model claims still need proof-of-possession.Sections 1–4 stand. Want me to split the routing half into its own issue so this one stays about socket admission?
adiled commentedon Oct 18, 2026
All four pieces landed on
socket/admission-integrity(PR #70).1. Declared
chisfilter nothing — done (cd15d0b)Broadcast now respects declared scope. Both the sigil pass and the no-owner fallback filter on it.
The blocker was resolved without solving it as described. The issue says synthetic clients must be distinguishable before a delivery gate can be added. Rather than tag them, I made an empty
chismean unfiltered — every bee predating the field sends an empty set, so refusing it traffic would be a wire break dressed as a security fix. That answers the issue's open question directly and means the sim suite keeps injecting tones without a hello.thrum_allis scoped too; it has no callers today, but leaving it open would make it a bypass for whoever reaches for it next.2. Identity is self-asserted — done (
fb11231)Bees already held an Ed25519 key whose pubkey hashes to the hid they already send. It was never used to sign anything on this path.
Signs
pubkey/signedAt/signature; humd verifies against the claimed hid under its own domain, so a peer-handshake signature cannot be replayed onto a socket hello. Unsigned stays admitted, markedhid_verified: false. Present-but-wrong is a hard reject.3. Nothing is revocable — done (
721e817,f0de28c)Operator lists hids; humd refuses them at hello — closed, error echo, not registered, not announced. Checked whether or not the bee proved the hid, because dropping the signature is the obvious evasion.
humctl revoke|unrevoke|revocations.Two corrections to the issue's premises:
HumdRegistryhas no callers anywhere in the tree. It is not "currently used for optional manifest anchoring" — it sits behind anonchainfeature flag with zero production references. There was nothing to hook into, and on-chain revocation is a contract change regardless.4. The skew window is a replay window — closed as a consequence of 2
The peer path already enforced 60s; the socket path does now. Both bind it to a key.
Rule 0 found a coverage hole worth recording
Removing the hid/pubkey match check failed nothing. It protects against a valid signature over a hid the signing key does not own, and no test isolated it — the two tests nominally covering it were caught downstream by the signature check instead. Now there is a test that fails when that gate is removed. Every gate in this work has been checked the same way.
Tests
Workspace 381 → 404, 0 failures. Each gate neutered in turn and confirmed to fail.
Not included
excluded from the workspace and buildhum-identityfrom gitmain, sosign_socket_hellodoes not exist for them until this merges. Wiringpaid-oraclewas attempted and reverted rather than ship code I could not compile.humctlsays so.