// Composition toys (javascript/src/compose). // caller: calls `nsid` with `input` plus a `progress` callback (through `proxy` when given), answers each callback, // returns the outcome. // lookup: doubles `n`, calling the `progress` capability it was given first when there is one. // even: a policy, allows calls whose input `n` is even. // spy: a policy that tries to invoke the `host` capability in the call it checks; it allows the call // only when that is refused (a policy run must not hold the caller's capabilities). use abi::{Call, Event, Handle, Run, cap, cap_token, entries}; use serde_json::{Value, json}; fn caller_handler(run: Run) -> Result { let mut state = if run.state.is_null() { json!({ "progress": [] }) } else { run.state }; Ok(match run.event { Event::Start => { let nsid = run.args["nsid"].as_str().ok_or("nsid is required")?; let mut input = run.args.get("input").cloned().unwrap_or_else(|| json!({})); input["progress"] = cap("progress"); let call = Call::new("q", nsid).input(input); let call = match run.args["proxy"].as_str() { Some(proxy) => call.proxy(proxy), None => call, }; Handle::new().call(call).state(state) } Event::Callback { cap, input, reply, .. } if cap == "progress" => { let list = state["progress"].as_array_mut().ok_or("bad state")?; list.push(input.clone()); let id = format!("reply-{}", list.len()); Handle::new().call(Call::new(id, reply).input(json!({ "seen": input }))).state(state) } Event::Result { id, result, .. } if id == "q" => { let outcome = match result.output() { Ok(output) => json!({ "ok": true, "output": output }), Err(error) => json!({ "ok": false, "error": error }), }; Handle::new().result(json!({ "q": outcome, "progress": state["progress"] })) } _ => Handle::new(), }) } fn lookup_handler(run: Run) -> Result { let n = run.args["n"].as_i64().ok_or("n is required")?; let answer = |ack: Value| Handle::new().result(json!({ "doubled": n * 2, "ack": ack, "caller": run.args["caller"] })); Ok(match (run.event, cap_token(&run.args["progress"])) { (Event::Start, Some(token)) => Handle::new().call(Call::new("p", token).input(json!({ "step": 1 }))), (Event::Start, None) => answer(Value::Null), (Event::Result { id, result, .. }, _) if id == "p" => answer(result.output().cloned().unwrap_or_else(|e| json!({ "error": e }))), _ => Handle::new(), }) } fn even_handler(run: Run) -> Result { let n = run.args["call"]["input"]["n"].as_i64(); let allow = n.is_some_and(|n| n % 2 == 0); let violations = if allow { json!([]) } else { json!([{ "msg": format!("n {} is not even", n.map_or("missing".into(), |n| n.to_string())), "policyId": "even" }]) }; Ok(Handle::new().result(json!({ "allow": allow, "violations": violations })).done()) } fn spy_handler(run: Run) -> Result { let verdict = |allow: bool, msg: String| Handle::new().result(json!({ "allow": allow, "violations": [{ "msg": msg, "policyId": "spy" }] })).done(); Ok(match (run.event, cap_token(&run.args["call"]["input"]["host"])) { (Event::Start, Some(token)) => Handle::new().call(Call::new("h", token).input(json!({ "from": "policy" }))), (Event::Start, None) => verdict(true, "no host capability".into()), (Event::Result { id, result, .. }, _) if id == "h" => match result.output() { Ok(_) => verdict(false, "the policy invoked the caller's capability".into()), Err(error) => verdict(true, error), }, _ => Handle::new(), }) } entries! { caller => caller_handler, lookup => lookup_handler, even => even_handler, spy => spy_handler }