Skip to content

Commit 082e350

Browse files
committed
Add policy decision normalizer contract
1 parent 25f4744 commit 082e350

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

docs/policy-decision-normalizer.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# SourceOS Policy Decision Normalizer
2+
3+
Status: v0.1 implementation contract
4+
Owner surface: sourceos-syncd / PolicyFabric / AgentPlane / SocioSphere
5+
6+
## Purpose
7+
8+
The policy decision normalizer converts raw policy, sandbox, IPC, filesystem, network, identity, and sync authorization observations into canonical `policy.decision` events.
9+
10+
Its primary job is to prevent secure expected behavior from becoming operator-facing garbage. A denied operation is not automatically an error. It must be classified by policy intent, actor profile, target class, and security semantics.
11+
12+
## Input classes
13+
14+
The normalizer accepts observations from:
15+
16+
- kernel sandbox or capability decisions;
17+
- PolicyFabric authorization decisions;
18+
- local IPC/mach/dbus/service lookup gates;
19+
- filesystem read/write/metadata gates;
20+
- network egress, trust lookup, and endpoint gates;
21+
- identity, entitlement, DID, credential, and role gates;
22+
- sync, replication, retention, purge, and repair gates;
23+
- agent runtime admission and action gates.
24+
25+
## Canonical output
26+
27+
Every normalized policy observation emits or attaches a canonical `policy.decision` event with:
28+
29+
- `decision.policy_bundle`
30+
- `decision.policy_rule`
31+
- `decision.operation`
32+
- `decision.target_class`
33+
- `decision.result`
34+
- `decision.semantic_outcome`
35+
- `decision.explanation_code`
36+
- `severity`
37+
- `outcome`
38+
- `operator_narrative`
39+
40+
## Result vs semantic outcome
41+
42+
`decision.result` records the mechanical policy decision:
43+
44+
- `allow`
45+
- `deny`
46+
- `defer`
47+
- `degrade`
48+
49+
`decision.semantic_outcome` records SourceOS interpretation:
50+
51+
- `allowed`
52+
- `blocked_expected`
53+
- `blocked_unexpected`
54+
- `blocked_attack_like`
55+
- `degraded`
56+
- `failed`
57+
- `observed`
58+
59+
The two must not be conflated.
60+
61+
Examples:
62+
63+
- `result=deny`, `semantic_outcome=blocked_expected`: sandbox blocked a telemetry component from reading raw executable data outside its profile.
64+
- `result=deny`, `semantic_outcome=blocked_attack_like`: a user process probed privileged identity, kernel, or agent control-plane boundaries.
65+
- `result=degrade`, `semantic_outcome=degraded`: trust lookup could not complete, but local-first fallback preserved safety.
66+
- `result=allow`, `semantic_outcome=allowed`: policy permitted an expected action.
67+
68+
## Severity mapping
69+
70+
The normalizer must assign severity from semantic outcome and registry defaults, not raw denial status.
71+
72+
Default mapping:
73+
74+
- `allowed` -> `info`
75+
- `blocked_expected` -> `notice`
76+
- `blocked_unexpected` -> `warning`
77+
- `blocked_attack_like` -> `critical`
78+
- `degraded` -> `warning`
79+
- `failed` -> `error`
80+
- `observed` -> `info`
81+
82+
Registry entries may narrow severity but must not inflate expected blocks to `error` unless the policy rule explicitly says the block indicates user-visible failure or integrity loss.
83+
84+
## Explanation-code registry
85+
86+
Every `policy.decision` must reference a known explanation code.
87+
88+
Minimum required codes:
89+
90+
- `POLICY_EXPECTED_METADATA_BOUNDARY`
91+
- `POLICY_EXPECTED_NETWORK_DISABLED`
92+
- `POLICY_UNEXPECTED_FILE_READ`
93+
- `POLICY_ATTACK_LIKE_PRIVILEGE_BOUNDARY_PROBE`
94+
- `POLICY_DEGRADED_TRUST_LOCAL_ONLY`
95+
96+
The registry records:
97+
98+
- code
99+
- title
100+
- domain
101+
- result
102+
- semantic outcome
103+
- severity
104+
- risk
105+
- default summary
106+
- default why
107+
- default next action
108+
- allowed operation classes
109+
- allowed target classes
110+
111+
## Normalization rules
112+
113+
1. Look up `explanation_code` in the registry.
114+
2. Validate that the observed result matches registry result unless an override is explicitly allowed.
115+
3. Validate that operation class and target class match the registry entry.
116+
4. Set event `outcome` from registry semantic outcome unless caller provides a stricter compatible outcome.
117+
5. Set event `severity` from registry severity unless caller provides a stricter compatible severity.
118+
6. Fill operator narrative from registry defaults when caller does not provide specific narrative text.
119+
7. Preserve raw evidence as evidence references, not primary product surface.
120+
8. Attach to an existing root trace when parent/root IDs are provided.
121+
122+
## Compatibility rules
123+
124+
A caller may provide more specific summary/why/next-action text, but may not use a known explanation code with contradictory semantics.
125+
126+
Invalid examples:
127+
128+
- `POLICY_EXPECTED_METADATA_BOUNDARY` with `outcome=blocked_attack_like`.
129+
- `POLICY_ATTACK_LIKE_PRIVILEGE_BOUNDARY_PROBE` with `severity=notice`.
130+
- `POLICY_EXPECTED_NETWORK_DISABLED` with `result=allow`.
131+
132+
## Non-goals
133+
134+
The normalizer is not a replacement for the policy engine.
135+
It does not decide access by itself.
136+
It records and explains policy decisions already made by the responsible control surface.
137+
It must not hide attack-like events under benign explanation codes.
138+
It must not generate false errors for expected blocks.
139+
140+
## Acceptance criteria
141+
142+
- Known explanation codes validate against registry semantics.
143+
- Expected sandbox/capability denials render as `notice` + `blocked_expected`.
144+
- Network-disabled policy outcomes render as successful local-first safety, not failure.
145+
- Unexpected file reads render as `warning` unless explicitly attack-like.
146+
- Privilege-boundary probes render as `critical` + `blocked_attack_like`.
147+
- Generated policy events validate against the canonical event schema.
148+
- Invalid contradictory combinations fail validation.

0 commit comments

Comments
 (0)