~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs

0
0
main
116 lines · 5.4 KB Raw History

Policies

A policy is a module that answers one question, "allow this?", with a verdict: { "allow": bool, "violations": [{ "msg", "policyId" }] }. Whoever runs a module decides which policies guard it; the module itself takes no policy input.

A policy on an edge

The simplest policy guards one edge of a composition. compose's even allows calls whose n is even:

examples/rust/compose/src/lib.rs

fn even_handler(run: Run<Value>) -> Result<Handle, String> {
    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())
}

It is named in the edge's policies (Compositions); a deny fails the call with policy even denied: … and the callee never runs.

A policy module

A reusable policy implements a contract, com.publicdomainrelay.temp.market.evaluatePolicy: the socialweb.computer market's input (who is asking, about whom, in scope or full mode, with the RFP, bid and accept records) and the verdict as output. The market's modules live in atlibs-socialweb-computer-requester; modules/policy-open/module.json there declares it:

	"implements": [
		{
			"nsid": "com.publicdomainrelay.temp.market.evaluatePolicy",
			"entry": "evaluate"
		}
	]

Let a module be guarded: an all-allow hook

A module that must check a policy declares the contract as a hook with combine: "all-allow" and a default. The socialweb.computer requester does, before it takes a bid (modules/requester/module.json):

			"nsid": "com.publicdomainrelay.temp.market.evaluatePolicy",
			"required": true,
			"combine": "all-allow",
			"default": "policy-requester-tangled-vouch",
			"reason": "kcp: each bid's bidder in scope mode, then the chosen winner in full mode with the rfp and bid refs, before the accept; every routed policy must allow"
  • No route: the host runs the default module, so the requester never runs unguarded.
  • Routes: every routed policy runs in edge order, and the first deny is the answer. An error or a malformed answer is a deny naming the route.
  • A route is an instance whose module implements the contract, or a caller handler (--handler approve=wasm:<ref>#<entry>, https://…, exec:…; in JS handlers: { approve }).

Two policies that both must allow (modules/requester/policy-composition.json):

	"instances": [
		{ "name": "requester", "module": "requester", "session": "requester" },
		{ "name": "vouch", "module": "policy-requester-tangled-vouch" },
		{ "name": "payload", "module": "policy-bid-payload" }
	],
	"edges": [
		{
			"from": "requester", "nsid": "com.publicdomainrelay.temp.market.evaluatePolicy", "to": "vouch",
			"reason": "kcp requester-tangled-vouch: the requester or its operator vouched for the bidder"
		},
		{
			"from": "requester", "nsid": "com.publicdomainrelay.temp.market.evaluatePolicy", "to": "payload",
			"reason": "kcp bid-payload: only bids with an allowed payload (default: free)"
		}
	],
	"limits": { "depth": 4, "calls": 40 }

Policies an RFP attaches

On the market an RFP can name more policies. rfpPolicyRoutes(manifest, from, records) adds one instance and edge per attached policy record after the module's own, so its own policy still runs first and the RFP's are ANDed after it. A record naming a policy no module implements fails closed. A record's args are the instance's input, so a policy module declares config: [{ "key": "args", "label": "Arguments", "type": "object" }].

javascript/src/compose/index.js

 * Add the policies an RFP attaches to `from`'s policy hook, after the routes it has: pass a
 * loaded manifest (loadComposition(...).manifest: its default routes the hook), so the requester's or the
 * bidder's own policy still runs, first, and the RFP's are ANDed after it (combine all-allow). Each
 * computer.socialweb.temp.policy.ghalite record's `name` (kcp-canonicalised) is the module policy-<name> (the
 * same publisher's record for an AT-URI instance), its `args` the instance's input.args. Throws (fail closed)
 * for a hook not routed yet, a record that is not an object, or a name no policy module implements.
 * @param {object} manifest  a loaded composition's manifest
 * @param {string} from  the instance whose hook (com.publicdomainrelay.temp.market.evaluatePolicy, combine all-allow) they serve
 * @param {object[]} records  the attached policy records' values
 * @returns {object}  the manifest with one instance and edge per record added after its own; load it again to run it
 */
export function rfpPolicyRoutes(manifest, from, records) {

The market policies

The socialweb.computer market's policies (policy-open, policy-requester-only-me, policy-requester-mutuals, policy-requester-tangled-vouch, policy-bidder-only-me, policy-bidder-mutuals, policy-bidder-tangled-vouch, policy-bid-payload, policy-under-4-cpus) share their logic in crates/policy of atlibs-socialweb-computer-requester, with the requester that uses them and their tests.

Previous: Compositions | Next: Transports