{"record":{"id":"6055683759aba0c6","repo":"unicity-aos/aos-ce","slug":"hook-adapter-oracle-dropping-mismatched-context-reply-on","errorCode":null,"errorMessage":"hook-adapter-oracle: dropping mismatched context reply on {reply_topic}","messagePattern":"hook-adapter-oracle: dropping mismatched context reply on (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"capsules/capsule-hook-adapter-oracle/src/lib.rs","lineNumber":280,"sourceCode":"        let remaining = if contexts.is_empty() {\n            HOST_HOOK_COLLECT_DEADLINE_MS - elapsed_ms\n        } else {\n            HOOK_QUIESCENCE_MS.min(HOST_HOOK_COLLECT_DEADLINE_MS - elapsed_ms)\n        };\n        match subscription.recv(remaining) {\n            Ok(poll) if poll.messages.is_empty() => break,\n            Ok(poll) => {\n                if poll.dropped != 0 || poll.lagged != 0 {\n                    log::warn(format!(\n                        \"hook-adapter-oracle: incomplete context fan-out on {reply_topic}; dropping all partial context\"\n                    ));\n                    return Ok(None);\n                }\n                for message in poll.messages {\n                    if message.topic != reply_topic\n                        || message.principal.verified() != Some(principal)\n                    {\n                        log::warn(format!(\n                            \"hook-adapter-oracle: dropping mismatched context reply on {reply_topic}\"\n                        ));\n                        continue;\n                    }\n                    match serde_json::from_str::<serde_json::Value>(&message.payload) {\n                        Ok(value) => {\n                            if let Some(context) = value\n                                .get(\"additional_context\")\n                                .and_then(serde_json::Value::as_str)\n                                .filter(|context| !context.trim().is_empty())\n                                && !push_context(&mut contexts, &mut context_bytes, context)\n                            {\n                                log::warn(format!(\n                                    \"hook-adapter-oracle: dropping context beyond {MAX_HOST_CONTEXT_BYTES} bytes\"\n                                ));\n                            }\n                        }\n                        Err(error) => log::warn(format!(","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-hook-adapter-oracle/src/lib.rs#L262-L298","documentation":"While collecting context replies, each message is checked against the expected reply topic and verified principal. A reply that doesn't match either is untrusted (wrong requester, spoofed topic, or stale subscriber output), so the adapter logs this warning and skips it via continue rather than incorporating it into the oracle's context.","triggerScenarios":"dispatch_oracle_hook -> collect_additional_context receives a message on the fan-out subscription where message.topic != reply_topic OR message.principal.verified() != Some(principal) — i.e. topic mismatch or the reply was not verifiably sent by the expected principal.","commonSituations":"A shared subscription also receiving other topics' traffic; hooks replying on a stale/wrong reply topic after a retry; principals re-authenticated between request and reply so the verified identity differs; another capsule broadcasting on a colliding topic.","solutions":["Ensure each hook replies on exactly the reply_topic provided in the request (echo it back verbatim).","Keep the principal's verified identity stable for the duration of request/reply; re-authenticate before the request if needed.","Give each context request a unique reply topic so unrelated traffic cannot collide.","Audit publishers writing to the reply topic namespace to eliminate foreign traffic."],"exampleFix":"// before: hook replies on its own static topic\nbus.publish(\"hook.oracle.context\", reply);\n// after: reply on the topic supplied in the request envelope\nbus.publish(&request.reply_topic, reply);","handlingStrategy":"validation","validationCode":"// verify reply topic and principal before consuming\nfn reply_ok(m: &Message, reply_topic: &str, principal: &Principal) -> bool {\n    m.topic == reply_topic && m.principal.verified() == Some(principal)\n}","typeGuard":"fn trusted_reply(m: &Message, want_topic: &str, want: &Principal) -> bool {\n    m.topic == want_topic && m.principal.verified() == Some(want)\n}","tryCatchPattern":null,"preventionTips":["Always reply on the exact reply_topic from the request envelope","Use a unique reply topic per request to avoid collisions","Keep the principal identity stable across the request/reply window"],"tags":["ipc","security","principal","hooks","rust"],"backgroundTag":"unexpected-response-shape","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}