Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions bindings/csharp/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,112 @@ var tasks = Enumerable.Range(0, 100).Select(i =>
var results = await Task.WhenAll(tasks);
```

## RVM Classes

### Program

`Program` represents a compiled RVM bytecode artifact. It can be created from
modules+entrypoints or from an `Engine`, serialized to binary, and loaded into
an `Rvm` for execution.

```csharp
public sealed class Program : IDisposable
{
// Compile from modules with entry points
public static Program CompileFromModules(
string dataJson,
IReadOnlyList<PolicyModule> modules,
IReadOnlyList<string> entryPoints);

// Compile with registered host-await builtins
public static Program CompileFromModules(
string dataJson,
IReadOnlyList<PolicyModule> modules,
IReadOnlyList<string> entryPoints,
IReadOnlyList<HostAwaitBuiltin> hostAwaitBuiltins);

// Compile from an Engine instance
public static Program CompileFromEngine(Engine engine, IReadOnlyList<string> entryPoints);

// Serialize/deserialize
public byte[] SerializeBinary();
public static Program DeserializeBinary(byte[] data, out bool isPartial);

// Debug listing
public string GenerateListing();

public void Dispose();
}
```

### Rvm

`Rvm` is the virtual machine that executes a loaded `Program`.

```csharp
public sealed class Rvm : IDisposable
{
// Load a compiled program
public void LoadProgram(Program program);

// Set data and input documents
public void SetDataJson(string dataJson);
public void SetInputJson(string inputJson);

// Configure execution
public void SetExecutionMode(ExecutionMode mode);

// Run
public string? Execute();
public string? ExecuteEntryPoint(string entryPoint);

// Suspendable-mode host-await interaction
public string? Resume(string valueJson);
public string? GetExecutionState();
public string? GetHostAwaitIdentifier();
public string? GetHostAwaitArgument();

// Run-to-completion-mode host-await pre-loading. Atomically replaces all
// previously configured responses for every identifier; pass every
// identifier the policy may invoke in a single call.
public void SetHostAwaitResponses(
IReadOnlyDictionary<string, IReadOnlyList<string>> responsesByIdentifier);

public void Dispose();
}
```

### HostAwaitBuiltin

Declares a function name that the compiler should treat as a host-await call.
When the VM encounters a call to this function, it suspends (suspendable mode)
or consumes a pre-loaded response (run-to-completion mode).

Registered builtins are restricted to exactly one argument at the compiler
level (use object packing to pass multiple values), so the C# struct does not
expose an `argCount` parameter.

```csharp
public readonly struct HostAwaitBuiltin
{
public string Name { get; }

public HostAwaitBuiltin(string name);
}
```

### ExecutionMode

Controls how the VM handles host-await instructions.

```csharp
public enum ExecutionMode
{
RunToCompletion = 0,
Suspendable = 1,
}
```

## Performance Considerations

### Compilation Overhead
Expand Down
83 changes: 83 additions & 0 deletions bindings/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,89 @@ var result = vm.Execute();
Console.WriteLine($"allow: {result}");
```

## RVM with Registered Host-Await Builtins

Host-await builtins let you register custom function names at compile time.
When the VM encounters a call to one of these functions, it suspends execution
so the host can resolve the call externally and resume with a value.

### Suspendable mode (resolve one call at a time)

```csharp
using Regorus;

const string Policy = """
package demo
import rego.v1

default allow := false

allow if {
account := get_account({"id": input.account_id})
account.status == "active"
}
""";

var modules = new[] { new PolicyModule("demo.rego", Policy) };
var entryPoints = new[] { "data.demo.allow" };
var builtins = new[] { new HostAwaitBuiltin("get_account") };

using var program = Program.CompileFromModules("{}", modules, entryPoints, builtins);
using var vm = new Rvm();
vm.SetExecutionMode(ExecutionMode.Suspendable);
vm.LoadProgram(program);
vm.SetInputJson("""{"account_id": "acct-42"}""");

// First Execute suspends when get_account() is called
vm.Execute();

// Inspect which builtin suspended and what argument was passed
var identifier = vm.GetHostAwaitIdentifier(); // "get_account"
var argument = vm.GetHostAwaitArgument(); // {"id":"acct-42"}

// Resolve externally, then resume
var result = vm.Resume("""{"status": "active", "name": "Alice"}""");
Console.WriteLine($"allow: {result}"); // true
```

### Run-to-completion mode (pre-load responses)

```csharp
using Regorus;

const string Policy = """
package demo
import rego.v1

default greeting := "unknown"

greeting := msg if {
msg := translate(input.lang)
}
""";

var modules = new[] { new PolicyModule("demo.rego", Policy) };
var entryPoints = new[] { "data.demo.greeting" };
var builtins = new[] { new HostAwaitBuiltin("translate") };

using var program = Program.CompileFromModules("{}", modules, entryPoints, builtins);
using var vm = new Rvm();
vm.SetExecutionMode(ExecutionMode.RunToCompletion);
vm.LoadProgram(program);
vm.SetInputJson("""{"lang": "es"}""");

// Queue responses before execution. SetHostAwaitResponses atomically replaces
// ALL prior responses for every identifier — pass every identifier the policy
// may invoke in a single call.
vm.SetHostAwaitResponses(new Dictionary<string, IReadOnlyList<string>>
{
["translate"] = new[] { "\"hola\"" },
});

var result = vm.Execute();
Console.WriteLine($"greeting: {result}"); // "hola"
```

## Azure RBAC Condition Evaluation

Evaluate Azure RBAC condition expressions directly with a JSON evaluation context:
Expand Down
Loading