desk_governance
A four-policy trading-desk set combining most of the language in one place:
permit_sell_open_session_rate_limited— a trader may sell only with an open session (!Logout since Login), an approval for the same stock within the last hour (formerly), and at most three sales in the last hour (acountrate limit).forbid_insider_notes— a provider guardrail: theText::Scaninformation provider (providers.json) flags notes mentioning “insider”.forbid_sell_amzn— a deny-overrides carve-out for AMZN.permit_admin_approve— an admin may approve a sale for any stock except one they themselves requested to sell in the last hour.
The trace follows alice (all events are hers, as the default event schema correlates history to the requesting principal):
@0— alice logs in (aLoginresponse, history only).@60— her MSFT sale is approved (anApproveSaleresponse, history only).@120— alice sells MSFT → allow (open session, approved, first sale).@180— alice sells AMZN → deny (forbid_sell_amzn; there is also no AMZN approval).@240— alice sells MSFT with notes mentioning an insider tip → deny (forbid_insider_notes).@300— alice logs out → deny (no rule permitsLogout; the request is still recorded in the history).@360— alice sells MSFT again → deny: her session is closed (aLogoutsince theLogin), and this would also be her fourth sale within the hour.
Policy
// A trading-desk governance set. Deny-overrides: the forbids win over any permit.
// 1. Permit a share sale only for a trader with an OPEN session (logged in and
// not since logged out) who has NOT sold more than 3 times in the last hour,
// and whose sale was approved for the same stock within the last hour.
@id("permit_sell_open_session_rate_limited")
permit ( principal is Desk::OAuthUser, action == Desk::Action::"SellShares", resource )
when temporal {
!Desk::Action::"Logout"::request{ input.user: context.input.user }
since within 8h
Desk::Action::"Login"::response{ input.user: context.input.user, output.result: true }
}
when temporal {
formerly within 1h Desk::Action::"ApproveSale"::response{
input.user: context.input.user,
input.stock: context.input.stock,
output.approved: true
}
}
when temporal {
exists (n: Long). (
(count for (t: Timepoint). where (
formerly within 1h (Desk::Action::"SellShares"::request{ input.user: context.input.user } && tp(t))
)) == n
&& n <= 3
)
};
// 2. Never permit a sale whose notes look like insider-trading chatter
// (computed by a content-scanning information provider).
@id("forbid_insider_notes")
forbid ( principal, action == Desk::Action::"SellShares", resource )
when { Text::Scan(context.input.notes).flagged == true };
// 3. Never permit selling AMZN, regardless of everything else.
@id("forbid_sell_amzn")
forbid ( principal, action == Desk::Action::"SellShares", resource )
when { context.input.stock == "AMZN" };
// 4. An admin (an IAM entity) may approve a sale for any stock except one they
// themselves requested to sell in the last hour.
@id("permit_admin_approve")
permit ( principal is Desk::IamEntity, action == Desk::Action::"ApproveSale", resource )
when temporal {
!Desk::Action::"SellShares"::request{ input.user: context.input.user, input.stock: context.input.stock }
since within 1h
Desk::Action::"Login"::response{ input.user: context.input.user }
};
Schema
namespace Desk {
entity Gateway;
entity OAuthUser = { id: String, desk: String } tags String;
entity IamEntity = { id: String };
type LoginInput = { user: String };
type LoginOutput = { result: Bool };
type LogoutInput = { user: String };
type ApproveInput = { user: String, stock: String };
type ApproveOutput = { approved: Bool };
type SellInput = { user: String, stock: String, shares: Long, notes: String };
type SellOutput = { proceeds: decimal };
action "Login" appliesTo {
principal: [OAuthUser, IamEntity],
resource: [Gateway],
context: { input: LoginInput, output?: LoginOutput }
};
action "Logout" appliesTo {
principal: [OAuthUser, IamEntity],
resource: [Gateway],
context: { input: LogoutInput }
};
action "ApproveSale" appliesTo {
principal: [OAuthUser, IamEntity],
resource: [Gateway],
context: { input: ApproveInput, output?: ApproveOutput }
};
action "SellShares" appliesTo {
principal: [OAuthUser, IamEntity],
resource: [Gateway],
context: { input: SellInput, output?: SellOutput }
};
}
Trace
@0 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") Desk::Action::"Login"::response(input: { user: "alice" }, output: { result: true }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u1")
@60 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") Desk::Action::"ApproveSale"::response(input: { user: "alice", stock: "MSFT" }, output: { approved: true }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u2")
@120 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") request_context(input: { user: "alice", stock: "MSFT", shares: 10, notes: "rebalancing" }) Desk::Action::"SellShares"::request(input: { user: "alice", stock: "MSFT", shares: 10, notes: "rebalancing" }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u3")
@180 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") request_context(input: { user: "alice", stock: "AMZN", shares: 10, notes: "rebalancing" }) Desk::Action::"SellShares"::request(input: { user: "alice", stock: "AMZN", shares: 10, notes: "rebalancing" }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u4")
@240 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") request_context(input: { user: "alice", stock: "MSFT", shares: 10, notes: "insider tip from the CFO" }) Desk::Action::"SellShares"::request(input: { user: "alice", stock: "MSFT", shares: 10, notes: "insider tip from the CFO" }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u5")
@300 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") request_context(input: { user: "alice" }) Desk::Action::"Logout"::request(input: { user: "alice" }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u6")
@360 scope(principal: Desk::OAuthUser::"alice", resource: Desk::Gateway::"gw1") request_context(input: { user: "alice", stock: "MSFT", shares: 10, notes: "rebalancing" }) Desk::Action::"SellShares"::request(input: { user: "alice", stock: "MSFT", shares: 10, notes: "rebalancing" }, callerPrincipal: Desk::OAuthUser::"alice", callerResource: Desk::Gateway::"gw1", requestId: "u7")
Expected Output
@120 (time point 0): ALLOW [rules: 0]
@180 (time point 1): DENY [rules: 2]
@240 (time point 2): DENY [rules: 1]
@300 (time point 3): DENY
@360 (time point 4): DENY
Provider Declarations
{
"availableProviders": {
"Text::Scan": {
"argumentTypes": [{ "paramType": "string" }],
"outputType": {
"paramType": "record",
"fields": { "flagged": { "paramType": "bool" } },
"required": ["flagged"]
},
"implementation": {
"kind": "rhai",
"script": "fn evaluate(text) { if type_of(text) == \"()\" { return #{ flagged: false }; } #{ flagged: regex_count(\"(?i)insider\", text) > 0 } }"
}
}
}
}