-
Notifications
You must be signed in to change notification settings - Fork 62
-
Hey there! I've got a use-case where policies might need to resolve remote member lists and therefore would benefit from an async builtin.
After looking around I'm seeing that there is actually a RegoVM, which is actually really close to what I want, because I'd like to drive the state machine myself, and have the VM suspended while I go do my async stuff, and then I can come back and continue execution until the next await point.
It looks like the RegoVM stuff is maybe new or not ready for general consumption?
Am I going in the right direction with this? If I run into blockers in RegoVM would you be open to updates that allow this workflow? I'd appreciate any hints!
Beta Was this translation helpful? Give feedback.
All reactions
Hey I found something that looks like it works!
The __builtin_host_await lets me trigger the host await, and I can just suffix the user's policy with a friendly wrapper around it to make nicer to use in the policy.
use regorus::{ PolicyModule, Value, languages::rego::compiler::Compiler, rvm::{ RegoVM, vm::{ExecutionMode, ExecutionState, SuspendReason}, }, }; static POLICY: &str = r#" package test import rego.v1 default allow := false allow if { user_exists(input.user_id) == "found" } "#; static EXTENSIONS: &str = r#" # Wrapper function around the host await builtin user_exists(username) := result if { result := __builtin_host_await(username, "user_exists") } "#;
Replies: 2 comments 2 replies
-
Hey I found something that looks like it works!
The __builtin_host_await lets me trigger the host await, and I can just suffix the user's policy with a friendly wrapper around it to make nicer to use in the policy.
use regorus::{ PolicyModule, Value, languages::rego::compiler::Compiler, rvm::{ RegoVM, vm::{ExecutionMode, ExecutionState, SuspendReason}, }, }; static POLICY: &str = r#" package test import rego.v1 default allow := false allow if { user_exists(input.user_id) == "found" } "#; static EXTENSIONS: &str = r#" # Wrapper function around the host await builtin user_exists(username) := result if { result := __builtin_host_await(username, "user_exists") } "#; fn main() -> anyhow::Result<()> { let compiled_policy = regorus::compile_policy_with_entrypoint( regorus::Value::Object(Default::default()), &[PolicyModule { id: "".into(), content: { let mut s = String::from(POLICY); s.push_str(EXTENSIONS); s } .into(), }], "data.test.allow".into(), )?; let program = Compiler::compile_from_policy(&compiled_policy, &["data.test.allow"])?; let mut vm = RegoVM::new(); vm.load_program(program); vm.set_input(Value::from_json_str( r#"{ "user_id": "user_abc123" }"#, )?); vm.set_execution_mode(ExecutionMode::Suspendable); vm.execute()?; loop { match vm.execution_state() { ExecutionState::Suspended { reason, .. } => { match reason { SuspendReason::HostAwait { argument, identifier, .. } => { let identifier_str = identifier.as_string()?.to_string(); match identifier_str.as_str() { "user_exists" => { // Extract the query from the argument let query = &**argument.as_string()?; // Check for user let response = if query == "user_abc123" { "found".to_string() } else { "not_found".to_string() }; // Provide the response to the async call vm.resume(Some(Value::from(response.as_str())))?; } other => { anyhow::bail!("Unknown async builtin identifier: {other:?}"); } } } _ => panic!(), } } ExecutionState::Completed { result } => { println!("\n[VM] Completed — result: {result:?}"); // Convert to bool for display let allowed = result.as_bool().copied().unwrap_or(false); println!("[RESULT] allow = {allowed}"); break; } state => panic!("Unexpected state: {state:?}"), } } Ok(()) }
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey @zicklag,
The host await builtin and the suspendable mode execution were designed specifically to support scenarios such as yours where the policy needs to fetch external data.
Glad that you found it and were able to figure out a solution.
If I run into blockers in RegoVM would you be open to updates that allow this workflow
Yes, RVM is fully supported. Lot of work is being put into RVM - multi policy language support, serialized compiled policies, optimized execution, policy proving etc.
Here are some playgrounds to try out:
- https://anakrish.github.io/rego-virtual-machine-playground/
- https://anakrish.github.io/rvm-policy-analysis/
Note that the interpreter is also fully supported. But given that it is an AST walking interpreter, it may not be possible to implement all concepts (e.g. host await) in the interpreter.
Beta Was this translation helpful? Give feedback.
All reactions
-
Awesome, that's great. I really like being able to use RVM like this.
And thanks a lot for publishing this crate! I think it's going to be incredibly useful.
I'm using it to create a customizable organization / community account management layer for ATProto ( the protocol behind Bluesky ).
It's really nice to be able to take a language like this and use it to provide customizable policies instead of having to hardcode the policy into my server.
Beta Was this translation helpful? Give feedback.
All reactions
-
That's a really cool use case, @zicklag! Sounds like a great fit!
Do let us know if you run into any issues with RVM/Regorus.
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1