Security & responsible disclosure
DISCLOSEHow to report a vulnerability
Email security@plumbtech.xyz. Please do not file a public GitHub issue. We acknowledge within 48 hours, and aim to patch within 7 days for critical issues. You will be credited in the release notes unless you request anonymity.
In scope: the gateway, worker, contracts, SDKs, explorer, and console. Out of scope: vendored dependencies (report upstream), DoS (we know), anything requiring physical access to the VPS.
RECEIPTSReceipt signing threat model
Every chat completion is signed with ed25519 over a canonical v1 payload. The private signing key lives on the gateway box, not on-chain. An attacker with the signing key can forge historical receipts — we mitigate by rotating the key viaSettlement.setSigner() and publishing the public key registry in the SDK and explorer. Forgery after rotation is detectable because the signer_key_idwon't match any entry in the registry.
The server stores request_hash and response_hash, not raw prompt text. A client can verify a receipt without trusting the operator by re-hashing the request/response they sent and checking the ed25519 signature with verify_receipt.
Verification modes
vanilla — the server signs with its ed25519 key. This is what the hosted instance runs today. Trust rests on the operator not colluding with upstream providers to swap out responses.
tee-nitro / tee-sgx — planned. Receipts also carry a TEE attestation quote. Verifiers check both the ed25519 signature and the attestation — proving the signing process ran inside the named enclave.
zkml — deferred. Zero-knowledge proof of inference for small models, eventually paired with Pipe.
CHAINOn-chain components
Contracts are deployed on Base Sepolia today. Settlement checkpoints batches of receipts as Merkle roots; HubRegistry attributes uploaded models; PipeOracle relays requests to an off-chain worker. All are verified on Blockscout; ABIs are pinned in the @plumb/contracts package.